Skip to content

Commit

Permalink
Stress test script
Browse files Browse the repository at this point in the history
This script stress tests calculators with random large numbers and ensures that all implementations return the same results.
  • Loading branch information
BenMorel committed Aug 18, 2020
1 parent 009a831 commit 2d2bf2b
Showing 1 changed file with 180 additions and 0 deletions.
180 changes: 180 additions & 0 deletions random-tests.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
<?php

/**
* This script stress tests calculators with random large numbers and ensures that all implementations return the same
* results. It is designed to run in an infinite loop unless a bug is found.
*/

declare(strict_types=1);

require __DIR__ . '/vendor/autoload.php';

use Brick\Math\Internal\Calculator;

(new class {
private $gmp;
private $bcmath;
private $native;

public function __construct()
{
$this->gmp = new Calculator\GmpCalculator();
$this->bcmath = new Calculator\BcMathCalculator();
$this->native = new Calculator\NativeCalculator();
}

public function __invoke() : void
{
for (;;) {
$a = self::generateRandomNumber();
$b = self::generateRandomNumber();
$c = self::generateRandomNumber();

$this->runTests($a, $b);
$this->runTests($b, $a);

if ($a !== '0') {
$this->runTests("-$a", $b);
$this->runTests($b, "-$a");
}

if ($b !== '0') {
$this->runTests($a, "-$b");
$this->runTests("-$b", $a);
}

if ($a !== '0' && $b !== '0') {
$this->runTests("-$a", "-$b");
$this->runTests("-$b", "-$a");
}

if ($c !== '0') {
$this->test("$a POW $b MOD $c", function(Calculator $calc) use($a, $b, $c) {
return $calc->modPow($a, $b, $c);
});
}
}
}

/**
* @param string $a The left operand.
* @param string $b The right operand.
*/
function runTests(string $a, string $b) : void
{
$this->test("$a + $b", function(Calculator $c) use($a, $b) {
return $c->add($a, $b);
});

$this->test("$a - $b", function(Calculator $c) use($a, $b) {
return $c->sub($a, $b);
});

$this->test("$a * $b", function(Calculator $c) use($a, $b) {
return $c->mul($a, $b);
});

if ($b !== '0') {
$this->test("$a / $b", function(Calculator $c) use($a, $b) {
return $c->divQR($a, $b);
});

$this->test("$a MOD $b", function(Calculator $c) use($a, $b) {
return $c->mod($a, $b);
});
}

if ($b !== '0' && $b[0] !== '-') {
$this->test("INV $a MOD $b", function(Calculator $c) use($a, $b) {
return $c->modInverse($a, $b);
});
}

$this->test("GCD $a, $b", function(Calculator $c) use($a, $b) {
return $c->gcd($a, $b);
});

if ($a[0] !== '-') {
$this->test("SQRT $a", function(Calculator $c) use($a, $b) {
return $c->sqrt($a);
});
}

$this->test("$a AND $b", function(Calculator $c) use($a, $b) {
return $c->and($a, $b);
});

$this->test("$a OR $b", function(Calculator $c) use($a, $b) {
return $c->or($a, $b);
});

$this->test("$a XOR $b", function(Calculator $c) use($a, $b) {
return $c->xor($a, $b);
});
}

/**
* @param string $test A string representing the test being executed.
* @param Closure $callback A callback function accepting a Calculator instance and returning a calculation result.
*/
private function test(string $test, Closure $callback) : void
{
static $counter = 0;
static $lastOutputTime = null;

$gmpResult = $callback($this->gmp);
$bcmathResult = $callback($this->bcmath);
$nativeResult = $callback($this->native);

if ($gmpResult !== $bcmathResult) {
self::failure('GMP', 'BCMath', $test);
}

if ($gmpResult !== $nativeResult) {
self::failure('GMP', 'Native', $test);
}

$counter++;
$time = microtime(true);

if ($lastOutputTime === null) {
$lastOutputTime = $time;
} elseif ($time - $lastOutputTime >= 0.1) {
echo "\r", number_format($counter);
$lastOutputTime = $time;
}
}

/**
* @param string $c1 The name of the first calculator.
* @param string $c2 The name of the second calculator.
* @param string $test A string representing the test being executed.
*/
private static function failure(string $c1, string $c2, string $test) : void
{
echo PHP_EOL;
echo 'FAILURE!', PHP_EOL;
echo $c1, ' vs ', $c2, PHP_EOL;
echo $test, PHP_EOL;
die;
}

private static function generateRandomNumber() : string
{
$length = random_int(1, 100);

$number = '';

for ($i = 0; $i < $length; $i++) {
$number .= random_int(0, 9);
}

$number = ltrim($number, '0');

if ($number === '') {
return '0';
}

return $number;
}
})();

0 comments on commit 2d2bf2b

Please sign in to comment.