// Matrix template class definition file. -*- C++ -*-
/* Copyright 2008 Mike Williams (mwill@jlab.org)
 *
 * This file is part of qft++.
 *
 * qft++ is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * qft++ 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with qft++.  If not, see <http://www.gnu.org/licenses/>.
 */
// Author: Mike Williams
#ifndef _Matrix_H
#define _Matrix_H
//_____________________________________________________________________________
// Standard C++ Headers:
#include <iostream>
#include <cassert>
#include <vector>
#include <complex>
#include <cstdlib>
// Local Headers:
#include "qft++/topincludes/c++-template-utils.hh"
#include "qft++/matrix/Matrix_Base.hh"
//_____________________________________________________________________________
/** @file Matrix.h
 *  @brief Matrix template class definition file.
 */
//_____________________________________________________________________________

using namespace std;
//_____________________________________________________________________________
/** @class Matrix
 *  @author Mike Williams
 *
 *  @brief General template class for handling matricies and matrix operations.
 *
 * Matrix is a template class for handling matricies and matrix operations.
 * A Matrix object can be any size (n x m) and store any type that can be 
 * stored in a C++ container class. This class has been written to be as
 * general and flexible as possible. Parameter passing has been optimized
 * using the Type class. Matrix has been designed so that tow instantiations,
 * Matrix<_A> and Matrix<_B>, are completely compatible as long as _A and _B 
 * are compatible (eg. _A*_B,_A+_B,...are defined).  Return types of 
 * <em>mixed type</em> matrix operations are determined by the OperationType
 * template classes (MultType,etc...).
 *
 * <b>Example Usage </b>
 * 
 * \include Matrix.ex
 */
//_____________________________________________________________________________

template <typename _Tp> class Matrix : public Matrix_Base {

private:
  // attributes:
  vector<_Tp> _data; ///< Matrix elements (type _Tp)

  // private functions:

  /// Copy @a matrix elements to this matrix
  template<typename V> void _Copy(const Matrix<V> &__matrix){
    int size = __matrix.Size();
    _data.resize(size);
    for(int i = 0; i < size; i++) _data[i] = __matrix._data[i];
  }

protected:
  // friends:
  template <typename V> friend class Matrix;

public:

  // create/copy/destroy:

  /** Default Constructor */
  Matrix() : Matrix_Base(){}

  /** Constructor
   * @param nrows Number of rows
   *  @param ncols Number of columns
   */
  Matrix(int __nrows,int __ncols) : Matrix_Base(__nrows,__ncols){
    _data.resize(__nrows*__ncols);
  }

  /** Constructor
   * @param nrows Number of rows
   *  @param ncols Number of columns
   *  @param init  Initial value of Matrix elements
   */
  Matrix(int __nrows,int __ncols,typename Type<_Tp>::ParamType __init)
    : Matrix_Base(__nrows,__ncols){
    _data.resize(__nrows*__ncols,__init);
  }

  /** Constructor
   * @param vv 2-D vector to convert to a Matrix 
   */
  template<typename V> Matrix(const vector<vector<V> > &__vv) : Matrix_Base(){
    _num_rows = (int)__vv.size();
    if(_num_rows == 0) _num_cols = 0; 
    else _num_cols = (int)__vv[0].size();
    _data.resize(_num_rows*_num_cols);

    for(int i = 0; i < _num_rows; i++){
      for(int j = 0; j < _num_cols; j++) (*this)(i,j) = __vv[i][j];
    }
  } 

  /// Copy Constructor
  template<typename V> Matrix(const Matrix<V> &__matrix):Matrix_Base(__matrix){
    this->_Copy(__matrix);
  }

  /** Destructor */
  virtual ~Matrix(){}

  // operators:

  /** Assignment operator
   *
   *  Note: Legal if @a Tp = @a T is a legal assignment. 
   *
   *  Note: Asserts @a matrix and @a this be the same size.
   */
  template <typename V> Matrix<_Tp>& operator=(const Matrix<V>& __matrix);
   
  /// Assignment operator (for 1 x 1 matrix only) 
  Matrix<_Tp>& operator=(typename Type<_Tp>::ParamType __val){
    assert(this->NumRows() == 1 && this->NumCols() == 1);
    _data[0] = __val;
    
    return *this;
  }

  /// Conversion operator to type @a Tp (valid only for 1x1)
  operator _Tp () const {
    if(_num_rows != 1 || _num_cols != 1) {
      cout << "Error! Attempt to convert a matrix (not 1x1) to a scalar." 
	   << endl;
    }
    assert(_num_rows == 1 || _num_cols == 1);
    return _data[0];
  }
  
  /** Matrix addition.
   *
   *  Note: Legal if @a Tp + @a V is a legal operation. 
   *
   *  Note: Asserts @a matrix and @a this be the same size.
   */
  template <typename V> Matrix<typename AddType<_Tp,V>::Type> 
  operator+(const Matrix<V> &__matrix) const;

  /** Matrix subtraction.
   *
   *  Note: Legal if @a Tp - @a V is a legal operation. 
   *
   *  Note: Asserts @a matrix and @a this be the same size.
   */
  template <typename V> Matrix<typename SubType<_Tp,V>::Type> 
  operator-(const Matrix<V> &__matrix) const;

  /** Matrix multiplication
   *
   *  Note: Legal if @a Tp * @a V is a legal operation. 
   *
   *  Note: Asserts the number of rows in @a matrix and the number of columns 
   *        in @a this be the same.
   */
  template <typename V> Matrix<typename MultType<_Tp,V>::Type> 
  operator*(const Matrix<V> &__matrix) const;

  /** Matrix multiplication with % operator
   *
   *  Note: Legal if @a Tp % @a V is a legal operation. 
   *
   *  Note: Asserts the number of rows in @a matrix and the number of columns 
   *        in @a this be the same.
   */
  template <typename V> Matrix<typename MultType<_Tp,V>::Type> 
  operator%(const Matrix<V> &__matrix) const;

  /** Matrix multiplication with | operator
   *
   *  Note: Legal if @a Tp | @a V is a legal operation. 
   *
   *  Note: Asserts the number of rows in @a matrix and the number of columns 
   *        in @a this be the same.
   */
  template <typename V> Matrix<typename MultType<_Tp,V>::Type> 
  operator|(const Matrix<V> &__matrix) const;

  /// \f$ M_{ij} \f$ * x for each matrix element
  template <typename V> 
  typename DisableIf<IsMatrix(V),Matrix<typename MultType<_Tp,V>::Type> >::Type
  operator*(const V &__x) const {
    int size = this->Size();
    Matrix<typename MultType<_Tp,V>::Type> ret(_num_rows,_num_cols);
    for(int i = 0; i < size; i++) ret._data[i] = this->_data[i] * __x;
    return ret;
  }

  /// \f$ M_{ij} \f$ / x for each matrix element
  template <typename V> 
  typename DisableIf<IsMatrix(V),Matrix<typename DivType<_Tp,V>::Type> >::Type
  operator/(const V &__x) const {
    int size = this->Size();
    Matrix<typename DivType<_Tp,V>::Type> ret(_num_rows,_num_cols);
    for(int i = 0; i < size; i++) ret._data[i] = this->_data[i] / __x;
    return ret;
  }
  
  /// \f$ M_{ij} \f$ % x for each matrix element
  template <typename V> 
  typename DisableIf<IsMatrix(V),Matrix<typename MultType<_Tp,V>::Type> >::Type
  operator%(const V &__x) const {
    int size = this->Size();
    Matrix<typename MultType<_Tp,V>::Type> ret(_num_rows,_num_cols);
    for(int i = 0; i < size; i++) ret._data[i] = this->_data[i] % __x;
    return ret;
  }

  /// \f$ M_{ij} \f$ | x for each matrix element
  template <typename V> 
  typename DisableIf<IsMatrix(V),Matrix<typename MultType<_Tp,V>::Type> >::Type
  operator|(const V &__x) const {
    int size = this->Size();
    Matrix<typename MultType<_Tp,V>::Type> ret(_num_rows,_num_cols);
    for(int i = 0; i < size; i++) ret._data[i] = this->_data[i] | __x;
    return ret;
  }

  /// Set @a this = @a this + @a matrix 
  template <typename V> Matrix<_Tp>& operator+=(const Matrix<V> &__matrix){
    (*this) = (*this) + __matrix;
    return *this;
  }

  /// Set @a this = @a this - @a matrix 
  template <typename V> Matrix<_Tp>& operator-=(const Matrix<V> &__matrix){
    (*this) = (*this) - __matrix;
    return *this;
  }

  /// Set @a this = @a this * @a matrix 
  template <typename V> Matrix<_Tp>& operator*=(const Matrix<V> &__matrix){
    (*this) = (*this) * __matrix;
    return *this;
  }

  /// Set @a this = @a this * @a x
  template <typename V> typename DisableIf<IsMatrix(V),Matrix<_Tp>&>::Type 
  operator*=(const V &__x){
    (*this) = (*this) * __x;
    return *this;
  }

  /// Set @a this = @a this / @a x
  template <typename V> typename DisableIf<IsMatrix(V),Matrix<_Tp>&>::Type 
  operator/=(const V &__x){
    (*this) = (*this) / __x;
    return *this;
  }

  /** Comparison operator
   *
   *  @param matrix A Matrix of the same type as @a this
   *
   *  Requires that the matricies be the same size, and that none of their
   *  elements return @a true when passed to the != operator for type @a _Tp.
   */
  bool operator==(const Matrix<_Tp> &__matrix) const;

  /// Comparison operator (see operator== for details)
  inline bool operator!=(const Matrix<_Tp> &__matrix) const {
    return !(*this == __matrix);
  }

  // Getters:

  /// Returns a reference to the (i,j) element of the matrix
  inline _Tp& Element(int __i, int __j){
    if(__i >= _num_rows || __j >= _num_cols)
      cout << "Error! Attempting to access non-existent element." << endl;
    assert(__i < _num_rows && __j < _num_cols);
    return _data[__i*_num_cols + __j];
  }

  /// Returns a constant reference to the (i,j) element of the matrix
  inline const _Tp& Element(int __i, int __j) const {
    if(__i >= _num_rows || __j >= _num_cols)
      cout << "Error! Attempting to access non-existent element." << endl;
    assert(__i < _num_rows && __j < _num_cols);
    return _data[__i*_num_cols + __j];
  }

  /// Returns a reference to the (i,j) element of the matrix
  inline _Tp& operator()(int __i,int __j) {
    return this->Element(__i,__j);
  }

  /// Returns a constant reference to the (i,j) element of the matrix
  inline const _Tp& operator()(int __i,int __j) const {
    return this->Element(__i,__j);
  }

  /// Returns a constant reference to the i'th array element
  inline const _Tp& operator[](int __i) const {
    return _data[__i];
  }

  /// Returns the matrix \f$ M_{jk}(i) \f$ from this matrix
  Matrix<_Tp> operator()(int __i) const {
    Matrix<_Tp> ret(_num_rows,_num_cols);
    for(int j = 0; j < this->Size(); j++) 
      ret._data[j] = this->_data[j].operator()(__i);
    
    return ret;
  }

  
  // Functions:

  /** Returns the trace of the matrix */
  _Tp Trace() const;

  /// Returns the trace of the matrix
  inline _Tp Tr() const {
    return this->Trace();
  }

  /** Returns the transpose of @a this matrix */
  Matrix<_Tp> Transpose() const;

  /// Return the transpose of @a this matrix
  inline Matrix<_Tp> T() const {
    return this->Transpose();
  }

  /** Returns the complex conjugate of @a this matrix
   * Note: Legal if conj(_Tp) exists 
   *  (see c++-template-utils/TemplateUtilFuncs.h) 
   */
  Matrix<_Tp> Conjugate() const {
    Matrix<_Tp> ret(_num_rows,_num_cols);
    for(int i = 0; i < this->Size(); i++) ret._data[i] = conj(this->_data[i]);
    return ret;
  }

  /// Returns the complex conjugate of @a this matrix (see Conjugate()).
  Matrix<_Tp> Conj() const {
    return this->Conjugate();
  }

  /// Returns the adjoint of @a this matrix (see Conjugate() and Transpose())
  Matrix<_Tp> Adjoint() const {
    Matrix<_Tp> ret(_num_rows,_num_cols);
    ret = (this->Transpose()).Conjugate();
    return ret;
  }

  /// Returns the adjoint of @a this matrix (see Conjugate() and Transpose())
  Matrix<_Tp> Dagger() const {
    return this->Adjoint();
  }

  /// Set each element of @a this matrix to zero
  /** Note: Legal if zero(_Tp) is legal 
   *  (see c++-template-utils/TemplateUtilFuncs.h)
   */
  void Zero() {
    for(int i = 0; i < this->Size(); i++) _data[i] = zero(_data[i]);
  }

  void invert(){  

    if(_num_rows != _num_cols){
    cout << "Error! invert() matrix must be diagonal."
         << endl;
    }
    assert(_num_rows == _num_cols);
   
    if (_num_rows <= 0) return;  // sanity check
    if (_num_rows == 1){
      _data[0] = unity(_data[0])/_data[0];
      return;
    }  
    // must be of dimension >= 2
    for (int i=1; i < _num_rows; i++) _data[i] /= _data[0]; // normalize row 0

    for (int i=1; i < _num_rows; i++)  { 
      for (int j=i; j < _num_rows; j++)  { // do a column of L
        _Tp sum = 0.0;
        for (int k = 0; k < i; k++)  
//             sum += _data[j*Size()+k] * _data[k*Size()+i];
//         _data[j*Size()+i] -= sum;
            sum += _data[j*_num_rows+k] * _data[k*_num_rows+i];
        _data[j*_num_rows+i] -= sum;
        }
      if (i == _num_rows-1) continue;
      for (int j=i+1; j < _num_rows; j++)  {  // do a row of U
        _Tp sum = zero( _data[0] );
        for (int k = 0; k < i; k++)
//        sum += _data[i*Size()+k]*_data[k*Size()+j];
//         _data[i*Size()+j] = 
//        (_data[i*Size()+j]-sum) / _data[i*Size()+i];
          sum += _data[i*_num_rows+k]*_data[k*_num_rows+j];
        _data[i*_num_rows+j] = 
          (_data[i*_num_rows+j]-sum) / _data[i*_num_rows+i];
      }
    }

  for ( int i = 0; i < _num_rows; i++ )  // invert L
    for ( int j = i; j < _num_rows; j++ )  {
      _Tp x = unity(_data[0]);
      if ( i != j ) {
        x = 0.0;
        for ( int k = i; k < j; k++ ) 
          x -= _data[j*_num_cols+k]*_data[k*_num_cols+i];
      }
      _data[j*_num_cols+i] = x / _data[j*_num_cols+j];
    }
  for ( int i = 0; i < _num_rows; i++ )   // invert U
    for ( int j = i; j < _num_rows; j++ )  {
      if ( i == j ) continue;
      _Tp sum = zero(_data[0]);
      for ( int k = i; k < j; k++ )
        sum += _data[k*_num_cols+j]*( (i==k) ? 1.0 : _data[i*_num_cols+k] );
      _data[i*_num_cols+j] = -sum;
    }
  for ( int i = 0; i < _num_rows; i++ )   // final inversion
    for ( int j = 0; j < _num_rows; j++ )  {
      _Tp sum = zero(_data[0]);
      for ( int k = ((i>j)?i:j); k < _num_rows; k++ )  
        sum += ((j==k)?1.0:_data[j*_num_cols+k])*_data[k*_num_cols+i];
      _data[j*_num_cols+i] = sum;
    }
  }


  /// Removes all elements from the matrix
  void Clear() {
    if(!_data.empty()) _data.clear();
    _num_rows = 0;
    _num_cols = 0;
  }

  /// Resize the matrix to be @a nrows X @a ncols.
  void Resize(int __nrows,int __ncols) {
    _num_rows = __nrows;
    _num_cols = __ncols;
    _data.resize(__nrows*__ncols);
  }

  /// Print the matrix to @a os
  void Print(std::ostream &__os = std::cout) const {
    for(int i = 0; i < _num_rows; i++){
      for(int j = 0; j < _num_cols; j++) __os << this->Element(i,j) << " ";
      __os << endl;
    }
  }

};
//_____________________________________________________________________________

/// @a x * \f$ M_{ij} \f$ for each matrix element
template<typename T1,typename T2> 
typename DisableIf<IsMatrix(T1),Matrix<typename MultType<T1,T2>::Type> >::Type
operator*(const T1 &__x,const Matrix<T2> &__matrix){
  int rows = __matrix.NumRows();
  int cols = __matrix.NumCols();
  Matrix<typename MultType<T1,T2>::Type> ret(rows,cols);

  for(int i = 0; i < rows; i++){
    for(int j = 0; j < cols; j++){
      ret(i,j) = __x * __matrix(i,j);
    }
  }
  return ret;
}  
//_____________________________________________________________________________

/// @a x % \f$ M_{ij} \f$ for each matrix element
template<typename T1,typename T2> 
typename DisableIf<IsMatrix(T1),Matrix<typename MultType<T1,T2>::Type> >::Type
operator%(const T1 &__x,const Matrix<T2> &__matrix){
  int rows = __matrix.NumRows();
  int cols = __matrix.NumCols();
  Matrix<typename MultType<T1,T2>::Type> ret(rows,cols);

  for(int i = 0; i < rows; i++){
    for(int j = 0; j < cols; j++){
      ret(i,j) = __x % __matrix(i,j);
    }
  }
  return ret;
}  
//_____________________________________________________________________________

/// @a x | \f$ M_{ij} \f$ for each matrix element
template<typename T1,typename T2> 
typename DisableIf<IsMatrix(T1),Matrix<typename MultType<T1,T2>::Type> >::Type
operator|(const T1 &__x,const Matrix<T2> &__matrix){
  int rows = __matrix.NumRows();
  int cols = __matrix.NumCols();
  Matrix<typename MultType<T1,T2>::Type> ret(rows,cols);

  for(int i = 0; i < rows; i++){
    for(int j = 0; j < cols; j++){
      ret(i,j) = __x | __matrix(i,j);
    }
  }
  return ret;
}  
//_____________________________________________________________________________

/// Overload of operator << for the Matrix class
template<typename T> 
ostream& operator<<(ostream &__os,const Matrix<T> &__matrix){
  __matrix.Print(__os);
  return __os;
}
//_____________________________________________________________________________

#include "qft++/matrix/Matrix.tcc" /* Matrix template class source file */

#endif /* _Matrix_H */
