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

/*
$Id: math_matrix.h,v 1.7 1996/12/03 11:28:47 theobald Exp $
*/

#ifndef LIDIA_MATH_MATRIX_H
#define LIDIA_MATH_MATRIX_H

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

template < class T >
class math_matrix : public base_matrix < T >
{
  /**
   ** constructors
   **/

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

 public: 
 
  inline ~math_matrix() {}

  /**
   ** BEGIN: arithmetic procedures
   **/
  
  /**
   ** addition
   **/
  
 public:
 
#ifndef HEADBANGER 
  inline friend void add(math_matrix < T > &RES, const math_matrix < T > &M, const math_matrix < T > &N)
    {RES.add(M, N);}
  inline friend void add(math_matrix < T > &RES, const math_matrix < T > &M, const T &a)
    {RES.add(M, a);}
  inline friend void add(math_matrix < T > &RES, const T &a, const math_matrix < T > &M)
    {RES.add(a, M);}
#endif

 protected:

  void add(const math_matrix < T > &, const math_matrix < T > &);
  void add(const math_matrix < T > &, const T &);
  void add(const T &, const math_matrix < T > &);

  /**
   ** subtraction
   **/
  
 public:
  
#ifndef HEADBANGER
  inline friend void subtract(math_matrix < T > &RES, const math_matrix < T > &M, const math_matrix < T > &N)
    {RES.subtract(M, N);}
  inline friend void subtract(math_matrix < T > &RES, const math_matrix < T > &M, const T &a)
    {RES.subtract(M, a);}
  inline friend void subtract(math_matrix < T > &RES, const T &a, const math_matrix < T > &M)
    {RES.subtract(a, M);}
#endif

 protected:
  
  void subtract(const math_matrix < T > &, const math_matrix < T > &);	
  void subtract(const math_matrix < T > &, const T &);
  void subtract(const T &, const math_matrix < T > &);

  /**
   ** multiplication 
   **/

 public:
  
#ifndef HEADBANGER
  inline friend void multiply(math_matrix < T > &RES, const math_matrix < T > &M, const math_matrix < T > &N)
    {RES.multiply(M, N);}
  inline friend void multiply(math_matrix < T > &RES, const math_matrix < T > &M, const T &a)
    {RES.multiply(M, a);}
  inline friend void multiply(math_matrix < T > &RES, const T &a, const math_matrix < T > &M)
    {RES.multiply(a, M);}
#endif
  
 protected:
  
  void multiply(const math_matrix < T > &, const math_matrix < T > &);
  void multiply(const math_matrix < T > &, const T &);
  void multiply(const T &, const math_matrix < T > &);

 public:
  
  inline friend void compwise_multiply(math_matrix < T > &RES, const math_matrix < T > &M, const math_matrix < T > &N)
    {RES.compwise_multiply(M, N);}

 protected:
  
  void compwise_multiply(const math_matrix < T > &, const math_matrix < T > &);

 public:

#ifndef HEADBANGER
  inline friend void multiply(T *&res, const math_matrix < T > &A, const T *v)
    {A.multiply_right(res, v);}
  inline friend void multiply(T *&res, const T *v, const math_matrix < T > &A)
    {A.multiply_left(res, v);}

  inline friend void multiply(math_vector < T > &res, const math_matrix < T > &A, const math_vector < T > &v)
    {A.multiply_right(res, v);}
  inline friend void multiply(math_vector < T > &res, const math_vector < T > &v, const math_matrix < T > &A)
    {A.multiply_left(res, v);}
#endif

 protected:
  
  void multiply_right(T *&, const T *) const;
  void multiply_right(math_vector < T > &, const math_vector < T > &) const;
  void multiply_left(T *&, const T *) const;
  void multiply_left(math_vector < T > &, const math_vector < T > &) const;

  /**
   ** division
   **/
  
 public:

#ifndef HEADBANGER  
  inline friend void divide(math_matrix < T > &RES, const math_matrix < T > &M, const T &a)
    {RES.divide(M, a);}
#endif
  
 protected:
  
  void divide(const math_matrix < T > &, const T &);

 public:
  
  inline friend void compwise_divide(math_matrix < T > &RES, const math_matrix < T > &M, const math_matrix < T > &N)
    {RES.compwise_divide(M, N);}
  
 protected:
  
  void compwise_divide(const math_matrix < T > &, const math_matrix < T > &);
  
  /**
   ** negation
   **/
  
 public:
  
#ifndef HEADBANGER
  inline friend void negate(math_matrix < T > &A, const math_matrix < T > &B)
    {A.negate(B);}
#endif
  
 protected:
  
  void negate(const math_matrix < T > &);
  
  /**
   ** END: arithmetic procedures
   ** BEGIN: arithmetic operators
   **/
  
  /**
   ** addition
   **/
  
 public:

  inline friend math_matrix < T > operator + (const math_matrix < T > &A, const math_matrix < T > &B)
    {math_matrix < T > RES(A.rows, A.columns); RES.add(A, B); return RES;}
  inline friend math_matrix < T > operator + (const math_matrix < T > &A, const T &a)
    {math_matrix < T > RES(A.rows, A.columns); RES.add(A, a); return RES;}
  inline friend math_matrix < T > operator + (const T &a, const math_matrix < T > &A)
    {math_matrix < T > RES(A.rows, A.columns); RES.add(a, A); return RES;}

  inline friend math_matrix < T > & operator += (math_matrix < T > &A, const math_matrix < T > &B) 
    {A.add(A, B); return A;}
  inline friend math_matrix < T > & operator += (math_matrix < T > &A, const T &a)
    {A.add(A, a); return A;}
  
  /**
   ** subtraction
   **/

 public:
  
  inline friend math_matrix < T > operator - (const math_matrix < T > &A, const math_matrix < T > &B)
    {math_matrix < T > RES(A.rows, A.columns); RES.subtract(A, B); return RES;}
  inline friend math_matrix < T > operator - (const math_matrix < T > &A, const T &a)
    {math_matrix < T > RES(A.rows, A.columns); RES.subtract(A, a); return RES;}
  inline friend math_matrix < T > operator - (const T &a, const math_matrix < T > &A)
    {math_matrix < T > RES(A.rows, A.columns); RES.subtract(a, A); return RES;}
  
  inline friend math_matrix < T > & operator -= (math_matrix < T > &A, const math_matrix < T > &B)
    {A.subtract(A, B); return A;}
  inline friend math_matrix < T > & operator -= (math_matrix < T > &A, const T &a)
    {A.subtract(A, a); return A;}
  
  /**
   ** multiplication
   **/
 
 public: 

  inline friend math_matrix < T > operator * (const math_matrix < T > &A, const math_matrix < T > &B)
    {math_matrix < T > RES(A.rows, B.columns); RES.multiply(A, B); return RES;}
  inline friend math_matrix < T > operator * (const math_matrix < T > &A, const T &m)
    {math_matrix < T > RES(A.rows, A.columns); RES.multiply(A, m); return RES;}
  inline friend math_matrix < T > operator * (const T &m, const math_matrix < T > &A)
    {math_matrix < T > RES(A.rows, A.columns); RES.multiply(m, A); return RES;}

#ifndef HEADBANGER
  inline friend T * operator * (const math_matrix < T > &A, const T *v)
    {
      T *b = new T[A.rows];
      memory_handler(b, "math_matrix", "operator * :: "
		     "Error in memory allocation (b)");
      A.multiply_right(b,v); 
      return b;
    }

  inline friend T * operator * (const T *v, const math_matrix < T > &A)
    {
      T *b = new T[A.columns];
      memory_handler(b, "math_matrix", "operator * :: "
		     "Error in memory allocation (b)");
      A.multiply_left(b, v);
      return b;
    }
#endif

 public:

  inline friend math_vector < T > operator * (const math_matrix < T > &A, const math_vector < T > &v)
    {math_vector < T > b; A.multiply_right(b, v); return b;}
  inline friend math_vector < T > operator * (const math_vector < T > &v, const math_matrix < T > &A)
    {math_vector < T > b; A.multiply_left(b, v); return b;} 
  
  inline friend math_matrix < T > & operator *= (math_matrix < T > &A, const math_matrix < T > &B)
    {A.multiply(A, B); return A;}
  inline friend math_matrix < T > & operator *= (math_matrix < T > &A, const T &m)
    {A.multiply(A, m); return A;}

  /**
   ** division
   **/

 public:

  inline friend math_matrix < T > operator / (const math_matrix < T > &A, const T &m)
    {math_matrix < T > RES(A.rows, A.columns); RES.divide(A, m); return RES;}
  
  inline friend math_matrix < T > & operator /= (math_matrix < T > &A, const T &m)
    {A.divide(A, m); return A;}

  /**
   ** negation 
   **/

 public:
  
  inline friend math_matrix < T > operator - (const math_matrix < T > &A)
    {math_matrix < T > B(A.rows, A.columns); B.negate(A); return B;}

  /**
   ** END: arithmetic operators
   **/
  
  /**
   ** comparisons
   **/

  inline friend bool operator == (const math_matrix < T > &A, const math_matrix < T > &B)
    {return A.equal(B);}

#ifndef HEADBANGER
  bool equal(const math_matrix < T > &) const;
  inline friend bool equal(const math_matrix < T > &A, const math_matrix < T > &B)
    {return A.equal(B);}
#endif

  inline friend bool operator != (const math_matrix < T > &A, const math_matrix < T > &B)
    {return !A.equal(B);}

#ifndef HEADBANGER
  inline bool unequal(const math_matrix < T > &A) const 
    {return (!equal(A));}
  inline friend bool unequal(const math_matrix < T > &A, const math_matrix < T > &B)
    {return !A.equal(B);}
#endif
  
  /**
   ** trace
   **/
  
  void trace(T &) const;
  inline T trace() const 
    {T tr; trace(tr); return tr;}
  inline friend T trace(const math_matrix < T > &A) 
    {T tr; A.trace(tr); return tr;}
};

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

#endif


