/*

Matrix.h: C++ matrix template class include file (Ver.1.0) 
Copyright (C) 1997-1998  Somnath Kundu    somnath@kagi.com
Copyright (C) 2004       Valerio Benfante cage@katamail.com

This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Library General Public License for more details.

You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/


/********************************************************************\
  Purpose: This matrix template class defines majority of the matrix
  operations as overloaded operators or methods. Users of this class
  have been assumed to be familiar with matrix algebra. I have not
  defined any specialization of this template here, so all the instances
  of matrix will be created implicitly by the compiler. The data types
  tested with this class are float, double, long double, complex<float>,
  complex<double>, complex<long double>. I think there should not be
  any problem with very large precision floating point classes.

  Operator/Method                          Description
  ---------------                          -----------
   operator ()   :   This function operator can be used as a
                     two-dimensional subscript operator to get/set
                     individual matrix elements.

   operator !    :   This operator has been used to calculate inversion
                     of matrix.

   operator ~    :   This operator has been used to return transpose of
                     a matrix.

   operator ^    :   It is used calculate power (by a scalar) of a matrix.
                     When using this operator in a matrix equation, care
                     must be taken by parenthesizing it because it has
                     lower precedence than addition, subtraction,
                     multiplication and division operators.

   operator >>   :   It is used to read matrix from input stream as per
                     standard C++ stream operators.

   operator <<   :   It is used to write matrix to output stream as per
                     standard C++ stream operators.

\********************************************************************/


#include <cstdlib>
#include <cmath>
#include <iostream>
#include <stdexcept>



using namespace std;







class matrix_error : public logic_error
{
    public:
        matrix_error (const string& what_arg) : logic_error( what_arg) {}
};



template <class T> 
class matrix
{
private:
   T **Val;
   size_t Row, Col, RowSiz, ColSiz;

   void realloc (size_t row, size_t col);
   int pivot (size_t row);

public:
    
   matrix (const matrix<T> & m);
   matrix (size_t row = 6, size_t col = 6);

    
   ~matrix ();

    
  size_t RowNo () { return Row; }
  size_t ColNO () { return Col; }

  size_t Rows () { return RowNo(); }
  size_t Cols () { return ColNO(); }
   T& operator () (size_t row, size_t col) throw (matrix_error) ;

    
   matrix<T>  operator + ()  { return *this; }
   matrix<T>  operator - () ;

    
   matrix<T> & operator = (const matrix<T> & m) ;

    
   matrix<T> & operator += (const matrix<T> & m) throw (matrix_error) ;
   matrix<T> & operator -= (const matrix<T> & m) throw (matrix_error) ;
   matrix<T> & operator *= (const matrix<T> & m) throw (matrix_error) ;
   matrix<T> & operator *= (const T& c) ;
   matrix<T> & operator /= (const T& c) ;
   matrix<T> & operator ^= (const size_t& pow) throw (matrix_error) ;

    
   template <class Ti> friend  bool operator == (const matrix<Ti> & m1, const matrix<Ti> & m2) ;
   template <class Ti> friend bool operator != (const matrix<Ti> & m1, const matrix<Ti> & m2) ;

    
   template <class Ti> friend matrix<Ti>  operator + (const matrix<Ti> & m1, const matrix<Ti> & m2) throw (matrix_error) ;
   template <class Ti> friend matrix<Ti>  operator - (const matrix<Ti> & m1, const matrix<Ti> & m2) throw (matrix_error) ;
   template <class Ti> friend matrix<Ti>  operator * (const matrix<Ti> & m1, const matrix<Ti> & m2) throw (matrix_error) ;

   template <class Ti> friend matrix<Ti>  operator * (const matrix<Ti> & m, const Ti& no) ;
  template <class Ti> friend matrix<Ti>  operator * (const Ti& no, const matrix<Ti> & m)  { return (m*no); }

  template <class Ti> friend matrix<Ti>  operator / (const matrix<Ti> & m1, const matrix<Ti> & m2) throw (matrix_error)  { return (m1 * !m2); }

   template <class Ti> friend matrix<Ti>  operator / (const matrix<Ti> & m, const Ti& no)  { return (m*(1/no)); }
   template <class Ti> friend matrix<Ti>  operator / (const Ti& no, const matrix<Ti> & m) throw (matrix_error)  { return (!m * no); }

   template <class Ti> friend matrix<Ti>  operator ~ (const matrix<Ti> & m) ;
  template <class Ti>  friend matrix<Ti>  operator ! (matrix<Ti>  m) throw (matrix_error) ;
  template <class Ti>  friend matrix<Ti>  operator ^ (const matrix<Ti> & m, const size_t& pow) throw (matrix_error) ;

    
   void Null (const size_t& row, const size_t& col) ;
   void Null () ;
   void Unit (const size_t& row) ;
   void Unit () ;
   void SetSize (size_t row, size_t col) ;

    
  matrix<T>  Solve (const matrix<T> & v) const throw (matrix_error) ;
  matrix<T>  Adj () throw (matrix_error) ;
  matrix<T>  ElimCol(unsigned int index) throw (matrix_error);
  matrix<T>  ElimRow(unsigned int index) throw (matrix_error);
  T Det () throw (matrix_error) ;
  T Norm () ;
  T Cofact (size_t row, size_t col) throw (matrix_error) ;
  T Cond () ;
  T Trace();
  
  

  bool IsSquare ()  { return (Row == Col); } 
  bool IsSingular () ;
  bool IsDiagonal () ;
  bool IsScalar () ;
  bool IsUnit () ;
  bool IsNull () ;
  bool IsSymmetric () ;
  bool IsSkewSymmetric () ;
  bool IsUpperTiangular () ;
  bool IsLowerTiangular () ;

    
  template <class Ti> friend istream& operator >> (istream& i, matrix<Ti> & m);
  template <class Ti> friend ostream& operator << (ostream& o, matrix<Ti> & m);
};








 
template <class T>  inline
matrix<T> ::matrix (size_t row, size_t col)
{
   RowSiz = Row = row;
   ColSiz = Col = col;
   Val = new T* [row];
   for (size_t i=0; i < row; i++)
      Val[i] = new T [col];
}

 
template <class T>  inline
matrix<T> ::matrix (const matrix<T> & m)
{
   RowSiz = Row = m.Row;
   ColSiz = Col = m.Col;

   Val = new T* [Row];
   size_t colsize = Col * sizeof(T);

   for (size_t i=0; i < Row; i++)
   {
      Val[i] = new T [Col];
      memcpy( Val[i], m.Val[i], colsize);
   }
}


 
template <class T>  inline
matrix<T> ::~matrix (void)
{
   for (size_t i=0; i < RowSiz; i++)
      delete [] Val[i];
   delete [] Val;
}


 
template <class T>  inline void 
matrix<T> ::realloc (size_t row, size_t col)
{
   if (row == RowSiz && col == ColSiz)
   {
      Row = RowSiz;
      Col = ColSiz;
      return;
   }
   size_t i;
   T** Val1 = new T* [row];
   for (i=0; i < row; i++)
      Val1[i] = new T [col];

   size_t colSize = ((( Col ) < ( col )) ? ( Col ) : ( col ))  * sizeof(T);
   size_t minRow = ((( Row ) < ( row )) ? ( Row ) : ( row )) ;
   
   for (i=0; i < minRow; i++)
      memcpy( Val1[i], Val[i], colSize);

  for (i=0; i < RowSiz; i++)
      delete [] Val[i];
  delete [] Val;

  RowSiz = Row = row;
  ColSiz = Col = col;
  Val = Val1;

  return;
}


 
template <class T>  inline void
matrix<T> ::SetSize (size_t row, size_t col) 
{
   size_t i,j;
   size_t oldRow = Row;
   size_t oldCol = Col;

   if (row != RowSiz || col != ColSiz)
      realloc( row, col);

   if (row > oldRow)
      for (i=oldRow; i < row; i++)
         for (j=0; j < oldCol; j++)
            Val[i][j] = T(0);

   if (col > oldCol)
      for (i=0; i < col; i++)
         for (j=oldCol; j < col; j++)
            Val[i][j] = T(0);
   return;
}

 
template <class T>  inline T&
matrix<T> ::operator () (size_t row, size_t col) throw (matrix_error) 
{
   if (row >= Row || col >= Col)
      throw matrix_error(   "matrixT::operator(): Index out of range!" ); ;
   return Val[row][col];
}

 
template <class T>  inline istream&
operator >> (istream& istrm, matrix<T> & m)
{
   for (size_t i=0; i < m.Row; i++)
      for (size_t j=0; j < m.Col; j++)
         istrm >> m.Val[i][j];
   return istrm;
}

 
template <class T>  inline ostream&
operator << (ostream &ostrm, matrix<T> & m)
{
   for (size_t i=0; i < m.Row; i++)
   {
       for (size_t j=0; j < m.Col; j++)
	 ostrm << m.Val[i][j] << '\t';
       ostrm << endl;
   }
   return ostrm;
}

 
template <class T>  inline matrix<T> &
matrix<T> ::operator = (const matrix<T> & m) 
{
   if (Row != m.Row || Col != m.Col)
      realloc( m.Row,m.Col);

   size_t colbyte = m.Col * sizeof(T);

   for (size_t i=0; i < m.Row; i++)
      memcpy( Val[i], m.Val[i], colbyte);

   return *this;
}

 
template <class T>  inline bool
operator == (const matrix<T> & m1, const matrix<T> & m2) 
{
   bool retVal = false;

   if (m1.Row != m2.Row || m1.Col != m2.Col)
      return retVal;

   for (size_t i=0; i < m1.Row; i++)
      for (size_t j=0; j < m1.Col; i++)
         if (m1.Val[i][j] != m2.Val[i][j])
            return retVal;

   return true;
}

 
template <class T>  inline bool 
operator != (const matrix<T> & m1, const matrix<T> & m2) 
{
    return (m1 == m2) ? false : true;
}


 
template <class T>  inline matrix<T> &
matrix<T> ::operator += (const matrix<T> & m) throw (matrix_error) 
{
   if (Row != m.Row || Col != m.Col)
      throw matrix_error(   "matrixT::operator+= : Inconsistent matrix size in addition!" ); ;
   for (size_t i=0; i < m.Row; i++)
      for (size_t j=0; j < m.Col; j++)
         Val[i][j] += m.Val[i][j];
   return *this;
}


 
template <class T>  inline matrix<T> &
matrix<T> ::operator -= (const matrix<T> & m) throw (matrix_error) 
{
   if (Row != m.Row || Col != m.Col)
      throw matrix_error(   "matrixT::operator-= : Inconsistent matrix size in subtraction!" ); ;

   for (size_t i=0; i < m.Row; i++)
      for (size_t j=0; j < m.Col; j++)
         Val[i][j] -= m.Val[i][j];
   return *this;
}

 
template <class T>  inline matrix<T> &
matrix<T> ::operator *= (const T& c) 
{
   for (size_t i=0; i < Row; i++)
      for (size_t j=0; j < Col; j++)
         Val[i][j] *= c;
   return *this;
}


 
template <class T>  inline matrix<T> &
matrix<T> ::operator *= (const matrix<T> & m) throw (matrix_error) 
{
   if (Col != m.Row)
      throw matrix_error(   "matrixT::operator*= : Inconsistance matrix size in multiplication!" ); ;
   *this = *this * m;
   return *this;
}


 
template <class T>  inline matrix<T> &
matrix<T> ::operator /= (const T& c) 
{
   for (size_t i=0; i < Row; i++)
      for (size_t j=0; j < Col; j++)
         Val[i][j] /= c;

   return *this;
}

 
template <class T>  inline matrix<T> &
matrix<T> ::operator ^= (const size_t& pow) throw (matrix_error) 
{
   for (size_t i=2; i <= pow; i++)
      *this = *this * *this;

   return *this;
}

 
template <class T>  inline matrix<T> 
matrix<T> ::operator - () 
{
   matrix<T>  temp(Row,Col);

   for (size_t i=0; i < Row; i++)
      for (size_t j=0; j < Col; j++)
         temp.Val[i][j] = - Val[i][j];

   return temp;
}

 
template <class T>  inline matrix<T> 
operator + (const matrix<T> & m1, const matrix<T> & m2) throw (matrix_error) 
{
   if (m1.Row != m2.Row || m1.Col != m2.Col)
      throw matrix_error(   "matrixT::operator+: Inconsistent matrix size in addition!" ); ;

   matrix<T>  temp(m1.Row,m1.Col);

   for (size_t i=0; i < m1.Row; i++)
      for (size_t j=0; j < m1.Col; j++)
         temp.Val[i][j] = m1.Val[i][j] + m2.Val[i][j];

   return temp;
}

 
template <class T>  inline matrix<T> 
operator - (const matrix<T> & m1, const matrix<T> & m2) throw (matrix_error) 
{
   if (m1.Row != m2.Row || m1.Col != m2.Col)
     throw matrix_error(   "matrixT::operator-: Inconsistent matrix size in subtraction!" ); ;

   matrix<T>  temp(m1.Row,m1.Col);

   for (size_t i=0; i < m1.Row; i++)
      for (size_t j=0; j < m1.Col; j++)
         temp.Val[i][j] = m1.Val[i][j] - m2.Val[i][j];

   return temp;
}


 
template <class T>  inline matrix<T> 
operator * (const matrix<T> & m, const T& no) 
{
   matrix<T>  temp(m.Row,m.Col);

   for (size_t i=0; i < m.Row; i++)
      for (size_t j=0; j < m.Col; j++)
         temp.Val[i][j] = no * m.Val[i][j];

   return temp;
}


 
template <class T>  inline matrix<T> 
operator * (const matrix<T> & m1, const matrix<T> & m2) throw (matrix_error) 
{
   if (m1.Col != m2.Row)
      throw matrix_error(   "matrixT::operator*: Inconsistent matrix size in multiplication!" ); ;

   matrix<T>  temp(m1.Row,m2.Col);

   for (size_t i=0; i < m1.Row; i++)
      for (size_t j=0; j < m2.Col; j++)
      {
         temp.Val[i][j] = T(0);
         for (size_t k=0; k < m1.Col; k++)
            temp.Val[i][j] += m1.Val[i][k] * m2.Val[k][j];
      }
   return temp;
}

 
template <class T>  inline matrix<T> 
operator ^ (const matrix<T> & m, const size_t& pow) throw (matrix_error) 
{
   matrix<T>  temp(m);

   for (size_t i=2; i <= pow; i++)
      temp = temp * temp;

   return temp;
}


 
template <class T>  inline matrix<T> 
operator  ~ (const matrix<T> & m) 
{
   matrix<T>  temp(m.Col,m.Row);

   for (size_t i=0; i < m.Row; i++)
      for (size_t j=0; j < m.Col; j++)
	 temp.Val[j][i] = m.Val[i][j];

   return temp;
}


 
template <class T>  inline matrix<T> 
operator ! (matrix<T>  m) throw (matrix_error) 
{
   size_t i,j,k;
   T a1,a2,*rowptr;

   if (m.Row != m.Col)
      throw matrix_error(   "matrixT::operator!: Inversion of a non-square matrix" ); ;

   matrix<T>  temp(m.Row,m.Col);

   temp.Unit();
   for (k=0; k < m.Row; k++)
   {
      int indx = m.pivot(k);
      if (indx == -1)
         throw matrix_error(   "matrixT::operator!: Inversion of a singular matrix" ); ;

      if (indx != 0)
      {
         rowptr = temp.Val[k];
         temp.Val[k] = temp.Val[indx];
         temp.Val[indx] = rowptr;
      }
      a1 = m.Val[k][k];
      for (j=0; j < m.Row; j++)
      {
         m.Val[k][j] /= a1;
         temp.Val[k][j] /= a1;
      }
      for (i=0; i < m.Row; i++)
         if (i != k)
         {
            a2 = m.Val[i][k];
            for (j=0; j < m.Row; j++)
            {
               m.Val[i][j] -= a2 * m.Val[k][j];
               temp.Val[i][j] -= a2 * temp.Val[k][j];
            }
         }
   }
   return temp;
}


 
template <class T>  inline matrix<T> 
matrix<T> ::Solve (const matrix<T> & v) const throw (matrix_error) 
{
   size_t i,j,k;
   T a1;

   if (!(Row == Col && Col == v.Row))
      throw matrix_error(   "matrixT::Solve():Inconsistent matrices!" ); ;

   matrix<T>  temp(Row,Col+v.Col);
   for (i=0; i < Row; i++)
   {
      for (j=0; j < Col; j++)
         temp.Val[i][j] = Val[i][j];
      for (k=0; k < v.Col; k++)
         temp.Val[i][Col+k] = v.Val[i][k];
   }
   for (k=0; k < Row; k++)
   {
      int indx = temp.pivot(k);
      if (indx == -1)
         throw matrix_error(   "matrixT::Solve(): Singular matrix!" ); ;

      a1 = temp.Val[k][k];
      for (j=k; j < temp.Col; j++)
         temp.Val[k][j] /= a1;

      for (i=k+1; i < Row; i++)
      {
         a1 = temp.Val[i][k];
         for (j=k; j < temp.Col; j++)
            temp.Val[i][j] -= a1 * temp.Val[k][j];
      }
   }
   matrix<T>  s(v.Row,v.Col);
   for (k=0; k < v.Col; k++)
      for (int m=int(Row)-1; m >= 0; m--)
      {
         s.Val[m][k] = temp.Val[m][Col+k];
         for (j=m+1; j < Col; j++)
            s.Val[m][k] -= temp.Val[m][j] * s.Val[j][k];
      }
   return s;
}


 
template <class T>  inline void
matrix<T> ::Null (const size_t& row, const size_t& col) 
{
   if (row != Row || col != Col)
      realloc( row,col);

   for (size_t i=0; i < Row; i++)
      for (size_t j=0; j < Col; j++)
         Val[i][j] = T(0);
   return;
}

 
template <class T>  inline void
matrix<T> ::Null() 
{
   for (size_t i=0; i < Row; i++)
      for (size_t j=0; j < Col; j++)
 	 Val[i][j] = T(0);
   return;
}

 
template <class T>  inline void
matrix<T> ::Unit (const size_t& row) 
{
   if (row != Row || row != Col)
      realloc( row, row);

   for (size_t i=0; i < Row; i++)
      for (size_t j=0; j < Col; j++)
         Val[i][j] = i == j ? T(1) : T(0);
  return;
}

 
template <class T>  inline void
matrix<T> ::Unit () 
{
   size_t row = ((( Row ) < ( Col )) ? ( Row ) : ( Col )) ;
   Row = Col = row;

   for (size_t i=0; i < Row; i++)
      for (size_t j=0; j < Col; j++)
         Val[i][j] = i == j ? T(1) : T(0);
   return;
}


 
template <class T>  inline int
matrix<T> ::pivot (size_t row)
{
  int k = int(row);
  double amax,temp;

  amax = -1;
  for (size_t i=row; i < Row; i++)
     if ( (temp = abs( Val[i][row])) > amax && temp != 0.0)
     {
       amax = temp;
       k = i;
     }
  if (Val[k][row] == T(0))
     return -1;
  if (k != int(row))
  {
     T* rowptr = Val[k];
     Val[k] = Val[row];
     Val[row] = rowptr;
     return k;
  }
  return 0;
}


 
template <class T>  inline T
matrix<T> ::Det () throw (matrix_error) 
{
   size_t i,j,k;
   T piv,detVal = T(1);

   if (Row != Col)
      throw matrix_error(   "matrixT::Det(): Determinant a non-square matrix!" ); ;

   matrix<T>  temp(*this);

   for (k=0; k < Row; k++)
   {
      int indx = temp.pivot(k);
      if (indx == -1)
         return 0;
      if (indx != 0)
         detVal = - detVal;
      detVal = detVal * temp.Val[k][k];
      for (i=k+1; i < Row; i++)
      {
         piv = temp.Val[i][k] / temp.Val[k][k];
         for (j=k+1; j < Row; j++)
            temp.Val[i][j] -= piv * temp.Val[k][j];
      }
   }
   return detVal;
}

 
template <class T>  inline T
matrix<T> ::Norm () 
{
   T retVal = T(0);

   for (size_t i=0; i < Row; i++)
      for (size_t j=0; j < Col; j++)
         retVal += Val[i][j] * Val[i][j];
   retVal = sqrt( retVal);

   return retVal;
}


 
template <class T>  inline T
matrix<T> ::Cond () 
{
   matrix<T>  inv(Row,Col);

   inv = ! (*this);
   T retVal = Norm() * inv.Norm();

   return retVal;
}



template <class T>  inline T
matrix<T> ::Trace(){
  T result(0);
  if(IsSquare()){
    for(unsigned int i=0;i<ColNO();i++){
      result+=Val[i][i];
    }
  }

  return result;
}
 

template <class T>  inline matrix<T>  
matrix<T> ::ElimCol(unsigned int index) throw (matrix_error)  {
  matrix<T> res(Rows(),Cols()-1);
  if(index > res.Cols()){
    throw matrix_error(   "matrixT::ElimCol(): index > res.Cols()" );
  }else{

    for(unsigned int theCol=0;theCol<index;theCol++){
      for(unsigned int theRow=0;theRow<Rows();theRow++){
	res(theRow,theCol)=Val[theRow][theCol];
      }
    }

    if(index+1<Cols()){
      for(unsigned int col=index+1;col<Cols();col++){
	for(unsigned int row=0;row<Rows();row++){
	  res(row,col-1)=Val[row][col];
	}
      }
    }
  }

  return res;

}

template <class T>  inline matrix<T>  
matrix<T> ::ElimRow(unsigned int index) throw (matrix_error)  {
  matrix<T> res(Rows()-1,Cols());
  if(index > res.Rows()){
    throw matrix_error(   "matrixT::ElimRows(): index > res.Rows()" );
  }else{
    for(unsigned int row=0;row<index;row++){
      for(unsigned int col=0;col<Cols();col++){
	res(row,col)=Val[row][col];
      }
    }
    if(index+1<Rows()){
      for(unsigned int row=index+1;row<Rows();row++){
	for(unsigned int col=0;col<Cols();col++){
	  res(row-1,col)=Val[row][col];
	}
      }
    }
  }

  return res;

}

template <class T>  inline T
matrix<T> ::Cofact (size_t row, size_t col) throw (matrix_error) 
{
   size_t i,i1,j,j1;

   if (Row != Col)
      throw matrix_error(   "matrixT::Cofact(): Cofactor of a non-square matrix!" ); ;

   if (row > Row || col > Col)
      throw matrix_error(   "matrixT::Cofact(): Index out of range!" ); ;

   matrix<T>  temp (Row-1,Col-1);

   for (i=i1=0; i < Row; i++)
   {
      if (i == row)
        continue;
      for (j=j1=0; j < Col; j++)
      {
      	 if (j == col)
            continue;
      	 temp.Val[i1][j1] = Val[i][j];
         j1++;
      }
      i1++;
   }
   T  cof = temp.Det();
   if ((row+col)%2 == 1)
      cof = -cof;

   return cof;
}


 
template <class T>  inline matrix<T> 
matrix<T> ::Adj () throw (matrix_error) 
{
   if (Row != Col)
      throw matrix_error(   "matrixT::Adj(): Adjoin of a non-square matrix." ); ;

   matrix<T>  temp(Row,Col);

   for (size_t i=0; i < Row; i++)
      for (size_t j=0; j < Col; j++)
         temp.Val[i][j] = Cofact(i,j);

   temp = ~temp;
   return temp;
}

 
template <class T>  inline bool
matrix<T> ::IsSingular () 
{
   if (Row != Col)
      return false;
   return (Det() == T(0));
}

 
template <class T>  inline bool
matrix<T> ::IsDiagonal () 
{
   if (Row != Col)
      return false;
   for (size_t i=0; i < Row; i++)
     for (size_t j=0; j < Col; j++)
        if (i != j && Val[i][j] != T(0))
          return false;
   return true;
}

 
template <class T>  inline bool
matrix<T> ::IsScalar () 
{
   if (!IsDiagonal())
     return false;
   T v = Val[0][0];
   for (size_t i=1; i < Row; i++)
     if (Val[i][i] != v)
        return false;
   return true;
}

 
template <class T>  inline bool
matrix<T> ::IsUnit () 
{
   if (IsScalar() && Val[0][0] == T(1))
     return true;
   return false;
}

 
template <class T>  inline bool
matrix<T> ::IsNull () 
{
   for (size_t i=0; i < Row; i++)
      for (size_t j=0; j < Col; j++)
         if (Val[i][j] != T(0))
            return false;
   return true;
}

 
template <class T>  inline bool
matrix<T> ::IsSymmetric () 
{
   if (Row != Col)
      return false;
   for (size_t i=0; i < Row; i++)
      for (size_t j=0; j < Col; j++)
         if (Val[i][j] != Val[j][i])
            return false;
   return true;
}
           
 
template <class T>  inline bool
matrix<T> ::IsSkewSymmetric () 
{
   if (Row != Col)
      return false;
   for (size_t i=0; i < Row; i++)
      for (size_t j=0; j < Col; j++)
         if (Val[i][j] != -Val[j][i])
            return false;
   return true;
}
   
 
template <class T>  inline bool
matrix<T> ::IsUpperTiangular () 
{
   if (Row != Col)
      return false;
   for (size_t i=1; i < Row; i++)
      for (size_t j=0; j < i-1; j++)
         if (Val[i][j] != T(0))
            return false;
   return true;
}

 
template <class T>  inline bool
matrix<T> ::IsLowerTiangular () 
{
   if (Row != Col)
      return false;

   for (size_t j=1; j < Col; j++)
      for (size_t i=0; i < j-1; i++)
         if (Val[i][j] != T(0))
            return false;

   return true;
}






