#include <cstring>
    #include <cassert>
    #include <algorithm>
    #include <iostream>
    #include <iomanip>

    template <typename T>
    class MaTran;
    

    template <typename T>
    class MaTran
    {
        int soHang;
        int soCot;
        int kichThuoc;
        T *_data;

    public:
        MaTran(int soHang, int soCot) 
            : soHang(soHang), soCot(soCot), 
              kichThuoc(soHang*soCot), _data(new T[kichThuoc])
        {}

        MaTran(MaTran const &mt)
            : soHang(mt.soHang), soCot(mt.soCot),
              kichThuoc(mt.kichThuoc), _data(new T[kichThuoc])
        {
            std::memcpy(_data, mt._data, kichThuoc * sizeof(T));
        }

        virtual ~MaTran() {
           if(_data) delete _data;
        }

        // chồng toán tử =, dùng để gán ma trận
        MaTran& operator=(MaTran const &mt) {
            if (this != &mt) {
                soHang = mt.soHang;
                soCot = mt.soCot;
                kichThuoc = mt.kichThuoc;
                if (_data) delete _data;
                _data = new T[kichThuoc];
                std::memcpy(_data, mt._data, kichThuoc * sizeof(T));
            }
            return *this;
        }

        // Toán tử xuất <<
        friend
        std::ostream& operator<<(std::ostream& os, MaTran<T> const &mt) {
            os << std::fixed << std::setprecision(2);
            for (int i=0;i<mt.soHang;i++) {
                os <<"\n\t";
                T const *hang = mt._data + i*mt.soCot;
                for(int j=0;j<mt.soCot;j++)
                   os <<std::setw(5)<< hang[j] << ' ';
            }
            os << std::endl;
            return os;
        }

        // Toán tử nhập >>
        friend
        std::istream& operator>>(std::istream& is, MaTran<T> &mt) {
            for (int i=0;i<mt.soHang;i++) {
                T *hang = mt._data + i*mt.soCot;
                for(int j=0;j<mt.soCot;j++) {
                   if (&is == &std::cin)
                       std::cout << "\tPhan tu[" << i + 1 << "," << j + 1 << "] = ";
                       is >> hang[j];
                }
            }
            return is;
        }

        // nạp chồng toán tử +=
        MaTran& operator+=(MaTran const &mt) {
            assert(soHang == mt.soHang && soCot == mt.soCot);
            for (int i = 0; i != kichThuoc; ++i) _data[i] += mt._data[i];
            return *this;
        }

        // nap chồng toán tử +
        friend MaTran<T> operator+(MaTran<T> const &m1, MaTran<T> const &m2) {
            MaTran<T> tong = m1;
            tong += m2;
            return tong;
        }

        // Làm tương tự với các phép -, *

        // viết các hàm tính toán khác như chuyển vị, định thức, ...
    };
    
    
    int main() {
    	MaTran<int> m1(3, 3), m2(3, 3);
    	std::cin >> m1; 
    	std::cout << "m1 = " << m1 << '\n';
    	std::cin >> m2;
    	std::cout << "m2 = " << m2 << '\n';
    	
    	std::cout << "MaTran<int> m3 = m1\n";
    	MaTran<int> m3 = m1;
    	std::cout << "m3 = " << m3 << '\n';
    	
    	std::cout << "m3 += m2\n";
    	m3 += m2;
    	std::cout << "m3 = " << m3 << '\n';
    	
    	std::cout << "m3 = m3 + m1 + m2\n";
    	m3 = m3 + m1 + m2;
    	std::cout << "m3 = " << m3 << '\n';
    	
    	return 0;
    }