#![allow(missing_docs)]
use crate::Freq;
#[inline]
pub fn mul_hi_u64(a: u64, b: u64) -> u64 {
let prod = a as u128 * b as u128;
(prod >> 64) as u64
}
#[inline]
pub fn reciprocal_shift(freq: Freq) -> u32 {
let mut shift = 1u32;
while freq > (1u32 << shift) {
shift += 1;
}
shift
}
#[inline]
pub fn compute_reciprocal_u64(freq: Freq) -> u64 {
let x0: u64 = (freq - 1) as u64;
let shift = reciprocal_shift(freq);
let x1: u64 = 1u64 << (shift + 31);
let t1 = x1 / freq as u64;
let x0_adj = x0 + ((x1 % freq as u64) << 32);
let t0 = x0_adj / freq as u64;
t0 + (t1 << 32)
}
#[inline]
pub fn compute_reciprocal_u32(freq: Freq) -> u32 {
let shift = reciprocal_shift(freq);
let bits: u32 = 32;
let nom: u64 = (1u64 << (shift + bits - 1)) + (freq - 1) as u64;
(nom / freq as u64) as u32
}
#[inline]
pub fn freq_one_reciprocal_u64() -> (u64, u32) {
(!0u64, 0)
}
#[inline]
pub fn freq_one_reciprocal_u32() -> (u32, u32) {
(!0u32, 0)
}
#[inline]
pub fn fast_quotient_u64(x: u64, freq_rcp: u64, rcp_shift: u32) -> u64 {
mul_hi_u64(x, freq_rcp) >> rcp_shift
}
#[inline]
pub fn fast_quotient_u32(x: u32, freq_rcp: u32, rcp_shift: u32) -> u32 {
((x as u64 * freq_rcp as u64) >> rcp_shift) as u32
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mul_hi_u64() {
assert_eq!(mul_hi_u64(3, 5), 0);
assert_eq!(mul_hi_u64(!0u64, 1u64), 0);
let a: u64 = 1u64 << 63;
assert_eq!(mul_hi_u64(a, a), 1u64 << 62);
assert_eq!(mul_hi_u64(!0u64, !0u64), !0u64 - 1);
}
#[test]
fn test_reciprocal_shift() {
assert_eq!(reciprocal_shift(1), 1);
assert_eq!(reciprocal_shift(2), 1); assert_eq!(reciprocal_shift(3), 2); assert_eq!(reciprocal_shift(4), 2); assert_eq!(reciprocal_shift(5), 3); assert_eq!(reciprocal_shift(8), 3); assert_eq!(reciprocal_shift(9), 4);
}
#[test]
fn test_compute_reciprocal_u64() {
let rcp = compute_reciprocal_u64(3);
assert!(rcp > 0);
let x: u64 = 1u64 << 31;
let q = fast_quotient_u64(x, rcp, reciprocal_shift(3) - 1);
assert_eq!(q, (1u64 << 31) / 3);
}
#[test]
fn test_fast_division_matches_exact() {
let freqs = [2u32, 3, 5, 10, 100, 1000, 0x7FFF];
for freq in freqs {
let shift = reciprocal_shift(freq) - 1;
let rcp = compute_reciprocal_u64(freq);
for x in [1u64, 100, 1u64 << 20, 1u64 << 31, 1u64 << 40, 1u64 << 50] {
let fast = fast_quotient_u64(x, rcp, shift);
let exact = x / freq as u64;
assert_eq!(fast, exact, "freq={} x={}", freq, x);
}
}
}
#[test]
fn test_freq_one_special_case_division() {
let (rcp, shift) = freq_one_reciprocal_u64();
assert_eq!(rcp, !0u64);
assert_eq!(shift, 0);
let x: u64 = 100;
let q = fast_quotient_u64(x, rcp, shift);
assert_eq!(q, x - 1);
}
}