#include <cstdlib>
#include <cstdio>
#include <math.h>
#define PAUSE {printf("Press \"Enter\" to continue\n"); fflush(stdin); getchar(); fflush(stdin);}
class Matrix;
double Det(const Matrix& a);
Matrix Diag(const int n);
Matrix Diag(const Matrix& v);
Matrix Inv(const Matrix& a);
Matrix Ones(const int rows, const int cols);
int Size(const Matrix& a, const int i);
Matrix Zeros(const int rows, const int cols);
class Exception
{
public:
  const char* msg;
  Exception(const char* arg)
   : msg(arg)
  {
  }
};
class Matrix
{
public:
    Matrix()
  {
            p = NULL;
    rows = 0;
    cols = 0;
  }
    Matrix(const int row_count, const int column_count)
  {
        p = NULL;
    if (row_count > 0 && column_count > 0)
    {
      rows = row_count;
      cols = column_count;
      p = new double*[rows];
      for (int r = 0; r < rows; r++)
      {
        p[r] = new double[cols];
                for (int c = 0; c < cols; c++)
        {
          p[r][c] = 0;
        }
      }
    }
  }
    Matrix(const Matrix& a)
  {
    rows = a.rows;
    cols = a.cols;
    p = new double*[a.rows];
    for (int r = 0; r < a.rows; r++)
    {
      p[r] = new double[a.cols];
            for (int c = 0; c < a.cols; c++)
      {
        p[r][c] = a.p[r][c];
      }
    }
  }
      double& operator()(const int r, const int c)
  {
    if (p != NULL && r > 0 && r <= rows && c > 0 && c <= cols)
    {
      return p[r-1][c-1];
    }
    else
    {
      throw Exception("Subscript out of range");
    }
  }
        double get(const int r, const int c) const
  {
    if (p != NULL && r > 0 && r <= rows && c > 0 && c <= cols)
    {
      return p[r-1][c-1];
    }
    else
    {
      throw Exception("Subscript out of range");
    }
  }
    Matrix& operator= (const Matrix& a)
  {
    rows = a.rows;
    cols = a.cols;
    p = new double*[a.rows];
    for (int r = 0; r < a.rows; r++)
    {
      p[r] = new double[a.cols];
            for (int c = 0; c < a.cols; c++)
      {
        p[r][c] = a.p[r][c];
      }
    }
    return *this;
  }
    Matrix& Add(const double v)
  {
    for (int r = 0; r < rows; r++)
    {
      for (int c = 0; c < cols; c++)
      {
        p[r][c] += v;
      }
    }
     return *this;
  }
    Matrix& Subtract(const double v)
  {
    return Add(-v);
  }
    Matrix& Multiply(const double v)
  {
    for (int r = 0; r < rows; r++)
    {
      for (int c = 0; c < cols; c++)
      {
        p[r][c] *= v;
      }
    }
     return *this;
  }
    Matrix& Divide(const double v)
  {
     return Multiply(1/v);
  }
    friend Matrix operator+(const Matrix& a, const Matrix& b)
  {
        if (a.rows == b.rows && a.cols == b.cols)
    {
      Matrix res(a.rows, a.cols);
      for (int r = 0; r < a.rows; r++)
      {
        for (int c = 0; c < a.cols; c++)
        {
          res.p[r][c] = a.p[r][c] + b.p[r][c];
        }
      }
      return res;
    }
    else
    {
            throw Exception("Dimensions does not match");
    }
        return Matrix();
  }
    friend Matrix operator+ (const Matrix& a, const double b)
  {
    Matrix res = a;
    res.Add(b);
    return res;
  }
    friend Matrix operator+ (const double b, const Matrix& a)
  {
    Matrix res = a;
    res.Add(b);
    return res;
  }
    friend Matrix operator- (const Matrix& a, const Matrix& b)
  {
        if (a.rows == b.rows && a.cols == b.cols)
    {
      Matrix res(a.rows, a.cols);
      for (int r = 0; r < a.rows; r++)
      {
        for (int c = 0; c < a.cols; c++)
        {
          res.p[r][c] = a.p[r][c] - b.p[r][c];
        }
      }
      return res;
    }
    else
    {
            throw Exception("Dimensions does not match");
    }
        return Matrix();
  }
    friend Matrix operator- (const Matrix& a, const double b)
  {
    Matrix res = a;
    res.Subtract(b);
    return res;
  }
    friend Matrix operator- (const double b, const Matrix& a)
  {
    Matrix res = -a;
    res.Add(b);
    return res;
  }
    friend Matrix operator- (const Matrix& a)
  {
    Matrix res(a.rows, a.cols);
    for (int r = 0; r < a.rows; r++)
    {
      for (int c = 0; c < a.cols; c++)
      {
        res.p[r][c] = -a.p[r][c];
      }
    }
    return res;
  }
    friend Matrix operator* (const Matrix& a, const Matrix& b)
  {
        if (a.cols == b.rows)
    {
      Matrix res(a.rows, b.cols);
      for (int r = 0; r < a.rows; r++)
      {
        for (int c_res = 0; c_res < b.cols; c_res++)
        {
          for (int c = 0; c < a.cols; c++)
          {
            res.p[r][c_res] += a.p[r][c] * b.p[c][c_res];
          }
        }
      }
      return res;
    }
    else
    {
            throw Exception("Dimensions does not match");
    }
        return Matrix();
  }
    friend Matrix operator* (const Matrix& a, const double b)
  {
    Matrix res = a;
    res.Multiply(b);
    return res;
  }
    friend Matrix operator* (const double b, const Matrix& a)
  {
    Matrix res = a;
    res.Multiply(b);
    return res;
  }
    friend Matrix operator/ (const Matrix& a, const Matrix& b)
  {
        if (a.rows == a.cols && a.rows == a.cols && b.rows == b.cols)
    {
      Matrix res(a.rows, a.cols);
      res = a * Inv(b);
      return res;
    }
    else
    {
            throw Exception("Dimensions does not match");
    }
        return Matrix();
  }
    friend Matrix operator/ (const Matrix& a, const double b)
  {
    Matrix res = a;
    res.Divide(b);
    return res;
  }
    friend Matrix operator/ (const double b, const Matrix& a)
  {
    Matrix b_matrix(1, 1);
    b_matrix(1,1) = b;
    Matrix res = b_matrix / a;
    return res;
  }
    Matrix Minor(const int row, const int col) const
  {
    Matrix res;
    if (row > 0 && row <= rows && col > 0 && col <= cols)
    {
      res = Matrix(rows - 1, cols - 1);
            for (int r = 1; r <= (rows - (row >= rows)); r++)
      {
        for (int c = 1; c <= (cols - (col >= cols)); c++)
        {
          res(r - (r > row), c - (c > col)) = p[r-1][c-1];
        }
      }
    }
    else
    {
      throw Exception("Index for minor out of range");
    }
    return res;
  }
    int Size(const int i) const
  {
    if (i == 1)
    {
      return rows;
    }
    else if (i == 2)
    {
      return cols;
    }
    return 0;
  }
    int GetRows() const
  {
    return rows;
  }
    int GetCols() const
  {
    return cols;
  }
    void Print() const
  {
    if (p != NULL)
    {
      printf("[");
      for (int r = 0; r < rows; r++)
      {
        if (r > 0)
        {
          printf(" ");
        }
        for (int c = 0; c < cols-1; c++)
        {
          printf("%.2f, ", p[r][c]);
        }
        if (r < rows-1)
        {
          printf("%.2f;\n", p[r][cols-1]);
        }
        else
        {
          printf("%.2f]\n", p[r][cols-1]);
        }
      }
    }
    else
    {
            printf("[ ]\n");
    }
  }
public:
    ~Matrix()
  {
        for (int r = 0; r < rows; r++)
    {
      delete p[r];
    }
    delete p;
    p = NULL;
  }
private:
  int rows;
  int cols;
  double** p;     };
int Size(const Matrix& a, const int i)
{
  return a.Size(i);
}
Matrix Ones(const int rows, const int cols)
{
  Matrix res = Matrix(rows, cols);
  for (int r = 1; r <= rows; r++)
  {
    for (int c = 1; c <= cols; c++)
    {
      res(r, c) = 1;
    }
  }
  return res;
}
Matrix Zeros(const int rows, const int cols)
{
  return Matrix(rows, cols);
}
Matrix Diag(const int n)
{
  Matrix res = Matrix(n, n);
  for (int i = 1; i <= n; i++)
  {
    res(i, i) = 1;
  }
  return res;
}
Matrix Diag(const Matrix& v)
{
  Matrix res;
  if (v.GetCols() == 1)
  {
        int rows = v.GetRows();
    res = Matrix(rows, rows);
        for (int r=1; r <= rows; r++)
    {
      res(r, r) = v.get(r, 1);
    }
  }
  else if (v.GetRows() == 1)
  {
        int cols = v.GetCols();
    res = Matrix(cols, cols);
        for (int c=1; c <= cols; c++)
    {
      res(c, c) = v.get(1, c);
    }
  }
  else
  {
    throw Exception("Parameter for diag must be a vector");
  }
  return res;
}
double Det(const Matrix& a)
{
  double d = 0;      int rows = a.GetRows();
  int cols = a.GetRows();
  if (rows == cols)
  {
        if (rows == 1)
    {
            d = a.get(1, 1);
    }
    else if (rows == 2)
    {
                  d = a.get(1, 1) * a.get(2, 2) - a.get(2, 1) * a.get(1, 2);
    }
    else
    {
            for (int c = 1; c <= cols; c++)
      {
        Matrix M = a.Minor(1, c);
                d += (c%2 + c%2 - 1) * a.get(1, c) * Det(M);       }
    }
  }
  else
  {
    throw Exception("Matrix must be square");
  }
  return d;
}
void Swap(double& a, double& b)
{
  double temp = a;
  a = b;
  b = temp;
}
Matrix Inv(const Matrix& a)
{
  Matrix res;
  double d = 0;      int rows = a.GetRows();
  int cols = a.GetRows();
  d = Det(a);
  if (rows == cols && d != 0)
  {
        if (rows == 1)
    {
            res = Matrix(rows, cols);
      res(1, 1) = 1 / a.get(1, 1);
    }
    else if (rows == 2)
    {
            res = Matrix(rows, cols);
      res(1, 1) = a.get(2, 2);
      res(1, 2) = -a.get(1, 2);
      res(2, 1) = -a.get(2, 1);
      res(2, 2) = a.get(1, 1);
      res = (1/d) * res;
    }
    else
    {
                              res = Diag(rows);         Matrix ai = a;          for (int c = 1; c <= cols; c++)
      {
                        int r;
        for (r = c; r <= rows && ai(r, c) == 0; r++)
        {
        }
        if (r != c)
        {
                    for (int s = 1; s <= cols; s++)
          {
            Swap(ai(c, s), ai(r, s));
            Swap(res(c, s), res(r, s));
          }
        }
                for (int r = 1; r <= rows; r++)
        {
          if(r != c)
          {
                        if (ai(r, c) != 0)
            {
              double f = - ai(r, c) / ai(c, c);
                                          for (int s = 1; s <= cols; s++)
              {
                ai(r, s) += f * ai(c, s);
                res(r, s) += f * res(c, s);
              }
            }
          }
          else
          {
                                    double f = ai(c, c);
            for (int s = 1; s <= cols; s++)
            {
              ai(r, s) /= f;
              res(r, s) /= f;
            }
          }
        }
      }
    }
  }
  else
  {
    if (rows == cols)
    {
      throw Exception("Matrix must be square");
    }
    else
    {
      throw Exception("Determinant of matrix is zero");
    }
  }
  return res;
}
int main(int argc, char *argv[])
{
    try
  {
        int cols = 3;
    int rows = 3;
    Matrix A = Matrix(cols, rows);
        int count = 0;
    for (int r = 1; r <= rows; r++)
    {
      for (int c = 1; c <= cols; c++)
      {
        count ++;
        A(r, c) = count;
      }
    }
        A(2,1) = 1.23;
        double centervalue = A(2,2);
    printf("centervalue = %f \n", centervalue);
    printf("\n");
        printf("A = \n");
    A.Print();
    printf("\n");
    Matrix B = Ones(rows, cols) + Diag(rows);
    printf("B = \n");
    B.Print();
    printf("\n");
    Matrix B2 = Matrix(rows, 1);        count = 1;
    for (int r = 1; r <= rows; r++)
    {
      count ++;
      B2(r, 1) = count * 2;
    }
    printf("B2 = \n");
    B2.Print();
    printf("\n");
    Matrix C;
    C = A + B;
    printf("A + B = \n");
    C.Print();
    printf("\n");
    C = A - B;
    printf("A - B = \n");
    C.Print();
    printf("\n");
    C = A * B2;
    printf("A * B2 = \n");
    C.Print();
    printf("\n");
        Matrix E = Diag(B2);
    printf("E = \n");
    E.Print();
    printf("\n");
        Matrix D = Matrix(2, 2);
    D(1,1) = 2;
    D(1,2) = 4;
    D(2,1) = 1;
    D(2,2) = -2;
    printf("D = \n");
    D.Print();
    printf("Det(D) = %f\n\n", Det(D));
    printf("A = \n");
    A.Print();
    printf("\n");
    printf("Det(A) = %f\n\n", Det(A));
    Matrix F;
    F = 3 - A ;
    printf("3 - A = \n");
    F.Print();
    printf("\n");
        Matrix G = Matrix(2, 2);
    G(1, 1) = 1;
    G(1, 2) = 2;
    G(2, 1) = 3;
    G(2, 2) = 4;
    printf("G = \n");
    G.Print();
    printf("\n");
    Matrix G_inv = Inv(G);
    printf("Inv(G) = \n");
    G_inv.Print();
    printf("\n");
    Matrix A_inv = Inv(A);
    printf("Inv(A) = \n");
    A_inv.Print();
    printf("\n");
    Matrix A_A_inv = A * Inv(A);
    printf("A * Inv(A) = \n");
    A_A_inv.Print();
    printf("\n");
    Matrix B_A = B / A;
    printf("B / A = \n");
    B_A.Print();
    printf("\n");
    Matrix A_3 = A / 3;
    printf("A / 3 = \n");
    A_3.Print();
    printf("\n");
    rows = 2;
    cols = 5;
    Matrix H = Matrix(rows, cols);
    for (int r = 1; r <= rows; r++)
    {
      for (int c = 1; c <= cols; c++)
      {
        count ++;
        H(r, c) = count;
      }
    }
    printf("H = \n");
    H.Print();
    printf("\n");
  }
  catch (Exception err)
  {
    printf("Error: %s\n", err.msg);
  }
  catch (...)
  {
    printf("An error occured...\n");
  }
  printf("\n");
  PAUSE;
  return EXIT_SUCCESS;
}