/* sparsepoly.inc
 * Daniel S. Roche, January 2011
 * See COPYING.txt for permissions.
 *
 * Some utilities for dealing with sparse polynomials in NTL.
 * (Note: for our purposes, all polynomials are univariate.)
 *
 * Include file (template implementations)
 */

#include <cassert>
#include <complex>
#include <set>

/* 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)
{
  size_t i;
  for (i=0; i<a.size() && i<b.size(); ++i)
    if (abs(a[i]-b[i]) >= e) return false;
  for (; i<a.size(); ++i)
    if (abs(a[i]) >= e) return false;
  for (; i<b.size(); ++i)
    if (abs(b[i]) >= e) return false;
  return true;
}

// 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)
{
  typename SparsePoly<T>::RepT::const_iterator aiter = a.rep.begin();
  typename SparsePoly<T>::RepT::const_iterator biter = b.rep.begin();
  while (aiter != a.rep.end() && biter != b.rep.end()) {
    if (aiter->second < biter->second) {
      if (abs(aiter->first) >= e) return false;
      ++aiter;
    }
    else if (biter->second < aiter->second) {
      if (abs(biter->first) >= e) return false;
      ++biter;
    }
    else {
      if (abs(aiter->first) < e) {
        if (abs(biter->first) >= e) return false;
      }
      else if (abs(biter->first) < e) return false;
      else if (abs(aiter->first - biter->first) >= e) return false;
      ++aiter;
      ++biter;
    }
  }
  return true;
}

// SparsePoly 2-norm
template <typename T>
T norm (const SparsePoly<T>& a) {
  T runningsum = 0.0;
  typename SparsePoly<T>::CoeffT temp;
  for (typename SparsePoly<T>::RepT::const_iterator iter = a.rep.begin();
       iter != a.rep.end(); ++iter)
  {
    temp = iter->first;
    temp *= temp;
    runningsum += std::abs(temp);
  }
  return std::sqrt(runningsum);
}

// Difference 2-norm. Necessary because there is no subtraction operation
template <typename T>
T diffnorm (const SparsePoly<T>& a, const SparsePoly<T>& b) {
  T runningsum = 0.0;
  typename SparsePoly<T>::CoeffT temp;
  typename SparsePoly<T>::RepT::const_iterator aiter = a.rep.begin();
  typename SparsePoly<T>::RepT::const_iterator biter = b.rep.begin();
  while (aiter != a.rep.end() && biter != b.rep.end()) {
    if (aiter->second == biter->second)
      temp = aiter->first - biter->first;
    else if (aiter->second < biter->second)
      temp = aiter->first;
    else temp = biter->first;
    temp *= temp;
    runningsum += std::abs(temp);
    ++aiter;
    ++biter;
  }
  while (aiter != a.rep.end()) {
    temp = aiter->first;
    temp *= temp;
    runningsum += std::abs(temp);
    ++aiter;
  }
  while (biter != b.rep.end()) {
    temp = aiter->first;
    temp *= temp;
    runningsum += std::abs(temp);
    ++biter;
  }
  return std::sqrt(runningsum);
}

// Convert between polynomials with different floating point types
template <typename T, typename U>
void conv (SparsePoly<T>& a, const SparsePoly<U>& b) {
  a.rep.resize(b.rep.size());
  typename SparsePoly<T>::RepT::iterator aiter = a.rep.begin();
  typename SparsePoly<U>::RepT::const_iterator biter = b.rep.begin();
  while (a != a.rep.end()) {
    aiter->first = biter->first;
    NTL::conv (aiter->second, biter->second);
  }
}

template <typename T, typename U>
void conv 
(std::vector< std::complex<T> >& a, const std::vector< std::complex<U> >& b) {
  a.resize(b.size());
  for (size_t i=0; i<a.size(); ++i) a[i] = b[i];
}


// Conversion from a dense vector to SparsePoly
template <typename T>
void conv (SparsePoly<T>& a, const std::vector< std::complex<T> >& b, T e) {
  a.rep.clear();
  for (size_t i = 0; i < b.size(); ++i) {
    if (abs(b[i]) >= e) {
      a.rep.resize(a.rep.size()+1);
      a.rep.back().first = b[i];
      conv(a.rep.back().second, i);
    }
  }
}

/* Conversion from SparsePoly to complex vector */
template <typename T>
void conv (std::vector< std::complex<T> >& a, const SparsePoly<T>& b) {
  if (b.rep.empty()) {
    a.clear();
    return;
  }
  a.clear();
  a.resize (NTL::to_long(b.rep.back().second)+1);
  for (typename SparsePoly<T>::RepT::const_iterator
       iter = b.rep.begin();
       iter != b.rep.end();
       ++iter)
    a[NTL::to_long(iter->second)] = iter->first;
}

// 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) {
  f.resize(d);
  for (size_t i=0; i<d; ++i) f[i] = randgen();
}

// Random SparsePoly with deg < d and sparsity <= t
template <typename T, typename G>
void random (SparsePoly<T>& f, const NTL::ZZ& d, long t, G randgen) {
  // Use STL set to build unique, sorted list of exponents
  std::set<NTL::ZZ> exset;
  for (long i=0; i<t; ++i)
    exset.insert(NTL::RandomBnd(d));

  f.rep.clear();
  for (std::set<NTL::ZZ>::iterator iter = exset.begin();
       iter != exset.end(); ++iter)
  {
    f.rep.resize(f.rep.size()+1);
    f.rep.back().first = randgen();
    f.rep.back().second = *iter;
  }
}

// Output a vector of complexes
template <typename T>
std::ostream& operator<< 
(std::ostream& out, const std::vector< std::complex<T> >& f) {
  out << "[ ";
  for (size_t i=0; i<f.size(); ++i) {
    out << f[i] << ' ';
  }
  return out << ']';
}

// Output a SparsePoly
template <typename T>
std::ostream& operator<< (std::ostream& out, const SparsePoly<T>& f)
{
  out << "[ ";
  for (typename SparsePoly<T>::RepT::const_iterator
       iter = f.rep.begin();
       iter != f.rep.end();
       ++iter)
    out << '(' << iter->first << ", " << iter->second << ") ";
  return out << ']';
}
