//**************************************************************
//
//  CS 240B     : Winter 2003 Ohio University Travis Dillon
//  Project 4.2 : template dynamic matrix class
//  file        : dynmat.tmpl
//  started     : 02-24-03
//
//**************************************************************

#ifndef DYNMAT_TMPL
#define DYNMAT_TMPL

#include "prog4b.h"

template <typename T>
class Dynmat
{
 public:
   Dynmat();  //c-tor no name 4X4
   Dynmat(string n);  //c-tor n 4X4
   Dynmat(int r, int c);  //c-tor no name rXc
   Dynmat(int r, int c, string n);  //c-tor n rXc
   ~Dynmat();  //destructor
   Dynmat(const Dynmat& orig);  //copy constructor
   Dynmat & operator =(const Dynmat& rop);  //assignment op
   Dynmat operator +(const Dynmat& rop)const;  //addition op
   Dynmat operator -(const Dynmat& rop)const;  //sub op
   Dynmat operator *(const Dynmat& rop)const;  //mult op
   friend Dynmat operator * <> (T fact, const Dynmat& rop);  //scaler
   T get_ele(int r, int c);  //assessor function
   void fill_const(T val);  //fill with constant val
   void fill_id();  //fill with 1 if r == c, 0 if r != c
   void fill_1();  //fill with 10 * r + c
   void fill_2();  //fill with (r+1) % (c+1)
   void fill_by_hand();  //user fill
   void write(ostream& os);  //write matrix info out
 private:
   int rows;  //number of rows in matrix
   int cols;  //number of columns in matrix
   T** eles;  //dynamic matrix
   string name;  //name of matrix
   int mat_num;  //number of particular matrix
   static int num_of_mats;  //total number of matrix declared
};


template <typename T>
int Dynmat<T> ::num_of_mats = 0;

template <typename T>
Dynmat <T> ::Dynmat()
{
   mat_num = ++ num_of_mats;
   name = "no name";
   rows = 4;
   cols = 4;
   eles = new T * [rows];
   for(int i(0); i < rows; ++i)  //create dynamic matrix
   {
      eles[i] = new T[cols];
   }
   for(int i(0); i < rows; ++i)  //initialize to zeros
      for(int j(0); j < cols; ++j)
         eles[i][j] = 0;
}

template <typename T>
Dynmat <T> ::Dynmat(string n)
{
   mat_num = ++ num_of_mats;
   name = n;
   rows = 4;
   cols = 4;
   eles = new T * [rows];
   for(int i(0); i < rows; ++i)  //create dynamic matrix
   {
      eles[i] = new T[cols];
   }
   for(int i(0); i < rows; ++i)  //initialize to zeros
      for(int j(0); j < cols; ++j)
         eles[i][j] = 0;
}

template <typename T>
Dynmat <T> ::Dynmat(int r, int c)
{
   mat_num = ++ num_of_mats;
   name = "no name";
   if(r < 0) r = 1;  //check for invalid rows and cols
   if(c < 0) c = 1;
   rows = r;
   cols = c;
   eles = new T * [rows];
   for(int i(0); i < rows; ++i)  //create matrix
   {
      eles[i] = new T[cols];
   }
   for(int i(0); i < rows; ++i)  //initialize to zeros
      for(int j(0); j < cols; ++j)
         eles[i][j] = 0;
}

template <typename T>
Dynmat <T> ::Dynmat(int r, int c, string n)
{
   mat_num = ++ num_of_mats;
   name = n;
   if(r < 1) r = 1;  //check for invalid rows and cols
   if(c < 1) c = 1;
   rows = r;
   cols = c;
   eles = new T * [rows];
   for(int i(0); i < rows; ++i)  //create matrix
   {
      eles[i] = new T[cols];
   }
   for(int i(0); i < rows; ++i)  //initialize to zeros
      for(int j(0); j < cols; ++j)
         eles[i][j] = 0;}

template <typename T>
Dynmat <T> ::~Dynmat()
{
   for(int i(0); i < rows; ++i)
   {
      delete [] eles[i];
   }
   delete [] eles;
}
template <typename T>
Dynmat <T> ::Dynmat(const Dynmat& orig)
{
   mat_num = ++ num_of_mats;
   rows = orig.rows;
   cols = orig.cols;
   eles = new T * [rows];
   for(int i(0); i < rows; ++i)
   {
      eles[i] = new T[cols];
   }
   for(int i(0); i < rows; ++i)  //copy data
      for(int j(0); j < cols; ++j)
         eles[i][j] = orig.eles[i][j];
   
   name = "clone of " + orig.name;
}

template <typename T>
Dynmat<T>&  Dynmat <T> ::operator =(const Dynmat<T>& rop)
{
   if(this == &rop) return *this;  //self assignment
   rows = rop.rows;  //resize calling matrix
   cols = rop.cols;
   for(int i(0); i < rows; ++i)
      for(int j(0); j < cols; ++j)
         eles[i][j] = rop.eles[i][j];  //copy data
   return *this;
}

template <typename T>
Dynmat <T> Dynmat <T> ::operator +(const Dynmat<T>& rop)const
{
   if(rows != rop.rows || cols != rop.cols)
   {  //check for incompatable sizes
      cout <<"\nError! Incompatable matrix sizes.\n";
      Dynmat temp(0,0,"size mismatch");
      return temp;
   }
   Dynmat temp(rows,cols);
   for(int i(0); i < rows; ++i)
      for(int j(0); j < cols; ++j)
         temp.eles[i][j] = eles[i][j] + rop.eles[i][j];
   return temp;
}

template <typename T>
Dynmat <T> Dynmat <T> ::operator -(const Dynmat<T>& rop)const
{
   if(rows != rop.rows || cols != rop.cols)
   {  //check for incompatable sizes
      cout <<"\nError! Incompatable matrix sizes.\n";
      Dynmat temp(0,0,"size mismatch");
      return temp;
   }
   Dynmat temp(rows,cols);
   for(int i(0); i < rows; ++i)
      for(int j(0); j < cols; ++j)
         temp.eles[i][j] = eles[i][j] - rop.eles[i][j];
   return temp;
}

template <typename T>
Dynmat <T> Dynmat <T> ::operator *(const Dynmat<T>& rop)const
{
   if(cols != rop.rows)
   {  //check for imcompatable sizes
      cout <<"\nError! Incompatable matrix sizes.\n";
      Dynmat temp(0,0,"size mismatch");
      return temp;
   }
   Dynmat temp(rows,rop.cols);
   for(int i(0); i < rows; ++i)
      for(int j(0); j < rop.cols; ++j)
         for(int k(0); k < cols; ++k)
            temp.eles[i][j] += eles[i * 1][k * 1] * rop.eles[k * 1][j * 1];
   return temp;
}

template <typename T>
Dynmat <T> operator * (T fact, const Dynmat <T> & rop)
{
   Dynmat <T> temp(rop.rows, rop.cols);
   for(int i(0); i < temp.rows; ++i)
      for(int j(0); j < temp.cols; ++j)
         temp.eles[i][j] = rop.eles[i][j] * fact;
   return temp;
}

template <typename T>
T Dynmat <T> ::get_ele(int r, int c)
{
   if((r > 0) && (r <= rows) && (c > 0) && (c <= cols))
   return eles[r][c];  //r and c must be in matrix size
   cout <<"\nError! Invalid matrix coordinates.\n";
   return 0;
}

template <typename T>
void Dynmat <T> ::fill_const(T val)
{
   for(int i(0); i < rows; ++i)
      for(int j(0); j < cols; ++j)
         eles[i][j] = val;
}

template <typename T>
void Dynmat <T> ::fill_id()
{
   for(int i(0); i < rows; ++i)
      for(int j(0); j < cols; ++j)
      {
         if(i == j)eles[i][j] = 1;  //true
         else eles[i][j] = 0;  //false
      }
}

template <typename T>
void Dynmat <T> ::fill_1()
{
   for(int i(0); i < rows; ++i)
      for(int j(0); j < cols; ++j)
         eles[i][j] = 10 * i + j;
}

template <typename T>
void Dynmat <T> ::fill_2()
{
   for(int i(0); i < rows; ++i)
      for(int j(0); j < cols; ++j)
         eles[i][j] = (i +1) % (j + 1);
}

template <typename T>
void Dynmat <T> ::fill_by_hand()
{
   for(int i(0); i < rows; ++i)
   {
      for(int j(0); j < cols; ++j)
      {
         cout << "\nEnter data for " << name
              <<"  matrix # "<<mat_num 
              << " row " << i
              << " column " << j << " :  ";
         cin >> eles[i][j];
      }
   }
}

template <typename T>
void Dynmat <T> ::write(ostream& os)
{
   os << "\nthe " << rows << " by " << cols
      << " dynamic array: " << name 
      << ", matrix #" << mat_num << ", is"
      << endl << endl;
   for(int i(0); i < rows; ++i)
   {
      for(int j(0); j < cols; ++j)
      {
         os << setw(6) << eles[i][j];
      }
      os << endl;
   }
}

#endif  //DYNMAT_TMPL