#!/usr/bin/perl -l

use constant WYCINEK => 10000;
use constant DUZYWYCINEK => WYCINEK * WYCINEK;

# $wynik = losuj_bigint( $dolny_zakres, $gorny_zakres )
# $wynik = losuj_bigint( $gorny_zakres )
# $wynik = losuj_bigint
#     Zwraca liczbe calkowita typu BigInt w zakresie od $dolny_zakres do $gorny_zakres.
#     Zwraca liczbe calkowita typu BigInt w zakresie od 1 do $gorny_zakres.
#     Zwraca liczbe calkowita typu BigInt w zakresie od 0 do 1.
sub losuj_bigint {
    use integer;
    my ( $dolny_zakres, $gorny_zakres ) = @_;

    # Domyslne ustawienia.
    $dolny_zakres = 0 unless defined $dolny_zakres;
    $gorny_zakres = 1 unless defined $gorny_zakres;

    # Sprawdzenie poprawnosci zakresow.
    ($dolny_zakres,$gorny_zakres) = ($gorny_zakres,$dolny_zakres) if $dolny_zakres > $gorny_zakres;

    while ( 1 ) {
        # Niektore wersje funkcji zapewniaja tylko 15 bitow precyzji.
        # Oznacza to, iz losowanie duzej liczby nigdy nie bedzie sprawiedliwe,
        # poniewaz tylko 2**15 liczb moze byc wybranych.
        #
        # Jesli zakres jest wystarczajaco maly, to wystarczy wybrac losowy element.
        # Zakres WYCINEK (10000) jest mniejszy od 2**15, co pozwala na losowy
        # wybor liczby, ktora bedzie wystarczajaco duza.
        my $roznica = $gorny_zakres - $dolny_zakres + 1;
        return $dolny_zakres + int ( rand $roznica ) if $roznica < WYCINEK;

        # W przeciwnym wypadku nalezy podzielic zakres na
        # podzakresy WYCINEK i wybrac jeden z nich. Kolejnym krokiem jest 
        # powtorzenie petli w celu wybrania jednego z podzakresow.
        #
        # Jesli zakres ma mniej niz DUZYWYCINEK (100000000) elementow,
        # to nalezy uwzglednic wplyw roznicy w wielkosci podzakresow.
        # Jesli zakres jest wiekszy od DUZYWYCINEK, to korekta nie jest
        # wymagana.

        # Uwaga: polecenie "use integer" na poczatku kodu funkcji 
        # ma specjalne przeznaczenie, gdyz zapewnia skrocenie wynikow
        # dzielenia do liczb calkowitych niezaleznie od tego, czy sa to
        # normalne liczby Perla (skracane dzieki uzyciu "use integer"
        # czy tez liczby BigInt (skracane, poniewaz dzielenie liczb tego typu 
        # zawsze daje w wyniku liczby calkowite). Uzycie funkcji int() mogloby
        # spowodowac problemy z liczbami BigInt.

        my $interwal = int( rand( WYCINEK ) );
        my $dolna_wart = $zakres_dolny + $roznica * $interwal / WYCINEK;
        my $gorna_wart = $zakres_dolny + $roznica * ($interwal+1) / WYCINEK - 1;
        my $maks_wart = ($roznica + WYCINEK - 1) / WYCINEK;

        # Zaakceptowanie nowych granic, jesli dowolny z ponizszych warunkow
        # jest spelniony:
        #       (A) zakres jest wystarczajaco duzy, aby korekta nie byla potrzebna
        #       (B) podzakres ma pelna wielkosc
        #       (C) podzakres ma mala wielkosc
        #           ale "dodatkowa" liczba w tym podzakresie nie zostala wybrana.
        ($dolny_zakres, $gorny_zakres) = ( $dolna_wart, $gorna_wart )
             if $roznica > DUZYWYCINEK
                || ($gorna_wart - $dolna_wart + 1) == $maks_wart
                || rand( $maks_wart ) >= 1.0;
    }
}

print losuj_bigint(1000);

use Math::BigInt;
$x = new Math::BigInt("999999999999999999999999999999999");
print losuj_bigint($x), "\n", losuj_bigint($x, 2 * $x);
