#include <iostream>
#include <array>

template <size_t N, size_t M>
struct Matrix {
	  std::array<std::array<double, M>, N> data_;
	
	  std::array<double, M>& operator [] (size_t index) { 
	  	return data_[index]; 
	  }
	  std::array<double, M> operator [] (size_t index) const { 
	  	return const_cast<Matrix&>(*this)[index]; 
	  }
	  
	  template <size_t I, size_t J, size_t K>
	  friend Matrix<I,K> operator* (const Matrix<I,J>&, const Matrix<J,K>&);
};	

template <size_t Size>
struct Vector : public Matrix <1,Size> {
	  double& operator [] (size_t index) { 
	  	return Matrix<1,Size>::data_[0][index]; 
	  }
	  double operator [] (size_t index) const { 
	  	return const_cast<Vector&>(*this)[index]; 
	  }
};

template <size_t I, size_t J, size_t K>
Matrix<I,K> operator* (const Matrix<I,J>& lhs, const Matrix<J,K>& rhs) {
	Matrix<I,K> r;
	for (size_t i=0; i < I; ++i)
	   for (size_t k=0; k < K; ++k) {
	  	r[i][k]=0;
	    for (size_t j=0; j < J; ++j)
	      r[i][k] += lhs[i][j]*rhs[j][k];
	  }
	  
	return r;
}

template <typename T, size_t N>
std::ostream& operator <<(std::ostream& os, const std::array<T,N>& a) {
	os << '(';
	size_t i=0;
	for (auto x: a) {
		if (i++ != 0) os << ',';
		os << x;
	}
	os << ')';
	return os;
}

template <size_t N, size_t M>
std::ostream& operator <<(std::ostream& os, const Matrix<N,M>& m) {
	return os << m.data_;
}

int main() {
	Vector<3> v;
	v[0] = 1; v[1] = 2; v[2] = 3;
	std::cout << "v=" << v << std::endl;
	
	Matrix<3,2> m;
	m[0][0]=1;m[0][1]=2;
	m[1][0]=3;m[1][1]=4;
	m[2][0]=5;m[2][1]=6;
	std::cout << "m=" << m << std::endl;
	
	Matrix<2,4> m1;
	m1[0][0]=1;m1[0][1]=2;m1[0][2]=3;m1[0][3]=4;
	m1[1][0]=5;m1[1][1]=6;m1[1][2]=7;m1[1][3]=8;
	std::cout << "m1=" << m1 << std::endl;

	std::cout << "m*m1=" << m*m1 << std::endl;
	std::cout << "v*m=" << v*m << std::endl;
}