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

/*
  $Id: ideal.c,v 1.6 1997/01/06 15:56:21 neis Exp $
*/

#define LIDIA_POINTER_ACCESS
#if defined(HAVE_MAC_DIRS) || defined(__MWERKS__)
#include <LiDIA:alg_number.h>
#include <LiDIA:debug.h>
#include <LiDIA:base_vector.h>
#include <LiDIA:bigfloat_lattice.h>
#else
#include <LiDIA/alg_number.h>
#include <LiDIA/debug.h>
#include <LiDIA/base_vector.h>
#include <LiDIA/bigfloat_lattice.h>
#endif

// Constructors & destructor:
alg_ideal::alg_ideal(const alg_number & a, const alg_number & b): module(a,b)
{
  multiply(*this,*((module *) this),alg_ideal(order((const nf_base *)O)));
  debug_handler_c("alg_ideal","in alg_ideal(const a_n, const a_n)",3,
                  cout << " The ideal is "<<(*this));
}


alg_ideal::alg_ideal(const base_matrix <bigint> & B,
             const bigint & d,
             const nf_base * O1): module(B, d, O1)
{}

alg_ideal::alg_ideal(const bigmod_matrix & B,
             const bigint & d,
             const nf_base * O1): module(B, d, O1)
{
  if (B.get_no_of_columns() != O1->degree() && B.get_modulus().is_zero())
    multiply(*this, *((module *)this), alg_ideal(order((const nf_base *)O)));
}

alg_ideal::alg_ideal(const nf_base * O1): module(O1)
{}

alg_ideal::alg_ideal(const alg_ideal & I): module(I)
{}

alg_ideal::~alg_ideal()
{}

// Procedural versions:
void multiply(alg_ideal & c, const alg_ideal & a, const alg_ideal & b)
{
  debug_handler("alg_ideal","in function multiply("
                "alg_ideal, const & alg_ideal, const & alg_ideal)");
  if (a.O != b.O) {
    lidia_error_handler("alg_ideal","multiply(...):: Multiplication of ideals "
                        "from different orders is not yet implemented");
  }

  if (c.O != a.O){
    c.O->dec_ref();
    c.O = a.O;
    c.O->inc_ref();
  }
  if (a.is_zero() || b.is_zero()){
    c.assign_zero();
    return;
  }
  c.is_exp = false;
  c.base.set_no_of_rows(a.degree());

  alg_number n1,n2;
  bigint * tmp;
  bigint d;
  multiply(d, a.base.get_modulus(), b.base.get_modulus());

  bigmod_matrix tmp1(a.degree(), (a.base.get_no_of_columns()+1)
                     * (b.base.get_no_of_columns()+1) - 1, d);

  register lidia_size_t i, col_no = 0;

  debug_handler_c("alg_ideal","multiply(...)", 1,
                  cout<<a<<" and "<<b;
                  cout<<"represented by"<<a.base<<" and "<<b.base);

  for(i=0; i<a.base.get_no_of_columns(); i++){
    alg_number n1(tmp=a.base.column(i), 1, a.which_base());
    delete[] tmp;
    for(register lidia_size_t j=0; j<b.base.get_no_of_columns(); j++){
      alg_number n2(tmp=b.base.column(j), 1, b.which_base());
      delete[] tmp;
      debug_handler_c("alg_ideal","multiply::multiply elements",1,
                      cout<<col_no);
      tmp1.sto_column(tmp = (n1*n2).coeff_vector().get_data(),
                      b.degree(),col_no++);
      delete[] tmp;
    }
  }

  // append b*a.modulus()
  math_vector<bigint> tmpvec;
  if (!(a.base.get_modulus().is_zero())){
    d.assign(a.base.get_modulus());
    for(i=0; i<b.base.get_no_of_columns(); i++){
      b.base.column_vector(tmpvec,i);
      debug_handler_c("alg_ideal","multiply::multiply elements",0,
                      cout<<tmpvec<<" "<<d<<" "<<col_no);
      tmp1.sto_column(tmp=(tmpvec * d).get_data(), b.degree(), col_no++);
      delete[] tmp;
    }
  }

  // append a*b.modulus()
  if (!(b.base.get_modulus().is_zero())){
    d.assign(b.base.get_modulus());
    for(i=0; i<a.base.get_no_of_columns(); i++){
      a.base.column_vector(tmpvec,i);
      debug_handler_c("alg_ideal","multiply::multiply elements",0,
                      cout<<tmpvec<<" "<<d<<" "<<col_no);
      tmp1.sto_column(tmp=(tmpvec * d).get_data(), a.degree(), col_no++);
      delete[] tmp;
    }
  }
  debug_handler_c("alg_ideal","multiply::Compute image of",0,
                  cout << tmp1);
  c.base.image(tmp1);
  c.den = a.den * b.den;
  c.normalize();
  debug_handler_c("alg_ideal","multiply::end of call",1,
                  cout <<"Product is "<<c<<"represented by "<<c.base<<endl);
}

 void multiply(alg_ideal &c, const alg_ideal &a, const alg_number & b)
{
  debug_handler("alg_ideal","in function "
                "multiply(alg_ideal, const & alg_ideal, const & alg_number)");
  if (a.O != b.O) {
    lidia_error_handler("alg_ideal","multiply(...)::alg_number and alg_ideal"
                        "which are to be multiplied must be over the"
                        "same order!");
  }
  if (c.O != a.O){
    c.O->dec_ref();
    c.O = a.O;
    c.O->inc_ref();
  }
  c.is_exp = false;
  lidia_size_t n = a.base.get_no_of_columns();
  bigint d = a.base.get_modulus();
  alg_number multiplier;
  multiply(multiplier, b, b.den);       // forget about denominator
  bigint N = norm(multiplier).numerator(); // it's anyway an integer !!

  if (!d.is_zero())
    c.base.set_no_of_columns(n+1);
  else
    c.base.set_no_of_columns(n);

  c.base.set_modulus(d*N);
  multiply(c.den, a.den, b.den);

  alg_number tmp(bigint(1), c.O);
  bigint * tmp2;
  for (register lidia_size_t i = 0; i < n; i++){
    a.base.column_vector(tmp.coeff, i);
    multiply(tmp, tmp, multiplier);
    c.base.sto_column(tmp2=tmp.coeff.get_data(), c.degree(), i);
    delete[] tmp2;
  }
  if (!d.is_zero()){
    multiply(tmp, multiplier, d);
    c.base.sto_column(tmp2=tmp.coeff.get_data(), c.degree(), n);
    delete[] tmp2;
  }
  c.base.image(c.base);
  c.normalize();
}

void multiply(alg_ideal &c, const alg_number &b, const alg_ideal &a)
{
  debug_handler("alg_ideal","in function "
                "multiply(alg_ideal, const & alg_ideal, const & alg_number)");
  if (a.O != b.which_base()) {
    lidia_error_handler("alg_ideal","multiply(...)::alg_number and alg_ideal"
                        "which are to be multiplied must be over the"
                        "same order!");
  }
  if (c.O != a.O){
    c.O->dec_ref();
    c.O = a.O;
    c.O->inc_ref();
  }
  c.is_exp = false;
  lidia_size_t n = a.base.get_no_of_columns();
  bigint d = a.base.get_modulus();
  alg_number multiplier;
  multiply(multiplier, b, b.den);       // forget about denominator
  bigint N = norm(multiplier).numerator(); // it's anyway an integer !!

  if (!d.is_zero())
    c.base.set_no_of_columns(n+1);
  else
    c.base.set_no_of_columns(n);

  c.base.set_modulus(d*N);
  multiply(c.den, a.den, b.den);

  alg_number tmp(bigint(1), c.O);
  bigint * tmp2;
  for (register lidia_size_t i = 0; i < n; i++){
    a.base.column_vector(tmp.coeff, i);
    multiply(tmp, tmp, multiplier);
    c.base.sto_column(tmp2=tmp.coeff.get_data(), c.degree(), i);
    delete[] tmp2;
  }
  if (!d.is_zero()){
    multiply(tmp, multiplier, d);
    c.base.sto_column(tmp2=tmp.coeff.get_data(), c.degree(), n);
    delete[] tmp2;
  }
  c.base.image(c.base);
  c.normalize();
}

void divide(alg_ideal &c, const alg_ideal &a, const alg_number & b)
{
  debug_handler("alg_ideal","in function "
                "divide(alg_ideal, const & alg_ideal, const & alg_number)");
  if (a.O != b.which_base()) {
    lidia_error_handler("alg_ideal","divide(...)::alg_number and alg_ideal"
                        "must be over the same order!");
  }
  if (c.O != a.O){
    c.O->dec_ref();
    c.O = a.O;
    c.O->inc_ref();
  }
  c.is_exp = false;
  c.base.set_modulus(0);

  bigmod_matrix abase(a.base);
  abase.lift(0);

  bigint d = gcd(a.den, b.den);
  divide(c.den, a.den, d);
  bigint multiplier;
  divide(multiplier, b.den, d);
  bigint old_multiplier(multiplier);

  alg_number divisor;
  multiply(divisor, b, b.den);  // forget about denominator

  c.base.set_no_of_columns(abase.get_no_of_columns());
  alg_number * results;
  results = new alg_number[c.base.get_no_of_columns()];
  alg_number tmp(bigint(1), c.O);

  register lidia_size_t i;
  for (i = 0; i < abase.get_no_of_columns(); i++){
    abase.column_vector(tmp.coeff, i);
    divide(results[i], tmp, divisor);
    multiplier = lcm(multiplier, results[i].den);
  }
  for (i = 0; i < abase.get_no_of_columns(); i++){
    multiply(results[i], results[i], multiplier);
    c.base.sto_column_vector(results[i].coeff, c.degree(), i);
    // Only works without reducing since c.base.modulus is set to zero !!!!
  }

  divide(multiplier, multiplier, old_multiplier);
  multiply(c.den, c.den, multiplier);
  c.base.image(c.base);
  c.normalize();
}

// void divide_orig(alg_ideal & c, const alg_ideal & aa, const module & bb)
// {
//     debug_handler("alg_ideal","in function divide_orig(alg_ideal &, const alg_ideal &, "
//                   "const module &");
//     alg_ideal a = aa;
//     module b = bb;
//     // Produce a basis of full Z-rank
//     lidia_size_t n=b.base.get_no_of_rows();
//     bigint d=b.den;
//     b.den = 1;          // Multiply b by den !!

//     //Now we have two integral Z -- ideals of full rank.
//     // Compute and save the norm of B (which is an integer !!)
//     bigint N=exp(b).numerator();
//     //    bigint Nsicher(N);
//     //    power(N,a.den, a.degree());
//     //    N = (N * norm(a)).numerator();
//     //    multiply(N,N,Nsicher);

//     // Compute: A ---> Hom( B --> A/(N(B) A) )
//     //          a ---> (    b --> a b      )

//     b.base.lift(0);
//     a.base.lift(0);

//     alg_number x,y,z;

//     bigint_matrix LGS(n,n*n);
//     bigint * tmp, denom;

//     register lidia_size_t i;

//     for (i=0;i<n;i++){
//         // compute the images of the basis of A
//         x=alg_number(tmp = a.base.column(i), 1, a.O);
//         delete[] tmp;
//         for (register lidia_size_t j=0; j < n; j++){
//                         // compute the images of the basis of B
//                         // under the MULT-with-x-homomorphism
//             y=alg_number(tmp = b.base.column(j), 1, b.O);
//             delete[] tmp;
//             z=x*y;   // the image under the MULT-with-a-homomorphism
//             // Put the image in the matrix of the LGS to compute
//             //  the representation relative to A
//             LGS.sto_column_vector(z.coeff_vector(),n,i*n+j);
//         }
//     }

//     // represent the columns of LGS relative to A.
//     debug_handler_c("alg_ideal","divide_orig",2,
//                     cout << "Solve "<<a.base <<LGS<<endl);

//     LGS.reginvimage(a.base,LGS);
//     debug_handler_c("alg_ideal","divide_orig",2,
//                     cout << "Solution is "<<LGS<<endl);

//     // since B now is an integral ideal, the denominators of the solutions
//     // must all be 1. Therefore just ignore the last row.

//     // Put the columns of LGS into Map.
//     bigmod_matrix Map(n*n,n,N);         // set ideal to 'N'.
//     for (i=0;i < n * n; i++){
//       for(register lidia_size_t j=0; j < n; j++){
//         Map.sto(j + (i % n)*n, i / n, LGS.member(j,i));
//       }
//     }

//     // Compute the kernel of this matrix mod 'N'.
//     debug_handler_c("alg_ideal","divide_orig",2,
//                     cout << "Computing kernel of " << Map<<endl;
//                     cout << "Result is "<<kernel(Map));
//     bigint_matrix kern = kernel(Map);
//     i = kern.get_no_of_columns();
//     kern.set_no_of_columns(i+n);
//     for (register lidia_size_t j=0; j < n; j++, i++)
//       kern.sto(j,i,N);
//     kern.hnf();
//     LGS.set_no_of_columns(n);
//     LGS.set_no_of_rows(n);
//     bigint_matrix garbage;
//     kern.split_h(garbage, LGS);
//     debug_handler_c("alg_ideal","divide_orig",2,
//                     cout << "Verify kernel: " <<Map*kern<<endl);
//     debug_handler_c("alg_ideal","divide_orig",2,
//                     cout << "Kernel is " << kern << endl);
//     c.assign(alg_ideal(a.base*LGS, N, a.O));
//     if (!a.den.is_one()) multiply(c.den, c.den, a.den);
//     c.base.hnf();
//     c.normalize();
//     if (!d.is_one()) multiply(c, c, d);
//     debug_handler_c("alg_ideal","divide_orig",2,
//                     cout << "so c is " << c<<endl);
//     debug_handler_c("alg_ideal","divide_orig",2,
//                     cout << "Verify: "<< bb*c<<a<<endl);

// }

void divide(alg_ideal & c, const alg_ideal & a, const module & bb)
{
  debug_handler("alg_ideal","in function divide(alg_ideal &, const alg_ideal &, "
                "const module &");
  if (a.O != bb.O) {
    lidia_error_handler("alg_ideal","divide(...):: Division of ideals from "
                        "different orders is not yet implemented");
  }

  debug_handler_c("alg_ideal","divide(...)", 2,
                  cout<<a<<" by "<<bb;
                  cout<<"represented by"<<a.base<<" and "<<bb.base);

  if (bb.base.get_modulus().is_zero() &&
      bb.base.get_no_of_columns() != bb.base.get_no_of_rows())
    lidia_error_handler("alg_ideal","divide():: Sorry, I can only "
                        "divide by modules of full rank");
  if (c.O != a.O){
    c.O->dec_ref();
    c.O = a.O;
    c.O->inc_ref();
  }

  if (a.is_zero()){
    c.base.set_no_of_rows(a.base.get_no_of_rows());
    c.assign_zero();
    c.is_exp = true;
    return;
  }
  c.is_exp = false;

  module b(bb);

  bigint d=b.den;
  b.den.assign_one();           // Multiply b by den !!

  //Now we have two integral Z -- ideals of full rank.
  // Compute and save the norm of B (which is an integer !!)
  bigint N=exp(b).numerator();
  //    bigint Nsicher(N);
  //    power(N,a.den, a.degree());
  //    N = (N * norm(a)).numerator();
  //    multiply(N,N,Nsicher);

  // Compute: A ---> Hom( B --> A/(N(B) A) )
  //          a ---> (    b --> a b + (N(B) A) )

  b.base.lift(0);
  lidia_size_t n = b.base.get_no_of_columns();

  alg_number x,y,z;
  bigint_matrix LGS(n, n*n);
  bigint * tmp1, denom;

  register lidia_size_t i;

  bigmod_matrix tmp(a.base);
  tmp.lift(0);

  for (i=0; i < n; i++){
    // compute the images of the basis of A
    x=alg_number(tmp1 = tmp.column(i), 1, a.O);
    delete[] tmp1;
    for (register lidia_size_t j=0; j < n; j++){
      // compute the images of the basis of B
      // under the MULT-with-x-homomorphism
      y=alg_number(tmp1 = b.base.column(j), 1, b.O);
      delete[] tmp1;
      multiply(z, x, y);        // the image under the MULT-with-a-homomorphism
      // Put the image in the matrix of the LGS to compute
      // the representation relative to A
      LGS.sto_column_vector(z.coeff_vector(),n,i*n+j);
    }
  }

  // represent the columns of LGS relative to A.
  debug_handler_c("alg_ideal","divide",2,
                  cout << "Solve "<<tmp <<LGS<<endl);

  LGS.reginvimage(tmp,LGS);
  debug_handler_c("alg_ideal","divide",2,
                  cout << "Solution is "<<LGS<<endl);

  // since B now is an integral ideal, the denominators of the solutions
  // must all be 1. Therefore just ignore the last row.

  // Put the columns of LGS into Map.
  bigmod_matrix Map(n*n,n,N);           // set ideal to 'N'.
  for (i=0;i < n * n; i++){
    for(register lidia_size_t j=0; j < n; j++){
      Map.sto(j + (i % n)*n, i / n, LGS.member(j,i));
    }
  }

  // Compute the kernel of this matrix mod 'N'.
  debug_handler_c("alg_ideal","divide",2,
                  cout << "Computing kernel of " << Map<<endl);
  bigmod_matrix kern(n,n,N);
  kern.kernel(Map);
  debug_handler_c("alg_ideal","divide",2,
                  cout << "Kernel is "<<kern;
                  cout << "Verify kernel: " <<Map*kern<<endl);
  kern.set_no_of_rows(n);

  bigint reduction_para;
  multiply(reduction_para, a.base.get_modulus(), N);
  kern.lift(reduction_para);
  tmp.reduce(reduction_para);

  debug_handler_c("alg_ideal","divide",2,
                 cout << "multiply "<<tmp<<"\nby"<<kern << endl);
  multiply(c.base, tmp, kern);
  debug_handler_c("alg_ideal","divide",2,
                 cout << "result is "<<c.base << endl);
  debug_handler_c("alg_ideal","divide",2,
                 cout << "so c.base is " << c.base << endl);

  if (!(a.den.is_one()))
    multiply(c.den, a.den, N);
  else
    c.den.assign(N);
  if (!(d.is_one())) multiply(c, *(module *)&c, d);
  c.normalize();
  debug_handler_c("alg_ideal","divide",2,
                  cout << "so c is " << c<<endl);
  debug_handler_c("alg_ideal","divide",2,
                  cout << "Verify: "<< bb*c<<a<<endl);
}

// void remainder(alg_ideal &c, const alg_ideal &a, const bigint &p)
// {
//     bigint_matrix garbage;
//     c.O = a.which_base();
//     if (!a.den.is_one())
//      error_handler("alg_ideal", "remainder:: Reducing mod p for a fractional "
//                    "alg_ideal ??");
//     c.den.assign_one();

//     remainder(garbage, a.base, p);
//     garbage.hnf();
//     register long i=garbage.rank();
//     if (i==0){
//      c.base.set_no_of_columns(1);
//     }
//     else{
//      c.base.set_no_of_columns(i);
//     }
//     garbage.split_h(garbage, c.base);
// }

// order module::ring_of_multipliers(const bigint &p) const
// {
//     // We are here in a member function of `Ip'
//     // Consider the kernel C of the map:
//     // O/pO -> End(Ip/pIp)
//     //   a  -> (b -> ab)
//     // then the result O' fullfills: O' =1/p * C
//     // quite similar to `pseudo-radical', but more complicated, since
//     // now we have matrices for each (b -> ab).

//     debug_handler("module","in member - function ring_of_multipliers(const bigint &, bigint &)");
//     register lidia_size_t i,j,k;
//     register lidia_size_t n=degree();
//     alg_number a,b,y;

//     bigint_matrix init(n,n*n);
//     bigint_matrix B(base);
//     bigmod_matrix C;
//     bigmod_matrix VW(n * n, n, p);
//     bigint_matrix v;
//     bigint * tmp = new bigint[n];
//     bigint * tmp2;
//     bigint rem;

//     if (base.get_no_of_columns()!= n)
//      B.assign((*this * order(O)).base);

//     for (i=0;i<n;tmp[i++] = 0){
//      tmp[i]=1;               //tmp=e_i;
//      // compute the images of the basis of O/pO
//      a= alg_number(tmp,1,O);
//      //a=e_i
//      for (j=0; j < n; j++){
//                        // compute the images of the basis of A+pO
//                        // under the MULT-with-a-homomorphism
//          b=alg_number(tmp2 = B.column(j), 1, O);
//          delete[] tmp2;
//          y=a*b;   // the image under the MULT-with-a-homomorphism
//          init.sto_column_vector(y.coeff_vector(),n,i*n+j);
//      }
//     }
//     delete[] tmp;

// // the image must be written relative to base;
//   debug_handler_c("module", "in member - function ring_of_multipliers(...)",
//                1, cout <<"solve "<<B<<init<<endl<<flush );
//   v.reginvimage(B, init);
//   debug_handler_c("module", "in member - function ring_of_multipliers(...)",
//                1, cout <<"solution is "<<v<<endl<<flush);
//   // move the result to the Matrix VW
//   for (i=0;i<n;i++){
//     for (j=0; j < n; j++){
//       for (k=0;k<n;k++){
//      VW.sto(k+j*n,i,v.member(k,i*n+j));
//       }
//     }
//   }
//   debug_handler_c("module", "in member - function ring_of_multipliers(...)",
//                1, cout << "Compute kernel of"<<VW);
//   C.kernel(VW);
//   debug_handler_c("module", "in member - function ring_of_multipliers(...)",
//                1, cout << "Kernel is " << C << flush);

//   // interpret C as a module M and lift it into the order!!
//   init.set_no_of_columns(k=C.get_no_of_columns());
//   base_vector <bigint> new_tmp;
//   for (j = 0; j < n; j++){
//     C.row_vector(new_tmp,j);
//     init.sto_row_vector(new_tmp,k,j);
//   }
//   module M(init,1,O);
//   M+= p * order(O);  // hopefully interpreted as module-mult.
//   debug_handler_c("module","in member - function ring_of_multipliers(...)",
//                4,cout <<"Module is"<<M<<flush);

// //  Instead of creating the multiplication table, we create the base:
//   if (!O->f.is_zero()){
//     M.den *= p;
//     if (O->base_computed()){
//       debug_handler_c("module",
//                    "in member - function ring_of_multipliers(...)",
//                    4,cout <<"Multiply module by "<<O->base<<flush);
//       multiply(M.base, O->base, M.base);
//       M.den *= O->den;
//     }
//     M.normalize();
//     debug_handler_c("module","ring_of_multipliers(...)",4,
//                  cout << "Transformation is  1/" << M.den << " * ";
//                  cout << M.base << endl << flush);

//     order result(M.base, M.den, O);
//     debug_handler_c("module","ring_of_multipliers(...)",4,
//                  cout << "So the new order has the following table";
//                  cout << result << endl << flush;
//                  cout << "and the Discriminant is" <<disc(result);
//                  cout << endl << flush);
//     return result;
//   }
//   else{
//     // If we can't create the base (because there is no field)
//     // create the MT instead.
//     base_vector <bigint> tmp;
//     init.set_no_of_columns((n*(n+1))/2);
//     for (i = 0; i < n; i++){
//       M.base.column_vector(tmp,i);
//       a=alg_number(tmp,1,O);
//       for (j = 0; j <=i; j++){
//      M.base.column_vector(tmp,j);
//      b=alg_number(tmp,1,O);
//      init.sto_column_vector((a*b).coeff_vector(), n, (i*(i+1))/2+j);
//       }
//     }
//     debug_handler_c("module",
//                  "in member - function ring_of_multipliers(...)",3,
//                  cout <<"solve "<<M.base<<init<<endl<<flush );

//     init.reginvimage(M.base, init);
//     init.set_no_of_rows(n);

//     debug_handler_c("module",
//                  "in member - function ring_of_multipliers(...)",3,
//                  cout << "p * MT is" << trans(init) << flush);

//     for (i = 0; i < init.get_no_of_rows(); i++)
//       for (j = 0; j < init.get_no_of_columns(); j++){
//      bigint q,r;
//      div_rem(q,r,init.member(i,j),p);
//      if (!r.is_zero())
//        error_handler("module", "ring_of multipliers::internal error::"
//                      "division by p unsuccesful");
//      init.sto(i,j,q);
//       }
//     debug_handler_c("module",,
//                  "in member - function ring_of_multipliers(...)",3,
//                  cout << "MT is" << trans(init) << flush);
//     return order(trans(init));
//   }
// }

// Reduction:
void alg_ideal::reduce()
{
  lidia_size_t i, n = degree();
  bigfloat_lattice lat(n,n);
  lat.set_basis_flag();
//  bigfloat_lattice_basis lat(n,n);

  bigmod_matrix tmp(base);
  tmp.lift(0);

  //cout <<(*this);
  int prec = (tmp.max_abs()).length();
  long store = bigfloat::get_precision();
  bigfloat::precision((long)((prec-3)*L2B10*bits_per_base_digit+4));
  for (i = 0; i < n; i++)
    for (register lidia_size_t j = 0; j < n; j++)
      lat.sto(i, j, bigfloat(tmp.member(i,j)));

  //cout <<"Zeros are " << ((nf_base *)O)->get_conjugates()<<flush;
  //cout << "Trafo is "<< lat<<flush;

  multiply(lat, ((nf_base *)O)->get_conjugates(), lat);

//   cout << "Lattice to reduce is "<<lat<<flush;
//   math_vector<bigfloat> a,b;
//   lat.row_vector(a, 0);
//   lat.row_vector(b, 1);
//   cout << "Difference is "<<a-b<<flush;
  bigint_matrix TRAFO(n,n);
//   lat.set_reduction_parameter(0.9999);
//   lat.lll_trans_var(TRAFO, 2);
  lat.lll(TRAFO, 0.9999, 2);
  //cout << "Reduced lattice is "<<lat<<flush;
  // Search the smallest column of lat:
  // ==================================
  lidia_size_t min_col;
  bigfloat minimum, tmp1, tmp2;
  bigfloat * lat_col;

  // compute the alg_number (called divisor) corresponding to
  // column 0:
  bigint * tmp_trafo,  * prod = new bigint[n];
  tmp_trafo = TRAFO.column(0);
  multiply(prod, tmp, tmp_trafo);
  delete[] tmp_trafo;

  alg_number divisor(prod,1,O);
  delete[] prod;

  cout << "Reducing by " <<divisor<<flush;
  // divide by this number and guarantee an integral ideal:
  divide(*this, *this, divisor);
  cout << " leads to denominator = "<<den<<endl<<flush;
  den = 1;
  bigfloat::precision(store);
}

// Other functions:
alg_ideal inverse(const alg_ideal & a)
{
    alg_ideal c = a;
    c.invert();
    return c;
}

// random (???) ideales
void alg_ideal::randomize(const bigint & b)
{
  debug_handler("alg_ideal", "in member-function "
                "randomize(const bigint &)");
  den.assign_one();
  base.set_no_of_columns(1);
  base.set_modulus(::randomize(b)+1);
  for (register lidia_size_t j=0;j<degree();j++)
    base.sto(j,0,::randomize(b));
  is_exp = false;
  multiply(*this, *(module *)this, alg_ideal(order(O)));
  den = ::randomize(b)+1;
  normalize();
}
