/* sparsepoly.h
 * Daniel S. Roche, January 2011
 * See COPYING.txt for permissions.
 *
 * Approximate sparse polynomials over complex numbers.
 * (Note: for our purposes, all polynomials are univariate.)
 * NTL is used for exponent arithmetic only.
 *
 * Header file
 */

#ifndef SPARSEPOLY_H
#define SPARSEPOLY_H

#include <ostream>
#include <vector>
#include <complex>
#include <utility>
#include <NTL/ZZ.h>

/* Compute the e-sparsity of a vector.
 * This is the number of entries with norm at least e
 */
template <typename T>
inline size_t sparsity 
(const std::vector< std::complex<T> >& v, T e) {
  long toRet = 0;
  for (size_t i=0; i<v.size(); ++i)
    if (abs(v[i]) >= e) ++toRet;
  return toRet;
}

/* Test whether two complex vectors are e-equal */
template <typename T>
bool equal (const std::vector< std::complex<T> >& a,
            const std::vector< std::complex<T> >& b, T e);

/* A sparse complex polynomial is parameterized over the coefficient type 
 * complex<Base>
 * and is represented by an STL vector of STL pairs of
 * nonzero coefficient and exponent, sorted by increasing exponents. 
 * The exponent type is NTL::ZZ.
 *
 * The functionality is very limited, just to what is necessary for
 * our purposes here. The internal storage is exposed for further
 * functionality. I am imitating NTL's style of function calls even
 * though I don't really like it. The exception is the use of templates.
 */ 
template <typename T>
class SparsePoly {
  public:
    typedef std::complex<T> CoeffT;
    typedef std::pair<CoeffT,NTL::ZZ> TermT;
    typedef std::vector<TermT> RepT;
    RepT rep;
};

// Degree of a SparsePoly
template <typename T>
inline void deg (NTL::ZZ &out, const SparsePoly<T>& f) {
  if (f.rep.empty()) conv(out,-1);
  else out = f.rep.back().second;
}

// SparsePoly comparison
// Returns true if all coefficients are either both within e of 0,
// or within e of each other.
template <typename T>
bool equal (const SparsePoly<T> &a, const SparsePoly<T> &b, T e);

// SparsePoly 2-norm
template <typename T>
T norm (const SparsePoly<T>& a);

// Difference 2-norm. Necessary because there is no subtraction operation
template <typename T>
T diffnorm (const SparsePoly<T>& a, const SparsePoly<T>& b);

// Convert between polynomials with different floating point types
template <typename T, typename U>
void conv (SparsePoly<T>& a, const SparsePoly<U>& b);

template <typename T, typename U>
void conv 
(std::vector< std::complex<T> >& a, const std::vector< std::complex<U> >& b);

// Conversion from dense vector to SparsePoly
template <typename T>
void conv (SparsePoly<T>& a, const std::vector< std::complex<T> > &b, T e);

/* Conversion from SparsePoly to dense vector.
 * All the exponents must fit into long's of course.
 */
template <typename T>
void conv (std::vector< std::complex<T> > &a, const SparsePoly<T> &b);

// Random array of complex<T> with deg = d-1
template <typename T, typename G>
void random (std::vector< std::complex<T> >& f, size_t d, G randgen);

// Random SparsePoly with deg < d and sparsity <= t
// randgen() returns random elements of complex<T>
template <typename T, typename G>
void random (SparsePoly<T>& f, const NTL::ZZ& d, long t, G randgen);

// Output a vector of complexes
template <typename T>
std::ostream& operator<< 
(std::ostream& out, const std::vector< std::complex<T> >& f);

// Output a SparsePoly
template <typename T>
std::ostream& operator<< (std::ostream &out, const SparsePoly<T> &f);

#include "sparsepoly.inc"

#endif // SPARSEPOLY_H
