/* benchmark.cc
 * Daniel S. Roche, January 2011
 * See COPYING.txt for permissions
 *
 * Benchmarking for approximate sparse interpolation routines.
 * This records the stability of the algorithm on random examples.
 */

#include <iostream>
#include <utility>
#include <vector>
#include <algorithm>
#include <NTL/ZZ.h>
#include "newinterp.h"

using namespace std;
using namespace NTL;

typedef double Ftype;

int main(int argc, char** argv) {
  if (argc != 5) {
    cout << "Usage: " << argv[0] << " degree-bound sparsity-bound trials precision"
         << endl
         << "\tdegree-bound is given as log_2 D" << endl
         << "\tprecision is given as -log[10](epsilon)" << endl;
    return 1;
  }

  long logD = atol(argv[1]);
  ZZ D;
  conv (D,1);
  D <<= logD;
  long T = atol(argv[2]);
  long numTrials = atol(argv[3]);
  long logE = atol(argv[4]);
  Ftype epsilon = manbits<Ftype>() + pow(((Ftype)10.0),-logE);

  randseed();
  zz_p::init(65521);

  vector< pair< SparsePoly<Ftype>, SparsePoly<Ftype> > > tests (numTrials);
  RandomGen<Ftype> rg (0.0,1.0);
  vector< pair< SparsePoly<Ftype>, SparsePoly<Ftype> > >::iterator iter;
  for (iter = tests.begin(); iter != tests.end(); ++iter) {
    random(iter->first, D, T, rg);
  }

  vector<Ftype> errors;
  long nfail = 0;

  for (iter = tests.begin(); iter != tests.end(); ++iter) {
    FakeApproxBB<Ftype> bb(iter->first, epsilon);
    if (!new_interpA (iter->second, bb, D, T)) {
      ++nfail;
      continue;
    }
    if (iter->first.rep.size() != iter->second.rep.size()) {
      ++nfail;
      continue;
    }
    for (size_t i=0; i < iter->first.rep.size(); ++i) {
      if (iter->first.rep[i].second != iter->second.rep[i].second) {
        // polynomials have different support
        ++nfail;
        continue;
      }
    }
    Ftype realnorm = norm(iter->first);
    errors.push_back (diffnorm(iter->first,iter->second)/realnorm);
  }

  cout << nfail << " failures" << endl;
  if (errors.size() == 0) return 2;

  sort (errors.begin(), errors.end());
  Ftype min = errors.front();
  Ftype max = errors.back();
  Ftype median = errors[errors.size()/2];
  Ftype mean = 0.0;
  for (vector<Ftype>::iterator eiter = errors.begin(); eiter != errors.end(); ++eiter) mean += *eiter;
  mean /= errors.size();

  cout << "eps:\t" << epsilon << endl;
  cout << "min:\t" << min << endl;
  cout << "max:\t" << max << endl;
  cout << "median:\t" << median << endl;
  cout << "mean:\t" << mean << endl;

  return 0;
}
