#include <iostream>
#include <vector>
#include <initializer_list>
#include <utility>

namespace stdx {

template <typename T, typename Allocator>
class matrix;

template<typename T, typename Allocator = std::allocator<T>>
class matrix_helper {
public:
    friend class matrix<T, Allocator>;

    using o_matrix = matrix<T, Allocator>;
    using p_vector = std::vector<T, Allocator>;
    using v_size_t = typename p_vector::size_type;

    typename p_vector::reference operator [] (v_size_t colindex) {
        return static_cast<p_vector &>(matrix_)[rowindex_ * matrix_.rowsize() + colindex];
    }

    operator typename p_vector::reference () {
        return static_cast<p_vector &>(matrix_)[rowindex_];
    }

    typename p_vector::reference operator = (T &&value) {
        return (typename p_vector::reference)(*this) = std::forward<T>(value);
    }

protected:
    matrix_helper(o_matrix &matrix, v_size_t rowindex) :
        matrix_(matrix),
        rowindex_(rowindex)
    {}

protected:
    o_matrix &matrix_;
    v_size_t rowindex_;
};

template<typename T, typename Allocator = std::allocator<T>>
class const_matrix_helper : public matrix_helper<T, Allocator> {
public:
    friend class matrix<T, Allocator>;

    using o_matrix = matrix<T, Allocator>;
    using p_vector = std::vector<T, Allocator>;
    using v_size_t = typename p_vector::size_type;

    typename p_vector::const_reference operator [] (v_size_t colindex) const {
        return const_cast<matrix_helper<T, Allocator> *>(this)->operator [](colindex);
    }

    operator typename p_vector::const_reference () const {
        return (typename p_vector::reference) const_cast<matrix_helper<T, Allocator> *>(this);
    }

protected:
    const_matrix_helper(const o_matrix &matrix, v_size_t rowindex) :
        matrix_helper<T, Allocator>(const_cast<o_matrix &>(matrix), rowindex)
    {}
};

template <typename T, typename Allocator = std::allocator<T>>
class matrix : public std::vector<T, Allocator> {
public:
    using p_vector = std::vector<T, Allocator>;
    using v_size_t = typename p_vector::size_type;

    matrix(std::initializer_list<std::initializer_list<T>> contents)
    {
        for (auto &row : contents) {
            rowsize_ = row.size();
            p_vector::insert(p_vector::end(), row);
        }
    }

    matrix(typename p_vector::size_type rowsize, std::initializer_list<T> contents) :
        p_vector(contents),
        rowsize_(rowsize)
    {}

    v_size_t rowsize() const {
        return rowsize_;
    }

    v_size_t colsize() const {
        return p_vector::size() / rowsize_;
    }

    const const_matrix_helper<T, Allocator> operator [] (v_size_t rowindex) const {
        return const_matrix_helper<T, Allocator>(*this, rowindex);
    }

    matrix_helper<T, Allocator> operator [] (v_size_t rowindex) {
        return matrix_helper<T, Allocator>(*this, rowindex);
    }

private:
    static constexpr v_size_t invalid_size = -1;
    v_size_t rowsize_ = invalid_size;
};

}

using std::cout;
using std::endl;

int main(int argc, char *argv[])
{
    (void) argc;
    (void) argv;

    stdx::matrix<int> m0 = {
        { 1, 2, 3 },
        { 4, 5, 6 },
        { 7, 8, 9 }
    };

    cout << "m0[1, 2] = " << m0[1][2] << endl;
    cout << "m0[1] = " << (int) m0[1] << endl;

    m0[1][2] = 42;
    m0[1] = 3;

    cout << "After change:\n" << endl;
    cout << "m0[1, 2] = " << m0[1][2] << endl;
    cout << "m0[1] = " << (int) m0[1] << endl;

    stdx::matrix<int> m1(2, { 1, 2, 3, 4 });

    return 0;
}
