/* test_blackbox.cc
 * Daniel S. Roche, January 2011
 * See COPYING.txt for permissions
 *
 * Simple tests for the blackbox classes
 */

#include <iostream>
#include <complex>
#include <fstream>
#include <vector>
#include <NTL/ZZ.h>
#include "misc.h"
#include "sparsepoly.h"
#include "blackbox.h"

using namespace NTL;
using namespace std;

typedef double Ftype;

int main (void) {
  cout << "********************************************************************************" << endl
       << "                             BLACKBOX TEST" << endl
       << "********************************************************************************" << endl;
  randseed();

  cout.precision(4);

  RandomGen<Ftype> rg (1.0, 5.0);
  Ftype epsilon = 0.0000001;
  vector< complex<Ftype> > df;

  SparsePoly<Ftype> sf;
  random(sf, to_ZZ(1000000), 5, rg);

  cout << sf << endl;

  FakeApproxBB<Ftype> bb (sf, epsilon);

  // Test ScaleBB
  Ftype scale = rg.randReal();
  ZZ denom = RandomBits_ZZ(10);
  ZZ num = RandomBnd(denom);
  ScaleBB< FakeApproxBB<Ftype> > scbb(bb,scale);
  complex<Ftype> u = bb.eval(num,denom)*scale;
  complex<Ftype> v = scbb.eval(num,denom);
  if (abs(u-v) > epsilon*abs(u)*2.0) {
    cout << "FAIL" << endl;
    return 1;
  }

  // Test SpinBB
  ZZ num2 = RandomBnd(denom);
  SpinBB< FakeApproxBB<Ftype> > spbb(bb,num2,denom);
  u = bb.eval(num+num2,denom);
  v = spbb.eval(num,denom);
  if (abs(u-v) > epsilon*abs(u)*4.0) {
    cout << "FAIL" << endl;
    return 2;
  }

  // Test ApproxModBB
  ApproxModBB< Ftype,FakeApproxBB<Ftype> > modbb (bb);

  long p = 11;
  
  modbb.eval (df, p);

  cout << df << endl;

  vector< complex<Ftype> > expected(p);
  for (SparsePoly<Ftype>::RepT::iterator iter = sf.rep.begin();
       iter != sf.rep.end(); ++iter)
  {
    expected[rem(iter->second,p)] += iter->first;
  }

  cout << expected << endl;

  double nf = norm(sf);
  cout << "scaled diff abs vals:";
  for (size_t i=0; i<expected.size(); ++i)
    cout << ' ' << abs(expected[i]-df[i])/nf;
  cout << endl;

  if (!equal(df,expected,epsilon*nf)) {
    cout << "FAIL" << endl;
    return 3;
  }

  cout << "SUCCESS" << endl;
  return 0;
}
