#!/usr/bin/perl

# [c] piotao, 20070112
# Program wykonujacy mnozenie macierzy
# Uruchomienie:
# pomnoz.pl plik-danych.txt
# - plik danych musi zawierac dwa bloki liczb, w N kolumnach i M wierszach
#   pomiedzy macierzami musi byc przynajmmiej jeden pusty wiersz

use strict;
use warnings;

# deklarujemy dane
my $N;   # wymiar poziomy macierzy
my $M;   # wymiar pionowy macierzy

# macierze:
my @A = my @B = my @C = ();    # wszystkie na razie sa pustymi tablicami

# okreslenie argumentow programu
# i zdecydowanie, czy dane beda podane z klawiatury interaktywnie, wczytane
# z pliku danych lub wczytane nieinteraktywnie ze standardowego wejscia

if( defined $ARGV[0] and -f $ARGV[0] ){  # mamy jeden argument i jest to plik
  #otwieramy i wczytujemy plik
  #zakladamy ze macierze sa wpisane w kolumnach i wierszach tak jak wyglada ich normalna
  #reprezentacja, a nastepna macierz jest oddzielona od poprzedniej jedna pusta linia, np:
  # 1 2 3 4 5
  # 2 3 4 5 6   <---n--->
  # 3 4 5 6 7
  #
  # 1 2 3  ^
  # 2 3 4  |
  # 4 3 4  n
  # 4 3 2  |
  # 2 3 4  v
  #
  my $macierz = 'a';               # pierwsza macierz, do ktorej czytamy, to A
  my $file;                        # deklarujemy lokalna zmienna plikowa
  if( open $file,'<',$ARGV[0] ){   # jezeli udalo sie otworzyc plik, to mamy go w uchwycie $file

    while( <$file> ){              # wczytujemy w petli pojedyncze wiersze (do zmiennej $_)
      chomp;                       # usuwamy z kazdego kolejnego wiersza znak nowej linii

      if($_ eq ''){                # jezeli wczytalismy pusta linie
        $macierz = 'b';            # zmieniamy macierz, do ktorej wczytujemy dane
        next;                      # i od razu zaczynamy nastepna iteracje petli
      }

      my @wiersz = split;          # dzielimy wczytany wiersz na osobne liczby i wpisujemy je do tablicy

      if($macierz eq 'a'){         # jezeli wczytywanie odbywa sie do macierzy A, to
        push @A,[@wiersz];         # zapamietujemy wiersz w macierzy A
      }
      else{                        # a w przeciwnym wypadku
        push @B,[@wiersz];         # zapamietujemy wczytany wiersz w macierzy B
      }
    }
  }
}
else{ # nie podano zadnego pliku lub plik nie istnieje.
  die "Nie podano pliku z danymi macierzy do przemnozenia. Uruchom program z nazwa pliku jako argumentem.\n";
}

# po wczytaniu macierzy, okreslamy ich rozmiar na podstawie macierzy A
$N = scalar @{$A[0]};   # dlugosc pierwszego wiersza macierzy to wymiar poziomy
$M = scalar @A;         # dlugosc tabeli @A to wymiar pionowy macierzy

# sprawdzamy rozmiary macierzy B
my $Mb = scalar @{$B[0]};  # wymiar poziomy macierzy B (musi byc rowny pionowemu A)
my $Nb = scalar @B;        # wymiar pionowy macierzy B (musi byc rowny poziomemu B)

unless($Mb == $M and $Nb == $N){
  # gdy rozmiary macierzy nie pasuja do siebie, napisz to i zdechnij
  die "Nie pasuja do siebie wymiary macierzy A i B! A=[$M,$N], B=[$Mb,$Nb]\n";
}

# teraz, gdy dane mamy wczytane, mozemy smialo mnozyc macierze!
for(my $n=0; $n<$N; $n++){                    # po wierszach @A i kolumnach @B
  for(my $m=0; $m<$M; $m++){                  # po kolumnach @A i wierszach @B
    for(my $k=0; $k<$N; $k++){                # petla sumujaca wyniki mnozen
      $C[$n][$m] += $A[$n][$k] * $B[$k][$m];
    }
  }
}

# piszemy teraz macierz A:
map{ map{ printf "%4i",$_ } @{$_};print "\n" } @A;
print "* \n";

#piszemy macierz B:
map{ map{ printf "%4i",$_ } @{$_};print "\n" } @B;
print "= \n";

# a teraz wypisywanie macierzy wynikowej:

# ale najpierw sprawdzamy, jaka jest najwieksza dlugosc liczby
# w calej macierzy, aby ladnie dobrac szerokosc kolumn
my $len = 0;
my $max = $len;
for(my $m=0;$m<$N;$m++){
  for(my $n=0;$n<$M;$n++){
    $len = length($C[$m][$n]);
    if($max < $len){ $max = $len }
  }
}
## sposob bardziej perlowy moze byc np. taki (sprawdz, to dziala)
#for(map{map{ $_ } @{$_} } @C){
# $max = ( ($len = length $_) > $max) ? $len : $max;
#}

# majac dlugosc najdluzszego elementu macierzy C, ustalamy format
# wypisywanych liczb:
$max++;
my $format = "\%${max}i";

# (ten sposob pisania wykorzystuje dwie zaglebione petle for
# i moze niektorym z Was wydawac sie prostszy, ale raczej
# nie jest prostszy... :) prostsze jest wykorzystanie MAP)
for(my $m=0;$m<$N;$m++){
  for(my $n=0;$n<$M;$n++){
    printf $format,$C[$m][$n];
  }
  print "\n";
}