//
// LiDIA - a library for computational number theory
//   Copyright (c) 1995 by the LiDIA Group
//
// File        : quadratic_order.h 
// Author      : Michael Jacobson (MJ)
// Last change : MJ, September 24 1996
//

#ifndef LIDIA_QUADRATIC_ORDER_H
#define LIDIA_QUADRATIC_ORDER_H

#if defined(HAVE_MAC_DIRS) || defined(__MWERKS__)
#include <LiDIA:bigint.h>
#include <LiDIA:bigfloat.h>
#include <LiDIA:bigrational.h>
#include <LiDIA:bigint_matrix.h>
#include <LiDIA:bigfloat_lattice.h>
#include <LiDIA:interface_lib.h>
#include <LiDIA:matrix_GL2Z.h>
#include <LiDIA:rational_factorization.h>
#include <LiDIA:timer.h>
#include <iomanip.h>
#include <stdlib.h>
#include <LiDIA:indexed_hash_table.h>

#else
#include <LiDIA/bigint.h>
#include <LiDIA/bigfloat.h>
#include <LiDIA/bigrational.h>
#include <LiDIA/bigint_matrix.h>
#include <LiDIA/bigfloat_lattice.h>
#include <LiDIA/interface_lib.h>
#include <LiDIA/matrix_GL2Z.h>
#include <LiDIA/rational_factorization.h>
#include <LiDIA/timer.h>
#include <iomanip.h>
#include <stdlib.h>
#include <LiDIA/indexed_hash_table.h>
#endif

#if !defined(__GNUG__)
#include <signal.h>
#else
#define SIGHUP  1       /* hangup */
#define SIGINT  2       /* interrupt */
#define SIGQUIT 3       /* quit */
#define SIGILL  4       /* illegal instruction (not reset when caught) */
#define SIGTERM 15
#endif

#ifdef LIDIA_WARNINGS
#define warning_handler_c(f, m, code) { warning_handler(f, m); code; }
#else
#define warning_handler_c(f, m, code) { }
#endif

#define CAND_NUMBER 1000

typedef unsigned char SIEBTYP;

#ifndef log2
#define log2(a)        ( log(a)*(1/log(2)) )
#endif

class quadratic_order;
class qi_class;
class qi_class_real;
class quadratic_ideal;
class quadratic_form;
class ideal_node;
class prime_lists;
class qo_node;
class qo_list;

bigint int_key(const int & G);
bigint long_key(const long & G);
bigint bigint_key(const bigint & G);


#define RBJTB 9			/* for regulator, use BJT if D < 10^6 */
#define RSHANKSB 40		/* for regulator, use shanks if D < 10^40 */
#define CGBJTB 11 		/* for class group, use BJT if D < 10^20 */
#define CGSHANKSB 12		/* for class group, use Shanks if D < 10^20 */

class quadratic_order
{
  private:

  /**
   ** A quadratic order is represented by the following:
   **
   **		Delta = discriminant of the order
   **           R = regulator (and precision)
   **           FU = fundamental unit
   **		h = class number
   **		CL = vector of class group invariants
   **		gens = set of generators of each cyclic subgroup
   **           L = approximation of L(1,X)
   **
   ** The following information is also stored for use in some computations:
   **
   **           hfact = integer factorization of h (for computations such as
   **			orders of elements)
   **           disc_fact = integer factorization of h (for computations such
   **			as orders of elements)
   **           fact_base = factor base of prime ideals
   **           U = transformation matrix from factor base to generators 
   **           UINV = inverse of U
   **		prin_list = list of principal ideals
   **/

  /* invariants */
  bigint Delta;
  bigfloat R;
  bigint h;
  bigfloat L;
  bigfloat Cfunc;
  base_vector <bigint> CL;
  base_vector <qi_class> gens;

  /* other data */
  rational_factorization hfact;
  rational_factorization disc_fact;
  base_vector <qi_class> fact_base;
  bigint_matrix U;
  bigint_matrix UINV;

  /* bound for nucomp and nudupl (if Delta < 0) */
  bigint nucomp_bound;

  /* floating point approximation precision */
  long prec;

  /* L(1) approximation using p < 2Q (Bach method) */
  bigfloat FI;
  long Q;

  /* whether to print timings or not */
  static int info;

  /* list of current quadratic orders */
  static qo_list qo_l;


  /***
   ** PRIVATE FUNCTIONS
   ***/


  void set_transformations();

  /* regulator */
  void rBJT();
  void rshanks();
  bigfloat find_hstar(const bigfloat & hR, const bigfloat & E);

  /* divisor of class number */
  bigint divisor_imag(const bigfloat & nFI, const bigfloat & F);
  bigint divisor_real(const bigfloat & nFI);

  /* class number, Shanks method */
  void cns_imag();
  void cns_real();

  /* class group, BJT method */
  void cgBJT_imag();
  void cgBJT_real();

  /* class group, Shanks method */
  void cgs_imag();
  void cgs_real();

  /* class group, when h is known */
  void cgh_imag();
  void cgh_real();

  /* class group, subexponential routines */
  void cgsubexp_imag();
  void cgsubexp_real();

  void cgsubexp_imag_more();
  void cgsubexp_imag_end();

  void new_polynomial_imag(quadratic_form & F, double size_A, int *FB, hash_table < quadratic_form > & used_polys, sort_vector <int> & Q_primes, sort_vector <int> & Q_exp, int dense_size, int force_prime);
  void new_polynomial_real(quadratic_form & F, double size_A, int *FB, hash_table < quadratic_form > & used_polys, sort_vector <int> & Q_primes, sort_vector <int> & Q_exp, int dense_size, int force_prime);

  static void qo_reverse(char *s);
  static void qo_itoa(unsigned long n, char *s, int base);
  friend void qo_get_name(char *s);
  friend bool qo_transform_relations(bool back);
  friend void read_relation_matrix(bigint_matrix & A);


  friend void qo_read_par(int decimal_digits, double &T, int &M, int &p_bound);

  bigfloat init_subexp(int & size_FB, int **FB, int p_bound, sort_vector < int > & used_primes, sort_vector <int> & D_primes);


  /* factoring the discriminant using the class group */
  void factor_imag();
  void factor_real();

  /* computing smallest power of diagonal element for relation */
  bigint exact_power(const bigint & omult, const qi_class & G, indexed_hash_table <ideal_node> & RT, indexed_hash_table <ideal_node> & QT, lidia_size_t numRpr, long & r, long & q);
  bigint exact_power_real(const bigint & omult, const qi_class & G, indexed_hash_table <ideal_node> & RT, indexed_hash_table <ideal_node> & QT, lidia_size_t numRpr, long & r, long & q, qi_class_real & Gstep);



  public:

  hash_table <qi_class_real> prin_list;	 /* principal ideal list */

  static prime_lists PL;

  /* file names needed for subexp routines */
  static char FAKM[];
  static char NEWR[];


  /**
   ** quadratic_order list maintaining functions
   **/
  static qo_node *add_to_list(qo_node *old, quadratic_order & newQO);
  static qo_node *add_dynamic_to_list(qo_node *old, quadratic_order & newQO);
  static void clear(qo_node *old);
  static quadratic_order *last_order();
  static qo_node *add_last(qo_node *old);
  static void print_all_orders();



  /**
   ** constructors and destructor
   **/

  quadratic_order();
  quadratic_order(const long D);
  quadratic_order(const bigint & D);
  quadratic_order(const quadratic_order & QO);

  ~quadratic_order();



  /**
   ** initialization
   **/

  static void verbose(int state);



  /**
   ** assignments 	
   **/

  bool assign(const long D);
  bool assign(const bigint & D);
  void assign(const quadratic_order & QO2);

  quadratic_order & operator = (const quadratic_order & QO);



  /**
   ** access functions 	
   **/

  inline bigint discriminant() const
    { return Delta; }
  inline bigint nu_bound() const
    { return nucomp_bound; }


  /**
   ** comparisons
   **/

  bool is_zero() const;
  bool is_equal(const quadratic_order & QO2) const;
  bool is_subset(quadratic_order & QO2);
  bool is_proper_subset(quadratic_order & QO2);

  friend bool operator == (const quadratic_order & QO1, const quadratic_order & QO2);
  friend bool operator != (const quadratic_order & QO1, const quadratic_order & QO2);

  friend bool operator <= (quadratic_order & QO1, quadratic_order & QO2);
  friend bool operator < (quadratic_order & QO1, quadratic_order & QO2);
  friend bool operator >= (quadratic_order & QO1, quadratic_order & QO2);
  friend bool operator > (quadratic_order & QO1, quadratic_order & QO2);

  friend bool operator ! (const quadratic_order & QO);


  /**
   ** basic functions
   **/

  friend bool is_quadratic_discriminant(const long D);
  friend bool is_quadratic_discriminant(const bigint & D);
  bool is_imaginary() const;
  bool is_real() const;
  bool is_R_computed() const;
  bool is_h_computed() const;
  bool is_L_computed() const;
  bool is_C_computed() const;
  bool is_CL_computed() const;

  friend void swap(quadratic_order & A, quadratic_order & B);


  /**
   ** high level functions
   **/

  bigint conductor();
  bool is_maximal();
  quadratic_order maximize();



  /**
   ** L functions, Littlewood indices, C function
   **/

  friend int kronecker(const bigint & D, const bigint & p);

  /* optimal value of Q for h* < h < 2h* approximation */
  long generate_optimal_Q();
  long get_optimal_Q();

  /* optimal value of Q to prove that h = 3 */
  long generate_optimal_Q_cnum(const bigfloat & h2, const bigfloat & t);
  long get_optimal_Q_cnum();

  /* optimal value of Q to compute C(Delta) */
  long generate_optimal_Q_cfunc();
  long get_optimal_Q_cfunc();

  /* truncated product estimate of L(s) */
  bigfloat estimate_L(const int s, const long nQ);

  /* Bach's method for estimating L(1) */
  bigfloat estimate_L1(const long nQ);
  bigfloat estimate_L1_error(const long nQ) const;


  /* L functions, C function, and Littlewood indices */
  bigfloat Lfunction();
  bigfloat LDfunction();
  bigfloat LLI();
  bigfloat LLI_D();
  bigfloat ULI();
  bigfloat ULI_D();
  bigfloat Cfunction();



  /**
   ** regulator, class number, class group
   **/

  bigfloat regulator();
  bigint class_number();
  base_vector <bigint> class_group();


  /* O(D^1/4) regulator computation */
  bigfloat regulator_BJT();

  /* O(D^1/5) regulator computation (uses estimate of L(1)) */
  bigfloat regulator_shanks();

  bigint class_number_shanks();

  /* from L(1) estimate and BJT algorithm, class number not known */
  base_vector <bigint> class_group_BJT();

  /* from L(1) estimate and my algorithm, class number not known */
  base_vector <bigint> class_group_shanks();

  /* class number known */
  base_vector <bigint> class_group_h();

  /* subexponential class group algorithm */
  base_vector <bigint> class_group_subexp();

  base_vector <bigint> class_group_subexp_more();
  base_vector <bigint> class_group_subexp_end();

  rational_factorization factor_h();
  bigint exponent();
  int p_rank(const bigint & p);
  base_vector <qi_class> generators(); 
  rational_factorization factor_discriminant();


  /**
   ** input/output
   **/

  friend istream & operator >> (istream & in, quadratic_order & QO);
  friend ostream & operator << (ostream & out, const quadratic_order & QO);

  friend void MyTime(long t);
};



#define OBJTB 10		/* for order, use BJT if D < 10^10 */
#define OSHANKSB 40		/* for order, use Shanks if D < 10^40 */
#define DLBJTB 40		/* for DL, use BJT if D < 10^40 */

class qi_class
{
  protected:

  /**
   ** An quadratic ideal equivalence class A is represented by a reduced,
   **	primitive, invertible ideal.
   **	is unique.
   **/

  bigint a;
  bigint b;

  static qo_node *current_order;
  static bigint Delta;        /* discriminant of the order */
  static bigint rootD;        /* floor(sqrt(|Delta|)) */
  static bigfloat rd;         /* sqrt(|Delta|) */
  static bigint PEA_L;        /* floor((Delta/4)^1/4) */

  static bigint ordmult;	/* largest exponent found by order_shanks */
  static rational_factorization omfact;

  /* whether to print timings or not */
  static int info;


  bigint conductor() const;
  bool is_invertible() const;

  bool is_normal() const;

  void normalize();
  void normalize_imag();
  void normalize_real();

  void reduce();
  void reduce_imag();
  void reduce_real();

  bigint oBJT_imag(long v) const;
  bigint oBJT_imag(long v, bigint & GO, bigint & NS) const;
  bigint oBJT_real() const;

  bigint omult_imag(const bigint & h, const rational_factorization & hfact) const;
  bigint omult_real(const bigint & h, const rational_factorization & hfact) const;

  bigint os_imag(long v, const bigint & hstar) const;
  bigint os_real(long v, const bigint & hstar) const;

  bigint osubexp_imag() const;
  bigint osubexp_real() const;

  bool DLBJT_imag(const qi_class & G, bigint & x, long v) const;
  bool DLBJT_real(const qi_class & G, bigint & x) const;

  bool DLh_imag(const qi_class & G, bigint & x) const;
  bool DLh_real(const qi_class & G, bigint & x) const;

  bool DLsubexp_imag(const qi_class & G, bigint & x) const;
  bool DLsubexp_real(const qi_class & G, bigint & x) const;

  friend base_vector <bigint> subBJT_imag(base_vector <qi_class> & G, base_vector <qi_class> & factor_base, bigint_matrix & U, base_vector <long> & v);
  friend base_vector <bigint> subBJT_real(base_vector <qi_class> & G, base_vector <qi_class> & factor_base, bigint_matrix & U);

  friend base_vector <bigint> subh_imag(base_vector <qi_class> & G, base_vector <qi_class> & factor_base, bigint_matrix & U);
  friend base_vector <bigint> subh_real(base_vector <qi_class> & G, base_vector <qi_class> & factor_base, bigint_matrix & U);


  /* utility function for group structure computations */
  friend void decode_vector(bigint_matrix & Bmat, const bigint & Bjj, long r, long q, base_vector <long> & Rvec, base_vector <long> & Qvec, long nR, long nQ);


  public:

  /**
   ** constructors and destructor
   **/

  qi_class();
  qi_class(const quadratic_form & qf);
  qi_class(const quadratic_ideal & A);
  qi_class(const qi_class_real & A);
  qi_class(const qi_class & A);

  ~qi_class();



  /** 
   ** initialization
   **/

  static void set_current_order(quadratic_order & QO);
  static void verbose(int state);



  /**
   ** assignment 
   **/

  void assign_zero();
  void assign_one();
  void assign_principal(const bigint & x, const bigint & y);
  bool assign(const bigint & a2, const bigint & b2);
  bool assign(const long a2, const long b2);

  void assign(const quadratic_form & qf);
  void assign(const quadratic_ideal & B);
  void assign(const qi_class_real & B);
  void assign(const qi_class & B);

  qi_class & operator = (const qi_class & A);



  /**
   ** access functions 
   **/

  static quadratic_order * get_current_order();
  static bigint discriminant();
  static bigint get_rootD();
  static bigfloat get_rd();

  inline bigint get_a() const
    { return a; }
  inline bigint get_b() const
    { return b; }
  inline bigint get_c() const
    {
      bigint c,temp;
      if (a.is_zero())
        c.assign_zero();
      else {
        square(c,b);
        subtract(c,c,Delta);
        shift_left(temp,a,2);
        divide(c,c,temp);
      }
      return c;
    }




  /**
   ** arithmetic operations
   **/


  friend void multiply(qi_class & C, const qi_class & A, const qi_class & B);
  friend void multiply_imag(qi_class & C, const qi_class & A, const qi_class & B);
  friend void nucomp(qi_class & C, const qi_class & A, const qi_class & B);
  friend void multiply_real(qi_class & C, const qi_class & A, const qi_class & B);

  void invert();
  friend void inverse(qi_class & A, const qi_class & B);
  friend qi_class inverse(const qi_class & B);

  friend void divide(qi_class & C, const qi_class & A, const qi_class & B);

  friend void square(qi_class & C, const qi_class & A);
  friend void square_imag(qi_class & C, const qi_class & A);
  friend void nudupl(qi_class & C, const qi_class & A);
  friend void square_real(qi_class & C, const qi_class & A);

  friend void power(qi_class & C, const qi_class & A, const bigint & i);
  friend void power(qi_class & C, const qi_class & A, const long i);
  friend void power_imag(qi_class & C, const qi_class & A, const bigint & i);
  friend void power_imag(qi_class & C, const qi_class & A, const long i);
  friend void nupower(qi_class & C, const qi_class & A, const bigint & i);
  friend void nupower(qi_class & C, const qi_class & A, const long i);
  friend void power_real(qi_class & C, const qi_class & A, const bigint & i);
  friend void power_real(qi_class & C, const qi_class & A, const long i);

  friend qi_class operator - (const qi_class & A);

  friend qi_class operator * (const qi_class & A, const qi_class & B);
  friend qi_class operator / (const qi_class & A, const qi_class & B);

  qi_class & operator *= (const qi_class & A);
  qi_class & operator /= (const qi_class & A);



  /**
   ** comparisons
   **/

  bool is_zero() const;
  bool is_one() const;
  bool is_equal(const qi_class & B) const;

  friend bool operator == (const qi_class & A, const qi_class & B);
  friend bool operator != (const qi_class & A, const qi_class & B);

  friend bool operator ! (const qi_class & A);



  /**
   ** basic functions
   **/

  friend void swap(qi_class & A, qi_class & B);
  friend bigint qi_class_key(const qi_class & G) { return G.get_a(); }



  /**
   ** high level functions
   **/

  friend bool prime_ideal(qi_class & A, const bigint & p);



  /**
   ** reduction operators
   **/

  void rho();
  friend void apply_rho(qi_class & A, const qi_class & B);
  friend qi_class apply_rho(const qi_class & B);
  
  void inverse_rho();
  friend void apply_inverse_rho(qi_class & A, const qi_class & B);
  friend qi_class apply_inverse_rho(const qi_class & B);



  /**
   ** equivalence and principality testing
   **/

  bool is_equivalent(const qi_class & B) const;
  bool is_principal() const;



  /**
   ** orders of elements
   **/

  bigint order_in_CL() const;

  /* order computation with no knowledge of class number */
  bigint order_BJT(long v) const;

  /* order compution when class number is known */
  bigint order_h() const;

  /* order compution when multiple of order is known */
  bigint order_mult(const bigint & h, const rational_factorization & hfact) const;

  /* order computation using estimate of L(1), class number not known */
  bigint order_shanks() const;

  /* subexponential order computation */
  bigint order_subexp() const;



  /**
   ** DL computation
   **/

  bool DL(const qi_class & G, bigint & x) const;

  /* DL computation with no knowledge of class number */
  bool DL_BJT(const qi_class & G, bigint & x, long v) const;

  /* DL computation when class number is known */
  bool DL_h(const qi_class & G, bigint & x) const;

  /* subexponential DL computation */
  bool DL_subexp(const qi_class & G, bigint & x) const;



  /**
   ** subgroup computation
   **/

  friend base_vector <bigint> subgroup(base_vector <qi_class> & G, base_vector <qi_class> & factor_base, bigint_matrix & U);

  /* subgroup computation with no knowledge of class number */
  friend base_vector <bigint> subgroup_BJT(base_vector <qi_class> & G, base_vector <qi_class> & factor_base, bigint_matrix & U, base_vector <long> & v);

  /* subgroup computation when class number is known */
  friend base_vector <bigint> subgroup_h(base_vector <qi_class> & G, base_vector <qi_class> & factor_base, bigint_matrix & U);


  /**
   ** input/output
   **/

  friend istream & operator >> (istream & in, qi_class & A);
  friend ostream & operator << (ostream & out, const qi_class & A);
};



#define PBJTB 40		/* for principality, use BJT if D < 10^40 */

class qi_class_real : public qi_class
{
  protected:

  /**
   ** An quadratic ideal equivalence class A is represented by a reduced,
   **	primitive ideal.  If the order is imaginary, then this representation
   **	is unique.
   **/

  bigfloat d;

  void reduce();

  public:

  /**
   ** constructors and destructor
   **/

  qi_class_real();
  qi_class_real(const quadratic_form & qf);
  qi_class_real(const quadratic_ideal & A);
  qi_class_real(const qi_class & A);
  qi_class_real(const qi_class_real & A);

  ~qi_class_real();



  /**
   ** assignment 
   **/

  void assign_zero();
  void assign_one();
  void assign_principal(const bigint & x, const bigint & y, const bigfloat & dist);
  bool assign(const bigint & a2, const bigint & b2, const bigfloat & dist);
  bool assign(const long a2, const long b2, const bigfloat & dist);
  void assign(const quadratic_form & qf, const bigfloat & dist);
  void assign(const quadratic_ideal & B, const bigfloat & dist);
  void assign(const qi_class & B, const bigfloat & dist);

  void assign_principal(const bigint & x, const bigint & y);
  bool assign(const bigint & a2, const bigint & b2);
  bool assign(const long a2, const long b2);
  void assign(const quadratic_form & qf);
  void assign(const quadratic_ideal & B);
  void assign(const qi_class & B);

  void assign(const qi_class_real & B);

  qi_class_real & operator = (const qi_class_real & A);



  /**
   ** access functions 
   **/

  inline bigfloat get_distance() const
    { return d; }



  /**
   ** arithmetic operations
   **/


  friend void multiply(qi_class_real & C, const qi_class_real & A, const qi_class_real & B);
  friend void multiply_real(qi_class_real & C, const qi_class_real & A, const qi_class_real & B);

  void invert();
  friend void inverse(qi_class_real & A, const qi_class_real & B);
  friend qi_class_real inverse(const qi_class_real & B);

  friend void divide(qi_class_real & C, const qi_class_real & A, const qi_class_real & B);

  friend void square(qi_class_real & C, const qi_class_real & A);
  friend void square_real(qi_class_real & C, const qi_class_real & A);

  friend void power(qi_class_real & C, const qi_class_real & A, const bigint & i);
  friend void power(qi_class_real & C, const qi_class_real & A, const long i);
  friend void power_real(qi_class_real & C, const qi_class_real & A, const bigint & i);
  friend void power_real(qi_class_real & C, const qi_class_real & A, const long i);

  friend qi_class_real nearest(const qi_class_real & S, const bigfloat & E);

  friend qi_class_real operator - (const qi_class_real & A);

  friend qi_class_real operator * (const qi_class_real & A, const qi_class_real & B);
  friend qi_class_real operator / (const qi_class_real & A, const qi_class_real & B);

  qi_class_real & operator *= (const qi_class_real & A);
  qi_class_real & operator /= (const qi_class_real & A);



  /**
   ** basic functions
   **/

  friend void swap(qi_class_real & A, qi_class_real & B);
  friend bigint qi_class_real_key(const qi_class_real & G) {return G.get_a(); };



  /**
   ** high_level functions
   **/

  friend bool prime_ideal(qi_class_real & A, const bigint & p);



  /**
   ** reduction operators
   **/

  void rho();
  friend void apply_rho(qi_class_real & A, const qi_class_real & B);
  friend qi_class_real apply_rho(const qi_class_real & B);

  void inverse_rho();
  friend void apply_inverse_rho(qi_class_real & A, const qi_class_real & B);
  friend qi_class_real apply_inverse_rho(const qi_class_real & B);

  /**
   ** equivalence and principality testing
   **/

  bool is_principal() const;
  bool is_principal_buch() const;
  bool is_principal_subexp() const;

  bool is_principal(bigfloat & dist) const;
  bool is_principal_buch(bigfloat & dist) const;
  bool is_principal_subexp(bigfloat & dist) const;

  bool is_equivalent(const qi_class_real & B) const;
  bool is_equivalent_buch(const qi_class_real & B) const;
  bool is_equivalent_subexp(const qi_class_real & B) const;

  bool is_equivalent(const qi_class_real & B, bigfloat & dist) const;
  bool is_equivalent_buch(const qi_class_real & B, bigfloat & dist) const;
  bool is_equivalent_subexp(const qi_class_real & B, bigfloat & dist) const;

  friend base_vector <bigint> subgroup(base_vector <qi_class_real> & G, base_vector <qi_class_real> & factor_base, bigint_matrix & U);

  /* subgroup computation with no knowledge of class number */
  friend base_vector <bigint> subgroup_BJT(base_vector <qi_class_real> & G, base_vector <qi_class_real> & factor_base, bigint_matrix & U, base_vector <long> & v);

  /* subgroup computation when class number is known */
  friend base_vector <bigint> subgroup_h(base_vector <qi_class_real> & G, base_vector <qi_class_real> & factor_base, bigint_matrix & U);



  /**
   ** input/output
   **/

  friend istream & operator >> (istream & in, qi_class_real & A);
  friend ostream & operator << (ostream & out, const qi_class_real & A);
};




class quadratic_ideal
{
  protected:

  /**
   ** An quadratic ideal is represented by a primitive quadratic ideal
   **   and a multiplier q.
   **
   **/

  bigint a;
  bigint b;
  bigrational q;
  qo_node *QO;

  bool is_normal() const;
  void normalize();
  void normalize_imag();
  void normalize_real();

  void reduce_imag();
  void reduce_real();

  void rho_imag();
  void rho_real();

  void inverse_rho_imag();
  void inverse_rho_real();

  friend void multiply_imag(quadratic_ideal & C, const quadratic_ideal & A, const quadratic_ideal & B);
  friend void multiply_real(quadratic_ideal & C, const quadratic_ideal & A, const quadratic_ideal & B);

  friend void square_imag(quadratic_ideal & C, const quadratic_ideal & A);
  friend void square_real(quadratic_ideal & C, const quadratic_ideal & A);

  public:

  /**
   ** constructors and destructor
   **/

  quadratic_ideal();
  quadratic_ideal(const quadratic_form & qf);
  quadratic_ideal(const qi_class & A);
  quadratic_ideal(const qi_class_real & A);
  quadratic_ideal(const quadratic_ideal & A);

  ~quadratic_ideal();



  /**
   ** assignments
   **/

  void assign_zero();
  void assign_one();
  void assign_principal(const bigint & x, const bigint & y);
  bool assign(const bigint & a2, const bigint & b2, const bigrational & q2);
  bool assign(const long a2, const long b2, const bigrational & q2);

  void assign_zero(quadratic_order & QO2);
  void assign_one(quadratic_order & QO2);
  void assign_principal(const bigint & x, const bigint & y, quadratic_order & QO2);
  bool assign(const bigint & a2, const bigint & b2, const bigrational & q2, quadratic_order & QO2);
  bool assign(const long a2, const long b2, const bigrational & q2, quadratic_order & QO2);

  void assign(const quadratic_form & qf);
  void assign(const qi_class & B);
  void assign(const qi_class_real & B);
  void assign(const quadratic_ideal & B);

  bool assign_order(quadratic_order & QO2);

  quadratic_ideal & operator = (const quadratic_ideal & A);



  /**
   ** access functions
   **/

  inline bigint get_a() const
    { return a; }
  inline bigint get_b() const
    { return b; }
  inline bigrational get_q() const
    { return q; }
  bigint get_c() const;
  quadratic_order * which_order() const;
  friend quadratic_order * which_order(const quadratic_ideal & A);
  bigint discriminant() const;



  /**
   ** arithmetic operations
   **/

  friend void add(quadratic_ideal & C, const quadratic_ideal & A, const quadratic_ideal & B);
  friend void intersect(quadratic_ideal & C, const quadratic_ideal & A, const quadratic_ideal & B);
  friend void multiply(quadratic_ideal & C, const quadratic_ideal & A, const quadratic_ideal & B);
  friend void divide(quadratic_ideal & C, const quadratic_ideal & A, const quadratic_ideal & B);
  void invert();
  friend void inverse(quadratic_ideal & A, const quadratic_ideal & B);
  friend quadratic_ideal inverse(const quadratic_ideal & B);
  void conjugate();
  friend void get_conjugate(quadratic_ideal & A, const quadratic_ideal & B);
  friend quadratic_ideal get_conjugate(const quadratic_ideal & B);
  friend void square(quadratic_ideal & C, const quadratic_ideal & A);
  friend void power(quadratic_ideal & C, const quadratic_ideal & A, const bigint & i);
  friend void power(quadratic_ideal & C, const quadratic_ideal & A, const long i);

  friend quadratic_ideal operator - (const quadratic_ideal & A);

  friend quadratic_ideal operator + (const quadratic_ideal & A, const quadratic_ideal & B);
  friend quadratic_ideal operator & (const quadratic_ideal & A, const quadratic_ideal & B);
  friend quadratic_ideal operator * (const quadratic_ideal & A, const quadratic_ideal & B);
  friend quadratic_ideal operator / (const quadratic_ideal & A, const quadratic_ideal & B);

  quadratic_ideal & operator += (const quadratic_ideal & A);
  quadratic_ideal & operator &= (const quadratic_ideal & A);
  quadratic_ideal & operator *= (const quadratic_ideal & A);
  quadratic_ideal & operator /= (const quadratic_ideal & A);



  /**
   ** comparisons
   **/

  bool is_zero() const;
  bool is_one() const;
  bool is_equal(const quadratic_ideal & B) const;
  bool is_subset(const quadratic_ideal & B) const;
  bool is_proper_subset(const quadratic_ideal & B) const;

  friend bool operator == (const quadratic_ideal & A, const quadratic_ideal & B);
  friend bool operator != (const quadratic_ideal & A, const quadratic_ideal & B);

  friend bool operator <= (const quadratic_ideal & A, const quadratic_ideal & B);
  friend bool operator < (const quadratic_ideal & A, const quadratic_ideal & B);
  friend bool operator >= (const quadratic_ideal & A, const quadratic_ideal & B);
  friend bool operator > (const quadratic_ideal & A, const quadratic_ideal & B);

  friend bool operator ! (const quadratic_ideal & A);



  /**
   ** basic functions
   **/

  friend void swap(quadratic_ideal & A, quadratic_ideal & B);
  friend bigint quadratic_ideal_key(const quadratic_ideal & G) { return G.get_a(); };


  /**
   ** high level functions
   **/

  bigrational smallest_rational() const;
  bool is_integral() const;
  bigint conductor() const;
  bool is_invertible() const;
  bigrational norm() const;
  quadratic_order ring_of_multipliers();
  friend bool prime_ideal(quadratic_ideal & A, const bigint & p);
  friend bool prime_ideal(quadratic_ideal & A, const bigint & p, quadratic_order & QO2);



  /**
   ** reduction
   **/

  bool is_reduced() const;
  void reduce();
  void rho();
  friend void apply_rho(quadratic_ideal & A, const quadratic_ideal & B);
  friend quadratic_ideal apply_rho(const quadratic_ideal & B);

  void inverse_rho();
  friend void apply_inverse_rho(quadratic_ideal & A, const quadratic_ideal & B);
  friend quadratic_ideal apply_inverse_rho(const quadratic_ideal & B);


  /**
   ** equivalence and principality testing
   **/

  bool is_equivalent(const quadratic_ideal & B) const;
  bool is_principal() const;



  /**
   ** order, DL, subgroup
   **/

  bigint order_in_CL() const;
  bool DL(quadratic_ideal & G, bigint & x) const;
  friend base_vector <bigint> subgroup(base_vector <quadratic_ideal> & G, base_vector <quadratic_ideal> & factor_base, bigint_matrix & U);
  bigfloat regulator();
  bigint class_number();
  base_vector <bigint> class_group();


  /**
   ** input/output
   **/

  friend istream & operator >> (istream & in, quadratic_ideal & A);
  friend ostream & operator << (ostream & out, const quadratic_ideal & A);
};



class quadratic_form
{
  protected:

  /**
   ** An quadratic form is represented by an ordered triple
   **   (a,b,c) which represents the polynomial ax^2 + bxy + cy^2, together
   **   with a pointer to a quadratic order.
   **
   **/

  bigint a;
  bigint b;
  bigint c;
  qo_node *QO;


  bigint norm_number();
  bigint norm_number_pos_def();
  bigint norm_number_indef();
  
  bool is_reduced_pos_def() const; 
  bool is_reduced_indef() const;
  bool is_reduced_irregular() const;
  
  void almost_reduce_irregular(matrix_GL2Z & U);
  void reduce_irregular();
  void reduce_irregular(matrix_GL2Z & U);

  bool prop_equivalent_pos_def(const quadratic_form & g) const;
  bool prop_equivalent_neg_def(const quadratic_form & g) const;
  bool prop_equivalent_indef(const quadratic_form & g) const;
  bool prop_equivalent_irregular(const quadratic_form & g) const;

  bool prop_equivalent_pos_def(const quadratic_form & g, matrix_GL2Z & U) const;
  bool prop_equivalent_neg_def(const quadratic_form & g, matrix_GL2Z & U) const;
  bool prop_equivalent_indef(const quadratic_form & g, matrix_GL2Z & U) const;
  bool prop_equivalent_irregular(const quadratic_form & g, matrix_GL2Z & U) const;

  bool comp_reps_irregular(bigint_matrix & Reps, const bigint & N);


 public:

  /**
   ** constructors and destructor
   **/

  quadratic_form();
  quadratic_form(const qi_class & A);
  quadratic_form(const qi_class_real & A);
  quadratic_form(const quadratic_ideal & A);
  quadratic_form(const quadratic_form & f);

  ~quadratic_form();


  /** 
   ** initialization
   **/

  static void set_current_order(quadratic_order & QO);

  /**
   ** assignments
   **/

  void assign_zero();
  void assign_one(const bigint & newDelta);
  void assign(const bigint & a2, const bigint & b2, const bigint & c2);
  void assign(const long a2, const long b2, const long c2);

  void assign(const qi_class & A);
  void assign(const qi_class_real & A);
  void assign(const quadratic_ideal & A);
  void assign(const quadratic_form & g);

  quadratic_form & operator = (const quadratic_form & g);


  /**
   ** access functions
   **/

  inline bigint get_a() const
    { return a; }
  inline bigint get_b() const
    { return b; }
  inline bigint get_c() const
    { return c; }
  bigint discriminant() const;
  quadratic_order * which_order() const;
  friend quadratic_order * which_order(const quadratic_form & f);



  /**
   ** arithmetic operations
   **/

  friend void compose(quadratic_form & f, const quadratic_form & g1, const quadratic_form & g2);
  friend void compose_reduce(quadratic_form & f, const quadratic_form & g1, const quadratic_form & g2);
  friend void nucomp(quadratic_form & f, const quadratic_form & g1, const quadratic_form & g2);
  void conjugate();
  friend void get_conjugate(quadratic_form & f, const quadratic_form & g);
  friend quadratic_form get_conjugate(const quadratic_form & f);
  friend void divide(quadratic_form & f, const quadratic_form & g1, const quadratic_form & g2);
  friend void divide_reduce(quadratic_form & f, const quadratic_form & g1, const quadratic_form & g2);
  friend void square(quadratic_form & f, const quadratic_form & g);
  friend void square_reduce(quadratic_form & f, const quadratic_form & g);
  friend void nudupl(quadratic_form & f, const quadratic_form & g);
  friend void power(quadratic_form & f, const quadratic_form & g, const bigint & i);
  friend void power(quadratic_form & f, const quadratic_form & g, const long i);
  friend void power_reduce(quadratic_form & f, const quadratic_form & g, const bigint & i);
  friend void power_reduce(quadratic_form & f, const quadratic_form & g, const long i);
  friend void nupower(quadratic_form & f, const quadratic_form & g, const bigint & i);
  friend void nupower(quadratic_form & f, const quadratic_form & g, const long i);

  friend quadratic_form operator - (const quadratic_form & f);

  friend quadratic_form operator * (const quadratic_form & f, const quadratic_form & g);
  friend quadratic_form operator / (const quadratic_form & f, const quadratic_form & g);

  quadratic_form & operator *= (const quadratic_form & f);
  quadratic_form & operator /= (const quadratic_form & f);



  /**
   ** comparisons
   **/

  bool is_zero() const;
  bool is_one() const;
  bool is_equal(const quadratic_form & g) const;
  int compare(const quadratic_form & g) const;
  int abs_compare(const quadratic_form & g) const;
 

  friend bool operator == (const quadratic_form & f, const quadratic_form & g);
  friend bool operator != (const quadratic_form & f, const quadratic_form & g);

  friend bool operator <= (const quadratic_form & f, const quadratic_form & g); 
  friend bool operator < (const quadratic_form & f, const quadratic_form & g);
  friend bool operator >= (const quadratic_form & f, const quadratic_form & g);
  friend bool operator > (const quadratic_form & f, const quadratic_form & g);

  friend bool operator ! (const quadratic_form & f);



  /**
   ** basic functions
   **/

  friend void swap(quadratic_form & f, quadratic_form & g);
  friend bigint quadratic_form_key(const quadratic_form & G) { return G.get_a(); };



  /**
   ** high level functions
   **/

  int definiteness() const;
  bool is_pos_definite() const;
  bool is_pos_semidefinite() const;
  bool is_indefinite() const;
  bool is_neg_definite() const;
  bool is_neg_semidefinite() const;
  bool is_regular() const;

  bigint content() const;
  bool is_primitive() const;

  bigint eval(const bigint & x , const bigint & y) const;
  bigint operator () (const bigint & x, const bigint & y);

  void transform(const matrix_GL2Z & U);

  friend bool prime_form(quadratic_form & f, const bigint & p, const bigint & newDelta);



  /**
   ** normalization and reduction
   **/

  bool is_normal() const;
  void normalize();
  void normalize(matrix_GL2Z & U);

  bool is_reduced() const;
  void reduce();
  void reduce(matrix_GL2Z & U);

  void rho();
  void rho(matrix_GL2Z & U);
  void inverse_rho();
  void inverse_rho(matrix_GL2Z & U);



  /**
   ** equivalence and principality testing
   **/

  bool is_equivalent(const quadratic_form & g) const;
  bool is_prop_equivalent(const quadratic_form & g) const;
  bool is_principal() const;

  bool is_equivalent(const quadratic_form & g, matrix_GL2Z & U) const;
  bool is_prop_equivalent(const quadratic_form & g, matrix_GL2Z & U) const;
  bool is_principal(matrix_GL2Z & U) const;



  /**
   ** order, DL, subgroup
   **/

  bigint order_in_CL() const;
  bool DL(quadratic_form & g, bigint & x) const;
  friend base_vector <bigint> subgroup(base_vector <quadratic_form> & G, base_vector <quadratic_form> & factor_base, bigint_matrix & U);
  bigfloat regulator();
  bigint class_number();
  base_vector <bigint> class_group();

  void fundamental_automorphism(matrix_GL2Z &);



  /**
   ** Representations
   **/

  bool representations(bigint_matrix & Reps, const bigint & N);



  /**
   ** input/output
   **/

  friend istream & operator >> (istream & in, quadratic_form & f);
  friend ostream & operator << (ostream & out, const quadratic_form & f);
};				



class ideal_node
{
  private:

  qi_class A;
  long index;

  public:

  ideal_node() {}
  ideal_node(const qi_class & G, const long idx)
    {A.assign(G); index = idx; }
  ideal_node(const qi_class & G, const bigint & idx)
    {A.assign(G); idx.longify(index); }
  ~ideal_node() {}

  qi_class get_A() const { return A; }
  long get_index() const {return index;}
  void assign(const qi_class & G, const long idx)
    { A.assign(G); index = idx; }
  void assign(const qi_class & G, const bigint & idx)
    { A.assign(G); idx.longify(index); }
  void assign(const ideal_node & G)
    { A.assign(G.A); index = G.index; }
  ideal_node operator = (const ideal_node & B)
    { A.assign(B.A); index = B.index; return *this; }
  friend bool operator == (const ideal_node & N1, const ideal_node & N2)
    { return (N1.A == N2.A); }
  friend bool operator < (const ideal_node & N1, const ideal_node & N2)
    { return (N1.A.get_a() < N2.A.get_a()); }
  friend bool operator <= (const ideal_node & N1, const ideal_node & N2)
    { return ((N1 < N2) || (N1 == N2)); }
  friend bool operator > (const ideal_node & N1, const ideal_node & N2)
    { return (N1.A.get_a() > N2.A.get_a()); }
  friend bool operator >= (const ideal_node & N1, const ideal_node & N2)
    { return ((N1 > N2) || (N1 == N2)); }
  void assign_zero()
    { A.assign_zero(); index = 0; }
  bool is_zero()
    { return A.is_zero(); }
  friend bigint ideal_node_key(const ideal_node & G)
    { qi_class A; A.assign(G.get_A()); return A.get_a();}
  friend void swap(ideal_node & A, ideal_node & B)
    { ideal_node C; C = A; A = B; B = C; }
  friend istream & operator >> (istream & in, ideal_node & A)
    { in >> A.A; in >> A.index; return in; }
  friend ostream & operator << (ostream & out, const ideal_node & A)
    { out << "[" << A.A << ", index = " << A.index << "]"; return out; }

};



#define MAXHVAL 100000          /* initial size of prime list */
#define MPRIMES 6500
#define MAXBITS 1001000


class prime_lists
{
  private:
    int already_read;

  public:

  long *primes;
  short *bitarray;
  long Lbitval;
  long Hbitval;

  prime_lists();
  ~prime_lists();

  void GetPrimes();
  void PrimeGen(long L, long H);
  void PrimeGen2();

  void MyFactor(long N, long & nfacts, long *FL, long *expL);
};



class qo_node
{
  private:
    
    quadratic_order *QO;      // pointer to a quadratic order
    unsigned int rfc;         // reference counter
    bool dynamic;             // true if the order was created dynamically
    qo_node *next;            // pointer to the successor

  public:

    // *** constructors ***

    qo_node() { rfc = 1; dynamic = false; };
    qo_node(quadratic_order *QO2, bool is_dyn)
      { QO = QO2; rfc = 1; dynamic = is_dyn; };
    qo_node(quadratic_order *QO2, qo_node *n, bool is_dyn)
      { QO = QO2; next = n; rfc = 1; dynamic = is_dyn; };
    ~qo_node()
      {  if (dynamic)  delete QO; };

    // *** rfc handling ***

    unsigned int get_ref() const
    { return rfc; }

    unsigned int inc_ref()
    { rfc++; return rfc; }

    unsigned int dec_ref()
    { rfc--; return rfc; }

    // *** basic functions ***

    const quadratic_order *get_qo() const
    {return QO;}

    quadratic_order *get_qo()
    {return QO;}
    
    void set_qo(quadratic_order *QO2)
    {QO = QO2;}

    void set_succ(qo_node *e)
    {next = e;}

    qo_node *succ()
    {return next;}    
};


class qo_list
{
  protected:
    
    qo_node * head;

  public:

    //
    // *** constructor ***
    //

    qo_list() 
     {head = NULL;}

    
    //
    // *** destructor ***
    //

    ~qo_list() 
      {
	qo_node *next;

        while (head != NULL)
	 {
	    next = head->succ();
	    delete head;
            head = next;
	 }
      }
	  

    //
    // *** basic functions ***
    //

    const qo_node *first() const
      {return head;}

    qo_node *first()
      {return head;}


    bool print(ostream & out = cout) const
    {

      //               true :  all list elements have been printed 

      if (head == NULL) {
         cout << "EMPTY\n";
         return false;
      }
      else
       {
	 qo_node *e = head;

	 while (e != NULL)
	  {
            if (e->get_qo())
              out << "<" << e->get_qo()->discriminant() << ", " << e->get_ref() << ">";
            else
              out << "<NULL, " << e->get_ref() << ">";
	    e = e->succ();
	  }
	 out << "\n";
	 return true;
       }
    }


    void nullify(quadratic_order *QO2)
    {
      //
      // searches for an existing qo with QO = QO2
      //

      qo_node *e = head;

      while (e != NULL)
      {
	// found ?
	if (e->get_qo() == QO2)
	 {
	   e->set_qo(NULL);
           break;
	 }
	else
	   e = e->succ();
      }
    }



    qo_node *insert(quadratic_order & QO2, bool is_dyn)
    {
      //
      // searches for an existing qo with QO = &QO2
      //

      qo_node *e = head;

      while (e != NULL)
      {
	// found ?
	if (e->get_qo() == &QO2)
	 {
	   e->inc_ref();
	   return e;
	 }
	else
	   e = e->succ();
      }

      //
      // quadratic_order is not in the list yet => create it
      //

      e = new qo_node(&QO2, head, is_dyn);
      head = e;
      return e;
    }



    qo_node *set_to(qo_node * a)
     {
       // WARNING: For efficiency reasons, the user has to take
       // the responsibility, that the pointer a really points to
       // an element of the list *this.

       if (a)   a->inc_ref();
       return a;
     }



    int clear(qo_node * elmnt)
     {

       // return value: 0  the qo_node to be deleted doesnt exist
       //               1  the rfc of the qo_node to be deleted is decreased
       //		2  the qo_node is deleted

       // WARNING: To avoid side effects, elmnt must point
       // to an element of the list *this or should be NULL, because,
       // if the rfc of elmnt is greater than one, the
       // rfc is decreased. In this case, it is not verified,
       // whether elmnt points to an elemnt of the list.

       int rc;

       if (head == NULL || elmnt == NULL)
          rc = 0;
       else
        {
          //
          // just decrease the ref. counter
          //

          if (elmnt->get_ref() > 1)
           {
             elmnt->dec_ref();
             rc = 1;
           }

          //
          // otherwise, remove the element from the list
          //

          else
           {
              qo_node *e, *pred;
              rc = 2;

              //	
              // elmnt is the head of the list
              //

              if (elmnt == head)
               {
                 e = head->succ();
                 delete head;
                 head = e;
               }

              //
              // search for the predecessor of elmnt in the list
              //

              else
               {
                 pred = head;
                 e    = head->succ();

                 while (e != elmnt && e != NULL)
                  {
                    pred = e;
                    e = e->succ();
                  }

                  // found ?

                  if (e == elmnt)
                   {
                     pred->set_succ(e->succ());
                     delete e;
                   }
                  else
                     rc = 0;
               }
            }
         }

      return (rc);
    }
};



inline void
nugcd(bigint & u, bigint & d, bigint & v1, bigint & v3, bigint & PEA_L)
{
  bigint t1, t3, q;
  bigint h, v1_x, v3_x, d_x, u_x;
  bool flag;

  remainder(v3,v3,d);
  if (v3.is_lt_zero())
    add(v3,v3,d);

  subtract(u,d,v3);
  if (abs(u) < abs(v3)) {
    v3.assign(u);
    flag = true;
  }
  else
    flag = false;

  v1.assign_one();
  u.assign_zero();

  // trivial case
  if (abs(v3) <= PEA_L) {
    if (flag)
      v3.negate();
    return;
  }

  // iteration
  u_x.assign(u);
  d_x.assign(d);
  v1_x.assign(v1);
  v3_x.assign(v3);

  do {
    div_rem(q,t3,d_x,v3_x);

    multiply(t1,q,v1_x);
    subtract(t1,u_x,t1);

    h.assign(u_x); u_x.assign(v1_x); v1_x.assign(t1); t1.assign(h);
    h.assign(d_x); d_x.assign(v3_x); v3_x.assign(t3); t3.assign(h);
  } while (abs(v3_x) > PEA_L) ;

  // store results
  if (u_x == v1) {
    u.assign(u_x); v1.assign(v1_x); d.assign(d_x); v3.assign(v3_x);
  }
  else if ( u_x != u ) {
    v1.assign(v1_x); u.assign(u_x); v3.assign(v3_x); d.assign(d_x);
  }

  // modify signs
  if (flag) {
    v1.negate();
    u.negate();
  }

  if (v1.is_lt_zero()) {
    v1.negate();
    v3.negate();
  }
}

#endif
