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

/*
$Id: base_matrix.h,v 1.17 1996/12/03 11:28:43 theobald Exp $
*/

#ifndef LIDIA_BASE_MATRIX_H
#define LIDIA_BASE_MATRIX_H

#if defined(HAVE_MAC_DIRS) || defined(__MWERKS__)
#include <LiDIA:lidia.h>
#include <LiDIA:base_vector.h>
#include <LiDIA:sparse_matrix.h>
#include <LiDIA:dense_matrix.h>
#else
#include <LiDIA/lidia.h>
#include <LiDIA/base_vector.h>
#include <LiDIA/sparse_matrix.h>
#include <LiDIA/dense_matrix.h>
#endif

#ifdef __GNUG__
class bigint_lattice;
class bigfloat_lattice;
class bigint_lattice_gensys;
class bigint_lattice_basis;
class bigfloat_lattice_gensys;
class bigfloat_lattice_basis;
class crt;
#endif

template < class T >
class base_matrix : public dense_matrix < T >  // MM
{
#ifdef __GNUG__
  friend class bigint_lattice;
  friend class bigfloat_lattice;
  friend class bigint_lattice_gensys;
  friend class bigint_lattice_basis;
  friend class bigfloat_lattice_gensys;
  friend class bigfloat_lattice_basis;
  friend class crt;
#endif  

  /**
   ** constructors
   **/
  
 public:

  base_matrix();
  base_matrix(lidia_size_t, lidia_size_t);
  base_matrix(const base_matrix < T > &);

#ifndef HEADBANGER
  base_matrix(lidia_size_t, lidia_size_t, const T **);
#endif

  
  /**
   ** destructor
   **/

 public: 
 
  ~base_matrix();
  
  /**
   ** Input / Output
   **/
  
 public:
  
  inline friend ostream & operator << (ostream &out, const base_matrix < T > &M) 
    {return M.print(out);}
  inline friend istream & operator >> (istream &in, base_matrix < T > &M) 
    {return M.read(in);}

 protected:
  
  ostream & print(ostream &) const;
  istream & read(istream &);

  /**
   ** BEGIN: access functions
   **/
  
  /**
   ** set print_mode / get print_mode
   **/

 public:
  
  inline void set_print_mode(unsigned long art) 
    {print_mode = art > 5 ? 0 : art;}
  
  inline unsigned long get_print_mode() 
    {return print_mode;}

  /**
   ** set storage_mode / get storage_mode
   **/

 public:
  
  inline void set_storage_mode(unsigned long art) 
    {storage_mode = art;}
  
  inline unsigned long get_storage_mode() 
    {return storage_mode;}

  /**
   ** set structure_mode / get structure_mode
   **/

 public:
  
  inline void set_structure_mode(unsigned long art) 
    {structure_mode = art;}
  
  inline unsigned long get_structure_mode() 
    {return structure_mode;}

  /**
   ** set info_mode / get info_mode
   **/

 public:
  
  inline void set_info_mode(unsigned long art) 
    {info_mode = art;}
  
  inline unsigned long get_info_mode() 
    {return info_mode;}

  /**
   ** set lattice_mode / get lattice_mode
   **/

 public:
  
  inline void set_lattice_mode(unsigned long art) 
    {lattice_mode = art;}
  
  inline unsigned long get_lattice_mode() 
    {return lattice_mode;}
  
  /**
   ** element access 
   **/

 public: 
 
  void sto(lidia_size_t, lidia_size_t, const T &);
  inline const T & operator() (lidia_size_t x, lidia_size_t y) const
  {return member(x,y);}
  T & operator() (lidia_size_t x, lidia_size_t y);
  const T & member(lidia_size_t, lidia_size_t) const;
  
  /**
   ** column access
   **/

 public:

#ifndef HEADBANGER
  void sto_column(const T *, lidia_size_t, lidia_size_t);
  T * column(lidia_size_t) const;
  inline T * get_column(lidia_size_t i) const
    {return column(i);}
  void column(T *, lidia_size_t) const;
  inline void get_column(T *v, lidia_size_t i) const
    {column(v, i);}
#endif

  void sto_column_vector(const base_vector < T > &, lidia_size_t, lidia_size_t);
  inline base_vector < T > operator() (lidia_size_t i) const 
    {base_vector < T > RES; column_vector(RES, i); return RES;}
  inline base_vector < T > column_vector(lidia_size_t i) const
    {base_vector < T > RES; column_vector(RES, i); return RES;}
  inline base_vector < T > get_column_vector(lidia_size_t i) const
    {base_vector < T > RES; column_vector(RES, i); return RES;}
  void column_vector(base_vector < T > &, lidia_size_t) const;
  inline void get_column_vector(base_vector < T > &RES, lidia_size_t i) const
    {column_vector(RES, i);}

  /**
   ** row access
   **/
  
 public:

#ifndef HEADBANGER
  void sto_row(const T *, lidia_size_t, lidia_size_t);
  T *row(lidia_size_t) const;
  inline T *get_row(lidia_size_t i) const
    {return row(i);}
  void row(T *, lidia_size_t) const;
  inline void get_row(T *v, lidia_size_t i) const
    {row(v, i);}
#endif

  void sto_row_vector(const base_vector < T > &, lidia_size_t, lidia_size_t);
  inline base_vector < T > operator[] (lidia_size_t i) const 
    {base_vector < T > RES; row_vector(RES, i); return RES;}
  inline base_vector < T > row_vector(lidia_size_t i) const
    {base_vector < T > RES; row_vector(RES, i); return RES;}
  inline base_vector < T > get_row_vector(lidia_size_t i) const
    {base_vector < T > RES; row_vector(RES, i); return RES;}
  void row_vector(base_vector < T > &, lidia_size_t) const;
  inline void get_row_vector(base_vector < T > &RES, lidia_size_t i) const
    {row_vector(RES, i);}

  /**
   ** array access
   **/

#ifdef __GNUG__
 private:
#else
 public:
#endif
  
#ifndef HEADBANGER
  inline T ** get_data_address() const 
    {return value;}
 
 public: 

  T ** get_data() const;
  void set_data(const T **, lidia_size_t, lidia_size_t);
#endif

  /**
   ** END: access functions
   **/
  
  /**
   ** split functions
   **/

 public:
  
  void split_t(base_matrix < T > &, base_matrix < T > &, base_matrix < T > &, base_matrix < T > &) const;
 
  void split_h(base_matrix < T > &, base_matrix < T > &) const;

  void split_v(base_matrix < T > &, base_matrix < T > &) const;

#ifndef HEADBANGER
  void split_h(T *, base_matrix < T > &) const;
  void split_h(base_matrix < T > &, T *) const;

  void split_v(T *, base_matrix < T > &) const;
  void split_v(base_matrix < T > &, T *) const;
#endif

  void split_h(base_vector < T > &, base_matrix < T > &) const;
  void split_h(base_matrix < T > &, base_vector < T > &) const;
  
  void split_v(base_vector < T > &, base_matrix < T > &) const;
  void split_v(base_matrix < T > &, base_vector < T > &) const;
  
  /**
   ** compose functions
   **/
  
  void compose_t(const base_matrix < T > &, const base_matrix < T > &, const base_matrix < T > &, const base_matrix < T > &);

  void compose_h(const base_matrix < T > &, const base_matrix < T > &);

  void compose_v(const base_matrix < T > &, const base_matrix < T > &);

#ifndef HEADBANGER
  void compose_h(const T *, const base_matrix < T > &);
  void compose_h(const base_matrix < T > &, const T *);

  void compose_v(const T *, const base_matrix < T > &);
  void compose_v(const base_matrix < T > &, const T *);
#endif


  void compose_h(const base_vector < T > &, const base_matrix < T > &);
  void compose_h(const base_matrix < T > &, const base_vector < T > &);

  void compose_v(const base_vector < T > &, const base_matrix < T > &);
  void compose_v(const base_matrix < T > &, const base_vector < T > &);

  /**
   ** exchange functions / swap functions
   **/

 public:

  inline friend void swap(base_matrix < T > &A, base_matrix < T > &B)
    {A.swap(B);}

 protected:
  
  void swap(base_matrix < T > &);  
  
 public:
  
  void swap_columns(lidia_size_t, lidia_size_t);
  void swap_rows(lidia_size_t, lidia_size_t);

  /**
   ** structur functions
   **/

 public:

  inline lidia_size_t get_no_of_rows() const 
    {return rows;}		/* number of rows  */
  inline lidia_size_t get_no_of_columns() const 
    {return columns;}	        /* number of columns */
  
  void set_no_of_rows(lidia_size_t);	/* increase / decrease number of rows    */
  void set_no_of_columns(lidia_size_t);	/* increase / decrease number of columns */

  void resize(lidia_size_t, lidia_size_t);

  inline void reset()
     {kill();}
  void kill();
  
  /**
   ** assignment
   **/
 
 public:
 
  inline base_matrix < T > & operator = (const base_matrix < T > &B)
    {assign(B); return *this;}

#ifndef HEADBANGER
  inline friend void assign(base_matrix < T > &RES, const base_matrix < T > &M) 
    {RES.assign(M);}
  void assign(const base_matrix < T > &);
#endif
  
  /**
   ** diagonal function
   **/
  
 public:

  inline friend void diag(base_matrix < T > &A, const T &a, const T &b) 
    {A.diag(a, b);}
  void diag(const T &, const T &);
  
  /**
   ** transpose function
   **/

 public:

  inline friend void trans(base_matrix < T > &AT, const base_matrix < T > &A) 
    {AT.trans(A);}
  void trans(const base_matrix < T > &);

  inline friend base_matrix < T > trans(const base_matrix < T > &A) 
    {return A.trans();}
  base_matrix < T > trans() const; 
  
  /**
   ** stream handling 
   **/  

 public:

#ifndef HEADBANGER
  void write_to_stream(ostream &) const;
  void read_from_stream(istream &);

  void write_to_gp(ostream &) const;
  void read_from_gp(istream &);

  void write_to_maple(ostream &) const;
  void read_from_maple(istream &);

  void write_to_mathematica(ostream &) const;
  void read_from_mathematica(istream &);

  void write_to_kash(ostream &) const;
  void read_from_kash(istream &);
#endif
};

#ifdef LIDIA_INCLUDE_C
#include <LiDIA/base_matrix.c>
#endif 

#endif





