#!/usr/bin/env perl
use strict;
use warnings;
use Test::More;
use Math::BigInt try=>"GMP,Pari";
use Math::Prime::Util::GMP qw/urandomb urandomr urandomm
seed_csprng random_bytes
todigits/;
my $use64 = (~0 > 4294967295);
my $extra = defined $ENV{EXTENDED_TESTING} && $ENV{EXTENDED_TESTING};
my $maxbits = $use64 ? 64 : 32;
my @nbit_check = (0..5,8,20,31,32,33,40);
my %nbit_range = map { $_ => 1 } grep { $_ <= 8 } @nbit_check;
my @large_nbit = (64,128,255,256,257,512,1024,2048,4096,8192,73100);
my $nsamples = $extra ? 30000 : 1000;
my $rsamples = $extra ? 10000 : 200;
plan tests => 0
+ scalar(@nbit_check)
+ scalar(@large_nbit)
+ scalar(keys %nbit_range)
+ 4
+ 5
+ 4
+ 3;
########
check_nbit_range($_) for @nbit_check;
########
for my $b (@large_nbit) {
my @bits = todigits(urandomb($b),2);
ok( scalar(@bits) <= $b, "Random $b-bit in range" );
}
########
check_range(100,110);
check_range(128,255);
check_range(2**24, 2**25-1);
check_range(Math::BigInt->new(10)**24, Math::BigInt->new(10)**25-1);
########
ok(!eval { urandomr(-10,100); }, "urandomr(-10,x)");
ok(!eval { urandomr(100,-10); }, "urandomr(x,-10)");
ok(!eval { urandomr(-1,-1); }, "urandomr(-1,-1)");
is(urandomr(123456,123456), 123456, "urandomr(x,x)=x");
is(urandomr(123457,123456), undef, "urandomr(x,y)=undef if x > y");
########
ok(!eval { urandomm(-1); }, "urandomm(-1)");
is(urandomm(0), 0, "urandomm(0)=0");
is(urandomm(1), 0, "urandomm(1)=0");
check_range_m(1234567);
########
seed_csprng(55,"BLAKEGrostlJHKeccakSkein--RijndaelSerpentTwofishRC6MARS");
is(unpack("h*",random_bytes( 4)),"538e1f65","random_bytes(4)");
is(unpack("h*",random_bytes(11)),"cbbac4ba12e6aa77bcfe6f","random_bytes(11)");
is(unpack("h*",random_bytes( 0)),"","random_bytes(0)");
########
sub check_nbit_range {
my $b = shift;
my $over = ($b < $maxbits) ? (1 << $b) : (Math::BigInt->new(1) << $b);
if (!$nbit_range{$b}) {
my @s = map { urandomb($b) } 1 .. $nsamples;
is(scalar(grep { $_ >= $over } @s), 0, "urandomb($b) values are in range");
} else {
# For $b=8 and a uniform random generator, the probability of a given
# 8-bit value not being selected is 1-1/256 = 0.99609375. After 1000
# tries, this is 0.01996 or about 2%. After 1000+2000 tries it's ~8e-6.
# The proper calculation is more tedious but for $b=8 and $nsamples=1000
# we will find it likely we need a second pass but no more.
my %t;
for my $try (1..10) {
$t{ urandomb($b) }++ for 1 .. $try * $nsamples;
last if scalar(keys %t) >= $over;
}
my @keys = keys %t;
is(scalar(grep { $_ >= $over } @keys), 0, "urandomb($b) values are in range");
is(scalar(@keys), $over, "urandomb($b) produces all values in range");
}
}
sub check_range {
my($lo,$hi) = @_;
my @s = map { urandomr($lo,$hi) } 1 .. $rsamples;
@s = map { ref($hi)->new("$_") } @s if ref($hi);
is( scalar(grep { $_ < $lo || $_ > $hi } @s), 0, "urandomr($lo,$hi) values are in range" );
}
sub check_range_m {
my($hi) = @_;
my @s = map { urandomm($hi) } 1 .. $rsamples;
@s = map { ref($hi)->new("$_") } @s if ref($hi);
is( scalar(grep { $_ > $hi } @s), 0, "urandomm($hi) values are in range" );
}