#include <func/func.hpp>
#include "../sample_functions/gamma.hpp"

#include <iostream>
#include <armadillo>
#include <vector>
#include <memory>
#include <iostream>
#include <limits>

#include <boost/multiprecision/cpp_bin_float.hpp>
#define ERR_PREC boost::multiprecision::cpp_bin_float_quad

// float128 is faster than cpp_bin_float_quad if the user's compiler supports it
//#include <boost/multiprecision/float128.hpp>
//#define ERR_PREC boost::multiprecision::float128

#define TYPE double

void print_usage() {
  std::cout << "Usage:" << std::endl
	    << "    ./experiment <minPow10Bytes> <maxPow10Bytes> <nSteps> <nExperiments> <nEvals> <seed>"
	    << std::endl;
}

/*
  Uses the LookupTableGenerator class to compute errors for varying table step sizes
  Or, will plot a collection of LUTs against a direct evaluation
*/
int main(int argc, char* argv[]) {
  if (argc != 7) {
      print_usage();
      exit(1);
  }

  double minexp  = std::stod(argv[1]);
  double maxexp  = std::stod(argv[2]);
  int nstepsizes = std::stoi(argv[3]);
  int nTests = std::stoi(argv[4]);
  int nEvals = std::stoi(argv[5]);
  int seed   = std::stoi(argv[6]);

  using namespace func;
  std::vector<std::string> implNames {
    "UniformLinearRawInterpTable",
    "UniformExactInterpTable<1>",
    "NonUniformExactInterpTable<1>",
  };

  std::cout << "Average time to compute map(L,X) for a random vector X of size " << nEvals << "\n";
  LookupTableGenerator<TYPE,TYPE,ERR_PREC> gen({FUNC_SET_F(MyFunction,TYPE)},MIN_ARG,MAX_ARG);

  // print each implementation name in one long row
  std::cout << "Table size (B) | Time (s)" << "\n";
  for(auto name : implNames)
    std::cout << name << " ";
  std::cout << "\n";

  // step sizes are some fraction of the max reasonable step size
  arma::vec bytes = arma::logspace(minexp, maxexp, nstepsizes);
  std::cerr << "plotting " << bytes.n_elem << " operator() times\n";
  for(auto B : bytes){
    std::cout << B << " ";
    std::vector<std::unique_ptr<LookupTable<TYPE>>> impls;
    for(auto name : implNames)
      impls.emplace_back(gen.generate_by_impl_size(name, static_cast<unsigned int>(B)));
    LookupTableComparator<TYPE> implCompare(impls, MIN_ARG, MAX_ARG, nEvals, seed);
    implCompare.run_timings(nTests);
    implCompare.compute_statistics();
    implCompare.print_csv(std::cout, Sorter::MEAN); std::cout << "\n";
    std::cerr << "computed B=" << B << "\n";
  }
}
