From 0f8728f266ae4b2c09945a9ca40fceb28a1a0f61 Mon Sep 17 00:00:00 2001 From: Benjamin Morel Date: Sat, 20 Apr 2019 01:49:09 +0200 Subject: [PATCH] Improve NativeCalculator::doMul() performance Calculating using blocks of digits, just like doAdd() and doSub(). --- src/Internal/Calculator/NativeCalculator.php | 55 ++++++++++++++++---- 1 file changed, 45 insertions(+), 10 deletions(-) diff --git a/src/Internal/Calculator/NativeCalculator.php b/src/Internal/Calculator/NativeCalculator.php index 3f5cdc5..a981bcd 100644 --- a/src/Internal/Calculator/NativeCalculator.php +++ b/src/Internal/Calculator/NativeCalculator.php @@ -394,26 +394,61 @@ private function doSub(string $a, string $b, int $x, int $y) : string */ private function doMul(string $a, string $b, int $x, int $y) : string { + $maxDigits = \intdiv($this->maxDigits, 2); + $complement = 10 ** $maxDigits; + $result = '0'; - for ($i = $x - 1; $i >= 0; $i--) { - $line = \str_repeat('0', $x - 1 - $i); + for ($i = $x - $maxDigits;; $i -= $maxDigits) { + $blockALength = $maxDigits; + + if ($i < 0) { + $blockALength += $i; + $i = 0; + } + + $blockA = (int) \substr($a, $i, $blockALength); + + $line = ''; $carry = 0; - for ($j = $y - 1; $j >= 0; $j--) { - $mul = (int) $a[$i] * (int) $b[$j] + $carry; - $digit = $mul % 10; - $carry = ($mul - $digit) / 10; - $line .= $digit; + + for ($j = $y - $maxDigits;; $j -= $maxDigits) { + $blockBLength = $maxDigits; + + if ($j < 0) { + $blockBLength += $j; + $j = 0; + } + + $blockB = (int) \substr($b, $j, $blockBLength); + + $mul = $blockA * $blockB + $carry; + $value = $mul % $complement; + $carry = ($mul - $value) / $complement; + + $value = (string) $value; + $value = \str_pad($value, $maxDigits, '0', STR_PAD_LEFT); + + $line = $value . $line; + + if ($j === 0) { + break; + } } if ($carry !== 0) { - $line .= $carry; + $line = $carry . $line; } - $line = \rtrim($line, '0'); + $line = \ltrim($line, '0'); if ($line !== '') { - $result = $this->add($result, \strrev($line)); + $line .= \str_repeat('0', $x - $blockALength - $i); + $result = $this->add($result, $line); + } + + if ($i === 0) { + break; } }