Xoshiro generation

master
Anton Smirnov 2022-08-03 05:02:25 +03:00
parent 2f29f5a006
commit 3d98b1c723
4 changed files with 72 additions and 40 deletions

View File

@ -10,8 +10,10 @@ require __DIR__ . '/../vendor/autoload.php';
//$rnd = new \Random\Randomizer(new \Arokettu\Random\Tests\DevEngines\Fail());
//$rnd = new \Random\Randomizer(new \Arokettu\Random\Tests\DevEngines\SingleByte());
//$rnd = new \Random\Randomizer(new \Arokettu\Random\Tests\DevEngines\ThreeBytes());
$rnd = new \Random\Randomizer(new \Random\Engine\PcgOneseq128XslRr64(123));
//$rnd = new \Random\Randomizer(new \Random\Engine\PcgOneseq128XslRr64(123));
//$rnd = new \Random\Randomizer(new \Random\Engine\PcgOneseq128XslRr64('1234567890123456'));
$rnd = new \Random\Randomizer(new \Random\Engine\Xoshiro256StarStar(123));
//$rnd = new \Random\Randomizer(new \Random\Engine\Xoshiro256StarStar('12345678901234567890123456789012'));
//var_dump($rnd->engine);
//var_dump($rnd->engine->__serialize());

View File

@ -0,0 +1,16 @@
<?php
require __DIR__ . '/../vendor/autoload.php';
$e = new \Random\Engine\Xoshiro256StarStar(4546546);
$e->generate();
$e->generate();
var_dump($e->__serialize());
echo base64_encode(serialize($e)), PHP_EOL;
$php82serialized = 'TzozMjoiUmFuZG9tXEVuZ2luZVxYb3NoaXJvMjU2U3RhclN0YXIiOjI6e2k6MDthOjA6e31pOjE7YTo0OntpOjA7czoxNjoiZGY5Yzk3NmQ3NTYzMTFiOCI7aToxO3M6MTY6IjY5MTA5ZjFhNjc0MTNjYWYiO2k6MjtzOjE2OiJmY2JmOGZlNjc0OTQ0N2UyIjtpOjM7czoxNjoiMmJkMGE4MTk3NGZmMTk1NyI7fX0=';
$e2 = unserialize(base64_decode($php82serialized));
var_dump(bin2hex($e->generate()), bin2hex($e2->generate()));

View File

@ -17,9 +17,9 @@ use const GMP_LSW_FIRST;
trait BigIntExportImport
{
private static $SIZEOF_UINT_32_T = 4;
private static $SIZEOF_UINT_64_T = 8;
private static $SIZEOF_UINT_128_T = 16;
private static $SIZEOF_UINT32_T = 4;
private static $SIZEOF_UINT64_T = 8;
private static $SIZEOF_UINT128_T = 16;
/** @var GMP|null 32-bit bitmask aka max 32-bit uint */
private static $UINT32_MASK = null;
@ -43,45 +43,45 @@ trait BigIntExportImport
private function importGmp32(string $value): GMP
{
$value = substr($value, 0, self::$SIZEOF_UINT_32_T);
$value = str_pad($value, self::$SIZEOF_UINT_32_T, "\0");
return gmp_import($value, self::$SIZEOF_UINT_32_T, GMP_LITTLE_ENDIAN | GMP_LSW_FIRST);
$value = substr($value, 0, self::$SIZEOF_UINT32_T);
$value = str_pad($value, self::$SIZEOF_UINT32_T, "\0");
return gmp_import($value, self::$SIZEOF_UINT32_T, GMP_LITTLE_ENDIAN | GMP_LSW_FIRST);
}
private function importGmp64(string $value): GMP
{
$value = substr($value, 0, self::$SIZEOF_UINT_64_T);
$value = str_pad($value, self::$SIZEOF_UINT_64_T, "\0");
return gmp_import($value, self::$SIZEOF_UINT_64_T, GMP_LITTLE_ENDIAN | GMP_LSW_FIRST);
$value = substr($value, 0, self::$SIZEOF_UINT64_T);
$value = str_pad($value, self::$SIZEOF_UINT64_T, "\0");
return gmp_import($value, self::$SIZEOF_UINT64_T, GMP_LITTLE_ENDIAN | GMP_LSW_FIRST);
}
private function importGmp128hilo(string $hi, string $lo): GMP
{
$value = $lo . $hi;
$value = substr($value, 0, self::$SIZEOF_UINT_128_T);
$value = str_pad($value, self::$SIZEOF_UINT_128_T, "\0");
return gmp_import($value, self::$SIZEOF_UINT_128_T, GMP_LITTLE_ENDIAN | GMP_LSW_FIRST);
$value = substr($value, 0, self::$SIZEOF_UINT128_T);
$value = str_pad($value, self::$SIZEOF_UINT128_T, "\0");
return gmp_import($value, self::$SIZEOF_UINT128_T, GMP_LITTLE_ENDIAN | GMP_LSW_FIRST);
}
private function exportGmp32(GMP $value): string
{
$value = $value & self::$UINT32_MASK;
$value = gmp_export($value, self::$SIZEOF_UINT_32_T, GMP_LITTLE_ENDIAN | GMP_LSW_FIRST);
return str_pad($value, self::$SIZEOF_UINT_32_T, "\0");
$value = gmp_export($value, self::$SIZEOF_UINT32_T, GMP_LITTLE_ENDIAN | GMP_LSW_FIRST);
return str_pad($value, self::$SIZEOF_UINT32_T, "\0");
}
private function exportGmp64(GMP $value): string
{
$value = $value & self::$UINT64_MASK;
$value = gmp_export($value, self::$SIZEOF_UINT_64_T, GMP_LITTLE_ENDIAN | GMP_LSW_FIRST);
return str_pad($value, self::$SIZEOF_UINT_64_T, "\0");
$value = gmp_export($value, self::$SIZEOF_UINT64_T, GMP_LITTLE_ENDIAN | GMP_LSW_FIRST);
return str_pad($value, self::$SIZEOF_UINT64_T, "\0");
}
private function exportGmp128hilo(GMP $value): array
{
$value = $value & self::$UINT128_MASK;
$value = gmp_export($value, self::$SIZEOF_UINT_128_T, GMP_LITTLE_ENDIAN | GMP_LSW_FIRST);
$value = str_pad($value, self::$SIZEOF_UINT_128_T, "\0");
return array_reverse(str_split($value, self::$SIZEOF_UINT_64_T));
$value = gmp_export($value, self::$SIZEOF_UINT128_T, GMP_LITTLE_ENDIAN | GMP_LSW_FIRST);
$value = str_pad($value, self::$SIZEOF_UINT128_T, "\0");
return array_reverse(str_split($value, self::$SIZEOF_UINT64_T));
}
}

View File

@ -13,8 +13,6 @@ use ValueError;
use function bin2hex;
use function get_debug_type;
use function gmp_export;
use function gmp_import;
use function gmp_init;
use function is_int;
use function is_string;
@ -22,11 +20,9 @@ use function random_bytes;
use function str_split;
use function strlen;
use const GMP_LITTLE_ENDIAN;
use const GMP_LSW_FIRST;
final class Xoshiro256StarStar implements Engine
{
use Shared\BigIntExportImport;
use Shared\Serialization;
private const SIZEOF_UINT64_T = 8;
@ -34,8 +30,6 @@ final class Xoshiro256StarStar implements Engine
/** @var GMP[] */
private $state;
/** @var GMP|null 64-bit bitmask */
private static $UINT64_MASK = null;
/** @var GMP|null */
private static $SPLITMIX64_1;
/** @var GMP|null */
@ -87,9 +81,8 @@ final class Xoshiro256StarStar implements Engine
*/
private function initConst(): void
{
if (self::$UINT64_MASK === null) {
self::$UINT64_MASK = gmp_init('ffffffffffffffff', 16);
}
$this->initGmpConst();
if (self::$SPLITMIX64_1 === null) {
self::$SPLITMIX64_1 = gmp_init('9e3779b97f4a7c15', 16);
}
@ -126,10 +119,10 @@ final class Xoshiro256StarStar implements Engine
$seeds = str_split($seed, 8);
$this->seed256(
gmp_import($seeds[0], self::SIZEOF_UINT64_T, GMP_LITTLE_ENDIAN | GMP_LSW_FIRST),
gmp_import($seeds[1], self::SIZEOF_UINT64_T, GMP_LITTLE_ENDIAN | GMP_LSW_FIRST),
gmp_import($seeds[2], self::SIZEOF_UINT64_T, GMP_LITTLE_ENDIAN | GMP_LSW_FIRST),
gmp_import($seeds[3], self::SIZEOF_UINT64_T, GMP_LITTLE_ENDIAN | GMP_LSW_FIRST)
$this->importGmp64($seeds[0]),
$this->importGmp64($seeds[1]),
$this->importGmp64($seeds[2]),
$this->importGmp64($seeds[3])
);
}
@ -140,7 +133,25 @@ final class Xoshiro256StarStar implements Engine
public function generate(): string
{
return "\0";
$r = ($this->rotl($this->state[1] * 5, 7) * 9) & self::$UINT64_MASK;
$t = ($this->state[1]) << 17 & self::$UINT64_MASK;
$this->state[2] ^= $this->state[0];
$this->state[3] ^= $this->state[1];
$this->state[1] ^= $this->state[2];
$this->state[0] ^= $this->state[3];
$this->state[2] ^= $t;
$this->state[3] = $this->rotl($this->state[3], 45);
return $this->exportGmp64($r);
}
private function rotl(GMP $x, int $k): GMP
{
$x = $x & self::$UINT64_MASK;
return (($x << $k) | ($x >> (64 - $k))) & self::$UINT64_MASK;
}
public function jump(): void
@ -159,10 +170,10 @@ final class Xoshiro256StarStar implements Engine
private function getStates(): array
{
return [
bin2hex(gmp_export($this->state[0], self::SIZEOF_UINT64_T, GMP_LITTLE_ENDIAN | GMP_LSW_FIRST)),
bin2hex(gmp_export($this->state[1], self::SIZEOF_UINT64_T, GMP_LITTLE_ENDIAN | GMP_LSW_FIRST)),
bin2hex(gmp_export($this->state[2], self::SIZEOF_UINT64_T, GMP_LITTLE_ENDIAN | GMP_LSW_FIRST)),
bin2hex(gmp_export($this->state[3], self::SIZEOF_UINT64_T, GMP_LITTLE_ENDIAN | GMP_LSW_FIRST)),
bin2hex($this->exportGmp64($this->state[0])),
bin2hex($this->exportGmp64($this->state[1])),
bin2hex($this->exportGmp64($this->state[2])),
bin2hex($this->exportGmp64($this->state[3])),
];
}
@ -171,6 +182,9 @@ final class Xoshiro256StarStar implements Engine
*/
private function loadStates(array $states): void
{
// TODO: Implement loadStates() method.
$this->state = [];
for ($i = 0; $i < 4; $i++) {
$this->state[$i] = $this->importGmp64(hex2bin($states[$i]));
}
}
}