#ifndef MATRIX_H
#define MATRIX_H

#include <string>

struct matrixError
{
    matrixError( std::string r) : reason(r) { }
    const std::string reason;
};
struct indexError : public matrixError
{
    indexError( int i, int j) : row(i), col(j), matrixError("Bad index") { }
    int row;
    int col;
};

template <class T> class matrix;

template <class T>
class proxy_matrix
{
public:
    proxy_matrix( matrix<T> *m, int i) : mptr(m), row(i) { }
    T& operator[](int j) { return mptr->at(row,j); }
private:
    matrix<T> *mptr;
    int        row;
};

template <class T>
class const_proxy_matrix
{
public:
    const_proxy_matrix( const matrix<T> *m, int i) : mptr(m), row(i) { }
    T operator[](int j) { return mptr->at(row,j); }
private:
    const matrix<T> *mptr;
    int              row;
};

template <class T>
class matrix
{
public:
       matrix( int i, int j );
       matrix( const matrix &other);
       ~matrix();
    matrix operator=( const matrix &other);

    int rows() const { return x; }
    int cols() const { return y; }

    proxy_matrix<T> operator[](int i) { return proxy_matrix<T>(this,i); }
    const_proxy_matrix<T> operator[](int i) const 
                            { return const_proxy_matrix<T>(this,i); }

    T& at( int i, int j) throw(indexError);
    T  at( int i, int j) const throw(indexError);
    T& operator()( int i, int j);
    T  operator()( int i, int j) const;
    
    matrix operator+=( const matrix &other);
    matrix operator+( const matrix &other);
           
private:
    int  x;
    int  y;
    T   *v;
    void copy( const matrix &other);
    void check( int i, int j) const throw(indexError);
};

template <class T>
matrix<T>::matrix( int i, int j)
{
    x = i; 
    y = j;
    v = new T[x*y];
}
template <class T>
matrix<T>::matrix( const matrix &other)
{
    copy( other);
}
template <class T>
matrix<T>::~matrix()
{
    delete [] v;
}
template <class T>
matrix<T> matrix<T>::operator=( const matrix &other)
{
    if ( this != &other )
    {
        delete [] v;
        copy( other);
    }
    return *this;
}
template <class T>
void matrix<T>::copy( const matrix &other)
{
    x = other.x;
    y = other.y;
    v = new T[x*y];
    for ( int i = 0; i < x*y; ++i )
        v[i] = other.v[i];
}
template <class T>
void matrix<T>::check( int i, int j) const throw( indexError )
{
    if ( ( 0 <= i  && i < x ) && ( 0 <= j  && j < y ) )
        /* ok */ ;
    else
        throw indexError(i,j); 
}
template <class T>
T& matrix<T>::at( int i, int j) throw(indexError)
{
    check(i,j); 
    return operator()(i,j); 
}
template <class T>
T matrix<T>::at( int i, int j) const throw( indexError)
{ 
    check(i,j); 
    return operator()(i,j); 
}
template <class T>
T& matrix<T>::operator()( int i, int j)
{ 
    return v[i*y + j]; 
}
template <class T>
T matrix<T>::operator() ( int i, int j) const 
{ 
    return v[i*y + j]; 
}
template <class T>
matrix<T> matrix<T>::operator+=( const matrix &other)
{
    for ( int i = 0; i < x*y; ++i )
        v[i] += other.v[i];
    return *this;
}
template <class T>
matrix<T> matrix<T>::operator+( const matrix &other)
{
    matrix<T> temp( *this );
    temp += other;
    return temp;
}

#endif /* MATRIX_H */