#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/cpu architecture supports it
//#include <boost/multiprecision/float128.hpp>
//#define ERR_PREC boost::multiprecision::float128

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

#define MIN_STEP_EXP -5.2
#define NUM_SAMPLES 30

template <typename TIN, typename TERR>
void compare(std::vector<std::string> implNames, double step, unsigned nTests, unsigned nEvals, unsigned seed){
  using namespace func;
  LookupTableGenerator<TIN> gen({FUNC_SET_F(MyFunction,TIN)}, MIN_ARG, MAX_ARG);
  std::vector<std::unique_ptr<LookupTable<TIN>>> impls;
  for(auto name : implNames)
    impls.emplace_back(gen.generate_by_step(name, step));
  LookupTableComparator<TIN> implCompare(impls, MIN_ARG, MAX_ARG, nEvals, seed);
  implCompare.run_timings(nTests);
  implCompare.compute_statistics();
  implCompare.print_csv(std::cout, Sorter::MEAN);
}

/*
  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 != 4) {
      print_usage();
      exit(1);
  }

  int nTests = std::stoi(argv[1]);
  int nEvals = std::stoi(argv[2]);
  int seed   = std::stoi(argv[3]);

  std::vector<std::string> implNames {
    "UniformExactInterpTable<3>",
  };

  // print each implementation name in one long row
  std::cout << "Average time to compute map(L,X) for a random vector X of size " << nEvals << "\n";
  std::cout << "Step size |Time (s)" << "\n";
  for(auto name : implNames){
    std::cout << name << "_float ";
    std::cout << name << "_double ";
    std::cout << name << "_quad ";
    //std::cout << name << "_float128 ";
  }
  std::cout << "\n";

  using namespace boost::multiprecision;
  // step sizes are some fraction of the max reasonable step size
  arma::vec stepsizes = arma::logspace(MIN_STEP_EXP, log10((MAX_ARG-MIN_ARG)/2), NUM_SAMPLES);
  std::cerr << "plotting " << stepsizes.n_elem << " operator() times\n";
  for(double h : stepsizes){
    std::cout << h << " ";
    compare<float,double>(implNames,h,nTests,nEvals,seed);
    compare<double,cpp_bin_float_quad>(implNames,h,nTests,nEvals,seed);
    //compare<float128,cpp_bin_float_oct>(implNames,h,nTests,nEvals,seed);
    compare<cpp_bin_float_quad,cpp_bin_float_oct>(implNames,h,nTests,nEvals,seed);
    std::cout << std::endl;
  }
}
