#include <iostream.h>
/* Linboxers,
      at last, as I promised David, I put the revised design of
   the generic field together and got it to compile (with some
   help of Will).
      There is a fieldtrait: it tells if the field is parameterized or
   not.  So both "double" and "Z mod 5 elements" are fields
   that the generic_add (see main() below) accepts as operand types.
   The compiler choses the instantiation of the code via a trait type.
   The trait could also contain the transliterations of the field
   operations (like Thierry suggested).  It currently doesn't, and
   the generic functions call operator+ from the field type.

      The same paradigm can be applied to most of our generic types,
   like the sticky vector type.
      The program compiles here with gnu 2.8.1

   Yours, Erich
*/

/* 
  Following is the generic interface code for fields,
  which is part of LinBox
*/
// the following 2 types are used as switches in template instantiation
//  type for unparam field
typedef struct is_unparam_field_struct {/* room for future members */}
        is_unparam_field;
// type for param field
typedef struct is_param_field_struct {/* room for future members */}
        is_param_field;

template<class Field, class Element> struct Param_field_traits {
   // by default, instantiate to trait of a parameterized field
   typedef is_param_field param_flag;
   typedef Field field_type;
   typedef Element element_type;
   Param_field_traits(Field& F) {param_field_ptr = &F; };
   Field* param_field_ptr; // address of the field
                           // the generic functions retrieve the
                           // field parameters through it
                           // see specialized_add below
};// end template Param_field_traits

template<class Field_type> struct Unparam_field_traits {
   // specialization for double, an unparameterized field type
   typedef is_unparam_field param_flag;
   typedef Field_type element_type;
};// end template Unparam_field_traits

template<class Field_or_Element, class Trait>
inline Field_or_Element& generic_add(const Field_or_Element& a,
                                     const Field_or_Element& b,
                                     const Trait& FT)
{typename Trait::param_flag flag;
 return specialized_add(a, b, FT, flag);
}// end generic_add

// specialized_add for unparameterized fields
template<class Field, class Trait>
inline Field& specialized_add(const Field& a, const Field& b,
                       const Trait& FT,
                       const is_unparam_field& flag)
{
 return *(new Field(a+b));
}

// specialized_add for parameterized fields
template<class Element, class Trait>
inline Element& specialized_add(const Element& a, const Element& b,
                         const Trait& FT,
                         const is_param_field& flag)
{return FT.param_field_ptr->add(a,b);
}

/*
    The particular implementations of fields are taken from
    existing libraries as plug-ins
*/
// integers modulo a small (int) prime
class z_p
{
  public:
    int modulus; // modulus is stored in the field object
    z_p (int m = 2) : modulus(m) {}

 // encapsulated z_p_elem type
 class z_p_elem {
   public:
    int residue; // each z_p::z_p_elem object only contains
                 // the residue, but not the modulus
    z_p_elem (z_p F=2, int start=0)
    {
      residue = start % F.modulus;
      if (residue < 0)
	residue = residue + F.modulus;
    }
 }; // end class z_p_elem

 z_p_elem& add(const z_p_elem& y, const z_p_elem& z) const
    {
      z_p_elem* x_ptr = new z_p_elem;
      x_ptr->residue = ( y.residue + z.residue ) % modulus;
      return *x_ptr;
    }
}; // end class z_p

ostream& operator<<(ostream& os, const z_p& F)
{
  os << "z_p field with modulus " << F.modulus << endl;
  return os;
}

ostream& operator<<(ostream& os, const z_p::z_p_elem& x)
{
  os << x.residue << " mod something" << endl;
  return os;
}

int main(void)
{
 static Unparam_field_traits<double> double_traits;
 z_p F5(5); // F5 is integers modulo 5
 Param_field_traits<z_p, typename z_p::z_p_elem> F5_trait(F5);

 z_p::z_p_elem x(F5,3);
 z_p::z_p_elem y(F5,4);

 cout << "double add: " << generic_add(1.2L, 3.4L,
                                       double_traits) << endl;
 cout << "z_p add: " << generic_add(x, y, F5_trait) << endl;
}
