use strict;
use warnings;
no warnings 'void';

use Benchmark qw/cmpthese/;
use Math::Random::ISAAC;
use Math::Random::ISAAC::XS;
use Math::Random::MT;
use Math::Random::MT::Auto;
use Math::Random::Xorshift;
use Math::Random::MTwist;
use Math::Random::PCG32;
use Math::Prime::Util::GMP;
use Math::Prime::Util;
use Bytes::Random::Secure;
use Bytes::Random::Secure::Tiny;
use Crypt::PRNG;

my $trials = shift || -1;

# There is no real point in seeding here.
my $time = time;
srand($time);
Math::Random::Xorshift::srand($time);
Math::Prime::Util::srand($time);
my $isaac = Math::Random::ISAAC::XS->new($time);
#my $mt = Math::Random::MT->new($time);
my $xor = Math::Random::Xorshift->new($time);
my $brs = Bytes::Random::Secure->new(NonBlocking=>1);
my $brst = Bytes::Random::Secure::Tiny->new(NonBlocking=>1);
my $pcg = Math::Random::PCG32->new(42,54);

print "\n     32-bit\n\n";
cmpthese($trials, {
  'CORE::rand' => sub { int(CORE::rand(4294967295)) for 1..1000; },
  #'MRMT::irand' => sub { $mt->irand for 1..1000; },
  'MR ISAAC irand' => sub { $isaac->irand for 1..1000; },
  'Xorshift irand' => sub { Math::Random::Xorshift::irand for 1..1000; },
#  'M::R::Xorshift#irand' => sub { $xor->irand for 1..1000; },
  'BRS irand' => sub { $brs->irand for 1..1000; },
  'BRST irand' => sub { $brst->irand for 1..1000; },
  'CryptPRNG irand' => sub { Crypt::PRNG::irand for 1..1000; },

  'MRMTA irand' => sub { (0xFFFFFFFF & Math::Random::MT::Auto::irand) for 1..1000; },
  'MTwist irand32' => sub { Math::Random::MTwist::_irand32 for 1..1000; },
  #'PCG32 irand' => sub { $pcg->irand for 1..1000; },
  'PCG32 irand' => sub { Math::Random::PCG32::irand($pcg) for 1..1000; },
  'MPUGMP irand' => sub { Math::Prime::Util::GMP::irand for 1..1000; },
  'MPU irand' => sub { Math::Prime::Util::irand for 1..1000; },
  #'MPU irand32' => sub { Math::Prime::Util::irand32 for 1..1000; },
}) if 1;

print "\n     64-bit\n\n";
cmpthese($trials, {
  # These have only 32-bit irand, nothing else.
  #'CORE::rand' => sub { int(CORE::rand(4294967295)) for 1..1000; },
  #'MRMT irand 32x2' => sub { (($mt->irand <<32)|$mt->irand) for 1..1000; },
  'MR ISAAC 32x2' => sub { (($isaac->irand <<32)|$isaac->irand) for 1..1000; },
  #'MRXorshift irand' => sub { $xor->irand for 1..1000; },
  'Xorshift 32x2' => sub { ((Math::Random::Xorshift::irand <<32)|Math::Random::Xorshift::irand) for 1..1000; },

  # These don't have 64-bit irand functions, but have random bytes.
  # Select the fastest of the two options.
  #'BRS prb' => sub { unpack("Q",$brs->bytes(8)) for 1..1000; },
  'BRS 32x2' => sub { (($brs->irand << 32)|$brs->irand) for 1..1000; },
  #'BRST prb' => sub { unpack("Q",$brst->bytes(8)) for 1..1000; },
  'BRST 32x2' => sub { (($brst->irand << 32)|$brst->irand) for 1..1000; },
  'CryptPRNG prb' => sub { unpack("Q",Crypt::PRNG::random_bytes(8)) for 1..1000; },
  #'CryptPRNG 32x2' => sub { ((Crypt::PRNG::irand << 32)|Crypt::PRNG::irand) for 1..1000; },

  # These have 64-bit irand functions
  #'PCG32 irand64' => sub { $pcg->irand64 for 1..1000; },
  'PCG32 irand64' => sub { Math::Random::PCG32::irand64($pcg) for 1..1000; },
  'MRMTA irand' => sub { Math::Random::MT::Auto::irand for 1..1000; },
  'MTwist irand64' => sub { Math::Random::MTwist::_irand64 for 1..1000; },
  'MPUGMP irand64' => sub { Math::Prime::Util::GMP::irand64 for 1..1000; },
  'MPU irand64' => sub { Math::Prime::Util::irand64 for 1..1000; },
  #'MPU prb' => sub { unpack("Q",Math::Prime::Util::random_bytes(8)) for 1..1000; },
  #'MPU 32x2' => sub { ((Math::Prime::Util::irand << 32)|Math::Prime::Util::irand) for 1..1000; },
}) if 1;
