// This is core/vnl/io/tests/golden_test_vnl_io.cxx

//:
// \file
// \brief Read in a golden data file, and check the values are correct.
//
// If you need to recreate the golden data file, run this test with
// the single parameter "create":
// \verbatim
//    golden_test_vnl_io create
// \endverbatim

#include <string>
#include <iostream>
#include "vsl/vsl_binary_io.h"
#include "vnl/vnl_vector_fixed.h"
#include <vnl/io/vnl_io_vector.h>
#include <vnl/io/vnl_io_vector_fixed.h>
#include "vnl/vnl_matrix_fixed.h"
#include <vnl/io/vnl_io_matrix.h>
#include <vnl/io/vnl_io_matrix_fixed.h>
#include <vnl/io/vnl_io_diag_matrix.h>
#include <vnl/io/vnl_io_real_npolynomial.h>
#include <vnl/io/vnl_io_real_polynomial.h>
#include <vnl/io/vnl_io_sparse_matrix.h>
#include "testlib/testlib_root_dir.h"
#include "testlib/testlib_test.h"

static void
golden_test_vnl_io(bool save_file)
{
  std::cout << "***********************************************************\n"
            << " Testing a golden data file for cross platform consistency\n"
            << "***********************************************************\n";


  //-----------------------------------------------------------------------------------
  // Create objects:
  // If the "create" flag was used on the command line, then the program saves an example
  // of each class
  // Otherwise it just fills them with values for comparison to the values read in.
  //------------------------------------------------------------------------------------

  // vnl_vector
  constexpr int n_vec = 50;
  vnl_vector<double> v_out(n_vec), v_in;

  for (int i = 0; i < n_vec; i++)
  {
    v_out(i) = (double)(i * i);
  }

  // vnl_matrix
  constexpr int m_mat = 10;
  constexpr int n_mat = 6;
  vnl_matrix<double> m_out(m_mat, n_mat), m_in;

  for (int i = 0; i < m_mat; i++)
  {
    for (int j = 0; j < n_mat; j++)
    {
      m_out(i, j) = (double)(i * j + i);
    }
  }

  // vnl_diag_matrix
  vnl_diag_matrix<double> diag_mat_out(v_out), diag_mat_in;

  // vnl_matrix_fixed
  double datablock[4] = { 1.1, 1.2, 2.1, 2.2 };
  vnl_matrix_fixed<double, 2, 2> m_fixed_out(datablock), m_fixed_in;

  // vnl_real_n_polynomial
  vnl_vector<double> coeffs(4);
  vnl_matrix<unsigned int> exponents(4, 2);

  coeffs(0) = 0.1;
  coeffs(1) = 0.2;
  coeffs(2) = 0.3;
  coeffs(3) = 0.5;

  exponents(0, 0) = 1;
  exponents(1, 0) = 2;
  exponents(2, 0) = 3;
  exponents(3, 0) = 4;
  exponents(0, 1) = 5;
  exponents(1, 1) = 6;
  exponents(2, 1) = 7;
  exponents(3, 1) = 8;


  vnl_real_npolynomial polyn_out(coeffs, exponents), polyn_in;

  // vnl_real_polynomial
  vnl_real_polynomial poly_out(v_out), poly_in(0);

  // vnl_sparse_matrix
  vnl_sparse_matrix<double> m_sparse_out(3, 3), m_sparse_in(3, 3);
  std::vector<int> col_1(3);
  std::vector<int> col_2(2);
  std::vector<int> col_3(1);

  col_1[0] = 1;
  col_1[1] = 2;
  col_1[2] = 3;
  col_2[0] = 1;
  col_2[1] = 3;
  col_3[0] = 2;

  std::vector<double> val_1(3);
  std::vector<double> val_2(2);
  std::vector<double> val_3(1);

  val_1[0] = 1.1;
  val_1[1] = 1.2;
  val_1[2] = 1.3;
  val_2[0] = 2.1;
  val_2[1] = 2.3;
  val_3[0] = 3.2;

  m_sparse_out.set_row(0, col_1, val_1);
  m_sparse_out.set_row(1, col_2, val_2);
  m_sparse_out.set_row(2, col_3, val_3);


  // vnl_vector_fixed
  vnl_vector_fixed<double, 3> v_fixed_out(1.2, 3.4, 5.6), v_fixed_in;

  // Save if option set
  if (save_file)
  {
    vsl_b_ofstream bfs_out("golden_test_vnl_io.bvl");
    TEST("Opened golden_test_vnl_io.bvl for writing ", !bfs_out, false);
    if (!bfs_out)
    {
      std::cerr << "Problems opening file for output - exiting\n";
      std::exit(1);
    }
    vsl_b_write(bfs_out, v_out);
    vsl_b_write(bfs_out, m_out);
    vsl_b_write(bfs_out, diag_mat_out);
    vsl_b_write(bfs_out, m_fixed_out);
    vsl_b_write(bfs_out, polyn_out);
    vsl_b_write(bfs_out, poly_out);
    vsl_b_write(bfs_out, m_sparse_out);
    vsl_b_write(bfs_out, v_fixed_out);
    bfs_out.close();
  }

  // Read in file to each class in turn
  std::string gold_path = testlib_root_dir() + "/core/vnl/io/tests/golden_test_vnl_io.bvl";
  vsl_b_ifstream bfs_in(gold_path.c_str());
  TEST("Opened golden_test_vnl_io.bvl for reading ", !bfs_in, false);
  vsl_b_read(bfs_in, v_in);
  vsl_b_read(bfs_in, m_in);
  vsl_b_read(bfs_in, diag_mat_in);
  vsl_b_read(bfs_in, m_fixed_in);
  vsl_b_read(bfs_in, polyn_in);
  vsl_b_read(bfs_in, poly_in);
  vsl_b_read(bfs_in, m_sparse_in);
  vsl_b_read(bfs_in, v_fixed_in);
  TEST("Finished reading file successfully", (!bfs_in), false);
  bfs_in.close();


  // Test that each object created is the same as read in from the file.
  TEST("v_out == v_in", v_out == v_in, true);
  TEST("m_out == m_in", m_out == m_in, true);
  TEST("diag_mat_out == diag_mat_in", diag_mat_out.diagonal() == diag_mat_in.diagonal(), true);
  TEST("m_fixed_out == m_fixed_in", m_fixed_out == m_fixed_in, true);
  TEST("polyn_out == polyn_in", poly_out.coefficients() == poly_in.coefficients(), true);
  TEST("poly_out == poly_in", poly_out.coefficients() == poly_in.coefficients(), true);


  // Code to compare sparse matrices
  m_sparse_out.reset();
  m_sparse_in.reset();
  bool test_result = true;

  while (m_sparse_out.next() && m_sparse_in.next())
  {
    if (m_sparse_out.getrow() != m_sparse_in.getrow() || m_sparse_out.getcolumn() != m_sparse_in.getcolumn() ||
        m_sparse_out.value() != m_sparse_in.value())
    {
      test_result = false;
      break;
    }
  }

  TEST("m_sparse_out == m_sparse_in", test_result, true);
  TEST("v_fixed_out == v_fixed_in", v_fixed_out == v_fixed_in, true);
}


static void
golden_test_vnl_io(int argc, char * argv[])
{
  golden_test_vnl_io(argc == 2 && std::string(argv[1]) == std::string("create"));
}

TESTMAIN_ARGS(golden_test_vnl_io);
