{ File: determin.pas }

{ Scopo: del calcolo del determinante di una matrice quadrata
         di dimensione massima fissata usando una procedura ricorsiva.

  ATTENZIONE:
    il metodo ricorsivo proposto per calcolare il determinante di una
    matrice quadrata (che si basa direttamente sulla definizione del calcolo
    di determinante utilizzando i minori della matrice)
    e` MOLTO INEFFICIENTE e non applicabile per matrici di grosse dimensioni.
    Viene proposto solo A SCOPO DIDATTICO.
}

program CalcolaDeterminante;
{ Legge una matrice quadrata di dimensione massima fissata da un file di testo,
  ne calcola il determinante utilizzando una procedura ricorsiva e
  lo stampa su video.
}

const
  NMAX = 4; { massima dimensione della matrice }

type
  TipoArrayMatrice = array [1..NMAX, 1..NMAX] of integer;
  TipoMatrice      = record
                       dim : 0..NMAX;
                       mat : TipoArrayMatrice;
                     end;



  function Determinante (matrice: TipoMatrice): integer;
  { Calcolo del determinante utilizzando una procedura ricorsiva. }
  
    procedure Minore (j               : integer;
                      matrice_in      : TipoMatrice;
                      var matrice_out : TipoMatrice);
    { Restituisce in matrice_out il j-esimo minore di matrice_in, cioe`
      il minore relativo all'elemento di riga 1, colonna j }
    var
      riga, colonna_in, colonna_out : integer; { necessari per i cicli }

    begin { Minore }
      { il minore ha dimensione inferiore di 1 rispetto alla matrice
        di input }
      matrice_out.dim := matrice_in.dim - 1;

      for riga := 2 to matrice_in.dim do
        { il ciclo esterno si occupa delle righe del minore;
          la prima riga della matrice di input va saltata }
      begin
        colonna_out := 1;
        for colonna_in := 1 to matrice_in.dim do
          { il ciclo interno si occupa delle colonne del minore }
        begin
          if colonna_in <> j then
            { occorre saltare la colonna j-esima di matrice_in }
          begin
            matrice_out.mat[riga-1,colonna_out]:= matrice_in.mat[riga,colonna_in];
            colonna_out := colonna_out + 1
          end
        end { for relativo alle colonne }
      end { for relativo alle righe }
    end; { Minore }
  
    function Coefficiente (j: integer): integer;
    { restituisce 1 se j e' dispari; restituisce -1 se j e' pari }
    begin { Coefficiente }
      if odd(j) then
        Coefficiente := 1
      else
        Coefficiente := -1
    end;  { Coefficiente }

  { variabili utilizzate nel corpo di Determinante }
  var
    somma, i    : integer;
    matrice_aux : TipoMatrice; { matrice ausiliaria in cui viene copiato il
                                 minore corrente }

  begin { Determinante }
    if matrice.dim = 1 then
      Determinante := matrice.mat[1,1]
    else if matrice.dim = 2 then
      Determinante := matrice.mat[1,1] * matrice.mat[2,2] -
                      matrice.mat[1,2] * matrice.mat[2,1]
    else
    begin
      somma := 0;
      for i := 1 to matrice.dim do
      begin
        Minore(i, matrice, matrice_aux);
        somma := somma +
                 Coefficiente(i) * matrice.mat[1,i] * Determinante(matrice_aux)
      end;
      Determinante := somma
    end
  end;  { Determinante }


  procedure LeggiMatrice (var matrice: TipoMatrice);
  { Legge una matrice da file. }
  var
    nome : string;    { nome del file dal quale leggere la matrice }
    f    : text;      { file logico }
    i, j : integer;

  begin { LeggiMatrice }
    write('Nome del file dal quale leggere la matrice ? ');
    readln(nome);
    assign(f, nome);
    reset(f);
    read(f, matrice.dim);  { la prima riga del file contiene la dimensione
                             della matrice }
    if matrice.dim > NMAX then
    begin
      writeln('Dimensione matrice troppo grande');
      matrice.dim := 0
    end
    else
      for i := 1 to matrice.dim do
        for j := 1 to matrice.dim do
          read(f, matrice.mat[i,j]);
    close(f)
  end; { LeggiMatrice }


  procedure StampaMatrice (matrice: TipoMatrice);
  { Stampa una matrice su video. }
  var
    i, j : integer;
  begin { StampaMatrice }
    for i := 1 to matrice.dim do
    begin
      for j := 1 to matrice.dim do
        write(matrice.mat[i,j]:7);
      writeln
    end;
    writeln
  end; { StampaMatrice }


{ variabili utilizzate nel corpo del programma principale }
var
  m : TipoMatrice;

begin { CalcolaDeterminante }
  LeggiMatrice(m);                     { lettura della matrice }
  writeln('Ho letto la matrice:');
  StampaMatrice(m);                    { stampa di verifica su video }
  writeln('Il suo determinante vale: ', Determinante(m));
end. { CalcolaDeterminante }