//
// LiDIA - a library for computational number theory
//   Copyright (c) 1995 by the LiDIA Group
//
// File        : bf_lattice_gensys.h
// 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
//
//

#ifndef _bigfloat_lattice_gensys_h
#define _bigfloat_lattice_gensys_h

#if defined(HAVE_MAC_DIRS) || defined(__MWERKS__)
#include <LiDIA:math_matrix.h>
#include <LiDIA:bi_lattice_gensys.h>
#include <LiDIA:bigint.h>
#include <LiDIA:bigfloat.h>
#include <LiDIA:base_vector.h>
#include <LiDIA:timer.h>
#else
#include <LiDIA/math_matrix.h>
#include <LiDIA/bi_lattice_gensys.h>
#include <LiDIA/bigint.h>
#include <LiDIA/bigfloat.h>
#include <LiDIA/base_vector.h>
#include <LiDIA/timer.h>
#endif

#ifndef rint
#define rint(x) (((x)-floor(x))<0.5?floor(x):ceil(x)) 
#endif

//
// Define for gcc 2.7.0 or higher
// !!! new ansi standard !!!
//
#if defined(__GNUC__) && __GNUC__ == 2 && __GNUC_MINOR__ >= 7 || __GNUC__ >= 3
#define cl(x) 
#define fcl(x) register lidia_size_t x
#else
#define cl(x) lidia_size_t x
#define fcl(x) x 
#endif

typedef int (*bfl_cmp_func)(const bigfloat*, const bigfloat*, long);

class bigfloat_lattice_gensys:public math_matrix< bigfloat > 
{
  protected :
//
// configuration of lattice
//
    sdigit         y_nom;
    sdigit         y_denom;
    double         y_par;
    
    bool           trans_flag;

//
// algorithm - information
//
    sdigit         reduction_steps;
    sdigit         swapping_steps;
    sdigit         correction_steps;
    
//
// static variables for temporary use to increase speed
//
    static bigfloat tempmz0;
    static bigfloat tempmz1;
    static bigfloat tempmz2;
    static bigfloat tempmz3;
    static bigfloat tempmz4;
    static bigfloat ergmz;
    
    static double   vectdblz;
    static bigint   vectbinz;
    static bigfloat vectbflz;

    static lidia_size_t vectsize;

  public :

//
// Constructor / Destructors 
//
    bigfloat_lattice_gensys(); 
//    bigfloat_lattice_gensys(lidia_size_t);
    bigfloat_lattice_gensys(lidia_size_t, lidia_size_t);
    bigfloat_lattice_gensys(lidia_size_t, lidia_size_t, const bigfloat**);
    bigfloat_lattice_gensys(const math_matrix < bigfloat >&);
    bigfloat_lattice_gensys(const bigfloat_lattice_gensys&);
    ~bigfloat_lattice_gensys();    

//
// Assignments
//
    void assign(const bigfloat_lattice_gensys&);
    bigfloat_lattice_gensys& operator = (const bigfloat_lattice_gensys&);
    friend void assign(bigfloat_lattice_gensys&, const bigfloat_lattice_gensys&);

//
// Set or get reduction info, paramters
//
    void set_reduction_parameter(double);
    void set_reduction_parameter(sdigit, sdigit);
    double get_reduction_parameter();
    void get_reduction_parameter(sdigit&, sdigit&);

//
// Set representation direction
// reduce column by column or row by row
//
    void set_representation_columns();
    void set_representation_rows(); 
    bool get_representation() { return (trans_flag); };

//
// get detailed information about lll - algorithm
//
    sdigit get_no_of_reduction_steps() { return (reduction_steps); };
    sdigit get_no_of_swaps() { return (swapping_steps); };
    sdigit get_no_of_corrections() { return (correction_steps); };
    
//
// Algorithms - Interface
//

//
// Special experimantal functions
//
//  randomize = permutation
//  sort by length 
//
   void randomize_vectors();
   void sort_big_vectors();
   void sort_small_vectors();

//
// int bfl_cmp_function(bigint *a, bigint *b, long)
// returns 
// >0     if    a  >  b
// =0     if    a  =  b
// <0     if    a  <  b
//
   void sort_vectors(bfl_cmp_func);

//
// Buchmann - Kessler Algorthm for 
// linaer generating systems
//
   void lin_gen_system(math_matrix< bigint >&, lidia_size_t&);
   void lin_gen_system(math_matrix< bigfloat >&, lidia_size_t&);
   inline friend bigint_lattice_gensys lin_gen_system(const bigfloat_lattice_gensys& L,
                                                      lidia_size_t& rank)
   {
     bigfloat_lattice_gensys TL(L);
     bigint_lattice_gensys T;
     TL.lin_gen_system(T, rank);
     return (T);
   }

//
// Variation of the Schnorr - Euchner - lllfp using doubles for
// parameter=1 and x*doubleprec for parameter=x
//
   void lll_gensys(lidia_size_t&, sdigit=1);
   void lll_gensys(const math_matrix< bigfloat >&, lidia_size_t&, sdigit=1);
   inline friend bigfloat_lattice_gensys lll_gensys(const bigfloat_lattice_gensys& L,
                                                    lidia_size_t& rank,
                                                    sdigit factor=1)
   {
     bigfloat_lattice_gensys TL(L);
     TL.lll_gensys(rank, factor);
     return (TL);
   }

   void lll_trans_gensys(math_matrix< bigint >&, lidia_size_t&, sdigit=1);
   void lll_trans_gensys(math_matrix< bigfloat >&, lidia_size_t&, sdigit=1);
   inline friend bigfloat_lattice_gensys lll_trans_gensys(const bigfloat_lattice_gensys& L,       
                                                          math_matrix< bigint >& T, 
                                                          lidia_size_t& rank,
                                                          sdigit factor=1)
   {
     bigfloat_lattice_gensys LT(L);
     LT.lll_trans_gensys(T, rank, factor);
     return (LT);
   }

   inline friend bigfloat_lattice_gensys lll_trans_gensys(const bigfloat_lattice_gensys& L,       
                                                          math_matrix< bigfloat >& T, 
                                                          lidia_size_t& rank,
                                                          sdigit factor=1)
   {
     bigfloat_lattice_gensys LT(L);
     LT.lll_trans_gensys(T, rank, factor);
     return (LT);
   }

   
   void lll_var_gensys(lidia_size_t&, sdigit=1);
   void lll_var_gensys(const math_matrix< bigfloat >&, lidia_size_t&, sdigit=1);
   inline friend bigfloat_lattice_gensys lll_var_gensys(const bigfloat_lattice_gensys& L,
                                                        lidia_size_t& rank,
                                                        sdigit factor=1)
   {
     bigfloat_lattice_gensys TL(L);
     TL.lll_var_gensys(rank, factor);
     return (TL);
   }


   void lll_trans_var_gensys(math_matrix< bigint >&, lidia_size_t&, sdigit=1);
   void lll_trans_var_gensys(math_matrix< bigfloat >&, lidia_size_t&, sdigit=1);
   inline friend bigfloat_lattice_gensys lll_trans_var_gensys(const bigfloat_lattice_gensys& L,
                                                              math_matrix< bigint >& T,
                                                              lidia_size_t& rank,
                                                              sdigit factor=1)
   {
     bigfloat_lattice_gensys LT(L);
     LT.lll_trans_var_gensys(T, rank, factor);
     return (LT);
   }

   inline friend bigfloat_lattice_gensys lll_trans_var_gensys(const bigfloat_lattice_gensys& L,
                                                              math_matrix< bigfloat >& T,
                                                              lidia_size_t& rank,
                                                              sdigit factor=1)
   {
     bigfloat_lattice_gensys LT(L);
     LT.lll_trans_var_gensys(T, rank, factor);
     return (LT);
   }

   
//
// Modified lll for genertating systems 
// of the form n x n+1
//
// Returns vector of dimension n 
// where you can find the relations 
//
   void mlll(base_vector< bigint >&);
   void mlll(const math_matrix< bigfloat >&, base_vector< bigint >&);
   inline friend bigfloat_lattice_gensys mlll(const bigfloat_lattice_gensys& L, 
                                              base_vector< bigint >& v)
   {
     bigfloat_lattice_gensys TL(L);
     TL.mlll(L, v);
     return (TL);
   }
   
   void mlll(bigint *&);
   void mlll(const math_matrix< bigfloat >&, bigint *&);
   inline friend bigfloat_lattice_gensys mlll(const bigfloat_lattice_gensys& L, 
                                              bigint*& v)
   {
     bigfloat_lattice_gensys TL(L);
     TL.mlll(L, v);
     return (TL);
   }
      
   void mlll(base_vector< bigfloat >&);
   void mlll(const math_matrix< bigfloat >&, base_vector< bigfloat >&);
   inline friend bigfloat_lattice_gensys mlll(const bigfloat_lattice_gensys& L, 
                                              base_vector< bigfloat >& v)
   {
     bigfloat_lattice_gensys TL(L);
     TL.mlll(L, v);
     return (TL);
   }
   
   void mlll(bigfloat *&);
   void mlll(const math_matrix< bigfloat >&, bigfloat *&);
   inline friend bigfloat_lattice_gensys mlll(const bigfloat_lattice_gensys& L, 
                                              bigfloat*& v)
   {
     bigfloat_lattice_gensys TL(L);
     TL.mlll(L, v);
     return (TL);
   }
   
//
// Compute a basis from lattice A and Transformation lattice T
//
   inline void compute_basis(const math_matrix< bigfloat >& A, const math_matrix< bigint >& T)
   {
     debug_handler("bigfloat_lattice_gensys","compute_basis(A, T)");
     math_matrix< bigfloat > Bi(columns, columns);
     bigfloatify(Bi, T);
     compute_basis(A, Bi);
   }
   
   inline void compute_basis(const math_matrix< bigfloat >& A, const math_matrix< bigfloat >& T) 
   {
     debug_handler("bigfloat_lattice_gensys","compute_basis(A, T)");
     if (trans_flag)
       ::multiply(*this, A, T);
     else
       ::multiply(*this, T, A);
   }
   
  protected :

//
// Functions needed by every constructor 
//
    void init_parameters();
    void do_some_other_stuff() {};
    void assign_the_rest(const bigfloat_lattice_gensys&);

    void bigfloatify(math_matrix< bigfloat >&, const math_matrix< bigint >&);

    sdigit compute_read_precision();
    sdigit compute_precision(); 

    void alpha_compute(bigfloat&);
    void gamma_compute(bigfloat&, sdigit);
    void zwei_pot_q_compute(bigfloat&, sdigit&, bigfloat&);

//
// Very funny, helpful, speedup
//
    void Tr_trans_swap(bigfloat_lattice_gensys&);
    
//
// Dimension Checking
//
    inline bool Tr_check_mlll()
    {
      if (trans_flag == true)
        {
          if (rows+1 != columns)
            return(false);
          else
            return(true);
        }
      else
        {
          if (columns+1 != rows)
            return(false);
          else
            return(true);
        }
    }

//
// Algorithms, real Implementaion
//
    void Tr_randomize_vectors();
    void Tr_sort_vectors(sdigit);
    void Tr_sort_vectors(bfl_cmp_func);

    void Tr_lll();   
    void Tr_lll_dbl();
    void Tr_lll_bfl(sdigit);

    void Tr_lll_var_dbl(math_matrix< bigint >&, sdigit);
    void Tr_lll_var_bfl(math_matrix< bigint >&, sdigit, sdigit);

    void Tr_lll_deep_insert_dbl(sdigit);
    void Tr_lll_deep_insert_bfl(sdigit, sdigit);
    
    void Tr_lll_trans(math_matrix< bigint >&);   
    void Tr_lll_trans_dbl(math_matrix< bigint >&);
    void Tr_lll_trans_bfl(math_matrix< bigint >&, sdigit);

    void Tr_lll_trans_var_dbl(math_matrix< bigint >&, math_matrix< bigint >&, 
                              sdigit);
    void Tr_lll_trans_var_bfl(math_matrix< bigint >&, math_matrix< bigint >&, 
                              sdigit, sdigit);

    void Tr_mlll_bfl(bigint*);

    void Tr_lin_gen_system(math_matrix< bigint >&, lidia_size_t&);

    void Tr_lll_dbl_gensys(lidia_size_t&);
    void Tr_lll_bfl_gensys(lidia_size_t&, sdigit);

    void Tr_lll_var_dbl_gensys(math_matrix< bigint >&,  
                               sdigit, lidia_size_t&);
    void Tr_lll_var_bfl_gensys(math_matrix< bigint >&,  
                               sdigit, lidia_size_t&, sdigit);

    void Tr_lll_trans_dbl_gensys(math_matrix< bigint >&, lidia_size_t&);
    void Tr_lll_trans_bfl_gensys(math_matrix< bigint >&, lidia_size_t&, sdigit);

    void Tr_lll_trans_var_dbl_gensys(math_matrix< bigint >&, 
                                     math_matrix< bigint >&, 
                                     sdigit, lidia_size_t&);
    void Tr_lll_trans_var_bfl_gensys(math_matrix< bigint >&, 
                                     math_matrix< bigint >&,  
                                     sdigit, lidia_size_t&, sdigit);

//
// "Vector" - Operations
//

#ifdef __GNUC__ 

//
// for double vectors
//
    inline friend 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];
    }
    
    inline friend 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;
    }
    
    inline friend 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];
    }
    
    inline friend 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];
    }
    
    inline friend 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;
    }
    
    inline friend 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];
    }
    
    inline friend 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];
    }
    
    inline friend 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);
    }
    
    inline friend 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
//
    inline friend 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]);
    }
    
    inline friend 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();
    }
    
    inline friend 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]);
    }
    
    inline friend 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]);
    }
    
    inline friend 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);
    }
    
    inline friend 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);
        }
    }
    
    inline friend 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);
        }
    }
    
    inline friend 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);
        }
    }
    
    inline friend 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
//
    inline friend 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]);
    }
    
    inline friend 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();
    }
    
    inline friend 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]);
    }
    
    inline friend 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]);
    }
    
    inline friend 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);
    }
    
    inline friend 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);
        }
    }
    
    inline friend 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);
        }
    }
    
    inline friend 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);
    }
    
    inline friend 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);
        }
    }
    
#else

//
// For double vectors
//
    friend void bfl_assign_dbl(double*, double*); 
    friend void bfl_assign_zero_dbl(double*);
    friend void bfl_add_dbl(double*, double*, double*);
    friend void bfl_subtract_dbl(double*, double*, double*);

    friend void bfl_scalmul_dbl(double*, const double&, double*);
    friend void bfl_scalprod_dbl(double&, double*, double*);
    friend void bfl_scalquad_dbl(double&, double*);
    
    friend void bfl_l1_norm_dbl(double&, double*);
    friend void bfl_l2_norm_dbl(double&, double*);

//
// for bigfloat vectors
//
    friend void bfl_assign_bfl(bigfloat*, bigfloat*); 
    friend void bfl_assign_zero_bfl(bigfloat*);
    friend void bfl_add_bfl(bigfloat*, bigfloat*, bigfloat*);
    friend void bfl_subtract_bfl(bigfloat*, bigfloat*, bigfloat*);

    friend void bfl_scalmul_bfl(bigfloat*, const bigfloat&, bigfloat*);
    friend void bfl_scalprod_bfl(bigfloat&, bigfloat*, bigfloat*);
    friend void bfl_scalquad_bfl(bigfloat&, bigfloat*);
    
    friend void bfl_l1_norm_bfl(bigfloat&, bigfloat*);
    friend void bfl_l2_norm_bfl(bigfloat&, bigfloat*);


//
// for bigint vectors
//
    friend void bfl_assign_bin(bigint*, bigint*); 
    friend void bfl_assign_zero_bin(bigint*);
    friend void bfl_add_bin(bigint*, bigint*, bigint*);
    friend void bfl_subtract_bin(bigint*, bigint*, bigint*);

    friend void bfl_scalmul_bin(bigint*, const bigint&, bigint*);
    friend void bfl_scalprod_bin(bigint&, bigint*, bigint*);
    friend void bfl_scalquad_bin(bigint&, bigint*);
    
    friend void bfl_l1_norm_bin(bigint&, bigint*);
    friend void bfl_l2_norm_bin(bigint&, bigint*);

#endif

    inline friend void bfl_swap_dbl(double*& a, double*& b)
    {
      debug_handler("bigint_lattice_gensys","void bfl_swap_dbl(a,b)");
      register double* temp=a;
      a=b;
      b=temp;
    }

    inline friend void bfl_swap_bfl(bigfloat*& a, bigfloat*& b)
    {
      debug_handler("bigint_lattice_gensys","bfl_swap_bfl(a,b)");
      register bigfloat* temp=a;
      a=b;
      b=temp;
    }
    
    inline friend void bfl_swap_bin(bigint*& a, bigint*& b)
    {
      debug_handler("bigfloat_lattice_gensys","bfl_swap_bin(a, b)");
      register bigint* temp=a;
      a=b;
      b=temp;
    }
    

};

#endif
