#include "matrix.h"


Matrix::Matrix(int rows_, int cols_):rows(rows_) , cols(cols_)
{
    data=new float *[rows];
    for (int i=0; i<rows; i++)
    {
       data[i]=new float[cols];
    }
    for (int i=0; i<rows; i++)
        for (int j=0; j<cols; j++)
    {
        data[i][j]=0;
    }
}

Matrix::Matrix(int rows_, int cols_, float val):rows(rows_) , cols(cols_)
{
data=new float *[rows];
    for (int i=0; i<rows; i++)
    {
       data[i]=new float[cols];
    }
    for (int i=0; i<rows; i++)
        for (int j=0; j<cols; j++)
    {
        data[i][j]=val;
    }
}

Matrix::Matrix(int rows_, int cols_, MatrixType m_type) :rows(rows_) , cols(cols_)
{
    srand( time(NULL) );
    data=new float* [rows];
    for (int i=0; i<cols; i++)
    {
        data[i]=new float [cols];
    }
    for (int i=0; i<rows; i++)
        for (int j=0; j<cols; j++)
    {
        if (m_type == Identity)
        {
            if (i==j)
            {
                data[i][j]=1;
            }
            else {
                data[i][j]=0;
            }
        }
        else if (m_type==Random)
        {

            data[i][j]=rand() % 20;
        }
        else
            data[i][j]=0;
    }

}

/*Copy constructor: to prevent shallow copy*/
Matrix::Matrix(const Matrix& m) :rows(m.rows) , cols(m.cols)
{


    data=new float* [m.rows];

    for (int i=0; i<rows; i++){

            data[i]=new float [m.cols];
        }
    for (int i=0; i<m.rows; i++)
        for (int j=0; j<m.cols; j++)
            data[i][j]=m.data[i][j] ;

}


Matrix::~Matrix()
{


}

int Matrix::get_rows() const
{
    return rows;
}

int Matrix::get_cols() const
{
    return cols;
}

void Matrix::print() const
{
for (int i=0; i<rows; i++)
        for (int j=0; j<cols; j++)
        {
            cout<<" "<<data[i][j]<<" ";
            if (j==cols-1)
                cout<<endl<<endl;
        }
}

float& Matrix::access(int r_ind, int c_ind)
{
    for (int i=0; i<rows; i++)
        for (int j=0; j<cols; j++)
            return data[r_ind][c_ind];
}

Matrix Matrix::operator+(const Matrix& m) const
{
     Matrix result(m);
    if (rows != m.rows || cols != m.cols)
        cout<<"invalid"<<endl;
    else {

        for (int i=0; i<m.rows; i++)
            for (int j=0; j<m.cols; j++)
                {
                    result.data[i][j]=data[i][j] + m.data[i][j];
                }
                return result;
    }
    return result;
}

Matrix Matrix::operator-(const Matrix& m) const
{
  Matrix result(m);
    if (rows != m.rows || cols != m.cols)
        cout<<"invalid"<<endl;
    else {

        for (int i=0; i<m.rows; i++)
            for (int j=0; j<m.cols; j++)
                {
                    result.data[i][j]=data[i][j] - m.data[i][j];
                }
                return result;
    }
    return result;
}

Matrix Matrix::operator*(float val) const
{
    Matrix result(rows,cols);
    for (int i=0; i<rows; i++)
            for (int j=0; j<cols; j++)
                {
                    result.data[i][j]=data[i][j] *val;
                }
        return result;

}

Matrix Matrix::operator/(float val) const
{}

Matrix Matrix::operator*(const Matrix& m) const
{
    Matrix result(rows,m.cols);
    if (cols != m.rows)
    {
        cout <<"invalid multiply" <<endl;
    }
    else{

        for (int i=0; i<rows; i++){
            for (int j=0; j<m.cols; j++){
                for (int k=0; k<m.rows; k++)
                    result.data[i][j]+=data[i][k]* m.data[k][j];
            }
        }

    }

return result;
}


Matrix Matrix::operator/(const Matrix& m) const
{}

bool Matrix::operator==(const Matrix& m) const
{
    if (rows != m.rows || cols!=m.cols)
        return false;
    else{
        for (int i=0; i<m.rows; i++)
            for (int j=0; j<m.cols; j++)
                {
                    if (data[i][j] != m.data[i][j])
                        return false;
                }
         }
}

bool Matrix::operator!=(const Matrix& m) const
{
    if (rows != m.rows || cols!=m.cols)
        return true;
    else{
        for (int i=0; i<m.rows; i++)
            for (int j=0; j<m.cols; j++)
                {
                    if (data[i][j] == m.data[i][j])
                        return false;
                }
         }
}


bool Matrix::isIdentity() const
{
    if (isSquare())
    {
    for (int i=0; i<rows; i++)
        for (int j=0; j<cols; j++){
            if (data[i][j]==1 && i==j)
                {
                    return true;
                }
            else
                return false;
      }
    }
    else
    {
        return false;
    }
    return false;
}

bool Matrix::isIdempotent() const
{

    for (int i=0; i<rows; i++)
            for (int j=0; j<cols; j++)
                {
                    if ((this->data[i][j]) *(this->data[i][j])==data[i][j])
                        continue;
                    else
                        return false ;
                }

    return true;


}


bool Matrix::isSquare() const
{
    if (rows==cols)
        return true;
    else
        return false;
}

bool Matrix::isSymmetric() const
{
   if (isSquare()){
   for (int i=0; i<rows; i++){
        for (int j=0; j<cols; j++){
            if (data[i][j] == data[j][i])
                return true;
            else
                return false;
            }
        }
   }
        return false;
 }


bool Matrix::isUpperTriangle() const
{}

bool Matrix::isLowerTriangle() const
{}

Matrix Matrix::transpose() const
{
    Matrix result(cols,rows);
    for (int i=0; i<cols; i++)
        for (int j=0; j<rows; j++)
            result.data[i][j]=data[j][i];
    return result;
}

void Matrix::fill(float val)
{

    data=new float *[rows];
    for (int i=0; i<rows; i++)
    {
       data[i]=new float[cols];
    }
    for (int i=0; i<rows; i++)
        for (int j=0; j<cols; j++)
    {
        data[i][j]=val;
    }

}

float Matrix::minElement() const
{   float temp=data[0][0];
    for (int i=0; i<rows; i++){
        for (int j=0; j<cols; j++)
        {
            if (temp>data[i][j])
                temp=data[i][j];
        }
    }
        return temp;

}

float Matrix::maxElement() const
{
    float temp=data[0][0];
    for (int i=0; i<rows; i++){
        for (int j=0; j<cols; j++)
        {
            if (temp<data[i][j])
                temp=data[i][j];
        }
    }
        return temp;


}

float Matrix::determinant() const
{


}

string Matrix::toString() const
{return string("");}
implement these functions and check for errors