//
// LiDIA - a library for computational number theory
//   Copyright (c) 1994, 1995,1996 by the LiDIA Group
//
// File        : alg_number.h
// Author      : Stefan Neis (SN)

/*
  $Id: alg_number.h,v 1.20 1997/01/17 12:44:12 neis Exp $
*/

#ifndef ALGEBRAIC
#define ALGEBRAIC

#include <stream.h>

#if defined(HAVE_MAC_DIRS) || defined(__MWERKS__)
#include <LiDIA:lidia.h>
#include <LiDIA:bigint.h>
#include <LiDIA:bigrational.h>
#include <LiDIA:bigfloat.h>

#include <LiDIA:base_vector.h>
#include <LiDIA:math_vector.h>

#include <LiDIA:bigint_matrix.h>
#include <LiDIA:bigmod_matrix.h>

#include <LiDIA:polynomial.h>
#else
#include <LiDIA/lidia.h>
#include <LiDIA/bigint.h>
#include <LiDIA/bigrational.h>
#include <LiDIA/bigfloat.h>

#include <LiDIA/base_vector.h>
#include <LiDIA/math_vector.h>

#include <LiDIA/bigint_matrix.h>
#include <LiDIA/bigmod_matrix.h>

#include <LiDIA/polynomial.h>
#endif

class number_field;
class order;
class alg_number;
class module;
class alg_ideal;
class prime_ideal;

class nf_base{
  // The classes allowed to change bases get immediate access.
  // They need to do all the work, since otherwise reference counting
  // gets difficult. So everything is private, since nobody is allowed to
  // explicitly use this class !!

  friend class number_field;		// Those need access to all
  friend class order;			// information of class nf_base.

  friend class alg_number;		// Those need access only to
  friend class module;			// the variable "current_base".
  friend class alg_ideal;
  friend class prime_ideal;

  // Additional friend functions:
  friend bool operator ==(const number_field &, const number_field &);
  friend ostream& operator<<(ostream&, const number_field&);
  friend istream& operator>>(istream&, number_field&);
  friend const bigint & disc(
#ifndef LIDIA_NO_MUTABLE
			     const
#endif
			     order & O);	// discriminant
  friend ostream& operator<<(ostream &, const order &);
  friend istream& operator>>(istream &, order &);
  friend void multiply(alg_number &, const alg_number &, const alg_number &);
  friend void multiply2(module &, const module &, const module &);
  friend bigint_matrix rep_matrix(const alg_number &);
  friend istream& operator>>(istream &, alg_number &);
  friend istream& operator>>(istream &, module &);

  static nf_base * current_base;	// Default nf_base
  static nf_base * dummy_base;		// Dummy nf_base
  int references;			// How many objects use this base.

#ifndef LIDIA_NO_MUTABLE
  mutable
#endif
  polynomial <bigint> f;		// the irreducible polynomial
#ifndef LIDIA_NO_MUTABLE
  mutable
#endif
  lidia_size_t real_roots;		// number of real roots

#ifndef LIDIA_NO_MUTABLE
  mutable
#endif
  bigint_matrix base;
#ifndef LIDIA_NO_MUTABLE
  mutable
#endif
  bigint den;
#ifndef LIDIA_NO_MUTABLE
  mutable
#endif
  math_matrix <bigint> table;		// multiplication table

  // Internal information, that is frequently needed and so stored
  // with the order, as soon, as it is computed:
#ifndef LIDIA_NO_MUTABLE
  mutable
#endif
  math_vector <bigint> One;		// Representation of 1
  math_matrix <bigfloat> conjugates;	// column i contains the conjugates
                                        // of w_i (0 <= i < degree().

  // Functions for checking, what is already computed:
  bool table_computed() const
  {return (table.get_no_of_columns()!=1);}

  void compute_table()
#ifndef LIDIA_NO_MUTABLE
    const
#endif
    ;

  bool base_computed() const
  {return (base.get_no_of_columns()!=1);}

  void compute_base()
#ifndef LIDIA_NO_MUTABLE
    const
#endif
    ;

  void compute_conjugates();

  // whether to use MT or polynomial for multiplying.
  bool using_necessary() const
  {return (f.is_zero() || base_computed());}

  const bigfloat & get_conjugate(lidia_size_t, lidia_size_t);
  const math_matrix <bigfloat> & get_conjugates();

  const polynomial <bigint> & which_polynomial()
#ifndef LIDIA_NO_MUTABLE
    const
#endif
    ;

  lidia_size_t degree() const;

  // Constructor and Destructor

  nf_base():references(0), f(), real_roots(0), base(), den(1), table(),
    One(), conjugates()
  {}			// Do nothing.
  ~nf_base(){}		// Just call member destructors.

  //  Default copy constructor and assignment are OK.

  void assign(const nf_base&);
  // Input and output are used by number_field and order!
  friend ostream& operator<<(ostream&, const nf_base&); // jeweils Polynom
  friend istream& operator>>(istream&, nf_base&);       // ein-, ausgeben

public:
  const math_vector <bigint> & get_one()
#ifndef LIDIA_NO_MUTABLE
    const
#endif
    ;	// 1
  void dec_ref(){
    references--;
    if (!references && this != nf_base::dummy_base){
      if (nf_base::current_base == this)
	nf_base::current_base = nf_base::dummy_base;
      delete this;
    }
  }
  void inc_ref(){
    references++;
  }
};

class number_field{
#ifdef LIDIA_DEBUG
  static int count;
#endif
  nf_base * base;			// pointer to the base of O

public:
  // Contructors & destructor:
  number_field();
  number_field(const polynomial < bigint > & p);
  number_field(const bigint *v, lidia_size_t deg);
  number_field(const order &);
  number_field(const number_field &);
  ~number_field();

  // Cast operator
  operator nf_base *() const
  { return base;}

  // member functions:
  const bigfloat & get_conjugate(lidia_size_t, lidia_size_t);
  const math_matrix <bigfloat> & get_conjugates();

  const polynomial <bigint> & which_polynomial() const
  { return base->which_polynomial();}

  nf_base * which_base() const
  {return base;}

  lidia_size_t degree() const
  { return base->degree();}

  lidia_size_t no_of_real_embeddings() const;

#ifndef HEADBANGER
  friend number_field overfield(const number_field &, const number_field &);
  void assign(const number_field & K);
#endif

  number_field & operator =(const number_field & F)
  {assign(F); return *this;}

  // Comparisions:
  // Since deciding whether fields are isomorphic is difficult,
  // we consider fields to be equal only, if they are generated by
  // the same polynomial.
  friend bool operator ==(const number_field &, const number_field &);
  friend bool operator !=(const number_field &, const number_field &);
  friend bool operator <=(const number_field &, const number_field &);
  friend bool operator <(const number_field &, const number_field &);
  friend bool operator >=(const number_field &, const number_field &);
  friend bool operator >(const number_field &, const number_field &);

  // friend functions:
  friend const polynomial < bigint > & which_polynomial(const number_field &);
  friend nf_base * which_base(const number_field &);
  friend lidia_size_t degree(const number_field &);
  friend lidia_size_t no_of_real_embeddings(const number_field &);
  // In-/Output:
  friend ostream& operator<<(ostream&, const number_field&); // jeweils Polynom
  friend istream& operator>>(istream&, number_field&);       // ein-, ausgeben
};

inline bool operator !=(const number_field & A, const number_field & B)
{ return !(A==B);}

inline bool operator <(const number_field & A, const number_field & B)
{ return (!(A==B) && (A<=B));}

inline bool operator >=(const number_field & A, const number_field & B)
{return B<=A;}

inline bool operator >(const number_field & A, const number_field & B)
{ return B<A;}

inline const polynomial <bigint> & which_polynomial(const number_field &F)
{ return F.which_polynomial();}

inline nf_base * which_base(const number_field & F)
{ return F.base;}

inline lidia_size_t degree(const number_field &F)
{ return F.degree();}

inline lidia_size_t no_of_real_embeddings(const number_field &F)
{ return F.no_of_real_embeddings();}

class order{
#ifdef LIDIA_DEBUG
  static int count;
#endif
#ifndef HEADBANGER
  // Components of the data - type:
  nf_base * base;			// pointer to the base of O
#ifndef LIDIA_NO_MUTABLE
  mutable
#endif
  bigint discriminant;	

  // Internal routine for the work involved in comparisons
  void compare(const order&, bool &, bool &) const;
#endif

public:
  // Contructors & destructor:
  order(const nf_base * base1 = nf_base::current_base);
  order(const polynomial<bigint> & p,	// initialize with equation order
	const base_matrix <bigint> & = bigint_matrix(),	// (default)
	const bigint & = 1);		// or with transformed eq. order.
  order(const base_matrix <bigint> &);	// table uebergeben
  order(const base_matrix <bigint> &, const bigint &,
	const nf_base * base1 = nf_base::current_base); // TRAFO uebergeben.
  order(const order &);			// copy constructor
  order(const number_field &);		// Maximal order !
  ~order();

  order & operator =(const order & O)
  {assign(O); return *this;}

  // member functions:
       // Accessing Information:

  const bigint_matrix & base_numerator() const
  {return base->base;}

  const bigint & base_denominator() const
  {return base->den;}

  const polynomial <bigint> & which_polynomial() const
  {return base->which_polynomial();}

  nf_base * which_base() const
  {return base;}

  const bigint & MT(lidia_size_t,lidia_size_t,lidia_size_t);	// accessing MT

  const bigfloat & get_conjugate(lidia_size_t, lidia_size_t);
  const math_matrix <bigfloat> & get_conjugates();

  lidia_size_t degree() const			// degree of extension
  { return base->degree();}

  lidia_size_t no_of_real_embeddings() const;

#ifndef HEADBANGER
  void assign(const order &);
#endif

  // Cast operator:
  operator alg_ideal() const;
  operator nf_base *() const
  { return base;}

  // Comparisions:
  // We are interested in comparing orders only if they are
  // over the same field! So nothing else is supported!!
  friend bool operator ==(const order&, const order&);
  friend bool operator !=(const order&, const order&);
  friend bool operator <=(const order&, const order&);
  friend bool operator <(const order&, const order&);
  friend bool operator >=(const order&, const order&);
  friend bool operator >(const order&, const order&);

  // friend functions:
  friend const bigint_matrix & base_numerator(const order &);
  friend const bigint & base_denominator(const order &);
  friend const polynomial <bigint> & which_polynomial(const order &);
  friend nf_base * which_base(const order &);
  friend void swap(order &, order &);

  // Number-theoretic functions:
  friend lidia_size_t degree(const order & O);	// degree of field extension
  friend lidia_size_t no_of_real_embeddings(const order & O);
  friend const bigint & disc(
#ifndef LIDIA_NO_MUTABLE
		     const
#endif
		     order & O);		// discriminant
  module pseudo_radical(const bigint & p) const;
                                       // computes pseudo-radical
  bool dedekind(const bigint & p, polynomial < bigint > & h2) const;
              // uses Dedekind criterion for prime p; returns true,
              // if p is an index divisor and sets extended to the new order.

  order maximize(const bigint & p) const;
              // maximizes order at p.
  order maximize()	// full maximization (round2)
#ifndef LIDIA_NO_MUTABLE
    const
#endif
    ;
  order maximize2()	// full maximization (round2) with a
                        // sligtly different strategy.
#ifndef LIDIA_NO_MUTABLE
    const
#endif
    ;

  // In-/Output:
  friend ostream& operator<<(ostream &, const order &);// table ausgeben
  friend istream& operator>>(istream &, order &);
           // tries to interpret input either as multiplication table
           // or as base transformation according to dimension.
};

inline const bigint_matrix & base_numerator(const order & O)
{return O.base_numerator();}

inline const bigint & base_denominator(const order & O)
{return O.base_denominator();}

inline const polynomial <bigint> & which_polynomial(const order & O)
{return O.which_polynomial();}

inline nf_base * which_base(const order & O)
{ return O.base;}

inline lidia_size_t degree(const order & O)
{return O.degree();}

inline lidia_size_t no_of_real_embeddings(const order & O)
{return O.no_of_real_embeddings();}

class alg_number{
#ifdef LIDIA_DEBUG
  static int count;
#endif
  bigint den;
  math_vector <bigint> coeff;
  nf_base * O;

public:
  // Constructors & destructor:
  alg_number(const nf_base * O1 = nf_base::current_base);
  alg_number(const bigint &, const nf_base * O1 = nf_base::current_base);
  alg_number(const base_vector <bigint> &, const bigint & i,
	     const nf_base * O1 = nf_base::current_base);
  alg_number(const bigint *, const bigint & i = 1,
	     const nf_base * O1 = nf_base::current_base);
  alg_number(const alg_number & a);
  ~alg_number();

  alg_number & operator =(const alg_number & a)
  {assign(a); return *this;}

  // member-functions
  const bigint & denominator() const
  {return den;}

  const math_vector <bigint> & coeff_vector() const
  {return coeff;}

  alg_number numerator() const
  {return alg_number(coeff, bigint(1), O);}

  nf_base * which_base() const
  {return O;}

  lidia_size_t degree() const
  { return O->degree();}

  bigfloat get_conjugate(lidia_size_t);
  math_vector <bigfloat> get_conjugates();

#ifndef HEADBANGER
  bool is_zero() const;
  bool is_one() const;

  void normalize();
  void negate();
  void invert();
  void multiply_by_2();
  void divide_by_2();

  void assign_zero();			// Remains member of same order
  void assign_one();			// Remains member of same order
  void assign(const bigint &);		// Remains member of same order
  void assign(const alg_number &);	// Becomes member of same order as a!!

  // Procedural versions of arithmetic operations:
  friend void add(alg_number &, const alg_number &, const alg_number &);
  friend void subtract(alg_number &, const alg_number &, const alg_number &);
  friend void multiply(alg_number &, const alg_number &, const alg_number &);
  friend void divide(alg_number &, const alg_number &, const alg_number &);

  friend void add(alg_number &, const alg_number &, const bigint &);
  friend void subtract(alg_number &, const alg_number &, const bigint &);
  friend void multiply(alg_number &, const alg_number &, const bigint &);
  friend void divide(alg_number &, const alg_number &, const bigint &);

  friend void add(alg_number &, const bigint &, const alg_number &);
  friend void subtract(alg_number &, const bigint &, const alg_number &);
  friend void multiply(alg_number &, const bigint &, const alg_number &);
  friend void divide(alg_number &, const bigint &, const alg_number &);

  friend void power(alg_number &, const alg_number &, const bigint &);
#endif
  friend void power_mod_p(alg_number &, const alg_number &,
			  const bigint &, const bigint &);

  // arithmetic operators:
  friend alg_number operator -(const alg_number &);
  friend alg_number operator +(const alg_number &, const alg_number &);
  friend alg_number operator +(const alg_number &, const bigint &);
  friend alg_number operator +(const bigint &, const alg_number &);
  friend alg_number operator -(const alg_number &, const alg_number &);
  friend alg_number operator -(const alg_number &, const bigint &);
  friend alg_number operator -(const bigint &, const alg_number &);
  friend alg_number operator *(const alg_number &, const alg_number &);
  friend alg_number operator *(const alg_number &, const bigint &);
  friend alg_number operator *(const bigint &, const alg_number &);
  friend alg_number operator /(const alg_number &, const alg_number &);
  friend alg_number operator /(const alg_number &, const bigint &);
  friend alg_number operator /(const bigint &, const alg_number &);

  alg_number & operator +=(const alg_number & a)
  { add(*this, *this, a); return *this;}

  alg_number & operator +=(const bigint & a)
  { add(*this, *this, a); return *this;}

  alg_number & operator -=(const alg_number & a)
  { subtract(*this, *this, a); return *this;}

  alg_number & operator -=(const bigint & a)
  { subtract(*this, *this, a); return *this;}

  alg_number & operator *=(const alg_number & a)
  { multiply(*this, *this, a); return *this;}

  alg_number & operator *=(const bigint & a)
  { multiply(*this, *this, a); return *this;}

  alg_number & operator /=(const alg_number & a)
  { divide(*this, *this, a); return *this;}

  alg_number & operator /=(const bigint & a)
  { divide(*this, *this, a); return *this;}

  // Comparisions:
  // By now, only comparision of numbers over the same order is implemented.
  friend bool operator ==(const alg_number&, const alg_number&);
  friend bool operator !=(const alg_number&, const alg_number&);

  bool operator ! () const
  {return is_zero();}

  // Some number-theoretic functions:
  friend lidia_size_t degree(const alg_number &);
  friend bigint_matrix rep_matrix(const alg_number &);
  friend bigrational norm(const alg_number &);	// Norm
  friend bigrational trace(const alg_number &);	// Trace
  friend polynomial <bigint> charpoly(const alg_number &);
                     // characteristic polynomial

  // other functions:
#ifndef HEADBANGER
  friend void negate(alg_number &, const alg_number &);
  friend void invert(alg_number &, const alg_number &);
  friend alg_number inverse(const alg_number &);
#endif
  friend const bigint & denominator(const alg_number &);
  friend const math_vector <bigint> & coeff_vector(const alg_number &);
  friend alg_number numerator(const alg_number &);
  friend nf_base * which_base(const alg_number &);
#ifndef HEADBANGER
  friend void square(alg_number &, const alg_number &);
#endif
  friend void swap(alg_number &, alg_number &);

  // random numbers
  void randomize(const bigint &);

  // In-/Output:
  friend ostream& operator<<(ostream&, const alg_number&);
  friend istream& operator>>(istream&, alg_number&);

  // friends:
  friend class module;
  //  friend module operator *(const module &, const module &);
  friend void multiply(alg_ideal &, const alg_ideal &, const alg_number &);
  friend void multiply(alg_ideal &, const alg_number &, const alg_ideal &);
  friend void divide(alg_ideal &, const alg_ideal &, const alg_number &);

};

inline alg_number operator -(const alg_number & a){
  alg_number c(-a.coeff, a.den, a.O);
  return c;
}

inline alg_number operator +(const alg_number & a, const alg_number & b){
  alg_number c(a.O);
  add(c,a,b);
  return c;
}

inline alg_number operator +(const alg_number & a, const bigint & b){
  alg_number c(a.O);
  add(c,a,b);
  return c;
}

inline alg_number operator +(const bigint & b, const alg_number & a){
  alg_number c(a.O);
  add(c,a,b);
  return c;
}

inline alg_number operator -(const alg_number & a, const alg_number & b){
  alg_number c(a.O);
  subtract(c,a,b);
  return c;
}

inline alg_number operator - (const alg_number & a, const bigint & b)
{
  alg_number c(a.O);
  subtract(c,a,b);
  return c;
}

inline alg_number operator - (const bigint & b, const alg_number & a)
{
  alg_number c(a.O);
  subtract(c,b,a);
  return c;
}

inline alg_number operator *(const alg_number & a, const alg_number & b)
{
  alg_number c(a.O);
  multiply(c,a,b);
  return c;
}

inline alg_number operator *(const alg_number & a, const bigint & b)
{
  alg_number c(a.O);
  multiply(c,a,b);
  return c;
}

inline alg_number operator *(const bigint & b, const alg_number & a)
{
  alg_number c(a.O);
  multiply(c,a,b);
  return c;
}

inline alg_number operator /(const alg_number & a, const alg_number & b)
{
  alg_number c(a.O);
  divide(c,a,b);
  return c;
}

inline alg_number operator /(const alg_number & a, const bigint & b)
{
  alg_number c(a.O);
  divide(c,a,b);
  return c;
}

inline alg_number operator /(const bigint & a, const alg_number & b)
{
  alg_number c(b.O);
  divide(c,a,b);
  return c;
}

inline bool operator !=(const alg_number & a, const alg_number & b)
{ return !(a==b);}

inline lidia_size_t degree(const alg_number & a)
{ return a.degree();}

inline const bigint & denominator(const alg_number & a)
{ return a.den;}

inline const math_vector <bigint> & coeff_vector(const alg_number & a)
{ return a.coeff;}

inline alg_number numerator(const alg_number & a)
{ return alg_number(a.coeff, bigint(1), a.O);}


inline nf_base * which_base(const alg_number & a)
{ return a.O;}


class module{
protected:
#ifdef LIDIA_DEBUG
  static int count;
#endif
#ifndef LIDIA_NO_MUTABLE
  mutable
#endif
  bigmod_matrix base;
  bigint den;
  nf_base * O;
#ifndef LIDIA_NO_MUTABLE
  mutable
#endif
  bool is_exp;

  void compare(const module&, bool &, bool &) const;
    // internal routine for comparisons

public:
  // Contructors & destructor:
  module(const nf_base * O1= nf_base::current_base);		   // zero module
  module(const alg_number & a,
	 const alg_number & b = alg_number(bigint(0)));
  module(const base_matrix <bigint> &, const bigint & d = 1,
	 const nf_base * O1 = nf_base::current_base);
  module(const bigmod_matrix &, const bigint & d = 1,
	 const nf_base * O1 = nf_base::current_base);
  module(const module &);
  ~module();

  virtual module & operator =(const module & A)
  {assign(A); return *this;}

  // member-functions
  const bigint & denominator() const
  {return den;}

  const bigmod_matrix & coeff_matrix() const
  {return base;}

  bigint_matrix z_basis() const
  {bigmod_matrix tmp(base); tmp.lift(0); return tmp;}

  module numerator() const
  {return module(base, bigint(1), O);}

  nf_base * which_base() const
  {return O;}

  lidia_size_t degree() const
  { return O->degree();}

#ifndef HEADBANGER
  bool is_zero() const;		// In the obvious sense
#endif

  bool is_whole_order() const;

#ifndef HEADBANGER
  bool is_one() const		// i.e. is_whole_order
  { return is_whole_order();}
#endif

  void normalize();

#ifndef HEADBANGER
  void invert();

  void assign_zero();		// Remains in the same order

  inline void assign_whole_order();	// Remains in the same order

  void assign_one()		// Remains in the same order, is the same as:
  {assign_whole_order();}

  void assign(const bigint &);	// Remains in the same order
  void assign(const alg_number &);// Becomes subset of same order as a !
  virtual void assign(const module &);	// Becomes subset of same order as a !

  // Procedural versions of arithmetic operations:
  friend void add(module &, const module &, const module &);
  friend void intersect(module &, const module &, const module &);
  friend void multiply(module &, const module &, const module &);
  //  friend void multiply2(module &, const module &, const module &);
  friend void multiply(module &, const module &, const bigint &);
  friend void multiply(module &, const bigint &, const module &);
  friend void divide(module &, const module &, const module &); // Warning:
  friend void divide(alg_ideal &, const alg_ideal &, const module &); // Warning:
  friend void divide(module &, const module &, const bigint &);
  friend void remainder(module &, const module &, const bigint &);

  friend void power(module &, const module &, const bigint &);
#endif

  // arithmetic operators:
  friend module operator +(const module &, const module &); // sum or union
  friend module operator &(const module &, const module &); // intersection
  friend module operator *(const module &, const module &); // product
  friend module operator *(const module &, const bigint &); // product
  friend module operator *(const bigint &, const module &); // product
  friend module operator /(const module &, const bigint &); // quotient
  friend module operator /(const module &, const module &); // quotient (??):
  // Division by an order is only guaranteed to produce correct results, if
  // you are in the maximal order!!
  friend module operator %(const module &, const bigint &); // Reduce mod pO

  module& operator +=(const module & a)
  { add(*this, *this, a); return *this;}

  module& operator &=(const module & a)
  { intersect(*this, *this, a); return *this;}

  module& operator *=(const module & a)
  { multiply(*this, *this, a); return *this;}

  module& operator *=(const bigint & a)
  { multiply(*this, *this, a); return *this;}

  module& operator /=(const bigint & a)
  { divide(*this, *this, a); return *this;}

  module& operator /=(const module & a)	// Warning:
  { divide(*this, *this, a); return *this;}

  module& operator %=(const bigint & a)
  { remainder(*this, *this, a); return *this;}

  // Comparisions:
  // By now, only comparision of modules over the same order is implemented.
  friend bool operator ==(const module&, const module&);
  friend bool operator !=(const module&, const module&);
  friend bool operator <=(const module&, const module&);
  friend bool operator <(const module&, const module&);
  friend bool operator >=(const module&, const module&);
  friend bool operator >(const module&, const module&);

  bool operator ! () const
  {return is_zero();}

  // Some number-theoretic function:
  friend lidia_size_t degree(const module &);
  friend bigrational norm(const module &);	// Norm
  friend bigrational exp(
#ifndef LIDIA_NO_MUTABLE
			 const
#endif
			 module &);	// exponent
  order ring_of_multipliers(const bigint &p) const;

  // other functions:
#ifndef HEADBANGER
  friend void invert(module &, const module &);
  friend module inverse (const module &);
#endif

  friend const bigint & denominator(const module &);
  friend const bigmod_matrix & coeff_matrix(const module &);
  friend bigint_matrix z_basis(const module &);
  friend module numerator(const module &);
  friend nf_base * which_base(const module &);
#ifndef HEADBANGER
  friend void square(module &, const module &);
#endif
  friend void swap(module &, module &);

  // random numbers
  void randomize(const bigint &);

  // In-/Output:
  friend ostream& operator<<(ostream &, const module &);
  friend istream& operator>>(istream &, module &);

  // friends:
  friend void multiply(alg_ideal &, const alg_ideal &, const alg_ideal &);
};

inline module operator +(const module & a, const module & b)
{ module c(a.O); add(c,a,b); return c;}

inline module operator &(const module & a, const module & b)
{ module c(a.O); intersect(c,a,b); return c;}

inline module operator *(const module & a, const module & b)
{ module c(a.O); multiply(c,a,b); return c;}

inline module operator *(const module &a, const bigint &b)
{module c(a.O); multiply(c,a,b); return c;}

inline module operator *(const bigint &b, const module &a)
{module c(a.O); multiply(c,a,b); return c;}

inline module operator /(const module & a, const module & b)
{ module c(a.O); divide(c,a,b); return c;}

inline module operator /(const module & a, const bigint & b)
{ module c(a.O); divide(c,a,b); return c;}

inline module operator %(const module &a, const bigint &p)
{ module c(a.O); remainder(c,a,p); return c;}

inline lidia_size_t degree(const module & a)
{ return a.degree();}

inline const bigint & denominator(const module & a)
{ return a.den;}

inline const bigmod_matrix & coeff_matrix(const module & a)
{ return a.base;}

inline bigint_matrix z_basis(const module & a)
{ return a.z_basis();}

inline module numerator(const module & a)
{ return module(a.base, bigint(1), a.O);}

inline nf_base * which_base(const module & a)
{return a.O;}


class alg_ideal: public module{
public:
  // Contructors & destructor:
  alg_ideal(const nf_base * O1= nf_base::current_base);	   // zero ideal
  alg_ideal(const alg_number & a,
	const alg_number & b = alg_number(bigint(0)));
  alg_ideal(const base_matrix <bigint> &, const bigint & d = 1,
	const nf_base * O1 = nf_base::current_base);
  alg_ideal(const bigmod_matrix &, const bigint & d = 1,
	const nf_base * O1 = nf_base::current_base);
  alg_ideal(const alg_ideal &);
  ~alg_ideal();

  module & operator =(const module & A)
  {multiply(*this, A, alg_ideal(order(A.which_base()))); return *this;}

  void assign(const module & A)
  {multiply(*this, A, alg_ideal(order(A.which_base())));}

  alg_ideal & operator =(const alg_ideal & A)
  {assign(A); return *this;}

  // member-functions

  alg_ideal numerator() const
  {return alg_ideal(base, bigint(1), O);}

#ifndef HEADBANGER
  // Procedural versions of arithmetic operations:
  friend void multiply(alg_ideal &, const alg_ideal &, const alg_ideal &);
  friend void multiply(alg_ideal &, const alg_ideal &, const alg_number &);
  friend void multiply(alg_ideal &, const alg_number &, const alg_ideal &);
  friend void divide(alg_ideal &, const alg_ideal &, const module &); // Warning:
  friend void divide(alg_ideal &, const alg_ideal &, const alg_number &);

#endif

  // arithmetic operators:
  friend alg_ideal operator +(const alg_ideal &, const alg_ideal &); // sum or union
  friend alg_ideal operator &(const alg_ideal &, const alg_ideal &); // intersection
  friend alg_ideal operator *(const alg_ideal &, const alg_ideal &); // product
  friend alg_ideal operator *(const alg_ideal &, const bigint &); // product
  friend alg_ideal operator *(const bigint &, const alg_ideal &); // product
  friend alg_ideal operator *(const alg_ideal &, const alg_number &); // product
  friend alg_ideal operator *(const alg_number &, const alg_ideal &); // product
  friend alg_ideal operator /(const alg_ideal &, const bigint &); // quotient
  friend alg_ideal operator /(const alg_ideal &, const alg_number &); // quotient
  friend alg_ideal operator /(const alg_ideal &, const module &); // quotient (??):
  // Division by an order is only guaranteed to produce correct results, if
  // you are in the maximal order!!

  alg_ideal& operator +=(const alg_ideal & a)
  { add(*this, *this, a); return *this;}

  alg_ideal& operator &=(const alg_ideal & a)
  { intersect(*this, *this, a); return *this;}

  alg_ideal& operator *=(const alg_ideal & a)
  { multiply(*this, *this, a); return *this;}

  alg_ideal& operator *=(const bigint & a)
  { multiply(*this, *(module *)this, a); return *this;}

  alg_ideal& operator /=(const bigint & a)
  { divide(*this, *(module *)this, a); return *this;}

  alg_ideal& operator /=(const module & a)	// Warning:
  { divide(*this, *this, a); return *this;}

  alg_ideal& operator %=(const bigint & a)
  { remainder(*this, *this, a); return *this;}

  // reduction:
  void reduce();
  friend alg_ideal reduce(const alg_ideal & a)
  {alg_ideal c(a); c.reduce(); return c;}

  // other functions:
#ifndef HEADBANGER
  friend alg_ideal inverse (const alg_ideal &);
#endif

  friend alg_ideal numerator(const alg_ideal &);

  // random numbers
  void randomize(const bigint &);
};

inline alg_ideal operator +(const alg_ideal & a, const alg_ideal & b)
{ alg_ideal c(a.which_base()); add(c,a,b); return c;}

inline alg_ideal operator &(const alg_ideal & a, const alg_ideal & b)
{ alg_ideal c(a.which_base()); intersect(c,a,b); return c;}

inline alg_ideal operator *(const alg_ideal & a, const alg_ideal & b)
{ alg_ideal c(a.which_base()); multiply(c,a,b); return c;}

inline alg_ideal operator *(const alg_ideal &a, const bigint &b)
{ alg_ideal c(a.which_base()); multiply(c,*(module *)&a,b); return c;}

inline alg_ideal operator *(const bigint &b, const alg_ideal &a)
{ alg_ideal c(a.which_base()); multiply(c,*(module *)&a,b); return c;}

inline alg_ideal operator *(const alg_ideal &a, const alg_number &b)
{ alg_ideal c(a.which_base()); multiply(c,a,b); return c;}

inline alg_ideal operator *(const alg_number &b, const alg_ideal &a)
{ alg_ideal c(a.which_base()); multiply(c,a,b); return c;}

inline alg_ideal operator /(const alg_ideal & a, const alg_ideal & b)
{ alg_ideal c(a.which_base()); divide(c,a,b); return c;}

inline alg_ideal operator /(const alg_ideal & a, const bigint & b)
{ alg_ideal c(a.which_base()); divide(c,*(module *)&a,b); return c;}

inline alg_ideal operator /(const alg_ideal & a, const alg_number & b)
{ alg_ideal c(a.which_base()); divide(c,a,b); return c;}

inline alg_ideal operator %(const alg_ideal &a, const bigint &p)
{ alg_ideal c(a.which_base()); remainder(c,a,p); return c;}

inline alg_ideal numerator(const alg_ideal & a)
{ return alg_ideal(a.coeff_matrix(), bigint(1), a.which_base());}


inline void module::assign_whole_order()	// Remains in the same order
{assign(alg_ideal(order(O)));}

#endif //ALGEBRAIC
