diff --git a/bigint/bigint_nonjs.mbt b/bigint/bigint_nonjs.mbt index 8ae12703b..4bcd57691 100644 --- a/bigint/bigint_nonjs.mbt +++ b/bigint/bigint_nonjs.mbt @@ -384,7 +384,15 @@ pub impl Mul for BigInt with fn mul(self : BigInt, other : BigInt) -> BigInt { if self.is_zero() || other.is_zero() { return zero } - let ret = if self.len < karatsuba_threshold || other.len < karatsuba_threshold { + // Specialize the (n-limb) x (1-limb) case. Factorial-style chains hit + // this on every multiplication, and the general grade-school loop has + // a per-i branch on `j < other_len` plus carry propagation overhead + // that disappears here. + let ret = if other.len == 1 { + self.mul_single_limb(other.limbs[0]) + } else if self.len == 1 { + other.mul_single_limb(self.limbs[0]) + } else if self.len < karatsuba_threshold || other.len < karatsuba_threshold { self.grade_school_mul(other) } else { self.karatsuba_mul(other) @@ -392,6 +400,32 @@ pub impl Mul for BigInt with fn mul(self : BigInt, other : BigInt) -> BigInt { { ..ret, sign: if self.sign == other.sign { Positive } else { Negative } } } +///| +/// Multiply the magnitude of `self` by a single radix-limb `x`. Returns +/// a `Positive`-signed result regardless of `self.sign` — the caller in +/// `Mul::mul` overwrites `sign` with the correct combined sign. This +/// matches the convention of the other magnitude-only multiply helpers +/// (`grade_school_mul`, `karatsuba_mul`). Do not call directly when a +/// signed product is needed. +fn BigInt::mul_single_limb(self : BigInt, x : UInt) -> BigInt { + let n = self.len + let limbs = FixedArray::make(n + 1, 0U) + let xq = x.to_uint64() + let mut carry = 0UL + for i in 0..> radix_bit_len + } + let len = if carry == 0UL { + n + } else { + limbs[n] = carry.to_uint() + n + 1 + } + { limbs, sign: Positive, len } +} + // Simplest way to multiply two BigInts. ///|