use num_bigint::{BigInt, BigUint};
use num_integer::Integer;
use num_traits::{One, Signed, ToPrimitive, Zero};
use super::binary_impl::Binary;
use super::error::BinaryError;
use super::xbinary::XBinary;
#[derive(Clone, Copy, Debug)]
pub enum ReciprocalRounding {
Floor,
Ceil,
}
pub fn reciprocal_of_biguint(
denominator: &BigUint,
precision_bits: usize,
rounding: ReciprocalRounding,
) -> Binary {
let numerator = BigUint::one() << precision_bits;
let quotient = match rounding {
ReciprocalRounding::Floor => numerator.div_floor(denominator),
ReciprocalRounding::Ceil => {
(&numerator + denominator - BigUint::one()).div_floor(denominator)
}
};
let exponent = -BigInt::from(precision_bits);
Binary::new(BigInt::from(quotient), exponent)
}
pub fn reciprocal_rounded_abs_extended(
value: &XBinary,
precision_bits: &BigInt,
rounding: ReciprocalRounding,
) -> Result<XBinary, BinaryError> {
match value {
XBinary::Finite(finite_value) => {
let abs_mantissa = finite_value.mantissa().abs();
let shift_bits = precision_bits - finite_value.exponent();
let quotient = if shift_bits.is_negative() {
match rounding {
ReciprocalRounding::Floor => BigInt::zero(),
ReciprocalRounding::Ceil => BigInt::one(),
}
} else {
let shift = precision_bits_to_usize(&shift_bits)?;
let numerator = BigInt::one() << shift;
match rounding {
ReciprocalRounding::Floor => numerator.div_floor(&abs_mantissa),
ReciprocalRounding::Ceil => numerator.div_ceil(&abs_mantissa),
}
};
let exponent = reciprocal_exponent(precision_bits)?;
Ok(XBinary::Finite(Binary::new(quotient, exponent)))
}
XBinary::NegInf | XBinary::PosInf => Ok(XBinary::Finite(Binary::zero())),
}
}
fn reciprocal_exponent(precision_bits: &BigInt) -> Result<BigInt, BinaryError> {
let precision = precision_bits_to_exponent(precision_bits)?;
Ok(-precision)
}
fn precision_bits_to_usize(precision_bits: &BigInt) -> Result<usize, BinaryError> {
if precision_bits.is_negative() {
return Err(BinaryError::ReciprocalOverflow);
}
precision_bits
.to_usize()
.ok_or(BinaryError::ReciprocalOverflow)
}
fn precision_bits_to_exponent(precision_bits: &BigInt) -> Result<BigInt, BinaryError> {
if precision_bits.is_negative() {
return Err(BinaryError::ReciprocalOverflow);
}
Ok(precision_bits.clone())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::test_utils::xbin;
#[test]
fn reciprocal_of_biguint_basic() {
let denom = BigUint::from(5u32);
let result = reciprocal_of_biguint(&denom, 8, ReciprocalRounding::Floor);
assert_eq!(result.mantissa(), &BigInt::from(51));
assert_eq!(result.exponent(), &BigInt::from(-8));
}
#[test]
fn reciprocal_of_biguint_rounding_modes() {
let denom = BigUint::from(3u32);
let floor = reciprocal_of_biguint(&denom, 8, ReciprocalRounding::Floor);
let ceil = reciprocal_of_biguint(&denom, 8, ReciprocalRounding::Ceil);
assert_eq!(floor.mantissa(), &BigInt::from(85));
assert_eq!(floor.exponent(), &BigInt::from(-8));
assert_eq!(ceil.mantissa(), &BigInt::from(43));
assert_eq!(ceil.exponent(), &BigInt::from(-7));
assert!(ceil > floor);
}
#[test]
fn reciprocal_of_infinity_is_zero() {
let precision = BigInt::from(10);
let result = reciprocal_rounded_abs_extended(
&XBinary::PosInf,
&precision,
ReciprocalRounding::Floor,
)
.expect("should succeed");
assert!(result.is_zero());
let result =
reciprocal_rounded_abs_extended(&XBinary::NegInf, &precision, ReciprocalRounding::Ceil)
.expect("should succeed");
assert!(result.is_zero());
}
#[test]
fn reciprocal_basic_computation() {
let value = xbin(1, 1); let precision = BigInt::from(4);
let result = reciprocal_rounded_abs_extended(&value, &precision, ReciprocalRounding::Floor)
.expect("should succeed");
if let XBinary::Finite(binary) = result {
assert_eq!(binary.mantissa(), &BigInt::from(1)); assert_eq!(binary.exponent(), &BigInt::from(-1)); } else {
panic!("expected finite result");
}
}
#[test]
fn reciprocal_rounding_modes() {
let value = xbin(3, 0); let precision = BigInt::from(8);
let floor = reciprocal_rounded_abs_extended(&value, &precision, ReciprocalRounding::Floor)
.expect("should succeed");
let ceil = reciprocal_rounded_abs_extended(&value, &precision, ReciprocalRounding::Ceil)
.expect("should succeed");
assert!(ceil >= floor);
}
#[test]
fn reciprocal_handles_negative_mantissa() {
let neg_value = xbin(-1, 2); let pos_value = xbin(1, 2); let precision = BigInt::from(8);
let neg_result =
reciprocal_rounded_abs_extended(&neg_value, &precision, ReciprocalRounding::Floor)
.expect("should succeed");
let pos_result =
reciprocal_rounded_abs_extended(&pos_value, &precision, ReciprocalRounding::Floor)
.expect("should succeed");
assert_eq!(neg_result, pos_result);
}
#[test]
fn reciprocal_negative_shift_floor_is_zero() {
let value = xbin(1, 100); let precision = BigInt::from(10);
let result = reciprocal_rounded_abs_extended(&value, &precision, ReciprocalRounding::Floor)
.expect("should succeed");
assert!(result.is_zero());
}
#[test]
fn reciprocal_negative_shift_ceil_is_one() {
let value = xbin(1, 100); let precision = BigInt::from(10);
let result = reciprocal_rounded_abs_extended(&value, &precision, ReciprocalRounding::Ceil)
.expect("should succeed");
if let XBinary::Finite(binary) = result {
assert_eq!(binary.mantissa(), &BigInt::from(1));
assert_eq!(binary.exponent(), &BigInt::from(-10));
} else {
panic!("expected finite result");
}
}
}