//
// LiDIA - a library for computational number theory
//   Copyright (c) 1994, 1995 by the LiDIA Group
//
// File        : bigint_matrix.h
// Author      : Patrick Theobald (PT)
// Last change : PT, Oct 28, 1995, initial version 
//

/*
$Id: bigint_matrix.h,v 1.22 1996/10/29 15:42:19 theobald Exp $
*/

#ifndef LIDIA_BIGINT_MATRIX_H
#define LIDIA_BIGINT_MATRIX_H

#ifndef LIDIA_PRIMELIST_SIZE
#define LIDIA_PRIMELIST_SIZE 300
#endif

#ifndef LIDIA_PRIMELIST_MAXSIZE
#define LIDIA_PRIMELIST_MAXSIZE 86416
#endif

#ifndef LIDIA_PRIMELIST_NAME
#define LIDIA_PRIMELIST_NAME "LIDIA_PRIMES"
#endif

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

class bigint_matrix : public math_matrix < bigint >
{
  /**
   ** constructors
   **/

 public:
  
  inline bigint_matrix() : math_matrix < bigint > () {}
  inline bigint_matrix(lidia_size_t a, lidia_size_t b) : math_matrix < bigint > (a,b) {} 
  inline bigint_matrix(lidia_size_t a, lidia_size_t b, const bigint **v) : math_matrix < bigint > (a,b,v) {}
  inline bigint_matrix(const base_matrix < bigint > &A) : math_matrix < bigint > (A) {}
    
  /**
   ** destructor
   **/
 
 public: 

  inline ~bigint_matrix() {}

  /**
   ** remainder
   **/
  
 public:

#ifndef HEADBANGER
  inline friend void remainder(bigint_matrix &A, const bigint_matrix &B, const bigint &mod)
    {A.remainder(B, mod);}
#endif

 protected:

  void remainder(const bigint_matrix &, const bigint &);

 public:

  inline friend bigint_matrix operator % (const bigint_matrix &A, const bigint &mod)
    {bigint_matrix RES(A.rows, A.columns); RES.remainder(A, mod); return RES;}
  inline friend bigint_matrix &operator %= (bigint_matrix &A, const bigint &mod)
    {A.remainder(A, mod); return A;}
  
  /**
   ** assignments
   **/

 public:
 
  inline bigint_matrix & operator = (const base_matrix < bigint >& B) 
    {base_matrix < bigint >::assign(B); return *this;}
  
  /**
   ** boolean functions
   **/
 
 public:
 
  bool is_column_zero(lidia_size_t) const;

#ifndef HEADBANGER
  inline friend bool is_column_zero(const bigint_matrix &A, lidia_size_t i)
    {return A.is_column_zero(i);}
#endif

  bool is_row_zero(lidia_size_t) const;

#ifndef HEADBANGER
  inline friend bool is_row_zero(const bigint_matrix &A, lidia_size_t i)
    {return A.is_row_zero(i);}
#endif

  bool is_matrix_zero() const;

#ifndef HEADBANGER
  inline friend bool is_matrix_zero(const bigint_matrix &A)
    {return A.is_matrix_zero();}
#endif

  /**
   ** norms and bounds
   **/
 
 public:
 
#ifndef HEADBANGER
  void max(bigint &) const;
  inline bigint max() const
    {bigint MAX; max(MAX); return MAX;}
#endif

  inline friend bigint max(const bigint_matrix &A)
    {return A.max();}

#ifndef HEADBANGER
  void max_abs(bigint &) const;
  inline bigint max_abs() const
    {bigint RES; max_abs(RES); return RES;}
#endif

  inline friend bigint max_abs(const bigint_matrix &A)
    {return A.max_abs();}

#ifndef HEADBANGER  
  void max_pos(bigint &, lidia_size_t &, lidia_size_t &) const;
  inline bigint max_pos(lidia_size_t &x, lidia_size_t &y) const
    {bigint MAX; max_pos(MAX, x, y); return MAX;}
#endif

  inline friend bigint max_pos(const bigint_matrix &A, lidia_size_t &x, lidia_size_t &y)
    {return A.max_pos(x, y);}

#ifndef HEADBANGER
  void max_abs_pos(bigint &, lidia_size_t &, lidia_size_t &) const;
  inline bigint max_abs_pos(lidia_size_t &x, lidia_size_t &y) const
    {bigint RES; max_abs_pos(RES, x, y); return RES;}
#endif

  inline friend bigint max_abs_pos(const bigint_matrix &A, lidia_size_t &x, lidia_size_t &y)
    {return A.max_abs_pos(x, y);}

#ifndef HEADBANGER  
  void min(bigint &) const;
  inline bigint min() const
    {bigint MIN; min(MIN); return MIN;}
#endif

  inline friend bigint min(const bigint_matrix &A)
    {return A.min();}  

#ifndef HEADBANGER
  void min_abs(bigint &) const;
  inline bigint min_abs() const
    {bigint MIN; min_abs(MIN); return MIN;}
#endif

  inline friend bigint min_abs(const bigint_matrix &A)
    {return A.min_abs();}

#ifndef HEADBANGER
  void min_pos(bigint &, lidia_size_t &, lidia_size_t &) const;
  inline bigint min_pos(lidia_size_t &x, lidia_size_t &y) const
    {bigint MIN; min_pos(MIN, x, y); return MIN;}
#endif

  inline friend bigint min_pos(const bigint_matrix &A, lidia_size_t &x, lidia_size_t &y)
    {return A.min_pos(x, y);}

#ifndef HEADBANGER
  void min_abs_pos(bigint &, lidia_size_t &, lidia_size_t &) const;
  inline bigint min_abs_pos(lidia_size_t &x, lidia_size_t &y) const
    {bigint MIN; min_abs_pos(MIN, x, y); return MIN;}
#endif

  inline friend bigint min_abs_pos(const bigint_matrix &A, lidia_size_t &x, lidia_size_t &y)
    {return A.min_abs_pos(x, y);}

#ifndef HEADBANGER  
  void hadamard(bigint &) const;
  inline bigint hadamard() const
    {bigint H; hadamard(H); return H;}
#endif

  inline friend bigint hadamard(const bigint_matrix &A)
    {bigint H; A.hadamard(H); return H;}

#ifndef HEADBANGER
  void hadamard2(bigint &) const;
  inline bigint hadamard2() const
    {bigint H; hadamard2(H); return H;}
#endif

  inline friend bigint hadamard2(const bigint_matrix &A)
    {bigint H; A.hadamard2(H); return H;}

#ifndef HEADBANGER
  void row_norm(bigint &, lidia_size_t, long) const;
  inline bigint row_norm(lidia_size_t i, long n) const
    {bigint RES; row_norm(RES, i, n); return RES;}
#endif

  inline friend bigint row_norm(const bigint_matrix &A, lidia_size_t i, long n)
    {bigint RES; A.row_norm(RES, i, n); return RES;}

#ifndef HEADBANGER
  void column_norm(bigint &, lidia_size_t, long) const;
  inline bigint column_norm(lidia_size_t i, long n) const
    {bigint RES; column_norm(RES, i, n); return RES;}
#endif

  inline friend bigint column_norm(const bigint_matrix &A, lidia_size_t i, long n)
    {bigint RES; A.column_norm(RES, i, n); return RES;}

  /**
   ** randomize
   **/
 
 public:
 
  void randomize(const bigint &);
  void randomize_with_det(const bigint &, const bigint &);
  
  /**
   ** Chinese remaindering theorem
   **/

 protected:			

  friend void chinrest(bigint &, const bigint *, const bigint *);
  inline friend bigint chinrest(const bigint *values, const bigint *prim)
    {bigint RES; ::chinrest(RES, values, prim); return RES;}
  
  void chinrest(const bigint_matrix *, const bigint *);
  inline friend bigint_matrix chinrest(const bigint_matrix *v, const bigint *prim)
    {bigint_matrix A(v[0].rows, v[0].columns); A.chinrest(v, prim); return A;}
  
  /**
   ** List of primes
   **/

 protected:

  friend bigint *get_primes(const bigint &, const bigint &);
  
  /**
   ** BEGIN: Linear algebra
   ** PART 1
   **/

  /**
   ** rank
   **/

 public:

#ifndef HEADBANGER 
  lidia_size_t rank() const;
#endif

  inline friend lidia_size_t rank(const bigint_matrix &B)
    {return B.rank();}

  /**
   ** rank and linearly independent rows
   **/

 public:

#ifndef HEADBANGER
  lidia_size_t *lininr() const;
  inline friend lidia_size_t *lininr(const bigint_matrix &B)
    {return B.lininr();}
#endif

  void lininr(base_vector < lidia_size_t > &) const;

#ifndef HEADBANGER
  inline friend void lininr(base_vector < lidia_size_t > &v, const bigint_matrix &B)
    {B.lininr(v);}
#endif 

#ifndef HEADBANGER
  lidia_size_t *lininr2() const;
  inline friend lidia_size_t *lininr2(const bigint_matrix &B)
    {return B.lininr2();}
#endif

  /**
   ** rank and linearly independent columns
   **/

 public:

#ifndef HEADBANGER
  lidia_size_t *lininc() const;
  inline friend lidia_size_t *lininc(const bigint_matrix &B)
    {return B.lininc();}
#endif

  void lininc(base_vector < lidia_size_t > &) const;

#ifndef HEADBANGER
  inline friend void lininc(base_vector < lidia_size_t > &v, const bigint_matrix &B)
    {B.lininc(v);}
#endif

  /**
   ** regular expansion
   **/

 public:

#ifndef HEADBANGER
  void regexpansion(const lidia_size_t *);
  inline friend void regexpansion(bigint_matrix &A, const lidia_size_t *v)
    { A.regexpansion(v);}
#endif

  /**
   ** adjoint matrix
   **/

 public:

#ifndef HEADBANGER
  void adj(const bigint_matrix &);
#endif

  inline friend bigint_matrix adj(const bigint_matrix &A)
    {bigint_matrix B(A.rows, A.columns); B.adj(A); return B;}
  
  /**
   ** lattice determinant
   **/
  
  /**
   ** BEGIN: INTERFACE - latticedet
   **/
  
 public:

#ifndef HEADBANGER
  inline void latticedet(bigint &DET) 
    {latticedet2(DET);}
  inline bigint latticedet() 
    {bigint RET; latticedet2(RET); return RET;}
#endif

  inline friend bigint latticedet(const bigint_matrix &A)
    {bigint DET; A.latticedet2(DET); return DET;}

  /** 
   ** END: INTERFACE - latticedet
   **/

 public:

#ifndef HEADBANGER
  void latticedet1(bigint &) const;
  inline bigint latticedet1() const
    { bigint DET; latticedet1(DET); return DET;}
#endif

  inline friend bigint latticedet1(const bigint_matrix &A)
    {bigint DET; A.latticedet1(DET); return DET;}

#ifndef HEADBANGER  
  void latticedet2(bigint &) const;
  inline bigint latticedet2() const
    {bigint DET; latticedet2(DET); return DET;}    
#endif

  inline friend bigint latticedet2(const bigint_matrix &A)
    {bigint DET; A.latticedet2(DET); return DET;}

#ifndef HEADBANGER  
  void latticedet3(bigint &) const;
  inline bigint latticedet3() const
    {bigint DET; latticedet3(DET); return DET;}    
#endif

  inline friend bigint latticedet3(const bigint_matrix &A)
    {bigint DET; A.latticedet3(DET); return DET;}
  
  /**
   ** determinant
   **/

 public:

#ifndef HEADBANGER
  void det(bigint &) const;
  inline bigint det() const
    {bigint DET; det(DET); return DET;}
#endif

  inline friend bigint det(const bigint_matrix &A)
    {bigint DET; A.det(DET); return DET;}

  /**
   ** characteristic polynomial
   **/

 public:

#ifndef HEADBANGER
  bigint *charpoly() const;
  inline friend bigint *charpoly(const bigint_matrix &A)
    {return A.charpoly();}
#endif

  void charpoly(base_vector < bigint > &) const;

#ifndef HEADBANGER
  inline friend void charpoly(base_vector < bigint > &v, const bigint_matrix &A)
     {A.charpoly(v);}
#endif

  /**
   ** END: Linear algebra
   ** PART 1
   **/

  /**
   ** BEGIN: Linear algebra
   ** PART 2
   **/

  /**
   ** Hermite normal form
   **/
  
  /**
   ** BEGIN: INTERFACE - HNF 
   **/
  
 public:

#ifndef HEADBANGER
  inline void hnf() 
    {hnf_havas_cont();}
  inline void hnf(bigint_matrix &A) 
    {hnf_havas_cont(A);}
#endif

  inline friend bigint_matrix hnf(const bigint_matrix &A) 
    {bigint_matrix B = A; B.hnf_havas_cont(); return B;}
  inline friend bigint_matrix hnf(const bigint_matrix &A, bigint_matrix &TR) 
    {bigint_matrix B = A; B.hnf_havas_cont(TR); return B;}
  
  /** 
   ** END: INTERFACE - HNF 
   **/

 public:
 
#ifndef HEADBANGER 
  void hnf_simple();
  void hnf_simple(bigint_matrix &);
#endif


  inline friend bigint_matrix hnf_simple(const bigint_matrix &A)
    {bigint_matrix B = A; B.hnf_simple(); return B;}
  inline friend bigint_matrix hnf_simple(const bigint_matrix &A, bigint_matrix &TRANS)
    {bigint_matrix B = A; B.hnf_simple(TRANS); return B;}

#ifndef HEADBANGER  
  void hnf_havas();
  void hnf_havas(bigint_matrix &);
#endif

  inline friend bigint_matrix hnf_havas(const bigint_matrix &A)
    {bigint_matrix B = A; B.hnf_havas(); return B;}
  inline friend bigint_matrix hnf_havas(const bigint_matrix &A, bigint_matrix &TRANS)
    {bigint_matrix B = A; B.hnf_havas(TRANS); return B;}

#ifndef HEADBANGER
  void hnf_havas_cont();
  void hnf_havas_cont(bigint_matrix &);
#endif

  inline friend bigint_matrix hnf_havas_cont(const bigint_matrix &A)
    {bigint_matrix B = A; B.hnf_havas_cont(); return B;}
  inline friend bigint_matrix hnf_havas_cont(const bigint_matrix &A, bigint_matrix &TRANS)
    {bigint_matrix B = A; B.hnf_havas_cont(TRANS); return B;}

#ifndef HEADBANGER
  void hnfmod_dkt(const bigint &);
  inline void hnfmod_dkt()
    {hnfmod_dkt(latticedet2());}
#endif

  inline friend bigint_matrix hnfmod_dkt(const bigint_matrix &A)
    {bigint_matrix B = A; B.hnfmod_dkt(B.latticedet2()); return B;}
  inline friend bigint_matrix hnfmod_dkt(const bigint_matrix &A, const bigint &h)
    {bigint_matrix B = A; B.hnfmod_dkt(h); return B;}

#ifndef HEADBANGER
  void hnfmod_cohen(const bigint &);
  inline void hnfmod_cohen()
    {hnfmod_cohen(latticedet2());}
#endif

  inline friend bigint_matrix hnfmod_cohen(const bigint_matrix &A)
    {bigint_matrix B = A; B.hnfmod_cohen(B.latticedet2()); return B;} 
  inline friend bigint_matrix hnfmod_cohen(const bigint_matrix &A, const bigint &h)
    {bigint_matrix B = A; B.hnfmod_cohen(h); return B;}

#ifndef HEADBANGER
  void hnfmod_mueller(bigint_matrix &);
#endif

  inline friend bigint_matrix hnfmod_mueller(const bigint_matrix &A, bigint_matrix &TRANS)
    {bigint_matrix B = A; B.hnfmod_mueller(TRANS); return B;}

  /**
   ** Kernel
   **/

  /**
   ** BEGIN: INTERFACE - kernel
   **/

 public:
 
#ifndef HEADBANGER 
  inline void kernel(const bigint_matrix &B) 
    {kernel1(B);}
#endif

  inline friend bigint_matrix kernel(const bigint_matrix &B) 
    {bigint_matrix RET; RET.kernel1(B); return RET;}
  
  /** 
   ** END: INTERFACE - kernel
   **/

 public:

#ifndef HEADBANGER
  void kernel1(const bigint_matrix &);
#endif

  inline friend bigint_matrix kernel1(const bigint_matrix &A)
    {bigint_matrix RET; RET.kernel(A); return RET;}

#ifndef HEADBANGER
  void kernel2(const bigint_matrix &);
#endif

  inline friend bigint_matrix kernel2(const bigint_matrix &A)
    {bigint_matrix RET; RET.kernel2(A); return RET;}
  
  /**
   ** regular Invimage
   **/

  /**
   ** BEGIN: INTERFACE - reginvimage
   **/

 public:
 
#ifndef HEADBANGER 
  inline void reginvimage(const bigint_matrix &A, const bigint_matrix &B) 
    {reginvimage1(A,B);}
#endif

  inline friend bigint_matrix reginvimage(const bigint_matrix &A, const bigint_matrix &B) 
    {bigint_matrix X; X.reginvimage(A,B); return X;}
  
  /** 
   ** END: INTERFACE - reginvimage
   **/

 public:

#ifndef HEADBANGER
  void reginvimage1(const bigint_matrix &, const bigint_matrix &);
#endif

  inline friend bigint_matrix reginvimage1(const bigint_matrix &A, const bigint_matrix &B)
    {bigint_matrix X; X.reginvimage1(A,B); return X;}

#ifndef HEADBANGER
  void reginvimage2(const bigint_matrix &, const bigint_matrix &);
#endif

  inline friend bigint_matrix reginvimage2(const bigint_matrix &A, const bigint_matrix &B)
    {bigint_matrix X; X.reginvimage(A,B); return X;}

  /**
   ** Image
   **/

  /**
   ** BEGIN: INTERFACE - image
   **/

public: 
 
#ifndef HEADBANGER
  inline void image(const bigint_matrix &B) 
    {image1(B);}
#endif

  inline friend bigint_matrix image(const bigint_matrix &B) 
    {bigint_matrix RET; RET.image1(B); return RET;}
  
  /** 
   ** END: INTERFACE - image
   **/

public:

#ifndef HEADBANGER
  void image1(const bigint_matrix &);
#endif

  inline friend bigint_matrix image1(const bigint_matrix &A)
    {bigint_matrix BILD; BILD.image1(A); return BILD;}

#ifndef HEADBANGER
  void image2(const bigint_matrix &);
#endif

  inline friend bigint_matrix image2(const bigint_matrix &A)
    {bigint_matrix BILD; BILD.image(A); return BILD;}

  /**
   ** InvImage
   **/

public:

#ifndef HEADBANGER
  void invimage(const bigint_matrix &, const bigint *);
  inline friend bigint_matrix invimage(const bigint_matrix &A, const bigint *b)
    {bigint_matrix B; B.invimage(A, b); return B;}
#endif

#ifndef HEADBANGER
  void invimage(const bigint_matrix &, const math_vector < bigint > &);
#endif

  inline friend bigint_matrix invimage(const bigint_matrix &A, const math_vector < bigint > &b)
    {bigint_matrix B; B.invimage(A, b); return B;}

  /**
   ** solve
   **/

public:

#ifndef HEADBANGER
  inline void solve(const bigint_matrix &A, const bigint *b)
    {invimage(A, b);}
  inline friend bigint_matrix solve(const bigint_matrix &A, const bigint *b)
    {bigint_matrix B; B.invimage(A, b); return B;}
#endif

#ifndef HEADBANGER
  inline void solve(const bigint_matrix &A, const math_vector < bigint > &b)
    {invimage(A, b);}
#endif

  inline friend bigint_matrix solve(const bigint_matrix &A, const math_vector < bigint > &b)
    {bigint_matrix B; B.invimage(A, b); return B;}

  /**
   ** Smith normal form
   **/

  /** 
   ** BEGIN: INTERFACE - SNF
   **/

public:

#ifndef HEADBANGER
  inline void snf() 
    {snf_havas();}
  inline void snf(bigint_matrix &A, bigint_matrix &B) 
    {snf_havas(A,B);}
#endif

  inline friend bigint_matrix snf(const bigint_matrix &A) 
    {bigint_matrix B = A; B.snf_havas(); return B;}
  inline friend bigint_matrix snf(const bigint_matrix &A, bigint_matrix &B, bigint_matrix &C) 
    {bigint_matrix D = A; D.snf_havas(B,C); return D;}

  /** 
   ** END: INTERFACE - SNF 
   **/

public:

#ifndef HEADBANGER
  void snf_hartley();
  void snf_hartley(bigint_matrix &, bigint_matrix &);
#endif

  inline friend bigint_matrix snf_hartley(const bigint_matrix &A)
    {bigint_matrix B = A; B.snf_hartley(); return B;}
  inline friend bigint_matrix snf_hartley(const bigint_matrix &A, bigint_matrix &T1, bigint_matrix &T2)
    {bigint_matrix B = A; B.snf_hartley(T1, T2); return B;}

#ifndef HEADBANGER
  void snf_simple();
  void snf_simple(bigint_matrix &, bigint_matrix &);
#endif

  inline friend bigint_matrix snf_simple(const bigint_matrix &A)
    {bigint_matrix B = A; B.snf_simple(); return B;}
  inline friend bigint_matrix snf_simple(const bigint_matrix &A, bigint_matrix &T1, bigint_matrix &T2)
    {bigint_matrix B = A; B.snf_simple(T1, T2); return B;}

#ifndef HEADBANGER
  void snf_havas();
  void snf_havas(bigint_matrix &, bigint_matrix &);
#endif

  inline friend bigint_matrix snf_havas(const bigint_matrix &A)
    {bigint_matrix B = A; B.snf_havas(); return B;}
  inline friend bigint_matrix snf_havas(const bigint_matrix &A, bigint_matrix &T1, bigint_matrix &T2)
    {bigint_matrix B = A; B.snf_havas(T1, T2); return B;}

#ifndef HEADBANGER  
  void snf_mult(long art = 1);
  void snf_mult(bigint_matrix &, bigint_matrix &, long art = 1);
#endif

  inline friend bigint_matrix snf_mult(const bigint_matrix &A, long art = 1)
    {bigint_matrix B = A; B.snf_mult(art); return B;}
  inline friend bigint_matrix snf_mult(const bigint_matrix &A, bigint_matrix &T1, bigint_matrix &T2, long art = 1)
    {bigint_matrix B = A; B.snf_mult(T1, T2, art); return B;}

#ifndef HEADBANGER  
  void snf_add(long art = 1);
  void snf_add(bigint_matrix &, bigint_matrix &, long art =1);
#endif

  inline friend bigint_matrix snf_add(const bigint_matrix &A, long art = 1)
    {bigint_matrix B = A; B.snf_add(art); return B;}
  inline friend bigint_matrix snf_add(const bigint_matrix &A, bigint_matrix &T1, bigint_matrix &T2, long art = 1)
    {bigint_matrix B = A; B.snf_add(T1, T2, art); return B;}

#ifndef HEADBANGER 
  void snf_new(long art = 1);
  void snf_new(bigint_matrix &, bigint_matrix &, long art = 1);
#endif

  inline friend bigint_matrix snf_new(const bigint_matrix &A, long art = 1)
    {bigint_matrix B = A; B.snf_new(art); return B;}
  inline friend bigint_matrix snf_new(const bigint_matrix &A, bigint_matrix &T1, bigint_matrix &T2, long art = 1)
    {bigint_matrix B = A; B.snf_new(T1, T2, art); return B;}

#ifndef HEADBANGER  
  void snfmod_dkt(const bigint &);
  inline void snfmod_dkt()
    {snfmod_dkt(latticedet2());}
#endif

  inline friend bigint_matrix snfmod_dkt(const bigint_matrix &A)
    {bigint_matrix B = A; B.snfmod_dkt(B.latticedet2()); return B;}
  inline friend bigint_matrix snfmod_dkt(const bigint_matrix &A, const bigint &h)
    {bigint_matrix B = A; B.snfmod_dkt(h); return B;}

#ifndef HEADBANGER
  void snfmod_cohen(const bigint &);
  inline void snfmod_cohen()
    {snfmod_cohen(latticedet2());}
#endif

  inline friend bigint_matrix snfmod_cohen(const bigint_matrix &A)
    {bigint_matrix B = A; B.snfmod_cohen(B.latticedet2()); return B;}
  inline friend bigint_matrix snfmod_cohen(const bigint_matrix &A, const bigint &h)
    {bigint_matrix B = A; B.snfmod_cohen(h); return B;}

  /**
   ** END: Linear algebra
   ** PART 2
   **/

 protected:

  void gauss();

  /**
   ** mgcd Computation
   **/

 protected:
    
  bigint *mgcd(const bigint *, lidia_size_t);
  
  inline friend bigint *mgcd1(const bigint *v, lidia_size_t l, bigint_matrix &T)
  {return T.mgcd1(v, l);}
  bigint *mgcd1(const bigint *, lidia_size_t);

  friend bigint *mgcd2(const bigint *, lidia_size_t);
  friend void mgcd2(bigint &, const bigint *, lidia_size_t);
  
  inline friend bigint *mgcd2(const bigint *v, lidia_size_t l, bigint_matrix &T)
  {return T.mgcd2(v, l);}
  bigint *mgcd2(const bigint *, lidia_size_t);

  inline friend bigint *mgcd3(const bigint *v, lidia_size_t l, bigint_matrix &T)
  {return T.mgcd3(v, l);}
  bigint *mgcd3(const bigint *, lidia_size_t);

  inline friend bigint *mgcd4(const bigint *v, lidia_size_t l, bigint_matrix &T)
  {return T.mgcd4(v, l);}
  bigint *mgcd4(const bigint *, lidia_size_t);
};

#endif




