// tester.cpp : Defines the entry point for the console application.
//
#include <iostream>
#include <cstdlib>
#include <algorithm>

typedef double tipoelem;

class matrice {
public:
    matrice(int, int, tipoelem inizializzatore = 0); /* costruttore */
    matrice(const matrice& rhs);
    ~matrice(void);
    tipoelem leggiMatrice(int, int);
    void scriviMatrice(int, int, tipoelem);
    void prodottoScalare(tipoelem);
    matrice matriceTrasposta(void);
    matrice matriceProdotto(matrice& M);
    matrice& operator=(const matrice&);
    void rand(void);
    void stampa(void);
private:
    int righe;
    int colonne;
    tipoelem **elementi;
};

using namespace std;

matrice::matrice(int r, int c, tipoelem inizializzatore) {
    this->colonne = c;
    this->righe = r;

    // allocazione dinamica della matrice
    elementi = new tipoelem*[righe];
    for (auto i = 0; i != righe; i++)
        this->elementi[i] = new tipoelem[colonne];

    // inizializzazione degli elementi
    for (auto i = 0; i != righe; i++)
        for (auto j = 0; j != colonne; j++)
            this->elementi[i][j] = inizializzatore;
}

matrice::matrice(const matrice& rhs) : colonne(rhs.colonne), righe(rhs.righe), elementi(new tipoelem*[rhs.righe])
{
    for (auto i = 0; i != righe; i++)
        this->elementi[i] = new tipoelem[colonne];

    for (auto i = 0; i != righe; i++)
        for (auto j = 0; j != colonne; j++)
            this->elementi[i][j] = rhs.elementi[i][j];
}

matrice::~matrice(void) {
    for (auto j = 0; j < this->righe; ++j) {
        delete[] this->elementi[j];
    }
    delete[] this->elementi;
}

tipoelem matrice::leggiMatrice(int i, int j) {
    return elementi[i][j];
}

void matrice::scriviMatrice(int i, int j, tipoelem scrittura) {
    elementi[i][j] = scrittura;
    return;
}

void matrice::prodottoScalare(tipoelem scalare) {
    for (auto i = 0; i < righe; i++)
        for (auto j = 0; j < colonne; j++)
            elementi[i][j] = elementi[i][j] * scalare;
    return;
}

matrice matrice::matriceTrasposta(void) {
    matrice trasposta(colonne, righe);

    for (auto i = 0; i < righe; i++)
        for (auto j = 0; j < colonne; j++)
            trasposta.scriviMatrice(j, i, leggiMatrice(i, j));
    return trasposta;
}

matrice matrice::matriceProdotto(matrice& M) {
    matrice prodotto(righe, colonne);

    for (auto i = 0; i < righe; i++)
        for (auto j = 0; j < righe; j++)
            prodotto.scriviMatrice(i, j, (matrice::leggiMatrice(i, j)*M.leggiMatrice(i, j)));
    return prodotto;
}

matrice& matrice::operator=(const matrice &m) 
{
    matrice temp(m);
    std::swap(temp.colonne, colonne);
    std::swap(temp.righe, righe);
    std::swap(temp.elementi, elementi);
    return *this;
}

void matrice::rand(void) {
    for (auto i = 0; i < righe; i++)
        for (auto j = 0; j < colonne; j++)
            matrice::scriviMatrice(i, j, 100.0);
    return;
}

void matrice::stampa(void) {
    for (auto i = 0; i < righe; i++) {
        for (auto j = 0; j < colonne; j++)
            std::cout << elementi[i][j] << " ";
        std::cout << std::endl;
    }
}

int main(void) {

    matrice A(3, 2), T(2, 3);
    A.rand();
    std::cout << "Stampa A" << std::endl;
    A.stampa();
    std::cout << "Stampa Trasposta T" << std::endl;
    T = A.matriceTrasposta();
    T.stampa();

    std::cout << std::endl;

    std::cout << "Stampa B" << std::endl;
    matrice B(4, 4);
    B.stampa();
    std::cout << "Stampa copia t in b" << std::endl;
    B = T;
    B.stampa();

    return (0);
}