#!/usr/bin/perl

use constant pi => 4 * atan2(1, 1);
use constant odwrotnosc_pi => 0.25 / atan2(1, 1);
use constant odwrotnosc_pierwiastka_dwa_pi => 1 / sqrt(8 * atan2(1, 1));
use constant pierwiastek_dwapi => sqrt(8 * atan2(1, 1));


# rozklad_bernoulliego($x, $p) zwraca 1-$p, jesli $x wynosi 0, $p jesli $x wynosi 1, 
# lub 0 w innych przypadkach..
#
sub rozklad_bernoulliego {
    my ($x, $p) = @_;
    return unless $p > 0 && $p < 1;
    return $x ? ( ($x == 1) ? $p : 0 ) : (1 - $p);
}
sub rozklad_bernoulliego_oczekiwana { $_[0] }
sub rozklad_bernoulliego_wariancja { $_[0] * (1 - $_[0]) }


# rozklad_beta( $x, $a, $b ) zwraca rozklad beta dla podanych parametrow $a i $b.
#
sub rozklad_beta {
    my ($x, $a, $b) = @_;
    return unless $a > 0 and $b > 0;
    return silnia ($a + $b - 1) / silnia ($a - 1) /
        silnia ($b - 1) * ($x ** ($a - 1)) * ((1 - $x) ** ($b - 1));
}

sub rozklad_beta_oczekiwana { $_[0] / ($_[0] + $_[1]) }
sub rozklad_beta_wariancja { ($_[0] * $_[1]) / (($_[0] + $_[1]) ** 2) /
                        ($_[0] + $_[1] + 1) }


# rozklad_dwumianowy($x, $n, $p);
# rozklad_dwumianowy_oczekiwana($n, $p);
#
sub rozklad_dwumianowy {
    my ($x, $n, $p) = @_;
    return unless $x >= 0 && $x == int $x && $n > 0 &&
        $n == int $n && $p > 0 && $p < 1;
    return silnia($n) / silnia($x) / silnia($n - $x) *
        ($p ** $x) * ((1 - $p) ** ($n - $x));
}

sub rozklad_dwumianowy { $_[0] * $_[1] }
sub rozklad_dwumianowy { $_[0] * $_[1] * (1 - $_[1]) }

sub rozklad_cauchy {
    my ($x, $a, $b) = @_;
    return unless $a > 0;
    return odwrotnosc_pi * $a / (($a ** 2) + (($x - $b) ** 2));
}
sub rozklad_cauchy_oczekiwana { $_[1] }

sub chi_kwadrat {
    my ($x, $n) = @_;
    return 0 unless $x > 0;
    return 1 / silnia($n/2 - 1) * (2 ** (-$n / 2)) *
        ($x ** (($n / 2) - 1)) * exp(-$x / 2);
}
sub chi_kwadrat_oczekiwana { $_[0] }
sub chi_kwadrat_wariancja { 2 * $_[0] }

sub rozklad_erlanga {
    my ($x, $a, $n) = @_;
    return unless $a > 0 && $n > 0 && $n == int($n);
    return 0 unless $x > 0;
    return ($a ** $n) * ($x ** ($n-1)) * exp(-$a * $x) / silnia($n-1);
}

sub rozklad_erlanga_oczekiwana { $_[1] / $_[0] }
sub rozklad_erlanga_wariancja { $_[1] / ($_[0] ** 2) }

sub rozklad_wykladniczy {
    my ($x, $a) = @_;
    return unless $a > 0;
    return 0 unless $x > 0;
    return $a * exp(-$a * $x);
}
sub rozklad_wykladniczy_oczekiwana { 1 / $_[0] }
sub rozklad_wykladniczy_wariancja { 1 / ($_[0] ** 2) }

sub rozklad_gamma {
    my ($x, $a, $b) = @_;
    return unless $a > -1 && $b > 0;
    return 0 unless $x > 0;
    return ($x ** $a) * exp(-$x / $b) / silnia($a) / ($b ** ($a + 1));
}

sub rozklad_gamma_oczekiwana { ($_[0] + 1) * $_[1] }
sub rozklad_gamma_wariancja { ($_[0] + 1) * ($_[1] ** 2) }

sub rozklad_gaussa {
    my ($x, $srednia, $wariancja) = @_;
    return odwrotnosc_pierwiastka_dwa_pi *

        exp( -( ($x - $srednia) ** 2 ) / (2 * $wariancja) ) /
            sqrt $wariancja;
}

sub rozklad_geometryczny {
    my ($x, $p) = @_;
    return unless $p > 0 && $p < 1;
    return 0 unless $x == int($x);
    return $p * ((1 - $p) ** ($x - 1));
}

sub rozklad_geometryczny_oczekiwana { 1 / $_[0] }
sub rozklad_geometryczny_wariancja { (1 - $_[0]) / ($_[0] ** 2) }

sub rozklad_hipergeometryczny {
    my ($x, $k, $m, $n) = @_;
    return unless $m > 0 && $m == int($m) && $n > 0 && $n == int($n) &&
        $k > 0 && $k <= $m + $n;
    return 0 unless $x <= $k && $x == int($x);
    return wybierz($m, $x) * wybierz($n, $k - $x) / wybierz($m + $n, $k);
}

sub rozklad_hipergeometryczny_oczekiwana { $_[0] * $_[1] / ($_[1] + $_[2]) }
sub rozklad_hipergeometryczny_wariancja {
    my ($k, $m, $n) = @_;
    return $m * $n * $k * ($m + $n - $k) / (($m + $n) ** 2) /
        ($m + $n - 1);
}


# rozklad_laplace($x, $a, $b)
sub rozklad_laplace {
    return unless $_[1] > 0;
    return $_[1] / 2 * exp( -$_[1] * abs($_[0] - $_[2]) );
}

sub rozklad_laplace_oczekiwana { $_[1] }
sub rozklad_laplace_wariancja { 2 / ($_[0] ** 2) }

sub rozklad_logarytmiczny {
    my ($x, $a, $b, $std) = @_;
    return unless $std > 0;
    return 0 unless $x > $a;
    return (exp -(((log($x - $a) - $b) ** 2) / (2 * ($std ** 2)))) /
        (pierwiastek_dwapi * $std * ($x - $a));
}

sub rozklad_logarytmiczny_oczekiwana { $_[0] + exp($_[1] + 0.5 * ($_[2] ** 2)) }
sub rozklad_logarytmiczny_wariancja { exp(2 * $_[1] + ($_[2] ** 2)) * (exp($_[2] ** 2)
                         - 1) }

sub rozklad_maxwella {
    my ($x, $a) = @_;
    return unless $a > 0;
    return 0 unless $x > 0;
    return sqrt(2 / pi) * ($a ** 3) * ($x ** 2) *
        exp($a * $a * $x * $x / -2);
}

sub rozklad_maxwella_oczekiwana { sqrt( 8/pi ) / $_[0] }
sub rozklad_maxwella_wariancja { (3 - 8/pi) / ($_[0] ** 2) }

sub rozklad_pascala {
    my ($x, $n, $p) = @_;
    return unless $p > 0 && $p < 1 && $n > 0 && $n == int($n);
    return 0 unless $x >= $n && $x == int($x);
    return wybierz($x - 1, $n - 1) * ($p ** $n) * ((1 - $p) ** ($x - $n));
}

sub rozklad_pascala_oczekiwana { $_[0] / $_[1] }
sub rozklad_pascala_wariancja { $_[0] * (1 - $_[1]) / ($_[1] ** 2) }

sub rozklad_poissona {
    my ($x, $a) = @_;
    return unless $a >= 0 && $x >= 0 && $x == int($x);
    return ($a ** $x) * exp(-$a) / silnia($x);
}

sub rozklad_poissona_oczekiwana { $_[0] }
sub rozklad_poissona_wariancja { $_[0] }

sub rozklad_rayleigha {
    my ($x, $a) = @_;
    return unless $a > 0;
    return 0 unless $x > 0;
    return ($a ** 2) * $x * exp( -($a ** 2) * ($x ** 2) / 2 );
}

sub rozklad_rayleigha_oczekiwana { sqrt(pi / 2) / $_[0] }
sub rozklad_rayleigha_wariancja { (2 - pi / 2) / ($_[0] ** 2) }

sub rozklad_rownomierny {
    my ($x, $a, $b) = @_;
    return unless $b > $a;
    return 0 unless $x > $a && $x < $b;
    return 1 / ($b - $a);
}

sub rozklad_rownomierny_oczekiwana { ($_[0] + $_[1]) / 2 }
sub rozklad_rownomierny_wariancja { (($_[1] - $_[0]) ** 2) / 12 }

