#!/usr/bin/perl

# $nwd = euklides( $a, $b ) oblicza NWD dla $a i $b.
sub euklides {
    use integer;
    my ( $a, $b ) = @_;

    while ( $b ) {
        my $r = $a % $b;
        $r += $b if $r < 0;

        # MIDPOINT

        $a = $b;
        $b = $r;
    }

    return $a;
}

sub nwd {
    use integer;
    my $nwd = shift || 1;
    while (@_) {
        my $kolejny = shift;
        # ($nwd, $kolejny) = ($kolejny, $nwd % $kolejny) while $kolejny;
        while( $kolejny ) {
            my $r = $nwd % $kolejny;
            $r += $kolejny if $r < 0;   # poprawka dla % w trybie integer 
            $nwd = $kolejny;
            $kolejny = $r;
        }
    }
    return $nwd;
}

# ( $nwd, $dzielnika, $dzielnikb ) = nwd_liniowy( $a, $b )
# Oblicza najwiekszy wspolny dzielnik $a i $b,
# a takze $dzielnika i $dzielnikb takie ze
#           $nwd == $a * $dzielnika + $b * $dzielnikb
sub nwd_liniowy {
    use integer;

    my ( $a, $b ) = @_;

    # Jesli jedna ze zmiennych jest zerem, to druga jest NWD.
    return ( $a, 1, 0 ) unless $b;
    return ( $b, 0, 1 ) unless $a;

    my ( $x1, $x2, $y1, $y2 ) = ( 0, 1, 1, 0 );

    # Jesli oryginalne wartosci $a i $b zostana nazwane A i B, 
    # to nastepujace relacje zostana zachowane dla kazdej iteracji:
    #                               $a == A * $x2 + B * $y2
    #                               $b == A * $x1 + B * $y1

    while ( 1 ) {
        # Nalezy obliczyc iloraz i reszte.
        my ( $q, $r );
        $r = $a % $b;
        # int % moze spowodowac bledy; nalezy to naprawic.
        $r += $b if $r < 0;
        $q = ($a - $r)/$b;

        # Jesli reszta ma wartosc zero, to $b zawiera NWD.
        # Zgodnie z wczesniej przedstawionymi relacjami:
        # $b == A * $x1 + B * $y1.
        return ( $b, $x1, $y1 ) unless $r;

        # Jesli reszta nie ma jeszcze wartosci zero, to nalezy
        # wykonac kolejna iteracje z zachowaniem relacji.

        ($a, $b)   = ($b, $r);
        ($x1, $x2) = ($x2 - $q*$x1, $x1);
        ($y1, $y2) = ($y2 - $q*$y1, $y1);
    }
}

print euklides(27, 39), "\n";	# wyswietla 3
print nwd(60, 480, 105), "\n";	# wyswietla 15

($nwd, $dzielnika, $dzielnikb) = nwd_liniowy(60, 480, 105);
print "nwd = $nwd, dzielnika = $dzielnika, dzielnikb = $dzielnikb.\n";



