#include <stdexcept>
#include <algorithm>
#include "matrix_proxy.h"
using namespace std;


matrix_proxy::matrix_proxy(matrix_expression& src, const matrix_expression& rows, const matrix_expression& cols)
    :  _src(src), _rows(rows), _cols(cols) {
    if (min(rows.numColumns(), rows.numRows()) != 1)
	throw invalid_argument("rows must be one-dimensional.");
    if (min(cols.numColumns(), cols.numRows()) != 1)
	throw invalid_argument("cols must be one-dimensional.");

    for (int k=0; k < numRows(); k++)
	if (_rows[k] < 0 || _rows[k] >= src.numRows())
	    throw out_of_range("invalid row specified");

    for (int k=0; k < numColumns(); k++)
	if (_cols[k] < 0 || _cols[k] >= src.numColumns())
	    throw out_of_range("invalid column specified");
}

// cannot do traditional assignment on proxies, since we cannot rebind the source matrix.
// We will only support value-based assignments
matrix_proxy& matrix_proxy::operator=(const matrix_proxy& other) {
    if (this != &other)
	matrix_expression::operator=(other);
    return *this;
}

int matrix_proxy::numRows() const {
    return _rows.numRows()*_rows.numColumns();
}

int matrix_proxy::numColumns() const {
    return _cols.numRows()*_cols.numColumns();
}

// Determine source of underlying storage
const matrix_expression& matrix_proxy::underlyingStorage() const {
    return _src.underlyingStorage();
}

// read-only version of indexing operator
double matrix_proxy::operator()(int r, int c) const {
    return _src(_rows[r], _cols[c]);
}

// write-access version of indexing operator
double& matrix_proxy::operator()(int r, int c) {
    return _src(_rows[r], _cols[c]);
}
