pub(crate) const CHUNK_THRESHOLD: u64 = 1u64 << 32;
#[inline(always)]
pub(crate) const fn div_u128_chunked(abs: u128, scale: u64) -> u128 {
let hi = (abs >> 64) as u64;
let lo = abs as u64;
let q_hi = hi / scale;
let r_hi = hi % scale;
let mid = (r_hi << 32) | (lo >> 32);
let q_mid = mid / scale;
let r_mid = mid % scale;
let bot = (r_mid << 32) | (lo as u32 as u64);
let q_bot = bot / scale;
((q_hi as u128) << 64) | ((q_mid as u128) << 32) | (q_bot as u128)
}
#[inline(always)]
pub(crate) const fn div_i128_by_scale(
value: i128,
scale: i128,
scale_u64: u64,
use_chunked: bool,
) -> Option<i64> {
if value == 0 {
return Some(0);
}
let is_negative = value < 0;
let abs_value = value.unsigned_abs();
let quotient = if use_chunked {
div_u128_chunked(abs_value, scale_u64)
} else {
abs_value / (scale as u128)
};
if is_negative {
if quotient > (i64::MAX as u128) + 1 {
return None;
}
Some((-(quotient as i128)) as i64)
} else {
if quotient > i64::MAX as u128 {
return None;
}
Some(quotient as i64)
}
}
#[inline(always)]
pub(crate) const fn div_i128_by_scale_wrapping(
value: i128,
scale: i128,
scale_u64: u64,
use_chunked: bool,
) -> i64 {
if value == 0 {
return 0;
}
let is_negative = value < 0;
let abs_value = value.unsigned_abs();
let quotient = if use_chunked {
div_u128_chunked(abs_value, scale_u64)
} else {
abs_value / (scale as u128)
};
let quotient = quotient as i64;
if is_negative {
quotient.wrapping_neg()
} else {
quotient
}
}
#[cfg(test)]
mod tests {
extern crate std;
use std::vec::Vec;
use super::*;
#[test]
fn chunked_matches_native_d64() {
let scale: u64 = 100_000_000;
let mut rng = 42u64;
for _ in 0..100_000 {
rng = rng.wrapping_mul(6_364_136_223_846_793_005).wrapping_add(1);
let a = rng as i64;
rng = rng.wrapping_mul(6_364_136_223_846_793_005).wrapping_add(1);
let b = rng as i64;
let product = (a as i128) * (b as i128);
let abs = product.unsigned_abs();
let native = abs / (scale as u128);
let chunked = div_u128_chunked(abs, scale);
assert_eq!(
chunked, native,
"mismatch for a={a}, b={b}, product={product}"
);
}
}
#[test]
fn chunked_matches_native_all_small_scales() {
let scales: Vec<u64> = (1..=9)
.map(|d| {
let mut s: u64 = 1;
for _ in 0..d {
s *= 10;
}
s
})
.collect();
for &scale in &scales {
assert!(scale < CHUNK_THRESHOLD, "scale {scale} >= 2^32");
let mut rng = scale.wrapping_mul(7919);
for _ in 0..10_000 {
rng = rng.wrapping_mul(6_364_136_223_846_793_005).wrapping_add(1);
let a = rng as i64;
rng = rng.wrapping_mul(6_364_136_223_846_793_005).wrapping_add(1);
let b = rng as i64;
let product = (a as i128) * (b as i128);
let abs = product.unsigned_abs();
let native = abs / (scale as u128);
let chunked = div_u128_chunked(abs, scale);
assert_eq!(chunked, native, "mismatch for scale={scale}, a={a}, b={b}");
}
}
}
#[test]
fn chunked_edge_cases() {
let scale: u64 = 100_000_000;
assert_eq!(div_u128_chunked(0, scale), 0);
assert_eq!(div_u128_chunked(1, scale), 0);
assert_eq!(div_u128_chunked(scale as u128, scale), 1);
assert_eq!(div_u128_chunked(scale as u128 - 1, scale), 0);
let max_product = (i64::MAX as u128) * (i64::MAX as u128);
let expected = max_product / (scale as u128);
assert_eq!(div_u128_chunked(max_product, scale), expected);
let exact_product = (i64::MAX as u128) * (scale as u128);
assert_eq!(div_u128_chunked(exact_product, scale), i64::MAX as u128);
}
#[test]
fn signed_wrapper_negative() {
let scale = 100_000_000i128;
let scale_u64 = 100_000_000u64;
let result = div_i128_by_scale(300_000_000, scale, scale_u64, true);
assert_eq!(result, Some(3));
let result = div_i128_by_scale(-300_000_000, scale, scale_u64, true);
assert_eq!(result, Some(-3));
let result = div_i128_by_scale(0, scale, scale_u64, true);
assert_eq!(result, Some(0));
let huge = (i64::MAX as i128) * (i64::MAX as i128);
assert!(div_i128_by_scale(huge, scale, scale_u64, true).is_none());
}
#[test]
fn chunked_vs_native_through_wrapper() {
let scale = 100_000_000i128;
let scale_u64 = 100_000_000u64;
let mut rng = 99u64;
for _ in 0..50_000 {
rng = rng.wrapping_mul(6_364_136_223_846_793_005).wrapping_add(1);
let a = rng as i64;
rng = rng.wrapping_mul(6_364_136_223_846_793_005).wrapping_add(1);
let b = rng as i64;
let product = (a as i128) * (b as i128);
let chunked = div_i128_by_scale(product, scale, scale_u64, true);
let native = div_i128_by_scale(product, scale, scale_u64, false);
assert_eq!(chunked, native, "mismatch for a={a}, b={b}");
}
}
}