#include <print>
#include <utility>
#include <exception>

// Klassentemplate für allgemeine Matrix
// Datenfeld muss dynamisch angelegt werden,
// da beim Transponieren z.B. eine 3x5 Matrix
// in eine 5x3 Matrix umgewandelt wird
template <typename T, int ROW, int COL>
class Matrix
{
    T **data;   // Zeiger-Zeiger(!) auf dyn. 2D-Feld
    int col;    // Spalten
    int row;    // Zeilen
  public:
    // ctor, reserviert 2D-Feld dynamisch
    Matrix()
    {
        row = ROW;  // Zeilen/Spalten merken
        col = COL;
        // 2D-Feld dyn. reservieren
        data = new T*[row];
        for (auto index=0; index<row; index++)
            data[index] = new T[col];
    }
    // dtor, loescht 2D-Feld
    ~Matrix()
    {
        // 2D-Feld loeschen
        for (auto index = 0; index < row; index++)
            delete[] data[index];
        delete[] data;
    }
    // Memberfunktionen zum setzen eines Matrix-Elements
    // Zeilen/Spalten-Nr. beginnen ab 1!
    void SetData(int _row, int _col, T val)
    {
        if ((_col < 1) || (_col > col) ||
            (_row < 1) || (_row > row))
            throw std::out_of_range{ "Falsch Zeile oder Spalte" };
        data[_row-1][_col-1] = val;
    }
    // Matrix transponieren (Zeilen/Spalten vertauschen)
    void Transpose()
    {
        // Neues 2D-Feld anlegen mit vertauschter Groesse
        // Spalte->Zeile, Zeile->Spalte
        T **temp;
        temp = new T*[col];
        for (auto index=0; index<col; index++)
            temp[index] = new T[row];
        // Matrix transponieren
        for (auto rindex=0; rindex<row; rindex++)
            for (auto cindex=0; cindex<col; cindex++)
                temp[cindex][rindex] = data[rindex][cindex];
        // Alte Matrix nun löschen
        for (auto index=0; index<row; index++)
            delete [] data[index];
        delete [] data;
        // Zeiger auf neue Matrix abspeichern
        data = temp;
        // Zeilen/Spalten Anzahl vertauschen
        std::swap(col,row);
    }
    // Matrix ausgeben
    void Print() const
    {
        for (auto rindex=0; rindex<row; rindex++)
        {
            for (auto cindex = 0; cindex < col; cindex++)
                std::print("{:5}", data[rindex][cindex]);
            std::println("");
        }
    }
};

// Partiell spezialisierte Matrix für eine quadratische
// Matrix (Zeilen=Spalten)
// Datenfeld kann statisch definiert werden da beim
// Transponieren sich die Groesse nicht aendert
template <typename T, int SIZE>
class Matrix<T,SIZE,SIZE>
{
    T data[SIZE][SIZE]; // dyn. 2D-Feld
  public:
    // Memberfunktionen zum setzen eines Matrix-Elements
    // Zeilen/Spalten-Nr. beginnen ab 1!
    void SetData(int row, int col, T val)
    {
        if ((col < 1) || (col > SIZE) ||
            (row < 1) || (row > SIZE))
            throw std::out_of_range{ "Falsch Zeile oder Spalte" };
        data[row-1][col-1] = val;
    }
    // Matrix transponieren
    // Bei quadratischen Matrizen muss kein temporäres 2D-Feld
    // angelegt werden; es können direkt die Matrix-Elemente
    // vertauscht werden
    void Transpose()
    {
        for (auto rindex=0; rindex<SIZE-2; rindex++)
            for (auto cindex=rindex+1;cindex<=SIZE-1; cindex++)
                std::swap(data[cindex][rindex],data[rindex][cindex]);
    }
    // Matrix ausgeben
    void Print() const
    {
        for (auto rindex=0; rindex<SIZE; rindex++)
        {
            for (auto cindex=0; cindex<SIZE; cindex++)
                std::print("{:6}", data[rindex][cindex]);
            std::println("");
        }
    }
};
