#!/usr/bin/env perl
use strict;
use warnings;
use v5.16;
use Math::Int64 qw(uint64 uint64_to_number);
use Getopt::Long;
use Test::More;
my $debug = 0;
my $s1 = 15939250660798104135;
my $s2 = 3988331200502121509;
my $seeds = [];
GetOptions(
'debug' => \$debug,
'seed1=i' => \$s1,
'seed2=i' => \$s2,
'random' => \&randomize_seeds,
'unit-tests' => \&run_unit_tests,
);
my $num = $ARGV[0] || 8;
my ($seed1, $seed2);
print color('yellow', "Seeding PRNG with: $s1 / $s2\n\n");
$seeds = [$s1, $s2];
my @y = ();
for (my $i = 1; $i <= $num; $i++) {
my $num32 = pcg32_perl($seeds);
my $num64 = pcg64_perl($seeds);
printf("%2d) %10u / %20u\n", $i, $num32, $num64);
}
sub pcg32_perl {
my ($s) = @_;
my $oldstate = $s->[0];
use integer;
$s->[0] = $oldstate * 6364136223846793005 + ($s->[1] | 1);
$s->[0] = iv_2_uv($s->[0]);
no integer;
my $xorshifted = (($oldstate >> 18) ^ $oldstate) >> 27;
$xorshifted = $xorshifted & 0xFFFFFFFF;
my $rot = ($oldstate >> 59);
my $invrot = 4294967296 - $rot;
my $ret = ($xorshifted >> $rot) | ($xorshifted << ($invrot & 31));
$ret = $ret & 0xFFFFFFFF;
if ($debug) {
print color('orange', "State : $oldstate/$s->[1]\n");
print color('orange', "State2: $s->[0]\n");
print color('orange', "Xor : $xorshifted\n");
print color('orange', "Rot : $rot\n");
}
return $ret;
}
sub iv_2_uv {
my $x = $_[0];
if ($x < 0) {
no integer;
$x += 18446744073709551615;
$x += 1;
}
return $x;
}
sub pcg64_perl {
my ($s) = @_;
my $inc = 1;
my $one = [$s->[0], $inc];
my $two = [$s->[1], $inc];
my $high = pcg32_perl($one);
my $low = pcg32_perl($two);
$s->[0] = $one->[0];
$s->[1] = $two->[0];
my $ret = ($high << 32) | $low;
return $ret;
}
sub pcg32_math64 {
my ($s) = @_;
my $oldstate = $s->[0];
$s->[0] = $oldstate * 6364136223846793005 + ($s->[1] | 1);
my $xorshifted = (($oldstate >> 18) ^ $oldstate) >> 27;
$xorshifted = $xorshifted & 0xFFFFFFFF;
my $rot = $oldstate >> 59;
my $invrot = 4294967296 - $rot;
my $ret = ($xorshifted >> $rot) | ($xorshifted << ($invrot & 31));
$ret = $ret & 0xFFFFFFFF;
$ret = uint64_to_number($ret);
if ($debug) {
print color('orange', "State : $oldstate/$s->[1]\n");
print color('orange', "State2: $s->[0]\n");
print color('orange', "Xor : $xorshifted\n");
print color('orange', "Rot : $rot\n");
}
return $ret;
}
sub color {
my ($str, $txt) = @_;
if (-t STDOUT == 0) { return $txt || ""; }
if (!length($str) || $str eq 'reset') { return "\e[0m"; }
my %color_map = qw(red 160 blue 27 green 34 yellow 226 orange 214 purple 93 white 15 black 0);
$str =~ s|([A-Za-z]+)|$color_map{$1} // $1|eg;
my ($fc,$cmd) = $str =~ /^(\d{1,3})?_?(\w+)?$/g;
my ($bc) = $str =~ /on_(\d{1,3})$/g;
if (defined($fc) && int($fc) > 255) { $fc = undef; }
my %cmd_map = qw(bold 1 italic 3 underline 4 blink 5 inverse 7);
my $cmd_num = $cmd_map{$cmd // 0};
my $ret = '';
if ($cmd_num) { $ret .= "\e[${cmd_num}m"; }
if (defined($fc)) { $ret .= "\e[38;5;${fc}m"; }
if (defined($bc)) { $ret .= "\e[48;5;${bc}m"; }
if (defined($txt)) { $ret .= $txt . "\e[0m"; }
return $ret;
}
sub randomize_seeds {
print color(51, "Using random seeds\n");
$s1 = perl_rand64();
$s2 = perl_rand64();
}
sub perl_rand64 {
my $low = int(rand() * (2**32-1));
my $high = int(rand() * (2**32-1));
my $ret = ($high << 32) | $low;
return $ret;
}
BEGIN {
if (eval { require Data::Dump::Color }) {
*k = sub { Data::Dump::Color::dd(@_) };
} else {
require Data::Dumper;
*k = sub { print Data::Dumper::Dumper(\@_) };
}
sub kd {
k(@_);
printf("Died at %2\$s line #%3\$s\n",caller());
exit(15);
}
}
sub quick_test32 {
my $seed = $_[0];
my @data = ();
for (my $i = 0; $i < 4; $i++) {
my $num = pcg32_perl($seed);
push(@data, $num);
}
my $ret = join(", ", @data);
return $ret;
}
sub quick_test64 {
my $seed = $_[0];
my @data = ();
for (my $i = 0; $i < 4; $i++) {
my $num = pcg64_perl($seed);
push(@data, $num);
}
my $ret = join(", ", @data);
return $ret;
}
sub run_unit_tests {
cmp_ok(quick_test32([11, 22]) , 'eq', '0, 1425092920, 3656087653, 1104107026');
cmp_ok(quick_test32([33, 44]) , 'eq', '0, 3850707138, 2930351490, 1110209703');
cmp_ok(quick_test32([55, 66]) , 'eq', '0, 1725101930, 224698313, 2870828486');
cmp_ok(quick_test32([12345, 67890]), 'eq', '0, 8251198, 44679150, 3046830521');
cmp_ok(quick_test32([9999, 9999]) , 'eq', '0, 521292032, 3698775557, 199399470');
cmp_ok(quick_test64([11, 22]) , 'eq', '0, 6120727489207695446, 7904312005358798897, 14733674221366828425');
cmp_ok(quick_test64([33, 44]) , 'eq', '0, 16538661225628040268, 5269891931295187491, 5495286771333204711');
cmp_ok(quick_test64([55, 66]) , 'eq', '0, 7409256372025208996, 8212781881022671801, 8831782971077082788');
cmp_ok(quick_test64([12345, 67890]), 'eq', '0, 35438628484449140, 42862460907032573, 519456495312580246');
cmp_ok(quick_test64([9999, 9999]) , 'eq', '0, 2238932229626677504, 14236525402126437484, 10387246122801752400');
cmp_ok(quick_test32([42862460907032573, 519456495312580246]) , 'eq', '319349001, 562730850, 2229409754, 561058538');
cmp_ok(quick_test32([6120727489207695446, 7904312005358798897]) , 'eq', '635930912, 2099303707, 1638577555, 1426136496');
cmp_ok(quick_test32([4841811808465514507, 7141191103728083377]) , 'eq', '1986408540, 4264878569, 3066617590, 731859269');
cmp_ok(quick_test64([42862460907032573, 519456495312580246]) , 'eq', '1371593519175525487, 17623029558467823369, 17850014000156247978, 768534907509427587');
cmp_ok(quick_test64([6120727489207695446, 7904312005358798897]) , 'eq', '2731302471965979098, 3465889473135782122, 4841811808465514507, 7141191103728083377');
cmp_ok(quick_test64([4841811808465514507, 7141191103728083377]) , 'eq', '8531559717926221063, 6031125200978744796, 3704366926003160989, 5594521440717127703');
done_testing();
exit(0);
}