#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

#define MIN_EXP 3
#define MAX_EXP 7
#define NUM_STEPS 200

/*
  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[]) {
  using namespace func;

  if(argc != 2){
    std::cout << "usage:\n";
    std::cout << "./a.out <filename>" << std::endl;
    return 1;
  }
  std::ifstream implNameFile(argv[1]);

  std::string name; std::vector<std::string> implNames;
  while(getline(implNameFile,name))
    implNames.push_back(name);
  implNameFile.close();


  std::cout << FUNCNAME << " over [" << MIN_ARG << ", " << MAX_ARG << "]\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) |Error" << "\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(MIN_EXP, MAX_EXP, NUM_STEPS);
  std::cerr << "plotting " << bytes.n_elem << " errors\n";
  for(auto B : bytes){
    std::cout << B << " ";
    for(auto name : implNames){
      auto h = (gen.generate_by_impl_size(name, (unsigned int) B))->step_size();
      std::cout << gen.error_at_step_size(name, h, 1.0) << " ";
    }
    std::cout << "\n";
    std::cerr << "computed B=" << B << "\n";
  }
}
