/*
 *  rmatrix.h
 *  (C) Porkolab Zoltan, ELTE, Budapest, Hungary
 *  (C) 2003
 */


#ifndef RMATRIX_H
#define RMATRIX_H

#include <map>
#include <stdexcept>

template <typename T>
class rmatrix
{
public:
    class index
    {
    public:
        index(int i, int j, int k) : i_(i), j_(j), k_(k) { }
        int size() const { return i_ * j_ * k_; }
        bool operator<( const index& rhs) const;

        const int i_;
        const int j_;
        const int k_;
    };

    // in real good implementation iterator would be inherited from 
    // map<index,T>::iterator, see: mmatrix_with_map.h
    typedef typename std::map<index,T>::iterator iterator;
    typedef typename std::map<index,T>::const_iterator const_iterator;

    rmatrix(int i, int j, int k) : size_(index(i,j,k)) { } 
    
    int size() const { return size_.size(); }
    bool stored(int i, int j, int k) const;

    T& at(int i, int j, int k)       /* throw (std::out_of_range) */;
    T  at(int i, int j, int k) const /* throw (std::out_of_range) */;
    T& operator()(int i, int j, int k)       /* throw () */;
    T  operator()(int i, int j, int k) const /* throw () */;

    iterator begin() { return m_.begin(); }    
    const_iterator begin() const { return m_.begin(); }    
    iterator end() { return m_.end(); }
    const_iterator end() const { return m_.end(); }

private:
    const index       size_;
    std::map<index,T> m_;   

    void check_range(int i,int j,int k) const /* throw (std::out_of_range) */;
    const_iterator stored_at(int i, int j, int k) const;
};

template <typename T>
bool rmatrix<T>::index::operator<( const index& rhs) const
{
    return  ( i_ < rhs.i_ )                         || 
            ( i_ == rhs.i_ && j_ <  rhs.j_ )        ||
            ( i_ == rhs.i_ && j_ == rhs.j_ && k_ < rhs.k_ ); 
}       

template <typename T>
typename rmatrix<T>::const_iterator rmatrix<T>::stored_at(int i, int j, int k) const
{
    return m_.find(index(i,j,k));
}
template <typename T>
bool rmatrix<T>::stored(int i, int j, int k) const
{
    return end() != stored_at(i,j,k);
}

template <typename T>
T& rmatrix<T>::operator()(int i, int j, int k)
{
    // may create new entry
    return m_[index(i,j,k)];    
}
template <typename T>
T rmatrix<T>::operator()(int i, int j, int k) const
{
    const_iterator ci = stored_at(i,j,k);
    return ci == end() ? T() : ci->second;
}
template <typename T>
void rmatrix<T>::check_range(int i, int j, int k) const
{
    if ( 0 <= i  &&  i < size_.i_  &&
         0 <= j  &&  j < size_.j_  &&
         0 <= k  &&  k < size_.k_  )
        /* range is OK */ ;
    else
        throw std::out_of_range("bad index"); 
}
template <typename T>
T& rmatrix<T>::at(int i, int j, int k)
{
    check_range(i,j,k);
    return operator()(i,j,k);    
}
template <typename T>
T rmatrix<T>::at(int i, int j, int k) const
{
    check_range(i,j,k);
    return operator()(i,j,k);    
}

#endif /* RMATRIX_H */