/* Produit matrice*matrice                      */
/* Evaluation du temps d'execution              */  

public class TestProduitMatriceMatriceAvecTransposition {

/* Fonction de creation d'une matrice de double */
/* initialise avec des nombres aleatoires       */
/* compris dans l'intervalle [0.0,max]          */
/* et un chiffre apres la virgule               */

  static double [][] initRandMatrice(int lignes,
                                     int colonnes,
                                     double max) {
    double [][] tab = new double[lignes][colonnes];
    for ( int i = 0 ; i < lignes ; i++ ) {
      for ( int j = 0 ; j < colonnes ; j++ ) {
        tab[i][j] = ((int) (Math.random()*max*1000.0))/1000.0; } }
    return tab;
  }

//////////////////////////////////////////////////

  static double [][] transpose(double [][] m) {
    int l = m.length;
    int c = m[0].length;
    double [][] mt = new double[c][l];
    for ( int i = 0 ; i < l ; i++ ) {
      for ( int j = 0 ; j < c ; j++ ) {
        mt[j][i] = m[i][j]; } }
    return mt;
  }

/* Fonction de calcul du produit d'un tableau   */
/* de dimension 2 de taille n.m                 */
/* par un tableau de dimension 2 de taille m.p  */
/* Resultat: Un tableau de dimension 2          */
/* de taille n.p                                */

  static void produitMatriceMatriceAvecTransposition(double [][] m1,double [][] m2,double [][] r) {
    int n = m1.length;
    int m = m2.length;
    int p = m2[0].length;
    double [][] mt = transpose(m2);
    for ( int i = 0 ; i < n ; i++ ) {
      for ( int j = 0 ; j < p ; j++ ) {
        double v = 0.0;
        for ( int k = 0 ; k < m ; k++ ) {
          v += m1[i][k]*mt[j][k]; }
        r[i][j] = v; } }
  }

/* Fonction de calcul du produit d'un tableau   */
/* de dimension 2 de taille n.m                 */
/* par un tableau de dimension 2 de taille m.p  */
/* Resultat: Un tableau de dimension 2          */
/* de taille n.p                                */

  static void produitMatriceMatrice(double [][] m1,double [][] m2,double [][] r) {
    int n = m1.length;
    int m = m2.length;
    int p = m2[0].length;
    for ( int i = 0 ; i < n ; i++ ) {
      for ( int j = 0 ; j < p ; j++ ) {
        double v = 0.0;
        for ( int k = 0 ; k < m ; k++ ) {
          v += m1[i][k]*m2[k][j]; }
        r[i][j] = v; } }
  }

  static void transposition(double [][] m) {
    int n = m.length;
    for ( int i = 0 ; i < n-1 ; i++ ) {
      for ( int j = i+1 ; j < n ; j++ ) {
        double aux = m[i][j];
        m[i][j] = m[j][i];
        m[j][i] = aux; } }
  }

/* Fonction de calcul du produit d'un tableau   */
/* de dimension 2 de taille n.m                 */
/* par un tableau de dimension 2 de taille m.p  */
/* Resultat: Un tableau de dimension 2          */
/* de taille n.p                                */

  static void produitMatricesCarreesAvecTransposition(double [][] m1,
                                                      double [][] m2,
                                                      double [][] r) {
    int n = m1.length;
    transposition(m2);
    for ( int i = 0 ; i < n ; i++ ) {
      for ( int j = 0 ; j < n ; j++ ) {
        double v = 0.0;
        for ( int k = 0 ; k < n ; k++ ) {
          v += m1[i][k]*m2[j][k]; }
        r[i][j] = v; } }
    transposition(m2);
  }

/* Fonction de calcul du produit d'un tableau   */
/* de dimension 2 de taille n.m                 */
/* par un tableau de dimension 2 de taille m.p  */
/* Resultat: Un tableau de dimension 2          */
/* de taille n.p                                */

  static void produitMatricesCarrees(double [][] m1,
                                     double [][] m2,
                                     double [][] r) {
    int n = m1.length;
    for ( int i = 0 ; i < n ; i++ ) {
      for ( int j = 0 ; j < n ; j++ ) {
        double v = 0.0;
        for ( int k = 0 ; k < n ; k++ ) {
          v += m1[i][k]*m2[k][j]; }
        r[i][j] = v; } }
  }
  
  static void testsRapiditeSurMatricesCarrees(int temps,int ni,int nf,int inc) {
    Ecran.formater("%6s%12s%14s%22s%14s","Taille","Appels","Temps total","Temps moyen","o(n*n*n)");
    Ecran.formater("%12s%14s%22s%14s\n","Appels","Temps total","Temps moyen","o(n*n*n)");
    for ( int n = ni ; n <= nf ; n = n+inc ) { 
      double [][] m1 = initRandMatrice(n,n,10.0);
      double [][] m2 = initRandMatrice(n,n,10.0);
      double [][] m = new double[n][n];
      { Utilitaires.Minuteur minuteur = new Utilitaires.Minuteur();
        minuteur.temps = temps*1000;
        minuteur.lancement();
        long i = 0;
        long ti = System.nanoTime();
        do {
          if ( i%2 == 0 )
            produitMatricesCarrees(m1,m2,m);
            else
            produitMatricesCarrees(m2,m1,m);
          i++; }
        while ( minuteur.enCours );
        long t = System.nanoTime()-ti;
        Ecran.formater("%6d%12d%14d%22.3f%14.4f",n,i,t,(double) t/i,((double) t/i)/((double) n*n*n)); }
      { Utilitaires.Minuteur minuteur = new Utilitaires.Minuteur();
        minuteur.temps = temps*1000;
        minuteur.lancement();
        long i = 0;
        long ti = System.nanoTime();
        do {
          if ( i%2 == 0 )
            produitMatricesCarreesAvecTransposition(m1,m2,m);
            else
            produitMatricesCarreesAvecTransposition(m2,m1,m);
          i++; }
        while ( minuteur.enCours );
        long t = System.nanoTime()-ti;
        Ecran.formater("%12d%14d%22.3f%14.4f\n",i,t,(double) t/i,((double) t/i)/((double) n*n*n)); } }
  }
  
  static void testsRapiditeSurMatricesQuelconques(int temps,int ni,int nf,int inc) {
    Ecran.formater("%18s%12s%14s%22s%14s","Tailles","Appels","Temps total","Temps moyen","o(n*m*p)");
    Ecran.formater("%12s%14s%22s%14s\n","Appels","Temps total","Temps moyen","o(n*m*p)");
    for ( int n = ni ; n <= nf ; n = n+inc ) {
      for ( int m = ni ; m <= nf ; m = m+inc ) { 
        for ( int p = ni ; p <= nf ; p = p+inc ) { 
          double [][] m1 = initRandMatrice(n,m,10.0);
          double [][] m2 = initRandMatrice(m,p,10.0);
          double [][] mat = new double[n][p];
          { Utilitaires.Minuteur minuteur = new Utilitaires.Minuteur();
            minuteur.temps = temps*1000;
            minuteur.lancement();
            long i = 0;
            long ti = System.nanoTime();
            do {
              produitMatriceMatrice(m1,m2,mat);
              i++; }
            while ( minuteur.enCours) ;
            long t = System.nanoTime()-ti;
            Ecran.formater("%6d%6d%6d%12d%14d%22.3f%14.4f",n,m,p,i,t,(double) t/i,((double) t/i)/((double) n*m*p)); }
          { Utilitaires.Minuteur minuteur = new Utilitaires.Minuteur();
            minuteur.temps = temps*1000;
            minuteur.lancement();
            long i = 0;
            long ti = System.nanoTime();
            do {
              produitMatriceMatriceAvecTransposition(m1,m2,mat);
              i++; }
            while ( minuteur.enCours) ;
            long t = System.nanoTime()-ti;
            Ecran.formater("%12d%14d%22.3f%14.4f\n",i,t,(double) t/i,((double) t/i)/((double) n*m*p)); }
      } } }
  }

//////////////////////////////////////////////////
  
  static boolean testEgalite(double [][] m1,double [][] m2) {
    int l = m1.length;
    int c = m1[0].length;
    for ( int i = 0 ; i < l ; i++ )
      for ( int j = 0 ; j < c ; j++ )
        if ( m1[i][j] != m2[i][j] )
          return false;
    return true;
  }
  
/* Programme principal                          */

  public static void main(String [] args) {
    { int n = 200;
      double [][] m1 = initRandMatrice(n,n,10.0);
      double [][] m2 = initRandMatrice(n,n,10.0);
      double [][] p1 = new double[n][n];
      double [][] p2 = new double[n][n];
      produitMatricesCarrees(m1,m2,p1);
      produitMatricesCarreesAvecTransposition(m1,m2,p2);
      Ecran.afficherln(testEgalite(p1,p2)); }
    { int n = 200;
      int m = 300;
      int p = 400;
      double [][] m1 = initRandMatrice(n,m,10.0);
      double [][] m2 = initRandMatrice(m,p,10.0);
      double [][] p1 = new double[n][p];
      double [][] p2 = new double[n][p];
      produitMatriceMatrice(m1,m2,p1);
      produitMatriceMatriceAvecTransposition(m1,m2,p2);
      Ecran.afficherln(testEgalite(p1,p2)); }
    Ecran.afficher("Nombre de secondes de test? ");
    int temps = Clavier.saisirInt();
    Ecran.afficher("Taille initiale? ");
    int ni = Clavier.saisirInt();
    Ecran.afficher("Taille finale  ? ");
    int nf = Clavier.saisirInt();
    Ecran.afficher("Increment      ? ");
    int inc = Clavier.saisirInt();
//    testsRapiditeSurMatricesCarrees(temps,ni,nf,inc);
    testsRapiditeSurMatricesQuelconques(temps,ni,nf,inc);
  }
}