diff --git a/tests/benchmarks/fractions.php b/tests/benchmarks/fractions.php new file mode 100644 index 0000000..303c98b --- /dev/null +++ b/tests/benchmarks/fractions.php @@ -0,0 +1,183 @@ +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; + } +} diff --git a/tests/benchmarks/negations.php b/tests/benchmarks/negations.php new file mode 100644 index 0000000..89080da --- /dev/null +++ b/tests/benchmarks/negations.php @@ -0,0 +1,49 @@ + 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"; + } + +})();