#include <stdexcept>             // defines various exceptions we throw
#include <sstream>               // need stringstream for operator<<
#include "matrix.h"
using namespace std;


/*****************************************************
 * matrix class
 ****************************************************/

matrix::matrix(int numRows, int numColumns, double value)
  : _nr(numRows), _nc(numColumns), data(numRows*numColumns, value) {}


matrix::matrix(const matrix_expression& m) {
  *this = m;   // rely on operator= implementation
}

matrix& matrix::operator=(const matrix& other) {
  if (this != &other) {
    _nr = other._nr;
    _nc = other._nc;
    data = other.data;
  }

  return *this;
}

matrix& matrix::operator=(const matrix_expression& m) {
  if (this != &m) {
    _nr = m.numRows();
    _nc = m.numColumns();
    data.resize(_nr * _nc);
  
    for (int r=0; r < _nr; r++)
      for (int c=0; c < _nc; c++)
	(*this)(r,c) = m(r,c);
  }

  return *this;
}



  // Returns the number of rows
int matrix::numRows() const {
  return _nr;
}

  // Returns the number of columns
int matrix::numColumns() const {
  return _nc;
}

  // Change the apparent size of this matrix.
  // Overall number of elements must be preserved.
void matrix::reshape(int r, int c) {
  if (r * c != _nr * _nc)
    throw invalid_argument("To reshape, the number of elements must not change.");

  _nr = r;
  _nc = c;
}


  // Provides read-only access via indexing operator
double matrix::operator()(int r, int c) const {
  if (r < 0 || r >= _nr || c < 0 || c >= _nc)
    throw out_of_range("Invalid indices for matrix");

  return data[r +  c * _nr];   // column-major
}

  // Provides write-access through indexing operator
double& matrix::operator()(int r, int c) {
  if (r < 0 || r >= _nr || c < 0 || c >= _nc)
    throw out_of_range("Invalid indices for matrix");

  return data[r +  c * _nr];   // column-major
}


/*****************************************************
 * IO operations
 ****************************************************/

istream& operator>>(istream& in, matrix& m) {
  // presumes that there is a blank line to terminate the matrix
  vector< vector<double> >  data;

  unsigned int numColumns = 0;
  bool done = false;
  while (!done) {
    vector<double> row;
    string temp;
    getline(in, temp);
    stringstream s(temp);
    double val;

    while (s >> val) {
      row.push_back(val);
    }

    if (row.size() == 0)
      done = true;
    else if (numColumns > 0 and numColumns != row.size())
      done = true;
    else {
      numColumns = row.size();
      data.push_back(row);
    }
  }

  m = matrix(data.size(), numColumns);
  for (int r=0; r < m.numRows(); r++)
    for (int c=0; c< m.numColumns(); c++)
      m(r,c) = data[r][c];

  return in;
}
