//
// LiDIA - a library for computational number theory
//   Copyright (c) 1995 by the LiDIA Group
//
// File        : bf_gsys_base.c
// Author      : Werner Backes (WB), Thorsten Lauer (TL) 
// Last change : WB/TL, Feb 10 1995, initial version
//             : WB/TL, Dez 21 1995, second template version 
//             : WB/TL, Jan 08 1996, stabilized template version
//             : WB/TL, Feb 29 1996, some information/function extensions
//
//

#define LIDIA_POINTER_ACCESS

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

// 
// variables for temporary use
//

// 
// For storing temporary values
//
bigfloat bigfloat_lattice_gensys::tempmz0;
bigfloat bigfloat_lattice_gensys::tempmz1;
bigfloat bigfloat_lattice_gensys::tempmz2;
bigfloat bigfloat_lattice_gensys::tempmz3;
bigfloat bigfloat_lattice_gensys::tempmz4;
bigfloat bigfloat_lattice_gensys::ergmz;

//
// For storing values during vector - operations
//
double bigfloat_lattice_gensys::vectdblz;
bigint bigfloat_lattice_gensys::vectbinz;
bigfloat bigfloat_lattice_gensys::vectbflz;

//
// vector - size for arithmetic operations
//
lidia_size_t bigfloat_lattice_gensys::vectsize;

// 
// Constructor / Destructor
//

//
// Simple constructors   
// 

bigfloat_lattice_gensys::bigfloat_lattice_gensys():math_matrix< bigfloat > ()
{
  debug_handler("bigfloat_lattice_gensys", "bigfloat_lattice_gensys()");
  init_parameters();
  do_some_other_stuff();
}

// bigfloat_lattice_gensys::bigfloat_lattice_gensys(lidia_size_t n):math_matrix< bigfloat > (n,1)
// {
//   debug_handler("bigfloat_lattice_gensys", "bigfloat_lattice_gensys(n)");
//   init_parameters();
//   do_some_other_stuff();
// }

bigfloat_lattice_gensys::bigfloat_lattice_gensys(lidia_size_t m, lidia_size_t n):math_matrix< bigfloat > (m,n)
{
  debug_handler("bigfloat_lattice_gensys", "bigfloat_lattice_gensys(m, n)");
  init_parameters();
  do_some_other_stuff();
}

//
// Constructor with dimension (m, n) 
// rows and columns and values bigfloat **abf
// 
bigfloat_lattice_gensys::bigfloat_lattice_gensys(lidia_size_t m, lidia_size_t n, const bigfloat **abf):math_matrix< bigfloat > (m,n,abf)
{
  debug_handler("bigfloat_lattice_gensys", "bigfloat_lattice_gensys(m, n, abf)");
  init_parameters();
  do_some_other_stuff();
}

//
// Copy - constructor 
// creating a copy of math_matrix< bigfloat > L
//
bigfloat_lattice_gensys::bigfloat_lattice_gensys(const math_matrix< bigfloat >& L):math_matrix< bigfloat > (L)
{
  debug_handler("bigfloat_lattice_gensys", "bigfloat_lattice_gensys(L)");
  init_parameters();
  do_some_other_stuff();
}

bigfloat_lattice_gensys::bigfloat_lattice_gensys(const bigfloat_lattice_gensys& L):math_matrix< bigfloat > (L)
{
  debug_handler("bigfloat_lattice_gensys", "bigfloat_lattice_gensys(L)");
  assign_the_rest(L);
  do_some_other_stuff();
}

//
// The destructor that frees allocated storage
//
bigfloat_lattice_gensys::~bigfloat_lattice_gensys()
{
  debug_handler("bigfloat_lattice_gensys", "~bigfloat_lattice_gensys()");
}

//
// Initialize Parameters
//
void bigfloat_lattice_gensys::init_parameters()
{
  debug_handler("bigfloat_lattice_gensys","init_parameters()");
//
// set the defaults for reduction quality (same as pari)
//
  y_par=0.99;
  y_nom=99;
  y_denom=100;
  trans_flag=true;

//
// reset algorithm information
//
  reduction_steps=0;
  swapping_steps=0;
  correction_steps=0;
}

//
// Assignments
//
void bigfloat_lattice_gensys::assign(const bigfloat_lattice_gensys& L)
{
  debug_handler("bigfloat_lattice_gensys","assign(L)");
  ((math_matrix< bigfloat > *)this)->assign(L);
  assign_the_rest(L);
}

bigfloat_lattice_gensys& bigfloat_lattice_gensys::operator = (const bigfloat_lattice_gensys& L)
{
  debug_handler("bigfloat_lattice_gensys","operator = (L)");
  assign(L);
  return(*this);
}

void assign(bigfloat_lattice_gensys& A, const bigfloat_lattice_gensys& B)
{
  debug_handler("bigfloat_lattice_gensys","assign(A, B)");
  A.assign(B);
}

void bigfloat_lattice_gensys::assign_the_rest(const bigfloat_lattice_gensys& B)
{
  debug_handler("bigfloat_lattice_gensys","assign_the_rest()");
//
// copy those elements which are not part of math_matrix< bigfloat >
//
  y_par=B.y_par;
  y_nom=B.y_nom;
  y_denom=B.y_denom;
  trans_flag=B.trans_flag;

//
// copy algorithm information
//
  reduction_steps=B.reduction_steps;
  swapping_steps=B.swapping_steps;
  correction_steps=B.correction_steps;
}

//
// Higher Functions
//
void bigfloat_lattice_gensys::set_reduction_parameter(double par)
{
  debug_handler("bigfloat_lattice_gensys","set_reduction_parameter(par)");
//
// the parameter for reduction has to be in ]0.5 , 1.0]
// it is not guarateed that the reduction terminates for 1.0
// it has to be greater than 0.5 due to the schnorr - euchner modifications
// 
  if ((par > 0.5) && (par <=1.0))
    {
      y_par=par;
      y_nom=(sdigit )(y_par*100);   
      y_denom=100; 
      return;
    }
//
// not in range -> default settings
//
  y_par=0.99;
  y_nom=99;   
  y_denom=100; 
}
  
void bigfloat_lattice_gensys::set_reduction_parameter(sdigit nom, sdigit denom) 
{ 
  debug_handler("bigfloat_lattice_gensys","set_reduction_parameter(nom, denom)");
  double par;
//
// the parameter nom/denom for reduction has to be in ]0.5 , 1.0]
// it is not guarateed that the reduction terminates for 1.0
// it has to be greater than 0.5 due to the schnorr - euchner modifications
// 
  par=(double )(nom)/(double)(denom);
  if ((par > 0.5) && (par <=1.0))
    {
      y_nom=nom;
      y_denom=denom; 
      y_par=(double )(nom)/(double )(denom);
      return;
    }
//
// not in range -> default settings
//
  y_par=0.99;
  y_nom=99;   
  y_denom=100; 
}

double bigfloat_lattice_gensys::get_reduction_parameter() 
{ 
  debug_handler("bigfloat_lattice_gensys","get_reduction_parameter()");
  return (y_par);
}

void bigfloat_lattice_gensys::get_reduction_parameter(sdigit& nom, sdigit& denom) 
{ 
  debug_handler("bigfloat_lattice_gensys","get_reduction_parameter(nom, denom)");
  nom=y_nom; 
  denom=y_denom; 
}

void bigfloat_lattice_gensys::set_representation_rows()
{
  debug_handler("bigfloat_lattice_gensys","set_representation_rows()");
  trans_flag=false;
}

void bigfloat_lattice_gensys::set_representation_columns()
{
  debug_handler("bigfloat_lattice_gensys","set_representation_columns()");
  trans_flag=true;
}

void bigfloat_lattice_gensys::randomize_vectors()
{
  debug_handler("bigfloat_lattice_gensys","randomize_vectors()");
  bigfloat_lattice_gensys A;
//
// First step is to transpose the lattice
// Second is to generate a permutation of the rows (using rand48 - functions)
// Third to transpose again
//
  Tr_trans_swap(A);
  A.Tr_randomize_vectors();
  A.Tr_trans_swap(*this); 
}

void bigfloat_lattice_gensys::sort_big_vectors()
{
  debug_handler("bigfloat_lattice_gensys","sort_big_vectors()");
  bigfloat_lattice_gensys A;
//
// First step is to transpose the lattice
// Second is to sort the rows by length (biggest first)
// Third to transpose again
//
  Tr_trans_swap(A);
  A.Tr_sort_vectors(1);
  A.Tr_trans_swap(*this); 
}

void bigfloat_lattice_gensys::sort_small_vectors()
{
  debug_handler("bigfloat_lattice_gensys","sort_small_vectors()");
  bigfloat_lattice_gensys A;
//
// First step is to transpose the lattice
// Second is to sort the rows by length (smallest first)
// Third to transpose again
//
  Tr_trans_swap(A);
  A.Tr_sort_vectors(-1);
  A.Tr_trans_swap(*this); 
}

void bigfloat_lattice_gensys::sort_vectors(bfl_cmp_func cpf)
{
  debug_handler("bigfloat_lattice_gensys","sort_vectors()");
  bigfloat_lattice_gensys A;
//
// First step is to transpose the lattice
// Second is to sort the rows by the given compare functions cpf
// (See header file for more information)
// Third to transpose again
//
  Tr_trans_swap(A);
  A.Tr_sort_vectors(cpf);
  A.Tr_trans_swap(*this); 
}

//
// Algorithm`s
//

//
// Buchmann - Kessler Algorthm for 
// linaer generating systems
// result: transformation matrix using bigints
//
void bigfloat_lattice_gensys::lin_gen_system(math_matrix< bigint >& L, lidia_size_t& rank)
{
  debug_handler("bigfloat_lattice_gensys","lin_gen_system(L,rank)");
  bigfloat_lattice_gensys TrThis;
//
// Give L the right dimension
//
  L.set_no_of_rows(columns);       //  Give lattice L right size
  L.set_no_of_columns(columns);    //  columns x columns

//
// Transpose lattice and perform Buchmann - Kessler
//
  Tr_trans_swap(TrThis);
  TrThis.assign_the_rest(*this);
  TrThis.Tr_lin_gen_system(L, rank);
  TrThis.Tr_trans_swap(*this);
}
   
//
// Buchmann - Kessler Algorthm for 
// linaer generating systems
// result: transformation matrix using bigfloats
//
void bigfloat_lattice_gensys::lin_gen_system(math_matrix< bigfloat >& L, lidia_size_t& rank)
{
  debug_handler("bigfloat_lattice_gensys","lin_gen_system(L,rank)");
  bigfloat_lattice_gensys TrThis;
  bigint_lattice_gensys TrBi;
//
// Transpose lattice and perform Buchmann - Kessler
//
  Tr_trans_swap(TrThis);
  TrThis.assign_the_rest(*this);
  TrThis.Tr_lin_gen_system(TrBi, rank);
  TrThis.Tr_trans_swap(*this); 
//
// Give L the right dimension
//
  L.set_no_of_rows(TrBi.rows);       //  Give lattice L right size
  L.set_no_of_columns(TrBi.columns);    //  columns x columns

//
// convert to bigfloat
//
  bigfloatify(L, TrBi);  
}

//
// Schnorr - Euchner modified lll
//
void bigfloat_lattice_gensys::lll_gensys(lidia_size_t& rank, sdigit x_factor)
{
  debug_handler("bigfloat_lattice_gensys","lll_gensys(rank, x_factor)");
  bigfloat_lattice_gensys TrThis;
//
// Transpose lattice and perform schnorr - euchner modified for 
// linear generating systems using double for approximation
// return in rank the rank of the lattice
//
  Tr_trans_swap(TrThis);
  TrThis.assign_the_rest(*this);
  if (x_factor < 2)
    TrThis.Tr_lll_dbl_gensys(rank);
  else
    TrThis.Tr_lll_bfl_gensys(rank, x_factor*8*SIZEOF_DOUBLE);    
  TrThis.Tr_trans_swap(*this);
  assign_the_rest(TrThis);   
}

void bigfloat_lattice_gensys::lll_gensys(const math_matrix< bigfloat >& A, lidia_size_t& rank, 
                                                 sdigit x_factor)
{
  debug_handler("bigfloat_lattice_gensys","lll_gensys(A, rank)");
  ((math_matrix< bigfloat > *)this)->assign(A);
  lll_gensys(rank, x_factor);
}

//
// Schnorr - Euchner modified lll
//
void bigfloat_lattice_gensys::lll_trans_gensys(math_matrix< bigint >& T, lidia_size_t& rank, sdigit x_factor)
{
  debug_handler("bigfloat_lattice_gensys","lll_trans_gensys(T, rank, x_factor)");
  bigfloat_lattice_gensys TrThis;

//
// Transpose lattice and perform schnorr - euchner modified for 
// linear generating systems using double for approximation
// return in rank the rank of the lattice
//
  Tr_trans_swap(TrThis);
  TrThis.assign_the_rest(*this);
  if (x_factor < 2)
    TrThis.Tr_lll_trans_dbl_gensys(T, rank);
  else
    TrThis.Tr_lll_trans_bfl_gensys(T, rank, x_factor*8*SIZEOF_DOUBLE);  
  TrThis.Tr_trans_swap(*this);
  assign_the_rest(TrThis);   
}

//
// Schnorr - Euchner modified lll
//
void bigfloat_lattice_gensys::lll_trans_gensys(math_matrix< bigfloat >& T, lidia_size_t& rank, 
                                                       sdigit x_factor)
{
  debug_handler("bigfloat_lattice_gensys","lll_trans_gensys(T, rank)");
  bigfloat_lattice_gensys TrThis;
  bigint_lattice_gensys TrBi;
//
// Transpose lattice and perform schnorr - euchner modified for 
// linear generating systems using double for approximation
// return in rank the rank of the lattice
//
  Tr_trans_swap(TrThis);
  TrThis.assign_the_rest(*this);
  if (x_factor < 2)
    TrThis.Tr_lll_trans_dbl_gensys(TrBi, rank);
  else
    TrThis.Tr_lll_trans_bfl_gensys(TrBi, rank, x_factor*8*SIZEOF_DOUBLE);  
  TrThis.Tr_trans_swap(*this);
  assign_the_rest(TrThis);   
//
// Convert to bigfloat
//
  T.set_no_of_columns(TrBi.rows);
  T.set_no_of_rows(TrBi.columns);
  bigfloatify(T, TrBi);
}

void bigfloat_lattice_gensys::lll_var_gensys(lidia_size_t& rank, sdigit x_factor)
{
  debug_handler("bigfloat_lattice_gensys","lll_var_gensys(rank, x_factor)");
  bigfloat_lattice_gensys A;
  bigint_lattice_gensys BiA;
  sdigit bit_len;
  
  Tr_trans_swap(A);
  A.assign_the_rest(*this);
  BiA.bigintify(bit_len, A);
  if (bit_len == 1)
    {
      BiA.y_par=A.y_par;
      if (x_factor < 2)
        BiA.Tr_lll_dbl_gensys(rank);
      else
        BiA.Tr_lll_bfl_gensys(rank, x_factor*8*SIZEOF_DOUBLE);
      A.reduction_steps=BiA.reduction_steps;
      A.swapping_steps=BiA.swapping_steps;
      A.correction_steps=BiA.correction_steps;
      bigfloatify(A, BiA);
    }
  else
    if (x_factor < 2)
      A.Tr_lll_var_dbl_gensys(BiA, bit_len, rank);   
    else
      A.Tr_lll_var_bfl_gensys(BiA, bit_len, rank, x_factor*8*SIZEOF_DOUBLE);   
  A.Tr_trans_swap(*this);
  assign_the_rest(A);
}

void bigfloat_lattice_gensys::lll_var_gensys(const math_matrix< bigfloat >& B, lidia_size_t& rank, 
                                                     sdigit x_factor)
{
  debug_handler("bigfloat_lattice_gensys","lll_var_gensys(B, rank, x_factor)");
  ((math_matrix< bigfloat > *)this)->assign(B);
  lll_var_gensys(rank, x_factor);
}

void bigfloat_lattice_gensys::lll_trans_var_gensys(math_matrix< bigint >& T, lidia_size_t& rank,
                                                           sdigit x_factor)
{
  debug_handler("bigfloat_lattice_gensys","lll_trans_var_gensys(T, rank, x_factor)");
  bigfloat_lattice_gensys A;
  bigint_lattice_gensys BiA;
  sdigit bit_len;
  
  Tr_trans_swap(A);
  A.assign_the_rest(*this);
  BiA.bigintify(bit_len, A);
  if (bit_len == 1)
    {
      BiA.y_par=A.y_par;
      if (x_factor < 2)
        BiA.Tr_lll_trans_dbl_gensys(T, rank);
      else
        BiA.Tr_lll_trans_bfl_gensys(T, rank, x_factor*8*SIZEOF_DOUBLE);
      bigfloatify(A, BiA);
      A.reduction_steps=BiA.reduction_steps;
      A.swapping_steps=BiA.swapping_steps;
      A.correction_steps=BiA.correction_steps;
    }
  else
    if (x_factor < 2)
      A.Tr_lll_trans_var_dbl_gensys(BiA, T, bit_len, rank);   
    else
      A.Tr_lll_trans_var_bfl_gensys(BiA, T, bit_len, rank, x_factor*8*SIZEOF_DOUBLE);   
  A.Tr_trans_swap(*this);
  assign_the_rest(A);
}

void bigfloat_lattice_gensys::lll_trans_var_gensys(math_matrix< bigfloat >& T, lidia_size_t& rank,
                                                           sdigit x_factor)
{
  debug_handler("bigfloat_lattice_gensys","lll_trans_var_gensys(T, rank)");
  bigfloat_lattice_gensys A;
  bigint_lattice_gensys BiA;
  bigint_lattice_gensys TBi;
  sdigit bit_len;
  
  Tr_trans_swap(A);
  A.assign_the_rest(*this);
  BiA.bigintify(bit_len, A);
  if (bit_len == 1)
    {
      BiA.y_par=A.y_par;
      if (x_factor < 2)
        BiA.Tr_lll_trans_dbl_gensys(TBi, rank);
      else
        BiA.Tr_lll_trans_bfl_gensys(TBi, rank, x_factor*8*SIZEOF_DOUBLE);
      bigfloatify(A, BiA);
      A.reduction_steps=BiA.reduction_steps;
      A.swapping_steps=BiA.swapping_steps;
      A.correction_steps=BiA.correction_steps;
    }
  else
    if (x_factor < 2)
      A.Tr_lll_trans_var_dbl_gensys(BiA, TBi, bit_len, rank);   
    else
      A.Tr_lll_trans_var_bfl_gensys(BiA, TBi, bit_len, rank, x_factor*8*SIZEOF_DOUBLE);   
  A.Tr_trans_swap(*this);
  assign_the_rest(A);
// 
// Conversion
//
  T.set_no_of_rows(TBi.rows);
  T.set_no_of_columns(TBi.columns);
  bigfloatify(T, TBi);
}

//
// Modified lll for genertating systems 
// of the form n x n+1
//
// Returns vector of dimension n 
// where you can find the relations 
//
//
// using bigints
//
void bigfloat_lattice_gensys::mlll(base_vector< bigint >& vect)
{
  debug_handler("bigfloat_lattice_gensys","mlll(vect)");
  if (Tr_check_mlll() == false)
    lidia_error_handler("bigfloat_lattice_gensys","mlll(vect) :: "
                        "Illegal size for modified lll (must have n x n+1)");

  bigfloat_lattice_gensys A;
  bigint *v;
  lidia_size_t dim;

  if (trans_flag)
    dim=columns;
  else
    dim=rows;
  v=new bigint[dim];  
  memory_handler(v, "bigfloat_lattice_gensys","mlll(vect) :: "
                    "not enough memory !");
// 
// Transpose lattice and perform modified lll
//
  Tr_trans_swap(A);
  A.assign_the_rest(*this);
  A.Tr_mlll_bfl(v);
  A.Tr_trans_swap(*this);
//
// return vector which satisfies the conditions
// discribed in the manual
// discribtion of the 0 - vector
//
  assign_the_rest(A);
  base_vector< bigint > temp(v, dim);
  vect=temp;
}

void bigfloat_lattice_gensys::mlll(const math_matrix< bigfloat >& A, base_vector< bigint >& vect)
{
  debug_handler("bigfloat_lattice_gensys","mlll(A, vect)");
  ((math_matrix< bigfloat > *)this)->assign(A);
  mlll(vect);
}

void bigfloat_lattice_gensys::mlll(bigint *&v)
{
  debug_handler("bigfloat_lattice_gensys","mlll(v)");
  if (!(Tr_check_mlll()))
    lidia_error_handler("bigfloat_lattice_gensys","mlll(v) :: "
                        "Illegal size for modified lll (must have n x n+1)");

  bigfloat_lattice_gensys A;
  lidia_size_t dim;
//
// Generate Vector of bigint
//

  if (trans_flag)
    dim=columns;
  else
    dim=rows;
  v=new bigint[dim];  
  memory_handler(v, "bigfloat_lattice_gensys","mlll(v) :: "
                    "not enough memory !");
// 
// Transpose lattice and perform modified lll
//
  Tr_trans_swap(A);
  A.assign_the_rest(*this);
  A.Tr_mlll_bfl(v);
  A.Tr_trans_swap(*this);
  assign_the_rest(A);
}

void bigfloat_lattice_gensys::mlll(const math_matrix< bigfloat >& A, bigint *&v)
{
  debug_handler("bigfloat_lattice_gensys","mlll(A, v)");
  ((math_matrix< bigfloat > *)this)->assign(A);
  mlll(v);
}

//
// using bigfloats
//
void bigfloat_lattice_gensys::mlll(base_vector< bigfloat >& vect)
{
  debug_handler("bigfloat_lattice_gensys","mlll(vect)");
  if (!(Tr_check_mlll()))
    lidia_error_handler("bigfloat_lattice_gensys","mlll(vect) :: "
                        "Illegal size for modified lll (must have n x n+1)");

  bigfloat_lattice_gensys A;
  bigint *v;
  bigfloat *bflv;
  cl(i);
  lidia_size_t dim;

  if (trans_flag)
    dim=columns;
  else
    dim=rows;

  v=new bigint[dim];  
  memory_handler(v, "bigfloat_lattice_gensys","mlll(vect) :: "
                    "not enough memory !");
  bflv=new bigfloat[dim];  
  memory_handler(bflv, "bigfloat_lattice_gensys","mlll(vect) :: "
                    "not enough memory !");
// 
// Transpose lattice and perform modified lll
//
  Tr_trans_swap(A);
  A.assign_the_rest(*this);
  A.Tr_mlll_bfl(v);
  A.Tr_trans_swap(*this);
  assign_the_rest(A);
//
// return vector which satisfies the conditions
// discribed in the manual
// discribtion of the 0 - vector
//
  for (fcl(i)=0;i<dim;i++)
    bflv[i].assign(v[i]);
  base_vector< bigfloat > temp(bflv, dim);
  vect=temp;
}

void bigfloat_lattice_gensys::mlll(const math_matrix< bigfloat >& A, base_vector< bigfloat >& vect)
{
  debug_handler("bigfloat_lattice_gensys","mlll(A, vect)");
  ((math_matrix< bigfloat > *)this)->assign(A);
  mlll(vect);
}

void bigfloat_lattice_gensys::mlll(bigfloat *&v)
{
  debug_handler("bigfloat_lattice_gensys","mlll(v)");
  if (!(Tr_check_mlll()))
    lidia_error_handler("bigfloat_lattice_gensys","mlll(v) :: "
                        "Illegal size for modified lll (must have n x n+1)");

  bigfloat_lattice_gensys A;
  bigint *vbin;
  lidia_size_t dim;
  cl(i);


//
// Generate Vector of bigint
//
  if (trans_flag)
    dim=columns;
  else
    dim=rows;
  v=new bigfloat[dim];  
  memory_handler(v, "bigfloat_lattice_gensys","mlll(v) :: "
                    "not enough memory !");
  vbin=new bigint[dim];  
  memory_handler(vbin, "bigfloat_lattice_gensys","mlll(v) :: "
                       "not enough memory !");
// 
// Transpose lattice and perform modified lll
//
  Tr_trans_swap(A);
  A.assign_the_rest(*this);
  A.Tr_mlll_bfl(vbin);
  A.Tr_trans_swap(*this);
  assign_the_rest(A);
  for (fcl(i)=0;i<dim;i++)
    v[i].assign(vbin[i]);
}

void bigfloat_lattice_gensys::mlll(const math_matrix< bigfloat >& A, bigfloat *&v)
{
  debug_handler("bigfloat_lattice_gensys","mlll(A, v)");
  ((math_matrix< bigfloat > *)this)->assign(A);
  mlll(v);
}

//
// Vector Operations
//
//
// Naming convention
//
// dbl -> double
// bin -> bigint
// bfl -> bigfloat
//
//
// bin_<operation>_<type>
//
// operation -> Bsp: perform scalar product 
// 
// type -> dbl, bin, bfl
//
// you have to set the static value "vectsize" to the
// length of the vector
//

#ifndef __GNUC__

//
// for double vectors
//
void bfl_assign_dbl(double* a, double* b)
{
  debug_handler("bigfloat_lattice_gensys","void bfl_assign_dbl(a, b)");
  cl(i);
  for (fcl(i)=bigfloat_lattice_gensys::vectsize-1;i>=0;i--)
    a[i]=b[i];
}

void bfl_assign_zero_dbl(double* a)
{
  debug_handler("bigfloat_lattice_gensys","void bfl_assign_zero_dbl(a)");
  cl(i);
  for (fcl(i)=bigfloat_lattice_gensys::vectsize-1;i>=0;i--)
    a[i]=0;
}

void bfl_add_dbl(double* c, double* a, double* b)
{
  debug_handler("bigfloat_lattice_gensys","void bfl_add_dbl(c, a, b)");
  cl(i);
  for (fcl(i)=bigfloat_lattice_gensys::vectsize-1;i>=0;i--)
    c[i]=a[i]+b[i];
}

void bfl_subtract_dbl(double* c, double* a, double* b)
{
  debug_handler("bigfloat_lattice_gensys","void bfl_subtract_dbl(c, a, b)");
  cl(i);
  for (fcl(i)=bigfloat_lattice_gensys::vectsize-1;i>=0;i--)
    c[i]=a[i]-b[i];
}

void bfl_scalmul_dbl(double* b, const double& d, double* a)
{
  debug_handler("bigfloat_lattice_gensys","void bfl_scalmul_dbl(b, d, a)");
  cl(i);
  for (fcl(i)=bigfloat_lattice_gensys::vectsize-1;i>=0;i--)
    b[i]=a[i]*d;
}

void bfl_scalprod_dbl(double& res, double* a, double* b)
{
  debug_handler("bigfloat_lattice_gensys","void bfl_scalprod_dbl(res, a, b)");
  cl(i);
  res=0;
  for (fcl(i)=bigfloat_lattice_gensys::vectsize-1;i>=0;i--)
    res+=a[i]*b[i];
}

void bfl_scalquad_dbl(double& res, double* a)
{
  debug_handler("bigfloat_lattice_gensys","void bfl_scalquad_dbl(res, a, b)");
  cl(i);
  res=0;
  for (fcl(i)=bigfloat_lattice_gensys::vectsize-1;i>=0;i--)
    res+=a[i]*a[i];
}

void bfl_l2_norm_dbl(double& norm, double* v)
{
  debug_handler("bigfloat_lattice_gensys","void bfl_l2_norm_dbl(norm, v)");
  cl(i);
  norm=0;		// Initialisation
  for (fcl(i)=bigfloat_lattice_gensys::vectsize-1;i>=0;i--)
    norm+=v[i]*v[i];
  norm=sqrt(norm);
}

void bfl_l1_norm_dbl(double& norm, double* v)
{
  debug_handler("bigfloat_lattice_gensys","void bfl_l1_norm_dbl(norm, v)");
  cl(i);
  norm=0;	 // Initialisation
  for (fcl(i)=bigfloat_lattice_gensys::vectsize-1;i>=0;i--)
    norm+=fabs(v[i]);
}

//
// for bigfloat vectors
//
void bfl_assign_bfl(bigfloat* a, bigfloat* b)
{
  debug_handler("bigfloat_lattice_gensys", "bfl_assign_bfl(a, b)");
  cl(i);
  for (fcl(i)=bigfloat_lattice_gensys::vectsize-1;i>=0;i--)
    a[i].assign(b[i]);
}

void bfl_assign_zero_bfl(bigfloat* a)
{
  debug_handler("bigfloat_lattice_gensys", "bfl_assign_zero_bfl(a)");
  cl(i);
  for (fcl(i)=bigfloat_lattice_gensys::vectsize-1;i>=0;i--)
    a[i].assign_zero();
}

void bfl_add_bfl(bigfloat* c, bigfloat* a, bigfloat* b)
{
  debug_handler("bigfloat_lattice_gensys", "bfl_add_bfl(c, a, b)");
  cl(i);
  for (fcl(i)=bigfloat_lattice_gensys::vectsize-1;i>=0;i--)
    ::add(c[i],a[i],b[i]);
}

void bfl_subtract_bfl(bigfloat* c, bigfloat* a, bigfloat* b)
{
  debug_handler("bigfloat_lattice_gensys", "bfl_subtract_bfl(c, a, b)");
  cl(i);
  for (fcl(i)=bigfloat_lattice_gensys::vectsize-1;i>=0;i--)
    ::subtract(c[i],a[i],b[i]);
}

void bfl_scalmul_bfl(bigfloat* b, const bigfloat& d, bigfloat* a)
{
  debug_handler("bigfloat_lattice_gensys", "bfl_scalmul_bfl(b, d, a)");
  cl(i);
  for (fcl(i)=bigfloat_lattice_gensys::vectsize-1;i>=0;i--)
    ::multiply(b[i], a[i], d);
}

void bfl_scalprod_bfl(bigfloat& res, bigfloat* a, bigfloat* b)
{
  debug_handler("bigfloat_lattice_gensys", "bfl_scalprod_bfl(res, a, b)");
  cl(i);
  res.assign_zero();
  for (fcl(i)=bigfloat_lattice_gensys::vectsize-1;i>=0;i--) 
    {    
      ::multiply(bigfloat_lattice_gensys::vectbflz, a[i], b[i]);
      ::add(res, res, bigfloat_lattice_gensys::vectbflz);
    }
}

void bfl_scalquad_bfl(bigfloat& res, bigfloat* a)
{
  debug_handler("bigfloat_lattice_gensys", "bfl_scalquad_bfl(res, a)");
  cl(i);
  res.assign_zero();
  for (fcl(i)=bigfloat_lattice_gensys::vectsize-1;i>=0;i--) 
    {    
      ::square(bigfloat_lattice_gensys::vectbflz, a[i]);
      ::add(res, res, bigfloat_lattice_gensys::vectbflz);
    }
}

void bfl_l1_norm_bfl(bigfloat& norm, bigfloat* v)
{
  debug_handler("bigfloat_lattice_gensys", "bfl_l1_norm_bfl(norm, v)");
  cl(i);
  norm.assign_zero();	 // Initialisation
  for (fcl(i)=bigfloat_lattice_gensys::vectsize-1;i>=0;i--) 
    {
      bigfloat_lattice_gensys::vectbflz.assign(abs(v[i]));
      ::add(norm, norm, bigfloat_lattice_gensys::vectbflz);
    }
}

void bfl_l2_norm_bfl(bigfloat& norm, bigfloat* v)
{
  debug_handler("bigfloat_lattice_gensys", "bfl_l2_norm_bfl(norm, v)");
  cl(i);
  norm.assign_zero();		// Initialisation
  for (fcl(i)=bigfloat_lattice_gensys::vectsize-1;i>=0;i--) 
    {
      ::square(bigfloat_lattice_gensys::vectbflz, v[i]);
      ::add(norm, norm, bigfloat_lattice_gensys::vectbflz);
    }
  ::sqrt(norm, norm);
}

//
// for bigint vectors
//
void bfl_assign_bin(bigint* a, bigint* b)
{
  debug_handler("bigfloat_lattice_gensys", "bfl_assign_bin(a, b)");
  cl(i);
  for(fcl(i)=bigfloat_lattice_gensys::vectsize-1;i>=0;i--)
    a[i].assign(b[i]);
}

void bfl_assign_zero_bin(bigint* a)
{
  debug_handler("bigfloat_lattice_gensys", "bfl_assign_zero_bin(a)");
  cl(i);
  for(fcl(i)=bigfloat_lattice_gensys::vectsize-1;i>=0;i--)
    a[i].assign_zero();
}

void bfl_add_bin(bigint* c, bigint* a, bigint* b)
{
  debug_handler("bigfloat_lattice_gensys", "bfl_add_bin(c, a, b)");
  cl(i);
  for(fcl(i)=bigfloat_lattice_gensys::vectsize-1;i>=0;i--)
    ::add(c[i],a[i],b[i]);
}

void bfl_subtract_bin(bigint* c, bigint* a, bigint* b)
{
  debug_handler("bigfloat_lattice_gensys", "bfl_subtract_bin(c, a, b)");
  cl(i);
  for(fcl(i)=bigfloat_lattice_gensys::vectsize-1;i>=0;i--)
    ::subtract(c[i],a[i],b[i]);
}

void bfl_scalmul_bin(bigint* b, const bigint& d, bigint* a)
{
  debug_handler("bigfloat_lattice_gensys", "bfl_scalmul_bin(b, d, a)");
  cl(i);
  for(fcl(i)=bigfloat_lattice_gensys::vectsize-1;i>=0;i--)
    ::multiply(b[i], a[i], d);
}

void bfl_scalprod_bin(bigint& res, bigint* a, bigint* b)
{
  debug_handler("bigint_lattice_gensys", "bfl_scalprod_bin(res, a, b)");
  cl(i);
  res.assign_zero();
  for(fcl(i)=bigfloat_lattice_gensys::vectsize-1;i>=0;i--) 
    {    
      ::multiply(bigfloat_lattice_gensys::vectbinz, a[i], b[i]);
      ::add(res, res, bigfloat_lattice_gensys::vectbinz);
    }
}

void bfl_scalquad_bin(bigint& res, bigint* a)
{
  debug_handler("bigfloat_lattice_gensys", "bfl_scalquad_bin(res, a)");
  cl(i);
  res.assign_zero();
  for(fcl(i)=bigfloat_lattice_gensys::vectsize-1;i>=0;i--) 
    {    
      ::square(bigfloat_lattice_gensys::vectbinz, a[i]);
      ::add(res, res, bigfloat_lattice_gensys::vectbinz);
    }
}

void bfl_l2_norm_bin(bigint& norm, bigint* v)
{
  debug_handler("bigfloat_lattice_gensys", "bfl_l2_norm_bin(norm, v)");
  cl(i);
  norm.assign_zero();		// Initialisation
  for(fcl(i)=bigfloat_lattice_gensys::vectsize-1;i>=0;i--) 
    {
      ::square(bigfloat_lattice_gensys::vectbinz, v[i]);
      ::add(norm, norm, bigfloat_lattice_gensys::vectbinz);
    }
//  ::sqrt(norm, norm);
}

void bfl_l1_norm_bin(bigint& norm, bigint* v)
{
  debug_handler("bigfloat_lattice_gensys", "bfl_l1_norm_bin(norm, v)");
  cl(i);
  norm.assign_zero();	 // Initialisation
  for(fcl(i)=bigfloat_lattice_gensys::vectsize-1;i>=0;i--) 
    {
      bigfloat_lattice_gensys::vectbinz.assign(abs(v[i]));
      ::add(norm, norm, bigfloat_lattice_gensys::vectbinz);
    }
}

#endif

//
// Magie !!!
//
void bigfloat_lattice_gensys::Tr_trans_swap(bigfloat_lattice_gensys& Tr)
{
  debug_handler("bigfloat_lattice_gensys","Tr_trans_swap(Tr)");
  bigfloat **TrBfl;
  cl(i);
  cl(j);
//
// Swap matrix only
//
  if (trans_flag == false)
    {
      Tr.set_no_of_rows(rows);
      Tr.set_no_of_columns(columns);
      TrBfl=Tr.value;
      Tr.value=value;
      value=TrBfl;
      return;
    }
//
// Transpose and swap matrix
//
  Tr.set_no_of_rows(columns);
  Tr.set_no_of_columns(rows);
  TrBfl=Tr.get_data_address();
  for (fcl(i)=0;i<rows;i++)
    for (fcl(j)=0;j<columns;j++)
      ::swap(value[i][j], Tr.value[j][i]);
}

//
// Real implementation of the algorithms
// They are working on the transposed lattice
//
void bigfloat_lattice_gensys::Tr_randomize_vectors()
{
  debug_handler("bigfloat_lattice_gensys","Tr_randomize_vectors()");
  char *bitvect;
  sdigit *perm;
  sdigit ran;
  bigfloat **temp;
  
  cl(i);
  cl(j);
  cl(l);
    
//
// Allocate memory 
//
  bitvect=new char[rows];
  memory_handler(bitvect,"bigfloat_lattice_gensys","Tr_randomize_vectors() :: "
                         "not enough memory !");
  perm=new sdigit[rows];
  memory_handler(perm,"bigfloat_lattice_gensys","Tr_randomize_vectors() :: "
                      "not enough memory !");
  temp=new bigfloat*[rows];
  memory_handler(temp,"bigfloat_lattice_gensys","Tr_randomize_vectors() :: "
                      "not enough memory !");
  
//
// Initialize srandom with a random value
//
  srandom((int )random());
//
// Clear bitvector
//
  for (fcl(i)=0;i<rows;bitvect[i++]=0);
//
// Generate a permutation
// Try rows times to find valid value
//
  for (fcl(i)=0;i<rows;i++)
    {
      for (fcl(j)=0;j<rows;j++)
        {
          ran=(sdigit )random()%rows;
          if (bitvect[ran] == 0)
            {
              bitvect[ran]=1;
              perm[i]=ran;
              break;
            }
          else
            if (j == rows-1)
              {
                for (fcl(l)=0;l<rows;l++)
                  if (bitvect[l] == 0)
                    {
                      perm[i]=l;
                      bitvect[l]=1;
                      break;
                    }
                break;
              }
        }
    }
    
//
// Perform permutation on lattice
//
  for (fcl(i)=0;i<rows;i++)
    temp[perm[i]]=value[i];
  for (fcl(i)=0;i<rows;i++)
    value[i]=temp[i];

//
// Free allocated storage
//
  delete[] perm;
  delete[] bitvect;
  delete[] temp;
}

void bigfloat_lattice_gensys::Tr_sort_vectors(sdigit vgl)
{
  debug_handler("bigfloat_lattice_gensys","Tr_sort_vectors()");
  cl(i);
  cl(j);
  bigfloat *quads;

//
// Allocate memory for scalar product of the vectors
//
  quads=new bigfloat[rows];
  memory_handler(quads, "bigfloat_lattice_gensys","Tr_sort_vectors() :: "
                        "not enough memory !");

//
// Compute scalar products
//
  vectsize=columns;
  for (fcl(i)=0;i<rows;i++)
    bfl_scalquad_bfl(quads[i], value[i]);
//
// sort by scalar products ("length^2")
// vgl = -1   ->   smallest vector first
// vgl =  1   ->   biggest vector first
//
  for (fcl(i)=0;i<rows-1;i++)
    for (fcl(j)=i+1;j<rows;j++)
       {
         if (quads[i].compare(quads[j]) == vgl)
           {
             bfl_swap_bfl(value[i], value[j]);
             ::swap(quads[i], quads[j]);
           }
       }
//
// Free allocated storage
//
   delete[] quads; 
}

void bigfloat_lattice_gensys::Tr_sort_vectors(bfl_cmp_func cpf)
{
  debug_handler("bigfloat_lattice_gensys","Tr_sort_vectors()");
  cl(i);
  cl(j);
//
// Sort vectors by specified compare function cpf
// Perform bubble sort 
//
  vectsize=columns;
  for (fcl(i)=0;i<rows-1;i++)
    for (fcl(j)=i+1;j<rows;j++)
      if (cpf(value[i], value[j], columns) > 0)
        bfl_swap_bfl(value[i], value[j]);
}

//
// Algorithm - Subroutines
//
void bigfloat_lattice_gensys::bigfloatify(math_matrix< bigfloat >& Bf, 
                                          const math_matrix< bigint >& Bi)
{
  debug_handler("bigfloat_lattice_gensys","bigfloatify(Bf, Bi)");
  bigint **BiAddr;
  bigfloat **BfAddr;
  lidia_size_t Bf_rows=0;
  lidia_size_t Bf_columns=0;
  cl(i);
  cl(j);
  Bf.set_no_of_rows(Bf_rows=Bi.get_no_of_rows());
  Bf.set_no_of_columns(Bf_columns=Bi.get_no_of_columns());
  BiAddr=Bi.get_data_address();
  BfAddr=Bf.get_data_address();  
  for (fcl(i)=0;i<Bf_rows;i++)
    for (fcl(j)=0;j<Bf_columns;j++)
      BfAddr[i][j].assign(BiAddr[i][j]);
}

sdigit bigfloat_lattice_gensys::compute_read_precision()
{
  debug_handler("bigfloat_lattice_gensys","compute_read_precision()");
  cl(x);
  cl(y);
  sdigit new_prec;
  sdigit read_prec=0;
  for (fcl(x)=0;x<rows;++x)
    for (fcl(y)=0;y<columns;++y)
      if (read_prec < (new_prec=(sdigit )(value[x][y].bit_length()/log(10)+1)))
        read_prec=new_prec;
  return (read_prec);
}

sdigit bigfloat_lattice_gensys::compute_precision()
{
  debug_handler("bigfloat_lattice_gensys","compute_precision()");
  bigfloat alpha,zweipotq;
  sdigit read_prec=compute_read_precision();
  sdigit n2=rows;
  sdigit prec;
  alpha_compute(alpha);
  zwei_pot_q_compute(zweipotq,n2, alpha);
  read_prec=2*read_prec+rows-1;
  prec=(long )(2*(((zweipotq.exponent()+zweipotq.bit_length()+1)*
        log(2)/log(10)+1)+read_prec)+(columns+rows)-1);
  return (prec);
}

void bigfloat_lattice_gensys::gamma_compute(bigfloat& g, long l)
{
  debug_handler("bigfloat_lattice_gensys","gamma_compute(g, l)");
  bigfloat ha[]={1, 4.0/3.0, 2, 4, 8, 64.0/3.0, 64, 256};
//
// Computation of the l-th Hermite - Constant gamma,
//
  bigfloat lg;
  if ((l>0)&&(l<9))
    g.assign(ha[l-1]);
  else
    {
      lg.assign(0.75);
      log(lg, lg);
      g.assign((sdigit )(l * (l-1)));
      g.divide_by_2();
      ::multiply(lg, lg, g);
      exp(g, lg); 
    }
}

void bigfloat_lattice_gensys::alpha_compute(bigfloat& alpha)
{
// alpha = max{vect[1].l2_norm(),...,vect[columns].l2_norm()}
// norm = vect[i].l2_norm(), 0 <= i < columns
  debug_handler("bigfloat_lattice_gensys","alpha_compute(alpha)");
  cl(i);
  vectsize=columns;
  bfl_l2_norm_bfl(alpha,value[0]);
  for (fcl(i)=1;i<rows;++i)
    {
      bfl_l2_norm_bfl(tempmz0,value[i]);
      if (alpha.compare(tempmz0) < 0)
	alpha.assign(tempmz0);
    }
}

void bigfloat_lattice_gensys::zwei_pot_q_compute(bigfloat& zweipotq, long& n2, bigfloat& alpha)
{
  debug_handler("bigfloat_lattice_gensys","zwei_pot_q_compute(zwpq, n2, alpha)");
  long beta = rows;

// Computation of beta = min (A.columns, A.rows)
  if (columns < rows)
    beta=columns;
 
  tempmz0.assign(n2);
  ::sqrt(tempmz0,tempmz0);
  tempmz0.divide_by_2();
  ::multiply(tempmz0,rows,tempmz0);
  tempmz1.assign(rows);
  ::sqrt(tempmz1,tempmz1);
  ::add(tempmz0,tempmz0,tempmz1);
 
  log(tempmz1,alpha);
  ::multiply(tempmz1,tempmz1,beta);
  exp(tempmz1,tempmz1);
  gamma_compute(tempmz2 ,beta);
 
  ::sqrt(tempmz2, tempmz2);
  ::multiply(tempmz2, tempmz2, tempmz1);
  ::multiply(tempmz2, tempmz2, tempmz0);
  tempmz0.assign(n2);
  ::multiply(tempmz0, tempmz0, rows);
  ::sqrt(tempmz0, tempmz0);
  ::add(tempmz0, tempmz0, 2);
  ::multiply(zweipotq, tempmz0, tempmz2);
  
  tempmz0.assign(beta + rows);
  tempmz0.divide_by_2();
  ::subtract(tempmz0, tempmz0, 1);
  tempmz0.multiply_by_2();
  exp(tempmz0, tempmz0);
  ::multiply(tempmz0, zweipotq, tempmz0);
 
  tempmz2.assign(beta + 1);
  tempmz2.divide_by_2();
  tempmz1.assign(columns);
  log(tempmz1,tempmz1);
  ::multiply(tempmz2, tempmz2, tempmz1);
  exp(tempmz2, tempmz2);
  ::divide(tempmz0, tempmz0, tempmz2); 
  ceil(zweipotq, tempmz0);
}

