#ifndef FIELD_H_INCLUDED
#define FIELD_H_INCLUDED

#include <string.h>
#include <string>
#include <stdio.h>

#include <assert.h>

#include <gmp.h>  //SHOULD BE REMOVED

class FieldElementImplementation
{
 public:
  int refCount;
  FieldElementImplementation():refCount(1){};
  virtual ~FieldElementImplementation(){};

  virtual FieldElementImplementation *one() const=0;

  virtual FieldElementImplementation *copy()const=0;

  virtual  bool isZero()const=0;
  virtual FieldElementImplementation *sum(const FieldElementImplementation &b)const=0;
  virtual FieldElementImplementation *difference(const FieldElementImplementation &b)const=0;
  virtual FieldElementImplementation *negation()const=0;
  virtual FieldElementImplementation *inverse()const=0;
  virtual std::string toString(bool writeIfOne=true, bool alwaysWriteSign=false, bool latexMode=false) const=0;
  virtual void operator*=(const FieldElementImplementation &a)=0;
  virtual mpq_t const *getGmpRationalTemporaryPointer()const
    {
      fprintf(stderr,"*this object is not implemented using GMP\n");
      assert(0);   

      return 0;
    }
};


class FieldElement
{
  FieldElementImplementation *implementingObject;
 public:
  inline FieldElement(FieldElementImplementation *implementingObject_):implementingObject(implementingObject_){}
 public:  
  FieldElement()              // This constructor causes a lot of trouble
    {                         // Having a legal FieldElement without an implementing object
      implementingObject=0;   // we must check for each assignment (copy constructor/assignment operator)
    }                         // if the pointer is zero. - And some operation will be illegal on
                              // on this element ( one(),..... )
  inline FieldElement(const FieldElement &a)
    {
      //      assert(a.implementingObject);
      if(a.implementingObject)
	{
	  implementingObject=a.implementingObject;
	  implementingObject->refCount++;
	}
      else
	implementingObject=0;
    }
  mpq_t const *getGmpRationalTemporaryPointer()const
    {
      return implementingObject->getGmpRationalTemporaryPointer();
    }
  void setImplementingObject(FieldElementImplementation *implementingObject_){implementingObject=implementingObject_;}
  inline ~FieldElement()
    {
      if(implementingObject && 0==(--(implementingObject->refCount)))delete implementingObject;
    }

  inline FieldElement& operator=(const FieldElement& a)
    {
      if(implementingObject&& 0==(--(implementingObject->refCount)))delete implementingObject;
      //assert(a.implementingObject);
      if(a.implementingObject)
	{
	  implementingObject=a.implementingObject;
	  implementingObject->refCount++;
	}
      else
	implementingObject=0;
      return *this;
    }
  inline FieldElement one() const
    {
      assert(implementingObject);
      return FieldElement(implementingObject->one());
    }
  inline bool isZero()
    {
      assert(implementingObject);
      return implementingObject->isZero();
    }
  virtual bool isOne()const
    {
      if(!implementingObject)return false;
      return (*this-one()).isZero();
    }
  inline friend FieldElement operator+(const FieldElement &a,const FieldElement &b)
    {
      assert(a.implementingObject);
      assert(b.implementingObject);
      return FieldElement(a.implementingObject->sum(*(b.implementingObject)));
    }
  inline friend FieldElement operator-(const FieldElement &a,const FieldElement &b)
    {
      assert(a.implementingObject);
      assert(b.implementingObject);
      return FieldElement(a.implementingObject->difference(*(b.implementingObject)));
    }
  inline friend FieldElement operator-(const FieldElement &b)
    {
      assert(b.implementingObject);
      return FieldElement(b.implementingObject->negation());
    }
  inline FieldElement inverse()const
    {
      assert(implementingObject);
      return FieldElement(implementingObject->inverse());
    }
  inline std::string toString(bool writeIfOne=true, bool alwaysWriteSign=false ,bool latexMode=false) const
    {
      assert(implementingObject);
      return implementingObject->toString(writeIfOne,alwaysWriteSign,latexMode);
    }
  inline void operator*=(const FieldElement &a)
    {
      assert(a.implementingObject);
      assert(implementingObject);
      if(implementingObject->refCount!=1)
	{
	  implementingObject->refCount--;
	  implementingObject=implementingObject->copy();
	}
      (*implementingObject)*=(*a.implementingObject);
	
    }
  inline friend FieldElement operator*(const FieldElement &a,const FieldElement &b)
    {
      FieldElement c=a;
      c*=b;
      return c;
    }
};

extern int FieldElementRationalsLiving;


class Field
{
  static class Field *currentField;               // ??
  static void checkInitialized();                 // ??
 protected:
  class Field *next;
  static class Field *list;
 public:
  Field();
  static Field *find(const char *name);
  static void printList(FILE *f);
  virtual FieldElement zHomomorphism(int n)=0;
  virtual const char *name()=0;

  static void setField(Field *f);                 // Do we really want these
  static FieldElement staticZHomomorphism(int n); // two procedures?
};

#endif
