#include <iostream>
#include <iomanip>
#include "Vvector_addition.h"
#include <verilated.h>
#include <verilated_vcd_c.h>

// encrypted data
int c_1[16] = {0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8,   // 0-7
			    0xffffffff, 0xfffffffe, 0xfffffffd, 0xfffffffc, 0xfffffffb, 0xfffffffa, 0xfffffff9, 0xfffffff8}; // 8-15

int c_2[16] = {0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9,   // 0-7
			    0x1, 0x2, 0x3, 0x4, 0xffffffff, 0xfffffffe, 0xfffffffd, 0xfffffffc}; // 8-15

// expected result
int c[16] = {0x3, 0x5, 0x7, 0x9, 0xb, 0xd, 0xf, 0x11,   // 0-7
			    0x0, 0x0, 0x0, 0x0, 0xfffffffa, 0xfffffff8, 0xfffffff6, 0xfffffff4}; // 8-15

int main(int argc, const char ** argv, const char ** env) {
  Verilated::commandArgs(argc, argv);

  // Treat the argument on the command-line as the place to start
  int width, length, ell;
  if (argc > 1 && argv[1][0] != '+') {
    width = atoi(argv[1]);
  } else {
    width = 16; // Default
  }

  Vvector_addition * dut = new Vvector_addition;  // Instantiate a new module

  // Enable dumping a VCD file

  Verilated::traceEverOn(true);
  VerilatedVcdC * tfp = new VerilatedVcdC;
  dut->trace(tfp, 99);
  tfp->open("vector_addition.vcd");

  dut->clk = 0;
  dut->start = 0;
  
  int time;
  std::cout << "width: " << width;
  std::cout << std::endl;
  int i = 0;
  int err = 0;
  int start_stamp = 100;
  int read_stamp = 120;
  
  for (time = 0 ; time < 10000 ; time += 10) {
    dut->clk = ((time % 20) >= 10) ? 1 : 0; // Simulate a 50 MHz clock
    if (time == 20) dut->reset = 1; // Handle "reset" on for four cycles
    if (time == 100) {
      dut->reset = 0;
      dut->start = 1;   // Put "start" on
    }

    // take inputs
    if (time >= start_stamp && time <= start_stamp + 20 && dut->clk == 1) {
        dut->c_1_1 = c_1[0];
        dut->c_1_2 = c_1[1];
        dut->c_1_3 = c_1[2];
        dut->c_1_4 = c_1[3];
        dut->c_1_5 = c_1[4];
        dut->c_1_6 = c_1[5];
        dut->c_1_7 = c_1[6];
        dut->c_1_8 = c_1[7];
        dut->c_1_9 = c_1[8];
        dut->c_1_10 = c_1[9];
        dut->c_1_11 = c_1[10];
        dut->c_1_12 = c_1[11];
        dut->c_1_13 = c_1[12];
        dut->c_1_14 = c_1[13];
        dut->c_1_15 = c_1[14];
        dut->c_1_16 = c_1[15];

        dut->c_2_1 = c_2[0];
        dut->c_2_2 = c_2[1];
        dut->c_2_3 = c_2[2];
        dut->c_2_4 = c_2[3];
        dut->c_2_5 = c_2[4];
        dut->c_2_6 = c_2[5];
        dut->c_2_7 = c_2[6];
        dut->c_2_8 = c_2[7];
        dut->c_2_9 = c_2[8];
        dut->c_2_10 = c_2[9];
        dut->c_2_11 = c_2[10];
        dut->c_2_12 = c_2[11]; 
        dut->c_2_13 = c_2[12];
        dut->c_2_14 = c_2[13];
        dut->c_2_15 = c_2[14];
        dut->c_2_16 = c_2[15];

        std::cout << "vector 1 input received: " << c_1[0];
        std::cout << std::endl;
        std::cout << "vector 1 input received: " << c_1[1];
        std::cout << std::endl;
        std::cout << "vector 1 input received: " << c_1[2];
        std::cout << std::endl;
        std::cout << "vector 1 input received: " << c_1[3];
        std::cout << std::endl;
        std::cout << "vector 1 input received: " << c_1[4];
        std::cout << std::endl;
        std::cout << "vector 1 input received: " << c_1[5];
        std::cout << std::endl;
        std::cout << "vector 1 input received: " << c_1[6];
        std::cout << std::endl;
        std::cout << "vector 1 input received: " << c_1[7];
        std::cout << std::endl;
        std::cout << "vector 1 input received: " << c_1[8];
        std::cout << std::endl;
        std::cout << "vector 1 input received: " << c_1[9];
        std::cout << std::endl;
        std::cout << "vector 1 input received: " << c_1[10];
        std::cout << std::endl;
        std::cout << "vector 1 input received: " << c_1[11];
        std::cout << std::endl;
        std::cout << "vector 1 input received: " << c_1[12];
        std::cout << std::endl;
        std::cout << "vector 1 input received: " << c_1[13];
        std::cout << std::endl;
        std::cout << "vector 1 input received: " << c_1[14];
        std::cout << std::endl;
        std::cout << "vector 1 input received: " << c_1[15];
        std::cout << std::endl;

        std::cout << "vector 2 input received: " << c_2[0];
        std::cout << std::endl;
        std::cout << "vector 2 input received: " << c_2[1];
        std::cout << std::endl;
        std::cout << "vector 2 input received: " << c_2[2];
        std::cout << std::endl;
        std::cout << "vector 2 input received: " << c_2[3];
        std::cout << std::endl;
        std::cout << "vector 2 input received: " << c_2[4];
        std::cout << std::endl;
        std::cout << "vector 2 input received: " << c_2[5];
        std::cout << std::endl;
        std::cout << "vector 2 input received: " << c_2[6];
        std::cout << std::endl;
        std::cout << "vector 2 input received: " << c_2[7];
        std::cout << std::endl;
        std::cout << "vector 2 input received: " << c_2[8];
        std::cout << std::endl;
        std::cout << "vector 2 input received: " << c_2[9];
        std::cout << std::endl;
        std::cout << "vector 2 input received: " << c_2[10];
        std::cout << std::endl;
        std::cout << "vector 2 input received: " << c_2[11];
        std::cout << std::endl;
        std::cout << "vector 2 input received: " << c_2[12];
        std::cout << std::endl;
        std::cout << "vector 2 input received: " << c_2[13];
        std::cout << std::endl;
        std::cout << "vector 2 input received: " << c_2[14];
        std::cout << std::endl;
        std::cout << "vector 2 input received: " << c_2[15];
        std::cout << std::endl;
    }

    dut->eval();     // Run the simulation for a cycle
    tfp->dump(time); // Write the VCD file for this cycle

    // compare outputs for (length) rows
    // for each row, compare outputs for (width * ell) cycles
    if (time >= read_stamp && dut->clk == 1) {
        std::cout << "vector output received: " << (int) dut->c_1;
        std::cout << std::endl;
        if (dut->c_1 == c[0])
            std::cout << " OK";
        else {
            std::cout << " INCORRECT expected " << std::setfill('0') << (int) c[0];
            err += 1;
        }
        std::cout << std::endl;

        std::cout << "vector output received: " << (int) dut->c_2;
        std::cout << std::endl;
        if (dut->c_2 == c[1])
            std::cout << " OK";
        else {
            std::cout << " INCORRECT expected " << std::setfill('0') << (int) c[1];
            err += 1;
        }
        std::cout << std::endl;

        std::cout << "vector output received: " << (int) dut->c_3;
        std::cout << std::endl;
        if (dut->c_3 == c[2])
            std::cout << " OK";
        else {
            std::cout << " INCORRECT expected " << std::setfill('0') << (int) c[2];
            err += 1;
        }
        std::cout << std::endl;

        std::cout << "vector output received: " << (int) dut->c_4;
        std::cout << std::endl;
        if (dut->c_4 == c[3])
            std::cout << " OK";
        else {
            std::cout << " INCORRECT expected " << std::setfill('0') << (int) c[3];
            err += 1;
        }
        std::cout << std::endl;

        std::cout << "vector output received: " << (int) dut->c_5;
        std::cout << std::endl;
        if (dut->c_5 == c[4])
            std::cout << " OK";
        else {
            std::cout << " INCORRECT expected " << std::setfill('0') << (int) c[4];
            err += 1;
        }
        std::cout << std::endl;

        std::cout << "vector output received: " << (int) dut->c_6;
        std::cout << std::endl;
        if (dut->c_6 == c[5])
            std::cout << " OK";
        else {
            std::cout << " INCORRECT expected " << std::setfill('0') << (int) c[5];
            err += 1;
        }
        std::cout << std::endl;

        std::cout << "vector output received: " << (int) dut->c_7;
        std::cout << std::endl;
        if (dut->c_7 == c[6])
            std::cout << " OK";
        else {
            std::cout << " INCORRECT expected " << std::setfill('0') << (int) c[6];
            err += 1;
        }
        std::cout << std::endl;

        std::cout << "vector output received: " << (int) dut->c_8;
        std::cout << std::endl;
        if (dut->c_8 == c[7])
            std::cout << " OK";
        else {
            std::cout << " INCORRECT expected " << std::setfill('0') << (int) c[7];
            err += 1;
        }
        std::cout << std::endl;

        std::cout << "vector output received: " << (int) dut->c_9;
        std::cout << std::endl;
        if (dut->c_9 == c[8])
            std::cout << " OK";
        else {
            std::cout << " INCORRECT expected " << std::setfill('0') << (int) c[8];
            err += 1;
        }
        std::cout << std::endl;

        std::cout << "vector output received: " << (int) dut->c_10;
        std::cout << std::endl;
        if (dut->c_10 == c[9])
            std::cout << " OK";
        else {
            std::cout << " INCORRECT expected " << std::setfill('0') << (int) c[9];
            err += 1;
        }
        std::cout << std::endl;

        std::cout << "vector output received: " << (int) dut->c_11;
        std::cout << std::endl;
        if (dut->c_11 == c[10])
            std::cout << " OK";
        else {
            std::cout << " INCORRECT expected " << std::setfill('0') << (int) c[10];
            err += 1;
        }
        std::cout << std::endl;

        std::cout << "vector output received: " << (int) dut->c_12;
        std::cout << std::endl;
        if (dut->c_12 == c[11])
            std::cout << " OK";
        else {
            std::cout << " INCORRECT expected " << std::setfill('0') << (int) c[11];
            err += 1;
        }
        std::cout << std::endl;

        std::cout << "vector output received: " << (int) dut->c_13;
        std::cout << std::endl;
        if (dut->c_13 == c[12])
            std::cout << " OK";
        else {
            std::cout << " INCORRECT expected " << std::setfill('0') << (int) c[12];
            err += 1;
        }
        std::cout << std::endl;

        std::cout << "vector output received: " << (int) dut->c_14;
        std::cout << std::endl;
        if (dut->c_14 == c[13])
            std::cout << " OK";
        else {
            std::cout << " INCORRECT expected " << std::setfill('0') << (int) c[13];
            err += 1;
        }
        std::cout << std::endl;

        std::cout << "vector output received: " << (int) dut->c_15;
        std::cout << std::endl;
        if (dut->c_15 == c[14])
            std::cout << " OK";
        else {
            std::cout << " INCORRECT expected " << std::setfill('0') << (int) c[14];
            err += 1;
        }
        std::cout << std::endl;

        std::cout << "vector output received: " << (int) dut->c_16;
        std::cout << std::endl;
        if (dut->c_16 == c[15])
            std::cout << " OK";
        else {
            std::cout << " INCORRECT expected " << std::setfill('0') << (int) c[15];
            err += 1;
        }
        std::cout << std::endl;

        
        dut->start = 0;

        break;
    }

  }

  tfp->close(); // Stop dumping the VCD file
  delete tfp;

  dut->final(); // Stop the simulation
  delete dut;

  return 0;
}