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

#if defined(HAVE_MAC_DIRS) || defined(__MWERKS__)
#include <LiDIA:quadratic_order.h>
#else
#include <LiDIA/quadratic_order.h>
#endif

main()
{
  bigint D;
  quadratic_form A,B,C,F,U;
  base_vector <quadratic_form> Gvec,FB;
  base_vector <bigint> S;
  bigint_matrix UM,Reps;
  bigint p,x,y,dlog,N;
  matrix_GL2Z M1,NMAT(1,0,0,-1);
  int m4,negdef;

  Gvec.set_mode(EXPAND);
  FB.set_mode(EXPAND);

  do {
    cout << "Enter an integer = 0 or 1 (mod 4): " << flush;
    cin >> D;
    m4 = remainder(D,4);
    if (m4 < 0)
      m4 += 4;
  } while (m4 > 1);

  if (D < 0) {
    cout << "- nefinite (0 = +definite / 1= -nefinite)? " << flush;
    cin >> negdef;
  }

  U.assign_one(D);
  if (negdef)
    U.transform(NMAT);
  cout << "Discriminant = " << U.discriminant() << "\n" << flush;
  cout << "Unit = " << U << "\n" << flush;

  if (U.is_regular())
    qi_class::set_current_order(*U.which_order());

  p = 3;
  while (!prime_form(A,p,D)) {
    p = next_prime(p);
  }
  p = next_prime(p);
  while (!prime_form(B,p,D)) {
    p = next_prime(p);
  }
  if (negdef) {
    A.transform(NMAT);
    B.transform(NMAT);
  }

  cout << "A = " << A << "\n" << flush;
  if ((A.is_regular()) && (A.is_primitive())) {
    cout << "A as qi_class - " << (qi_class) A << "\n" << flush;
    if (A.is_indefinite())
      cout << "A as qi_class_real - " << (qi_class_real) A << "\n" << flush;
  }
  if (A.is_regular())
    cout << "A as quadratic_ideal - " << (quadratic_ideal) A << "\n" << flush;
  cout << "definiteness - " << A.definiteness() << "\n" << flush;
  cout << "A.content = " << A.content() << "\n" << flush;
  cout << "primitive? " << A.is_primitive() << "\n" << flush;
  cout << "normal? " << A.is_normal() << "\n" << flush;
  C = A;
  M1 = matrix_GL2Z(1,0,0,1);
  C.normalize(M1);
  cout << "A normalized = " << C << "\n" << flush;
  cout << "transformation =\n" << M1 << "\n" << flush;
  C = A;
  C.transform(M1);
  cout << "A*M = " << C << "\n" << flush;
  cout << "reduced? " << A.is_reduced() << "\n" << flush;
  C = A;
  M1 = matrix_GL2Z(1,0,0,1);
  C.reduce(M1);
  cout << "A reduced = " << C << "\n" << flush;
  cout << "transformation =\n" << M1 << "\n" << flush;
  C = A;
  C.transform(M1);
  cout << "A*M = " << C << "\n\n" << flush;

  cout << "B = " << B << "\n" << flush;
  cout << "B.a = " << B.get_a() << "\n" << flush;
  cout << "B.b = " << B.get_b() << "\n" << flush;
  cout << "B.c = " << B.get_c() << "\n" << flush;
  cout << "conjugate = " << get_conjugate(B) << "\n" << flush;
  cout << "B*conjugate(B) = " << B*get_conjugate(B) << "\n" << flush;
  C = B;
  C.reduce();
  cout << "B reduced = " << C << "\n" << flush;
  if ((B.is_regular()) && (B.is_primitive())) {
    cout << "B as qi_class - " << qi_class(B) << "\n" << flush;
    if (B.is_indefinite())
      cout << "B as qi_class_real - " << qi_class_real(B) << "\n" << flush;
  }
  if (B.is_regular())
    cout << "B as quadratic_ideal - " << quadratic_ideal(B) << "\n\n" << flush;

  cout << "Testing composition...\n" << flush;
  cout << "A*B = " << A * B << "\n" << flush;
  C = A*B;
  C.reduce();
  cout << "reduced = " << C << "\n" << flush;
  cout << "Testing square...\n" << flush;
  square(C,A);
  cout << "A*A = " << C << "\n" << flush;
  C = A*A;
  C.reduce();
  cout << "reduced = " << C << "\n" << flush;
  if (!A.is_indefinite() && A.is_regular()) {
    cout << "Testing nucomp...\n" << flush;
    nucomp(C,A,B);
    cout << "A*B = " << C << "\n" << flush;
    cout << "Testing nudupl...\n" << flush;
    nudupl(C,A);
    cout << "A*A = " << C << "\n" << flush;
  }
  cout << "Testing division...\n" << flush;
  cout << "A/B = " << A / B << "\n" << flush;
  cout << "is (A/A) = (1)? " << ((A / A) == U) << "\n\n" << flush;

  if (U.is_indefinite()) {
    cout << "Testing rho() and inverse_rho()...\n" << flush;
    cout << U << "\n" << flush;
    C = U;
    do {
      C.rho();
      cout << C << "\n" << flush;
    } while (!C.is_one());

    cout << "Testing rho() and inverse_rho() (non-principal ideal)...\n" << flush;
    cout << A << "\n" << flush;
    C = A;
    C.normalize();
    F = C;
    do {
      C.rho();
      cout << C << "\n" << flush;
    } while (!C.is_equal(F));
  }
  cout << "\n" << flush;

  cout << "Testing rho() and inverse_rho()...\n" << flush;
  if (A.is_regular()) {
    power(C,A,30);
    cout << "A^30 = " << C << "\n" << flush;
    C.reduce();
    cout << "reduced = " << C << "\n" << flush;
    power(C,A,30);
    M1 = matrix_GL2Z(1,0,0,1);
    cout << "A^30 = " << C << "\n" << flush;
    do {
      C.rho(M1);
      cout << C << "\n" << flush;
    } while (!C.is_reduced());
    cout << "transformation =\n" << M1 << "\n" << flush;
    power(C,A,30);
    C.transform(M1);
    cout << "A^30*M = " << C << "\n\n" << flush;
  }

/*
  power(C,A,30);
  M1 = matrix_GL2Z(1,0,0,1);
  cout << "A^30 = " << C << "\n" << flush;
  do {
    C.inverse_rho();
    cout << C << "\n" << flush;
  } while (!C.is_reduced());
  cout << "transformation =\n" << M1 << "\n" << flush;
  power(C,A,30);
  C.transform(M1);
  cout << "A^30*M = " << C << "\n\n" << flush;
*/
 
  cout << "testing principality test:\n" << flush;
  power(C,A,A.class_number());
  C.reduce();
  cout << "P = " << C << "\n" << flush;
  M1 = matrix_GL2Z(1,0,0,1);
  cout << "principal? " << C.is_principal(M1) << "\n" << flush;
  cout << "transformation =\n" << M1 << "\n" << flush;
  C = U;
  C.transform(M1);
  cout << "U*M = " << C << "\n\n" << flush;

  M1 = matrix_GL2Z(1,0,0,1);
  cout << "A = " << A << "\n" << flush;
  cout << "principal? " << A.is_principal(M1) << "\n" << flush;
  cout << "transformation =\n" << M1 << "\n" << flush;
  C = U;
  C.transform(M1);
  cout << "U*M = " << C << "\n\n" << flush;

  cout << "testing equivalence test:\n" << flush;
  cout << "A = " << A << "\n" << flush;
  cout << "B = " << B << "\n" << flush;
  C = A;
  C.rho();
  cout << "C = rho(A) = " << C << "\n" << flush;
  M1 = matrix_GL2Z(1,0,0,1);
  cout << "B equivalent to A? " << B.is_equivalent(A,M1) << "\n" << flush;
  cout << "transformation =\n" << M1 << "\n" << flush;
  F = A;
  F.transform(M1);
  cout << "A*M = " << F << "\n\n" << flush;
  M1 = matrix_GL2Z(1,0,0,1);
  cout << "A equivalent to C? " << A.is_equivalent(C,M1) << "\n" << flush;
  cout << "transformation =\n" << M1 << "\n" << flush;
  F = C;
  F.transform(M1);
  cout << "C*M = " << F << "\n\n" << flush;
  M1 = matrix_GL2Z(1,0,0,1);
  cout << "C equivalent to A? " << C.is_equivalent(A,M1) << "\n" << flush;
  cout << "transformation =\n" << M1 << "\n" << flush;
  F = A;
  F.transform(M1);
  cout << "A*M = " << F << "\n\n" << flush;

  cout << "testing proper equivalence test:\n" << flush;
  cout << "A = " << A << "\n" << flush;
  cout << "B = " << B << "\n" << flush;
  C = A;
  C.rho();
  cout << "C = rho(A) = " << C << "\n" << flush;
  M1 = matrix_GL2Z(1,0,0,1);
  cout << "A equivalent to B? " << A.is_prop_equivalent(B,M1) << "\n" << flush;
  cout << "transformation =\n" << M1 << "\n" << flush;
  F = B;
  F.transform(M1);
  cout << "B*M = " << F << "\n\n" << flush;
  M1 = matrix_GL2Z(1,0,0,1);
  cout << "A equivalent to C? " << A.is_prop_equivalent(C,M1) << "\n" << flush;
  cout << "transformation =\n" << M1 << "\n" << flush;
  F = C;
  F.transform(M1);
  cout << "C*M = " << F << "\n\n" << flush;
  M1 = matrix_GL2Z(1,0,0,1);
  cout << "C equivalent to A? " << C.is_prop_equivalent(A,M1) << "\n" << flush;
  cout << "transformation =\n" << M1 << "\n" << flush;
  F = A;
  F.transform(M1);
  cout << "A*M = " << F << "\n\n" << flush;

  cout << "Testing power...\n" << flush;
  cout << "Input x: " << flush;
  cin >> x;
  cout << "\n" << flush;
  power(C,A,x);
  cout << "C = A^" << x << " = " << C << "\n" << flush;
  C.reduce();
  cout << "reduced = " << C << "\n\n" << flush;
  power(C,A,x);

  cout << "testing order, DL, and subgroup algorithms...\n" << flush;
  cout << "order of " << A << " = " << A.order_in_CL() << "\n" << flush;
  cout << "order of " << B << " = " << B.order_in_CL() << "\n" << flush;

  if (C.DL(A,dlog))
    cout << "log_A C = " << dlog << "\n" << flush;
  else
    cout << "no log_A C:  order(A) = " << dlog << "\n" << flush;
  if (C.DL(B,dlog))
    cout << "log_B C = " << dlog << "\n" << flush;
  else
    cout << "no log_B C:  order(B) = " << dlog << "\n" << flush;

  Gvec[0] = A;
  Gvec[1] = B;
  Gvec[2] = A*B;
  S = subgroup(Gvec,FB,UM);
  cout << "< " << Gvec << " > = " << S << "\n" << flush;
  cout << "elements used: " << FB << "\n" << flush;
  cout << "transformation matrix:\n" << flush;
  cout << UM << "\n\n" << flush;

  cout << "testing regulator, class number, class group...\n" << flush;
  if (A.is_indefinite())
    cout << "R = " << A.regulator() << "\n" << flush;
  cout << "h = " << A.class_number() << "\n" << flush;
  cout << "CL = " << A.class_group() << "\n\n" << flush;

  cout << "testing order, DL, and subgroup algorithms (with knowledge of h)...\n" << flush;
  cout << "order of " << A << " = " << A.order_in_CL() << "\n" << flush;
  cout << "order of " << B << " = " << B.order_in_CL() << "\n" << flush;
  if (C.DL(A,dlog))
    cout << "log_A C = " << dlog << "\n" << flush;
  else
    cout << "no log_A C:  order(A) = " << dlog << "\n" << flush;
  if (C.DL(B,dlog))
    cout << "log_B C = " << dlog << "\n" << flush;
  else
    cout << "no log_B C:  order(B) = " << dlog << "\n" << flush;
  Gvec[0] = A;
  Gvec[1] = B;
  Gvec[2] = A*B;
  S = subgroup(Gvec,FB,UM);
  cout << "< " << Gvec << " > = " << S << "\n" << flush;
  cout << "elements used: " << FB << "\n" << flush;
  cout << "transformation matrix:\n" << flush;
  cout << UM << "\n\n" << flush;

/*
  cout << "testing representation routines...\n" << flush;
  cout << "Enter an integer: " << flush;
  cin >> N;
  if (A.representations(Reps,N)) {
    cout << A << " represents N:\n" << flush;
    cout << Reps << "\n\n" << flush;
  }
  else
    cout << A << "does not represent N\n\n" << flush;

  if (B.representations(Reps,N)) {
    cout << B << " represents N:\n" << flush;
    cout << Reps << "\n\n" << flush;
  }
  else
    cout << B << "does not represent N\n\n" << flush;
*/
}
