Skip to content
36 changes: 35 additions & 1 deletion bigint/bigint_nonjs.mbt
Original file line number Diff line number Diff line change
Expand Up @@ -384,14 +384,48 @@ 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)
}
{ ..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..<n {
let product = self.limbs[i].to_uint64() * xq + carry
limbs[i] = (product & radix_mask).to_uint()
carry = product >> radix_bit_len
Comment on lines +414 to +418

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

radix_mask and radix_bit_len are already in the right types: let radix_mask : UInt64 = radix - 1 (line 89) and let radix_bit_len = 32 (line 81, Int, which is the expected shift-width type for UInt64 >> Int in moonbit). So product & radix_mask is UInt64 & UInt64 and product >> radix_bit_len is UInt64 >> Int, no implicit conversion. The existing grade_school_mul, karatsuba_mul, and div helpers in this same file all use these constants the same way (see e.g. lines 595, 597, 670–688), so the new code follows established convention and stays in sync if those constants are ever retyped.

}
let len = if carry == 0UL {
n
} else {
limbs[n] = carry.to_uint()
n + 1
}
{ limbs, sign: Positive, len }

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documented in ada88f8. The magnitude-only contract here mirrors grade_school_mul (line 447 in this file returns sign: Positive the same way) and karatsuba_mulMul::mul always overwrites sign with the combined sign of the operands at the dispatch site. Renaming to mul_single_limb_abs would break the naming symmetry with those other helpers, so I kept the name and added a doc comment that spells out the contract and warns against direct callers needing a signed product.

}

// Simplest way to multiply two BigInts.

///|
Expand Down
Loading