use crate::algos::support::mg_divide::mul_div_pow10_with;
use crate::int::types::Int;
use crate::support::rounding::RoundingMode;
#[inline]
#[must_use]
pub(crate) fn mul_native<const N: usize, const SCALE: u32>(
a: Int<N>,
b: Int<N>,
mode: RoundingMode,
) -> Int<N> {
if N == 1 {
let n = a.as_i128() * b.as_i128();
let scaled: i128 = if SCALE == 0 {
n
} else {
let m_mag: u64 = 10u64.pow(SCALE);
crate::macros::arithmetic::i128_divrem_by_u64_with_mode(n, m_mag, mode)
};
assert!(
scaled >= i64::MIN as i128 && scaled <= i64::MAX as i128,
"attempt to multiply with overflow"
);
return Int::<N>::from_i128(scaled);
}
let ai = a.as_i128();
let bi = b.as_i128();
match mul_div_pow10_with::<SCALE>(ai, bi, mode) {
Some(q) => Int::<N>::from_i128(q),
None => panic!("attempt to multiply with overflow"),
}
}
#[cfg(test)]
mod tests {
use super::mul_native;
use crate::int::types::Int;
use crate::support::rounding::RoundingMode;
const MODE: RoundingMode = RoundingMode::HalfToEven;
#[test]
fn mul_native_n1_matches_naive() {
const S: u32 = 6;
let m = 10i128.pow(S);
let cases: &[(i64, i64)] = &[
(0, 0),
(1_000_000, 2_000_000),
(-1_000_000, 2_000_000),
(1_000_000, -2_000_000),
(-1_000_000, -2_000_000),
(1_234_567, 7_654_321),
(999_999, 999_999),
(i32::MAX as i64, 1_000_000),
];
for &(a, b) in cases {
let want = ((a as i128) * (b as i128)) / m;
let got = mul_native::<1, S>(Int::<1>::from_i64(a), Int::<1>::from_i64(b), MODE);
assert_eq!(got.to_i128(), want, "mul_native n1 ({a}, {b})");
}
}
#[test]
fn mul_native_n2_matches_naive() {
const S: u32 = 12;
let m = 10i128.pow(S);
let cases: &[(i128, i128)] = &[
(0, 0),
(1_000_000_000_000_i128, 2_000_000_000_000_i128),
(-1_000_000_000_000_i128, 2_000_000_000_000_i128),
(5_000_000_000_000_i128, 4_000_000_000_000_i128),
];
for &(a, b) in cases {
assert_eq!((a * b) % m, 0, "test operands must be exact for ({a}, {b})");
let want = (a * b) / m;
let got = mul_native::<2, S>(Int::<2>::from_i128(a), Int::<2>::from_i128(b), MODE);
assert_eq!(got.to_i128(), want, "mul_native n2 ({a}, {b})");
}
}
}