BigInteger: speed up Barrett reductions

the changes for #1994 (commit 2689c727) slowed things down
unnecessarily.
This commit is contained in:
terrafrost 2024-09-24 06:37:52 -05:00
parent db92f1b198
commit c3cd458bcf
3 changed files with 45 additions and 9 deletions

View File

@ -66,7 +66,7 @@ abstract class Barrett extends Base
$m_length = strlen($m); $m_length = strlen($m);
if (strlen($n) >= 2 * $m_length) { if (strlen($n) > 2 * $m_length) {
return bcmod($n, $m); return bcmod($n, $m);
} }
@ -75,6 +75,13 @@ abstract class Barrett extends Base
return self::regularBarrett($n, $m); return self::regularBarrett($n, $m);
} }
// n = 2 * m.length // n = 2 * m.length
$correctionNeeded = false;
if ($m_length & 1) {
$correctionNeeded = true;
$n .= '0';
$m .= '0';
$m_length++;
}
if (($key = array_search($m, $cache[self::VARIABLE])) === false) { if (($key = array_search($m, $cache[self::VARIABLE])) === false) {
$key = count($cache[self::VARIABLE]); $key = count($cache[self::VARIABLE]);
@ -131,7 +138,7 @@ abstract class Barrett extends Base
$result = bcsub($result, $m); $result = bcsub($result, $m);
} }
return $result; return $correctionNeeded ? substr($result, 0, -1) : $result;
} }
/** /**

View File

@ -56,7 +56,7 @@ abstract class Barrett extends Base
$m_length = count($m); $m_length = count($m);
// if (self::compareHelper($n, $static::square($m)) >= 0) { // if (self::compareHelper($n, $static::square($m)) >= 0) {
if (count($n) >= 2 * $m_length) { if (count($n) > 2 * $m_length) {
$lhs = new $class(); $lhs = new $class();
$rhs = new $class(); $rhs = new $class();
$lhs->value = $n; $lhs->value = $n;
@ -70,6 +70,13 @@ abstract class Barrett extends Base
return self::regularBarrett($n, $m, $class); return self::regularBarrett($n, $m, $class);
} }
// n = 2 * m.length // n = 2 * m.length
$correctionNeeded = false;
if ($m_length & 1) {
$correctionNeeded = true;
array_unshift($n, 0);
array_unshift($m, 0);
$m_length++;
}
if (($key = array_search($m, $cache[self::VARIABLE])) === false) { if (($key = array_search($m, $cache[self::VARIABLE])) === false) {
$key = count($cache[self::VARIABLE]); $key = count($cache[self::VARIABLE]);
@ -109,6 +116,10 @@ abstract class Barrett extends Base
$temp = array_slice($n[self::VALUE], $m_length - 1); $temp = array_slice($n[self::VALUE], $m_length - 1);
// if even: ((m.length >> 1) + 2) + (m.length >> 1) == m.length + 2 // if even: ((m.length >> 1) + 2) + (m.length >> 1) == m.length + 2
// if odd: ((m.length >> 1) + 2) + (m.length >> 1) == (m.length - 1) + 2 == m.length + 1 // if odd: ((m.length >> 1) + 2) + (m.length >> 1) == (m.length - 1) + 2 == m.length + 1
// note that these are upper bounds. let's say m.length is 2. then you'd be multiplying a
// 3 digit number by a 1 digit number. if you're doing 999 * 9 (in base 10) the result will
// be a 4 digit number. but if you're multiplying 111 * 1 then the result will be a 3 digit
// number.
$temp = $class::multiplyHelper($temp, false, $u, false); $temp = $class::multiplyHelper($temp, false, $u, false);
// if even: (m.length + 2) - ((m.length >> 1) + 1) = m.length - (m.length >> 1) + 1 // if even: (m.length + 2) - ((m.length >> 1) + 1) = m.length - (m.length >> 1) + 1
// if odd: (m.length + 1) - ((m.length >> 1) + 1) = m.length - (m.length >> 1) // if odd: (m.length + 1) - ((m.length >> 1) + 1) = m.length - (m.length >> 1)
@ -116,17 +127,19 @@ abstract class Barrett extends Base
// if even: (m.length - (m.length >> 1) + 1) + m.length = 2 * m.length - (m.length >> 1) + 1 // if even: (m.length - (m.length >> 1) + 1) + m.length = 2 * m.length - (m.length >> 1) + 1
// if odd: (m.length - (m.length >> 1)) + m.length = 2 * m.length - (m.length >> 1) // if odd: (m.length - (m.length >> 1)) + m.length = 2 * m.length - (m.length >> 1)
$temp = $class::multiplyHelper($temp, false, $m, false); $temp = $class::multiplyHelper($temp, false, $m, false);
// at this point, if m had an odd number of digits, we'd (probably) be subtracting a 2 * m.length - (m.length >> 1)
// at this point, if m had an odd number of digits, we'd be subtracting a 2 * m.length - (m.length >> 1) digit // digit number from a m.length + (m.length >> 1) + 1 digit number. ie. there'd be an extra digit and the while loop
// number from a m.length + (m.length >> 1) + 1 digit number. ie. there'd be an extra digit and the while loop
// following this comment would loop a lot (hence our calling _regularBarrett() in that situation). // following this comment would loop a lot (hence our calling _regularBarrett() in that situation).
$result = $class::subtractHelper($n[self::VALUE], false, $temp[self::VALUE], false); $result = $class::subtractHelper($n[self::VALUE], false, $temp[self::VALUE], false);
while (self::compareHelper($result[self::VALUE], $result[self::SIGN], $m, false) >= 0) { while (self::compareHelper($result[self::VALUE], $result[self::SIGN], $m, false) >= 0) {
$result = $class::subtractHelper($result[self::VALUE], $result[self::SIGN], $m, false); $result = $class::subtractHelper($result[self::VALUE], $result[self::SIGN], $m, false);
} }
if ($correctionNeeded) {
array_shift($result[self::VALUE]);
}
return $result[self::VALUE]; return $result[self::VALUE];
} }

View File

@ -74,6 +74,14 @@ abstract class EvalBarrett extends Base
return $func; return $func;
} }
$correctionNeeded = false;
if ($m_length & 1) {
$correctionNeeded = true;
$m = clone $m;
array_unshift($m->value, 0);
$m_length++;
}
$lhs = new $class(); $lhs = new $class();
$lhs_value = &$lhs->value; $lhs_value = &$lhs->value;
@ -99,8 +107,12 @@ abstract class EvalBarrett extends Base
$cutoff = count($m) + (count($m) >> 1); $cutoff = count($m) + (count($m) >> 1);
$code = ' $code = $correctionNeeded ?
if (count($n) >= ' . (2 * count($m)) . ') { 'array_unshift($n, 0);' :
'';
$code .= '
if (count($n) > ' . (2 * count($m)) . ') {
$lhs = new ' . $class . '(); $lhs = new ' . $class . '();
$rhs = new ' . $class . '(); $rhs = new ' . $class . '();
$lhs->value = $n; $lhs->value = $n;
@ -141,6 +153,10 @@ abstract class EvalBarrett extends Base
$code .= self::generateInlineCompare($m, 'temp', $subcode); $code .= self::generateInlineCompare($m, 'temp', $subcode);
if ($correctionNeeded) {
$code .= 'array_shift($temp);';
}
$code .= 'return $temp;'; $code .= 'return $temp;';
eval('$func = function ($n) { ' . $code . '};'); eval('$func = function ($n) { ' . $code . '};');