#include <cstdint>
#include <iostream>
#include <algorithm>
#include <vector>

template <typename T>
class Matrix
{
    public:
        Matrix(std::size_t rows, std::size_t columns);
        Matrix(std::initializer_list<std::initializer_list<T>> init);
        auto rows() const -> std::size_t;
        auto columns() const -> std::size_t;
        auto operator()(std::size_t i, std::size_t j) const -> const T&;
        auto operator()(std::size_t i, std::size_t j) -> T&;
    private:
        const std::size_t rows_;
        const std::size_t columns_;
        std::vector<T> elements_;
        
};

template <typename T>
Matrix<T>::Matrix(std::size_t rows, std::size_t columns)
: rows_{ rows }, columns_{ columns }, elements_( rows * columns )
{
}

template <typename T>
Matrix<T>::Matrix(std::initializer_list<std::initializer_list<T>> init)
: Matrix{ 
    init.size(),
    std::max(
        init,
        [](const auto& a, const auto& b) {
            return a.size() < b.size();
        }
    ).size()
  }
{
    std::size_t i = 0;
    for (const auto& row : init)
    {
        if (i >= rows())
            break;
        std::size_t j = 0;
        for (const auto& value : row)
        {
            if (j >= columns())
                break;
            (*this)(i, j) = value;
            ++j;
        }
        ++i;
    } 
}

template <typename T>
auto Matrix<T>::rows() const -> std::size_t
{
    return rows_;
}

template <typename T>
auto Matrix<T>::columns() const -> std::size_t
{
    return columns_;
}

template <typename T>
auto Matrix<T>::operator()(std::size_t i, std::size_t j) const -> const T&
{
    return elements_[ i * columns() + j];
}

template <typename T>
auto Matrix<T>::operator()(std::size_t i, std::size_t j) -> T&
{
    return elements_[ i * columns() + j];
}
        
template <typename T>
std::ostream& operator<<(std::ostream& out, const Matrix<T>& m)
{
    out << typeid(T).name() << m.rows() << "x" << m.columns();
    out << "{ ";
    for (std::size_t i = 0; i < m.rows(); ++i)
    {
        if (i > 0)
            out << ", ";
        out << "{ ";
        for (std::size_t j = 0; j < m.columns(); ++j)
        {
            if (j > 0)
                out << ", ";
            out << m(i, j);
        }
        out << " }";
    }
    out << " }";
    return out;
}

auto main() -> int
{
    Matrix<float> m
    {
        { 1, 2, 3 },
        { 4, 5, 6 },
        { 7, 8, 9 }
    };
    
    std::cout << m << std::endl;
}