#include "CoCoA/library.H"
using namespace CoCoA;
using namespace std;

void GBasisExample(const ring& R)
{
  const PolyRing P = NewPolyRing(R, 3); //create R[x[0],x[1],x[2]]
  const vector<RingElem>& x = indets(P);

  GlobalOutput() << "-- Coefficient ring is " << R << endl;

  // Fill Igens with the ideal generators.
  vector<RingElem> Igens(2, zero(P));
  Igens[0] = power(x[0],391) - power(x[2],390) * (x[0]-x[2])/23;
  Igens[1] = power(x[0],3)/57 - 9001*power(x[0],2)*x[2] + power(x[1],3);

  ideal I = ideal(P, Igens); // Create ideal I from Igens.

  vector<RingElem> GBasis = TidyGens(I); // might throw an exception

  GlobalOutput() << "There are " << GBasis.size() << " elements in the GBasis" << endl;
  GlobalOutput() << "The first one is " << GBasis[0] << endl;
  GlobalOutput() << "The last one is " ;
  RingElem f = GBasis.back();
  int j=0;
  // print only the first 6 terms
  for (SparsePolyIter it=BeginIter(f); !IsEnded(it); ++it,++j)
  {
    if (j==6)
    {
      GlobalOutput() << " + (.." << NumTerms(f)-6 << " more terms..)";
      break;
    }
    GlobalOutput() << " + (" << coeff(it) << ")*" << PP(it);
  }
  GlobalOutput() << endl;
  GlobalOutput() << "===================================" << endl;
}


int program()
{
  RingZ Z = NewRingZ(); // Use type RingZ so that the call to NewZmod works.
  ZZ BigPrime = ZZ(1234567) * ZZ(7654321) - 44444444;
  GlobalOutput() << "IsPPrime(BigPrime) returns " << IsPPrime(BigPrime) << endl;

  GBasisExample(NewQuotientRing(Z, ideal(Z, 7)));        //  Z/(7)
  GBasisExample(NewQuotientRing(Z, ideal(Z, BigPrime))); //  Z/(BigPrime)

  GBasisExample(NewFractionField(Z));  //  Q

  // Loop which repeatedly tries a computation at ever higher precisions
  // until we don't get an InsufficientPrecision error.
  for (size_t PrecBits = 32; ; PrecBits *= 2)
  {
    try
    {
      GBasisExample(NewRingFloat(PrecBits));
      break;
    }
    catch (RingFloat::InsufficientPrecision)
    {
      cout << "FAILED!" << endl;
    }
  }
  return 0;
}


//----------------------------------------------------------------------
// Use main() to handle any uncaught exceptions and warn the user about them.
int main()
{
  try
  {
    program();
    return 0;
  }
  catch (CoCoA::ErrorInfo& err)
  {
    GlobalErrput() << "***ERROR***  UNCAUGHT CoCoA error";
    ANNOUNCE(err);
  }
  catch (std::exception& exc)
  {
    GlobalErrput() << "***ERROR***  UNCAUGHT std::exception: " << exc.what() << endl;
  }
  catch(...)
  {
    GlobalErrput() << "***ERROR***  UNCAUGHT UNKNOWN EXCEPTION" << endl;
  }
  return 1;
}
