-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
395 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,183 @@ | ||
<?php | ||
|
||
declare(strict_types = 1); | ||
|
||
namespace Simplex\Tests\Benchmarks; | ||
|
||
use function Simplex\Math\abs; | ||
use function Simplex\Math\add; | ||
use function Simplex\Math\div; | ||
use function Simplex\Math\gcd; | ||
use function Simplex\Math\mul; | ||
use function Simplex\Math\sub; | ||
use function Simplex\Math\isNegative; | ||
|
||
|
||
trait FractionBase | ||
{ | ||
public readonly string $n; | ||
public readonly string $d; | ||
|
||
|
||
public function add(self $a): self | ||
{ | ||
return new self( | ||
add( | ||
mul($this->n, $a->d), | ||
mul($this->d, $a->n), | ||
), | ||
mul($this->d, $a->d), | ||
); | ||
} | ||
|
||
|
||
public function subtract(self $a): self | ||
{ | ||
return new self( | ||
sub( | ||
mul($this->n, $a->d), | ||
mul($this->d, $a->n), | ||
), | ||
mul($this->d, $a->d), | ||
); | ||
} | ||
|
||
|
||
public function multiply(self|string $a): self | ||
{ | ||
if (is_string($a)) { | ||
return new self( | ||
mul($a, $this->n), | ||
$this->d, | ||
); | ||
} | ||
|
||
return new self( | ||
mul($this->n, $a->n), | ||
mul($this->d, $a->d), | ||
); | ||
} | ||
|
||
|
||
public function divide(self $a): self | ||
{ | ||
return new self( | ||
mul($this->n, $a->d), | ||
mul($this->d, $a->n), | ||
); | ||
} | ||
|
||
|
||
public function abs(): self | ||
{ | ||
return new self(abs($this->n), $this->d); | ||
} | ||
|
||
|
||
public function canonicalize(): self | ||
{ | ||
$gcd = gcd($this->n, $this->d); | ||
$n = div($this->n, $gcd); | ||
$d = div($this->d, $gcd); | ||
|
||
if (isNegative($d)) { | ||
$n = mul($n, '-1'); | ||
$d = mul($d, '-1'); | ||
} | ||
|
||
return new self($n, $d); | ||
} | ||
} | ||
|
||
|
||
final class FractionCanonicalized | ||
{ | ||
use FractionBase; | ||
|
||
|
||
public function __construct(string $n, string $d = '1') | ||
{ | ||
if ($d === '0') { | ||
throw new \RuntimeException('Division by zero.'); | ||
} | ||
|
||
$gcd = gcd($n, $d); | ||
$n = div($n, $gcd); | ||
$d = div($d, $gcd); | ||
|
||
if (isNegative($d)) { | ||
$n = mul($n, '-1'); | ||
$d = mul($d, '-1'); | ||
} | ||
|
||
$this->n = $n; | ||
$this->d = $d; | ||
} | ||
|
||
|
||
public function isEqualTo(self|string $a): bool | ||
{ | ||
if (is_string($a)) { | ||
return $this->d === '1' && $this->n === $a; | ||
} | ||
|
||
return $this->n === $a->n && $this->d === $a->d; | ||
} | ||
} | ||
|
||
|
||
final class FractionIsEqualMultiplication | ||
{ | ||
use FractionBase; | ||
|
||
|
||
public function __construct(string $n, string $d = '1') | ||
{ | ||
if ($d === '0') { | ||
throw new \RuntimeException('Division by zero.'); | ||
} | ||
|
||
$this->n = $n; | ||
$this->d = $d; | ||
} | ||
|
||
|
||
public function isEqualTo(self|string $a): bool | ||
{ | ||
if (is_string($a)) { | ||
return $this->n === mul($a, $this->d); | ||
} | ||
|
||
return mul($this->n, $a->d) === mul($a->n, $this->d); | ||
} | ||
} | ||
|
||
|
||
final class FractionIsEqualCanonicalization | ||
{ | ||
use FractionBase; | ||
|
||
|
||
public function __construct(string $n, string $d = '1') | ||
{ | ||
if ($d === '0') { | ||
throw new \RuntimeException('Division by zero.'); | ||
} | ||
|
||
$this->n = $n; | ||
$this->d = $d; | ||
} | ||
|
||
|
||
public function isEqualTo(self|string $a): bool | ||
{ | ||
$canThis = $this->canonicalize(); | ||
|
||
if (is_string($a)) { | ||
return $canThis->d === '1' && $canThis->n === $a; | ||
} | ||
|
||
$canA = $a->canonicalize(); | ||
return $canThis->n === $canA->n && $canThis->d === $canA->d; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
<?php | ||
|
||
declare(strict_types = 1); | ||
|
||
namespace Simplex\Tests\Benchmarks; | ||
|
||
|
||
final class NegateBC | ||
{ | ||
public static function negate(string $n): string | ||
{ | ||
return bcmul($n, '-1', 0); | ||
} | ||
} | ||
|
||
|
||
final class NegateString | ||
{ | ||
public static function negate(string $n): string | ||
{ | ||
if ($n === '0') { | ||
return '0'; | ||
} | ||
|
||
if (strncmp($n, '-', 1) === 0) { | ||
return substr($n, 1); | ||
} | ||
|
||
return '-' . $n; | ||
} | ||
} | ||
|
||
|
||
function negateBC(string $n): string { | ||
return bcmul($n, '-1', 0); | ||
} | ||
|
||
|
||
function negateString(string $n): string { | ||
if ($n === '0') { | ||
return '0'; | ||
} | ||
|
||
if ($n[0] === '-') { | ||
return substr($n, 1); | ||
} | ||
|
||
return '-' . $n; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
<?php | ||
|
||
declare(strict_types = 1); | ||
|
||
namespace Simplex\Tests\Benchmarks; | ||
|
||
|
||
require_once __DIR__ . '/../../src/Simplex/Math/math.php'; | ||
require_once __DIR__ . '/negations.php'; | ||
require_once __DIR__ . '/fractions.php'; | ||
|
||
|
||
// ============================================================================= | ||
// === STRING MANIPULATION ===================================================== | ||
// ============================================================================= | ||
|
||
(static function (): void { | ||
|
||
echo "\n"; | ||
|
||
echo '> String Manipulation Benchmarks', "\n\n"; | ||
|
||
$cases = [ | ||
'Remove "." - str_replace()' => static fn(string $n, int $dotPos): string => str_replace('.', '', $n), | ||
'Remove "." - substr()' => static fn(string $n, int $dotPos): string => str_replace('.', '', $n), | ||
]; | ||
|
||
$caseNo = 0; | ||
$steps = 1e8; | ||
|
||
foreach ($cases as $title => $callback) { | ||
echo ' ', ++$caseNo, '. ', $title, '...'; | ||
|
||
$t = hrtime(true); | ||
|
||
$n = '1234567890.0987654321'; | ||
for ($i = 0; $i < $steps; $i++) { | ||
$res = $callback($n, 10); | ||
} | ||
|
||
assert(($res ?? '') === '12345678900987654321'); | ||
|
||
$t = hrtime(true) - $t; | ||
echo ' ', $t / 1e9, 's', "\n"; | ||
} | ||
|
||
})(); | ||
|
||
|
||
// ============================================================================= | ||
// === NEGATION ================================================================ | ||
// ============================================================================= | ||
|
||
(static function (): void { | ||
|
||
echo "\n"; | ||
|
||
echo '> Negation Benchmarks', "\n\n"; | ||
|
||
$cases = [ | ||
'BCMath - class' => static fn(string $n): string => NegateBC::negate($n), | ||
'BCMath - function' => static fn(string $n): string => negateBC($n), | ||
'String manipulation - class' => static fn(string $n): string => NegateString::negate($n), | ||
'String manipulation - function' => static fn(string $n): string => negateString($n), | ||
'Manual negation' => static function (string $n): string { | ||
if ($n === '0') { | ||
return '0'; | ||
|
||
} elseif (strncmp($n, '-', 1) === 0) { | ||
return substr($n, 1); | ||
} | ||
|
||
return '-' . $n; | ||
}, | ||
]; | ||
|
||
$caseNo = 0; | ||
$steps = 1e6 - 1; | ||
|
||
foreach ($cases as $title => $callback) { | ||
echo ' ', ++$caseNo, '. ', $title, '...'; | ||
|
||
$t = hrtime(true); | ||
|
||
$n = '123456789'; | ||
for ($i = 0; $i < $steps; $i++) { | ||
$n = $callback($n); | ||
} | ||
|
||
assert($n === '-123456789'); | ||
|
||
$t = hrtime(true) - $t; | ||
echo ' ', $t / 1e9, 's', "\n"; | ||
} | ||
|
||
echo "\n"; | ||
|
||
})(); | ||
|
||
|
||
// ============================================================================= | ||
// === FRACTION - IS EQUAL TO ================================================== | ||
// ============================================================================= | ||
|
||
(static function (): void { | ||
|
||
echo '> Fraction isEqualTo Benchmarks', "\n\n"; | ||
|
||
$cases = [ | ||
'Automatic canonicalization' => [ | ||
static fn(string $n, string $d = '1'): FractionCanonicalized => new FractionCanonicalized($n, $d), | ||
static function (FractionCanonicalized $f): FractionCanonicalized { return $f; }, | ||
], | ||
|
||
'Denominator multiplication' => [ | ||
static fn(string $n, string $d = '1'): FractionIsEqualMultiplication => new FractionIsEqualMultiplication($n, $d), | ||
static function (FractionIsEqualMultiplication $f): FractionIsEqualMultiplication { return $f->canonicalize(); }, | ||
], | ||
|
||
'Canonicalization inside isEqualTo()' => [ | ||
static fn(string $n, string $d = '1'): FractionIsEqualCanonicalization => new FractionIsEqualCanonicalization($n, $d), | ||
static function (FractionIsEqualCanonicalization $f): FractionIsEqualCanonicalization { return $f->canonicalize(); }, | ||
], | ||
]; | ||
|
||
$caseNo = 0; | ||
$steps = 1e4; | ||
|
||
foreach ($cases as $title => $case) { | ||
echo ' ', ++$caseNo, '. ', $title, '...'; | ||
|
||
$t = hrtime(true); | ||
[$factory, $canonicalization] = $case; | ||
|
||
$f = $factory('9223372036854775807'); | ||
|
||
for ($i = 0; $i < $steps; $i++) { | ||
$f = $f->multiply($factory('9223372036854775806', '2')) | ||
->divide($factory('9223372036854775806', '2')) | ||
->add($factory('9223372036854775806', '2')) | ||
->subtract($factory('9223372036854775806', '2')); | ||
} | ||
|
||
$f = $f->subtract($factory('9223372036854775806')); | ||
|
||
assert($f->isEqualTo($factory('9223372036854775807', '9223372036854775807'))); | ||
assert(!$f->isEqualTo($factory('9223372036854775808', '9223372036854775807'))); | ||
|
||
assert($f->isEqualTo($factory('1'))); | ||
assert(!$f->isEqualTo($factory('2'))); | ||
|
||
assert($f->isEqualTo('1')); | ||
assert(!$f->isEqualTo('2')); | ||
|
||
$f = $canonicalization($f); | ||
assert($f->n === '1'); | ||
assert($f->d === '1'); | ||
|
||
$t = hrtime(true) - $t; | ||
echo ' ', $t / 1e9, 's', "\n"; | ||
} | ||
|
||
})(); |