#!/usr/bin/perl

# jakobian($tablica_funkcji, $punkt) 
# Oblicza macierz jakobinu w punkcje $punkt dla tablicy funkcji
# wskazywanej przez $tablica_funkcji.
# $punkt to odwolanie do tablicy wspolrzednych.
#
sub jakobian {
    my ($tablica_funkcji, $punkt) = @_;
    my ($delta, $i, $j, $k, $wspolrzedne, @wartosci, @funkcja, @jakobian);
    my $epsilon = 1e-8;

    # Przekazanie punktu do kazdej funkcji.
    #
    for ($i = 0; $i < @$tablica_funkcji; $i++) {
        $wartosci[$i] = &{$tablica_funkcji->[$i]}( @$punkt );
    }

    for ($i = 0; $i < @$punkt; $i++) {
        $wspolrzedne = $punkt->[$i];
        $delta = $epsilon * abs($wspolrzedne) || $epsilon;
        $punkt->[$i] = $delta + $wspolrzedne;
        $delta = $punkt->[$i] - $wspolrzedne;
        for ($k = 0; $k < @$tablica_funkcji; $k++) {
            $funkcja[$k] = &{$tablica_funkcji->[$k]}( @$punkt );
        }
        $punkt->[$i] = $wspolrzedne;
        for ($j = 0; $j < @$tablica_funkcji; $j++) {
            $jakobian[$j][$i] = ($funkcja[$j] - $wartosci[$j]) / $delta;
        }
    }
    return @jakobian;
}

sub f {
    my ($x, $y, $z) = @_;
    return 3*$x + 2*$x*$y + 4*$y*$z;
} 
sub g {
    my ($x, $y, $z) = @_;
    return unless $x;
    return 4/$x + log($x*$y) + $z**$x;
}

sub h {
    my ($x, $y, $z) = @_;
    return $x * $y * $z;
}

@jakobian = jakobian( [\&f, \&g, \&h], [3, 4, -2] );
foreach $wiersz (@jakobian) {
    for ($kolumna = 0; $kolumna < @$wiersz; $kolumna++) {
        print $wiersz->[$kolumna], " ";
    }
    print "\n";
}