//
// LiDIA - a library for computational number theory
//   Copyright (c) 1994, 1995 by the LiDIA Group
//
// File        : qi_class_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()
{
  quadratic_order QO1;
  bigint D;
  qi_class A,B,C,U;
  base_vector <qi_class> Gvec,FB;
  base_vector <bigint> S;
  bigint_matrix UM;
  bigint p,x,y,dlog;
  int vb;

  cout << "Enter verbosity: " << flush;
  cin >> vb;
  qi_class::verbose(vb);

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

  do {
    cout << "Enter a quadratic discriminant: " << flush;
    cin >> D;
  } while (!QO1.assign(D));

  qi_class::set_current_order(QO1);

  U.assign_one();
  cout << "Current order = " << *U.get_current_order() << "\n" << flush;
  cout << "Discriminant = " << U.discriminant() << "\n" << flush;
  cout << "(1) = " << U << "\n" << flush;

  p = 3;
  while (!prime_ideal(A,p)) {
    p = next_prime(p);
  }
  cout << "A = " << A << "\n" << flush;
  if (QO1.is_real())
    cout << "A as qi_class_real - " << (qi_class_real) A << "\n" << flush;
  cout << "A as quadratic_ideal - " << (quadratic_ideal) A << "\n" << flush;
  cout << "A as quadratic_form - " << (quadratic_form) A << "\n" << flush;

  p = next_prime(p);
  while (!prime_ideal(B,p)) {
    p = next_prime(p);
  }

  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 << "inverse = " << -B << "\n" << flush;
  cout << "B*inverse(B) = " << B*inverse(B) << "\n" << flush;
  if (QO1.is_real())
    cout << "B as qi_class_real - " << qi_class_real(B) << "\n" << flush;
  cout << "B as quadratic_ideal - " << quadratic_ideal(B) << "\n" << flush;
  cout << "B as quadratic_form - " << quadratic_form(B) << "\n\n" << flush;

  cout << "Testing multiplication...\n" << flush;
  cout << "A*B = " << A * B << "\n" << flush;
  cout << "Testing square...\n" << flush;
  square(C,A);
  cout << "A*A = " << C << "\n" << flush;
  if (QO1.is_imaginary()) {
    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;

  power(C,A,30);
  cout << "A^30 = " << C << "\n\n" << flush;

  if (U.discriminant() > 0) {
    cout << "Testing rho() and inverse_rho()...\n" << flush;
    cout << U << "\n" << flush;
    apply_rho(C,U);
    cout << "rho(U) - " << C << "\n" << flush;
    C.inverse_rho();
    cout << "U.inverse_rho() - " << C << "\n" << flush;
    do {
      C.rho();
      cout << C << "\n" << flush;
    } while (!C.is_one());
    cout << "inverse_rho(C) - " << apply_inverse_rho(C) << "\n\n" << flush;

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

  cout << "testing principality test:\n" << flush;
  cout << "Enter x and y corresponding to a quadratic integer of the form\n" << flush;
  cout << "x + (\\D + sqrt(\\D))/2 y\n" << flush;
  cout << "input x: " << flush;
  cin >> x;
  cout << "\ninput y: " << flush;
  cin >> y;
  C.assign_principal(x,y);
  cout << "P = " << C << "\n" << flush;
  cout << "principal? " << C.is_principal() << "\n\n" << flush;

  cout << "A = " << A << "\n" << flush;
  cout << "principal? " << A.is_principal() << "\n\n" << flush;

  cout << "testing equivalence test:\n" << flush;
  cout << "A = " << A << "\n" << flush;
  cout << "B = " << B << "\n" << flush;
  if (QO1.is_real()) {
    C = apply_rho(A);
    cout << "C = " << apply_rho(A) << "\n" << flush;
  }
  cout << "B equivalent to A? " << B.is_equivalent(A) << "\n" << flush;
  if (QO1.is_real()) {
    cout << "A equivalent to C? " << A.is_equivalent(C) << "\n" << flush;
    cout << "C equivalent to A? " << C.is_equivalent(A) << "\n" << flush;
  }
  cout << "\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\n" << flush;

  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 order, DL, and subgroup algorithms (with knowledge of h)...\n" << flush;
  QO1.class_group();
  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;
}
