mod binary_impl;
mod display;
mod error;
mod reciprocal;
mod shift;
mod ubinary;
mod uxbinary;
mod xbinary;
pub use binary_impl::Binary;
pub use error::{BinaryError, XBinaryError};
pub use reciprocal::{ReciprocalRounding, reciprocal_of_biguint, reciprocal_rounded_abs_extended};
pub use ubinary::UBinary;
pub use uxbinary::UXBinary;
pub use xbinary::XBinary;
use num_bigint::{BigInt, BigUint};
use num_traits::{One, Zero};
use crate::ordered_pair::{AbsDistance, AddWidth, Interval, Unsigned};
pub type Bounds = Interval<XBinary, UXBinary>;
pub type FiniteBounds = Interval<Binary, UBinary>;
impl FiniteBounds {
pub fn lo(&self) -> &Binary {
self.small()
}
pub fn hi(&self) -> Binary {
self.large()
}
pub fn width_as_binary(&self) -> Binary {
self.width().to_binary()
}
pub fn interval_add(&self, other: &Self) -> Self {
let new_lower = self.lo().add(other.lo());
let new_width = self.width().add(other.width());
Self::from_lower_and_width(new_lower, new_width)
}
pub fn interval_sub(&self, other: &Self) -> Self {
let other_hi = other.hi();
let new_lower = self.lo().sub(&other_hi);
let new_width = self.width().add(other.width());
Self::from_lower_and_width(new_lower, new_width)
}
pub fn interval_neg(&self) -> Self {
let new_lower = self.hi().neg();
Self::from_lower_and_width(new_lower, self.width().clone())
}
pub fn scale_positive(&self, k: &UBinary) -> Self {
let k_binary = k.to_binary();
let new_lower = self.lo().mul(&k_binary);
let new_width = self.width().mul(k);
Self::from_lower_and_width(new_lower, new_width)
}
pub fn scale_bigint(&self, k: &BigInt) -> Self {
use num_traits::Signed;
let abs_k = UBinary::new(k.magnitude().clone(), BigInt::zero());
if k.is_negative() {
self.interval_neg().scale_positive(&abs_k)
} else {
self.scale_positive(&abs_k)
}
}
pub fn midpoint(&self) -> Binary {
let width = self.width().to_binary();
let half_width = Binary::new(width.mantissa().clone(), width.exponent() - BigInt::one());
self.lo().add(&half_width)
}
}
impl Unsigned for BigUint {}
impl AbsDistance<BigInt, BigUint> for BigInt {
fn abs_distance(self, other: BigInt) -> BigUint {
(self - other).magnitude().clone()
}
}
impl AddWidth<BigInt, BigUint> for BigInt {
fn add_width(self, width: BigUint) -> Self {
self + BigInt::from(width)
}
}
#[cfg(test)]
mod integration_tests {
use super::*;
use crate::test_utils::{bin, xbin};
#[test]
fn bounds_reject_invalid_order() {
let lower = xbin(1, 0);
let upper = xbin(-1, 0);
let result = Bounds::new_checked(lower, upper);
assert!(result.is_err());
}
#[test]
fn binary_to_ubinary_to_xbinary_roundtrip() {
let original = bin(7, 3);
let ubinary = UBinary::try_from_binary(&original).expect("should succeed");
let back = ubinary.to_binary();
assert_eq!(original, back);
}
#[test]
fn uxbinary_xbinary_conversion() {
use num_bigint::BigUint;
let ub = UBinary::new(BigUint::from(5u32), BigInt::from(2));
let uxb = UXBinary::Finite(ub);
let xb = XBinary::from(uxb);
if let XBinary::Finite(binary) = xb {
assert_eq!(binary.mantissa(), &BigInt::from(5));
assert_eq!(binary.exponent(), &BigInt::from(2));
} else {
panic!("expected finite value");
}
}
#[test]
fn bounds_from_lower_and_width_constructs_correctly() {
let lower = xbin(5, 0);
let width = UXBinary::Finite(UBinary::new(
num_bigint::BigUint::from(3u32),
BigInt::from(0),
));
let bounds = Bounds::from_lower_and_width(lower.clone(), width.clone());
assert_eq!(bounds.small(), &xbin(5, 0));
assert_eq!(bounds.width(), &width);
assert_eq!(bounds.large(), xbin(8, 0));
}
#[test]
fn bounds_from_lower_and_width_matches_new() {
let lower = xbin(10, 0);
let upper = xbin(25, 0);
let via_new = Bounds::new(lower.clone(), upper.clone());
let via_from_lower_and_width =
Bounds::from_lower_and_width(lower.clone(), via_new.width().clone());
assert_eq!(via_new, via_from_lower_and_width);
}
#[test]
fn bounds_from_lower_and_width_zero_width() {
let lower = xbin(42, 0);
let width = UXBinary::Finite(UBinary::new(
num_bigint::BigUint::from(0u32),
BigInt::from(0),
));
let bounds = Bounds::from_lower_and_width(lower.clone(), width);
assert_eq!(bounds.small(), &xbin(42, 0));
assert_eq!(bounds.large(), xbin(42, 0));
}
#[test]
fn finite_bounds_interval_subtraction() {
let a = FiniteBounds::new(bin(1, 0), bin(2, 0)); let b = FiniteBounds::new(bin(3, 0), bin(5, 0));
let result = a.interval_sub(&b);
assert_eq!(result.lo(), &bin(-4, 0));
assert_eq!(result.hi(), bin(-1, 0));
}
#[test]
fn finite_bounds_interval_negation() {
let a = FiniteBounds::new(bin(1, 0), bin(3, 0)); let neg_a = a.interval_neg(); assert_eq!(neg_a.lo(), &bin(-3, 0));
assert_eq!(neg_a.hi(), bin(-1, 0));
}
#[test]
fn finite_bounds_join_overlapping() {
let a = FiniteBounds::new(bin(1, 0), bin(4, 0)); let b = FiniteBounds::new(bin(3, 0), bin(6, 0));
let result = a.join(&b);
assert_eq!(result.lo(), &bin(1, 0));
assert_eq!(result.hi(), bin(6, 0));
}
#[test]
fn finite_bounds_join_disjoint() {
let a = FiniteBounds::new(bin(1, 0), bin(2, 0)); let b = FiniteBounds::new(bin(5, 0), bin(7, 0));
let result = a.join(&b);
assert_eq!(result.lo(), &bin(1, 0));
assert_eq!(result.hi(), bin(7, 0));
}
#[test]
fn finite_bounds_join_nested() {
let outer = FiniteBounds::new(bin(1, 0), bin(10, 0)); let inner = FiniteBounds::new(bin(3, 0), bin(5, 0));
let result = outer.join(&inner);
assert_eq!(result.lo(), &bin(1, 0));
assert_eq!(result.hi(), bin(10, 0));
}
#[test]
fn finite_bounds_intersection_overlapping() {
let a = FiniteBounds::new(bin(1, 0), bin(4, 0)); let b = FiniteBounds::new(bin(3, 0), bin(6, 0));
let result = a.intersection(&b).expect("should overlap");
assert_eq!(result.lo(), &bin(3, 0));
assert_eq!(result.hi(), bin(4, 0));
}
#[test]
fn finite_bounds_intersection_disjoint() {
let a = FiniteBounds::new(bin(1, 0), bin(2, 0)); let b = FiniteBounds::new(bin(5, 0), bin(7, 0));
let result = a.intersection(&b);
assert!(
result.is_none(),
"disjoint intervals should have no intersection"
);
}
#[test]
fn finite_bounds_intersection_nested() {
let outer = FiniteBounds::new(bin(1, 0), bin(10, 0)); let inner = FiniteBounds::new(bin(3, 0), bin(5, 0));
let result = outer.intersection(&inner).expect("should overlap");
assert_eq!(result.lo(), &bin(3, 0));
assert_eq!(result.hi(), bin(5, 0));
}
#[test]
fn finite_bounds_intersection_touching() {
let a = FiniteBounds::new(bin(1, 0), bin(3, 0)); let b = FiniteBounds::new(bin(3, 0), bin(5, 0));
let result = a.intersection(&b).expect("should touch at a point");
assert_eq!(result.lo(), &bin(3, 0));
assert_eq!(result.hi(), bin(3, 0));
}
}