/* blackbox.h
 * Daniel S. Roche, January 2011
 * See COPYING.txt for permissions.
 *
 * Approximate black box for complex polynomial evaluation
 * See "Diversification improves interpolation", Giesbrecht & Roche, 2011
 *
 * Header file
 */

#ifndef BLACKBOX_H
#define BLACKBOX_H

#include <complex>
#include <vector>
#include <NTL/ZZ.h>
#include "sparsepoly.h"

/* Base class for an approximate roots-of-unity black box.
   Represents evaluating an unknown polynomial at an exact root of unity.
   Each evaluation is performed +/- the given precision bound.
 */
template <typename T>
class ApproxBB {
  public:
    typedef T FloatT;
    typedef std::complex<T> BaseT;

    // The precision of this black box
    const T epsilon;

    ApproxBB (T eps) :epsilon(eps) { }

    // Returns f(exp(2*Pi*num/denom))
    virtual BaseT eval (const NTL::ZZ& num, const NTL::ZZ& denom) = 0;
};

/* Scale an approximate black box.
 * Given scaling factor c, computes f(x)*c for any x
 */
template <typename BB>
class ScaleBB : public ApproxBB<typename BB::FloatT> {
  public:
  typedef typename BB::FloatT FloatT;
  typedef typename BB::BaseT BaseT;

  private:
  BB& bb;
  FloatT scale;

  public:
  ScaleBB (BB& thebb, FloatT c) 
    :ApproxBB<typename BB::FloatT>::ApproxBB(thebb.epsilon), 
     bb(thebb), scale(c) { }

  BaseT eval (const NTL::ZZ& num, const NTL::ZZ& denom) {
    return bb.eval(num,denom) * scale;
  }
};

/* Spin an approximate black box.
 * Given an exact root of unity w, given as wnum and wdenom
 * such that w = exp(e^(2*pi*wnum/wdenom),
 * computes f(w x) for any x.
 * Note that wnum and wdenom can be changed after some time.
 */
template <typename BB>
class SpinBB : public ApproxBB<typename BB::FloatT> {
  public:
  typedef typename BB::FloatT FloatT;
  typedef typename BB::BaseT BaseT;

  private:
  BB& bb;

  public:
  NTL::ZZ wnum;
  NTL::ZZ wdenom;

  SpinBB (BB& thebb, const NTL::ZZ& thewnum = NTL::to_ZZ(0), 
          const NTL::ZZ& thewdenom = NTL::to_ZZ(1))
    :ApproxBB<typename BB::FloatT>::ApproxBB(thebb.epsilon),
     bb(thebb), wnum(thewnum), wdenom(thewdenom) { }

  BaseT eval (const NTL::ZZ& num, const NTL::ZZ& denom);
};

/* Class to evaluate a polynomial given by
 * an approximate roots-of-unity black box
 * modulo x^p-1 for chosen p's.
 * This is accomplished by evaluating at p'th roots of
 * unity and then interpolating using FFTW.
 * BB should be a subclass of ApproxBB<T>.
 */
template <typename T, typename BB>
class ApproxModBB {
  private:
  BB& bb;

  public:
  ApproxModBB (BB& thebb);

  /* out will store the coefficients of the unknown polynomial f
   * modulo x^p - 1.  */
  void eval (std::vector< std::complex<T> >& out, long p);
};

/* Fake approximate roots-of-unity black box.
 * Constructed with a reference to a SparsePoly
 */
template <typename T>
class FakeApproxBB : public ApproxBB<T> {
  private:
    const SparsePoly<T> f;
  public:
    typedef typename ApproxBB<T>::FloatT FloatT;
    typedef typename ApproxBB<T>::BaseT BaseT;

    FakeApproxBB (const SparsePoly<T>& thef, T eps)
      :ApproxBB<T>(eps), f(thef) { }

    // Returns f(exp(2*Pi*num/denom))
    BaseT eval (const NTL::ZZ& num, const NTL::ZZ& denom);
};

#include "blackbox.inc"

#endif // BLACKBOX_H
