use crate::deployment_profiles::DeploymentProfile;
#[cfg(feature = "infinite-precision")]
use num_bigint::BigInt;
#[cfg(feature = "infinite-precision")]
use num_traits::{Zero, One, ToPrimitive};
use crate::fixed_point::universal::ugod::{UniversalTieredArithmetic, DomainType as UGODDomainType};
use std::fmt;
use std::cmp::Ordering;
use crate::fixed_point::core_types::domain_metadata::{
ShadowMetadata,
DomainType,
ExactnessType,
};
use crate::fixed_point::domains::binary_fixed::{I256, I512, I1024};
use crate::fixed_point::symbolic::symbolic_arithmetic::{
mul_i8_rational, mul_i16_rational, mul_i32_rational, mul_i64_rational, mul_i128_rational,
add_i8_rational, add_i16_rational, add_i32_rational, add_i64_rational, add_i128_rational,
sub_i8_rational, sub_i16_rational, sub_i32_rational, sub_i64_rational, sub_i128_rational,
div_i8_rational, div_i16_rational, div_i32_rational, div_i64_rational, div_i128_rational,
gcd_unsigned,
};
#[derive(Debug, Clone)]
pub struct RationalNumber {
storage: RationalStorage,
metadata: ShadowMetadata,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RationalStorage {
Tiny { num: i8, den: u8 },
Small { num: i16, den: u16 },
Medium { num: i32, den: u32 },
Large { num: i64, den: u64 },
Huge { num: i128, den: u128 },
#[cfg(not(feature = "embedded"))]
Massive { num: I256, den: I256 },
Ultra { num: I512, den: I512 },
#[cfg(feature = "infinite-precision")]
Infinite { num: BigInt, den: BigInt },
}
pub use crate::fixed_point::core_types::errors::OverflowDetected;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RationalParts {
Tiny(i8, u8),
Small(i16, u16),
Medium(i32, u32),
Large(i64, u64),
Huge(i128, u128),
#[cfg(not(feature = "embedded"))]
Massive(I256, I256),
Ultra(I512, I512),
#[cfg(feature = "infinite-precision")]
Infinite(BigInt, BigInt),
}
impl RationalParts {
pub fn tier_level(&self) -> u8 {
match self {
RationalParts::Tiny(..) => 1,
RationalParts::Small(..) => 2,
RationalParts::Medium(..) => 3,
RationalParts::Large(..) => 4,
RationalParts::Huge(..) => 5,
#[cfg(not(feature = "embedded"))]
RationalParts::Massive(..) => 6,
RationalParts::Ultra(..) => 7,
#[cfg(feature = "infinite-precision")]
RationalParts::Infinite(..) => 8,
}
}
pub fn try_as_i128(&self) -> Option<(i128, u128)> {
match self {
RationalParts::Tiny(n, d) => Some((*n as i128, *d as u128)),
RationalParts::Small(n, d) => Some((*n as i128, *d as u128)),
RationalParts::Medium(n, d) => Some((*n as i128, *d as u128)),
RationalParts::Large(n, d) => Some((*n as i128, *d as u128)),
RationalParts::Huge(n, d) => Some((*n, *d)),
#[cfg(not(feature = "embedded"))]
RationalParts::Massive(n, d) => {
if n.fits_in_i128() && d.fits_in_i128() {
Some((n.as_i128(), d.as_i128() as u128))
} else {
None
}
},
RationalParts::Ultra(..) => None, #[cfg(feature = "infinite-precision")]
RationalParts::Infinite(n, d) => {
match (n.to_i128(), d.to_u128()) {
(Some(n_i128), Some(d_u128)) => Some((n_i128, d_u128)),
_ => None,
}
},
}
}
#[cfg(not(feature = "embedded"))]
pub fn try_as_i256_pair(&self) -> Option<(I256, I256)> {
match self {
RationalParts::Tiny(n, d) => Some((I256::from_i128(*n as i128), I256::from_i128(*d as i128))),
RationalParts::Small(n, d) => Some((I256::from_i128(*n as i128), I256::from_i128(*d as i128))),
RationalParts::Medium(n, d) => Some((I256::from_i128(*n as i128), I256::from_i128(*d as i128))),
RationalParts::Large(n, d) => Some((I256::from_i128(*n as i128), I256::from_i128(*d as i128))),
RationalParts::Huge(n, d) => Some((I256::from_i128(*n), I256::from_i128(*d as i128))),
#[cfg(not(feature = "embedded"))]
RationalParts::Massive(n, d) => Some((*n, *d)),
RationalParts::Ultra(..) => None, #[cfg(feature = "infinite-precision")]
RationalParts::Infinite(..) => None,
}
}
pub fn try_as_i512_pair(&self) -> Option<(I512, I512)> {
match self {
RationalParts::Tiny(n, d) => Some((I512::from_i128(*n as i128), I512::from_i128(*d as i128))),
RationalParts::Small(n, d) => Some((I512::from_i128(*n as i128), I512::from_i128(*d as i128))),
RationalParts::Medium(n, d) => Some((I512::from_i128(*n as i128), I512::from_i128(*d as i128))),
RationalParts::Large(n, d) => Some((I512::from_i128(*n as i128), I512::from_i128(*d as i128))),
RationalParts::Huge(n, d) => Some((I512::from_i128(*n), I512::from_i128(*d as i128))),
#[cfg(not(feature = "embedded"))]
RationalParts::Massive(n, d) => Some((I512::from_i256(*n), I512::from_i256(*d))),
RationalParts::Ultra(n, d) => Some((*n, *d)),
#[cfg(feature = "infinite-precision")]
RationalParts::Infinite(..) => None, }
}
}
impl RationalStorage {
#[allow(unreachable_code)]
pub fn max_tier_for_profile() -> u8 {
#[cfg(feature = "infinite-precision")] { return 8; }
#[cfg(not(feature = "infinite-precision"))] { return 7; }
7 }
pub fn memory_size(&self) -> usize {
match self {
RationalStorage::Tiny { .. } => 2, RationalStorage::Small { .. } => 4, RationalStorage::Medium { .. } => 8, RationalStorage::Large { .. } => 16, RationalStorage::Huge { .. } => 32, #[cfg(not(feature = "embedded"))]
RationalStorage::Massive { .. } => 64, RationalStorage::Ultra { .. } => 128, #[cfg(feature = "infinite-precision")]
RationalStorage::Infinite { .. } => 48, }
}
pub fn tier_level(&self) -> u8 {
match self {
RationalStorage::Tiny { .. } => 1,
RationalStorage::Small { .. } => 2,
RationalStorage::Medium { .. } => 3,
RationalStorage::Large { .. } => 4,
RationalStorage::Huge { .. } => 5,
#[cfg(not(feature = "embedded"))]
RationalStorage::Massive { .. } => 6,
RationalStorage::Ultra { .. } => 7,
#[cfg(feature = "infinite-precision")]
RationalStorage::Infinite { .. } => 8,
}
}
}
impl RationalNumber {
pub fn new(numerator: i128, denominator: u128) -> Self {
assert_ne!(denominator, 0, "RationalNumber: Denominator cannot be zero");
let storage = Self::optimal_storage_tier(numerator, denominator);
let metadata = Self::analyze_metadata(numerator, denominator);
Self { storage, metadata }
}
#[cfg(not(feature = "embedded"))]
pub fn from_i256_pair(numerator: I256, denominator: I256) -> Self {
assert!(!denominator.is_zero(), "RationalNumber: Denominator cannot be zero");
let storage = RationalStorage::Massive {
num: numerator,
den: denominator,
};
use crate::fixed_point::core_types::domain_metadata::{ShadowMetadata, DomainType, ExactnessType};
let metadata = ShadowMetadata::new(DomainType::Symbolic, ExactnessType::Exact);
Self { storage, metadata }
}
pub fn from_i512_pair(numerator: I512, denominator: I512) -> Self {
assert!(!denominator.is_zero(), "RationalNumber: Denominator cannot be zero");
let storage = RationalStorage::Ultra {
num: numerator,
den: denominator,
};
use crate::fixed_point::core_types::domain_metadata::{ShadowMetadata, DomainType, ExactnessType};
let metadata = ShadowMetadata::new(DomainType::Symbolic, ExactnessType::Exact);
Self { storage, metadata }
}
fn optimal_storage_tier(numerator: i128, denominator: u128) -> RationalStorage {
let max_val = numerator.abs().max(denominator as i128);
let max_tier = RationalStorage::max_tier_for_profile();
let optimal_tier = match max_val {
0..=127 if max_tier >= 1 => {
RationalStorage::Tiny {
num: numerator as i8,
den: denominator as u8
}
},
128..=32767 if max_tier >= 2 => {
RationalStorage::Small {
num: numerator as i16,
den: denominator as u16
}
},
32768..=2147483647 if max_tier >= 3 => {
RationalStorage::Medium {
num: numerator as i32,
den: denominator as u32
}
},
2147483648..=9223372036854775807 if max_tier >= 4 => {
RationalStorage::Large {
num: numerator as i64,
den: denominator as u64
}
},
_ if max_tier >= 5 => {
RationalStorage::Huge {
num: numerator,
den: denominator
}
},
#[cfg(feature = "infinite-precision")]
_ => {
RationalStorage::Infinite {
num: BigInt::from(numerator),
den: BigInt::from(denominator),
}
},
#[cfg(not(feature = "infinite-precision"))]
_ => {
RationalStorage::Huge {
num: numerator,
den: denominator,
}
}
};
optimal_tier
}
fn analyze_metadata(_numerator: i128, _denominator: u128) -> ShadowMetadata {
ShadowMetadata::new(
DomainType::Symbolic,
ExactnessType::Exact,
)
}
pub fn tier_level(&self) -> u8 {
self.storage.tier_level()
}
pub fn universal_tier(&self) -> u8 {
symbolic_to_universal_tier(self.tier_level())
}
pub fn from_ratio(numerator: i128, denominator: u128) -> Self {
Self::new(numerator, denominator)
}
#[cfg(feature = "infinite-precision")]
pub fn from_bigint_ratio(numerator: BigInt, denominator: BigInt) -> Self {
Self {
storage: RationalStorage::Infinite {
num: numerator,
den: denominator
},
metadata: ShadowMetadata::new(DomainType::Symbolic, ExactnessType::Exact),
}
}
pub fn numerator_i128(&self) -> Option<i128> {
match &self.storage {
RationalStorage::Tiny { num, .. } => Some(*num as i128),
RationalStorage::Small { num, .. } => Some(*num as i128),
RationalStorage::Medium { num, .. } => Some(*num as i128),
RationalStorage::Large { num, .. } => Some(*num as i128),
RationalStorage::Huge { num, .. } => Some(*num),
#[cfg(not(feature = "embedded"))]
RationalStorage::Massive { num, .. } => {
if num.fits_in_i128() { Some(num.as_i128()) } else { None }
},
RationalStorage::Ultra { num, .. } => {
if num.fits_in_i128() { Some(num.as_i128()) } else { None }
},
#[cfg(feature = "infinite-precision")]
RationalStorage::Infinite { num, .. } => num.to_i128(),
}
}
pub fn denominator_i128(&self) -> Option<i128> {
match &self.storage {
RationalStorage::Tiny { den, .. } => Some(*den as i128),
RationalStorage::Small { den, .. } => Some(*den as i128),
RationalStorage::Medium { den, .. } => Some(*den as i128),
RationalStorage::Large { den, .. } => Some(*den as i128),
RationalStorage::Huge { den, .. } => Some(*den as i128),
#[cfg(not(feature = "embedded"))]
RationalStorage::Massive { den, .. } => {
if den.fits_in_i128() { Some(den.as_i128()) } else { None }
},
RationalStorage::Ultra { den, .. } => {
if den.fits_in_i128() { Some(den.as_i128()) } else { None }
},
#[cfg(feature = "infinite-precision")]
RationalStorage::Infinite { den, .. } => den.to_i128(),
}
}
#[cfg(feature = "infinite-precision")]
pub fn numerator(&self) -> BigInt {
match &self.storage {
RationalStorage::Tiny { num, .. } => BigInt::from(*num),
RationalStorage::Small { num, .. } => BigInt::from(*num),
RationalStorage::Medium { num, .. } => BigInt::from(*num),
RationalStorage::Large { num, .. } => BigInt::from(*num),
RationalStorage::Huge { num, .. } => BigInt::from(*num),
#[cfg(not(feature = "embedded"))]
RationalStorage::Massive { num, .. } => BigInt::from(num.as_i128()),
RationalStorage::Ultra { num, .. } => BigInt::from(num.as_i128()),
RationalStorage::Infinite { num, .. } => num.clone(),
}
}
#[cfg(feature = "infinite-precision")]
pub fn denominator(&self) -> BigInt {
match &self.storage {
RationalStorage::Tiny { den, .. } => BigInt::from(*den),
RationalStorage::Small { den, .. } => BigInt::from(*den),
RationalStorage::Medium { den, .. } => BigInt::from(*den),
RationalStorage::Large { den, .. } => BigInt::from(*den),
RationalStorage::Huge { den, .. } => BigInt::from(*den),
#[cfg(not(feature = "embedded"))]
RationalStorage::Massive { den, .. } => BigInt::from(den.as_i128()),
RationalStorage::Ultra { den, .. } => BigInt::from(den.as_i128()),
RationalStorage::Infinite { den, .. } => den.clone(),
}
}
pub fn extract_native(&self) -> RationalParts {
match &self.storage {
RationalStorage::Tiny { num, den } => RationalParts::Tiny(*num, *den),
RationalStorage::Small { num, den } => RationalParts::Small(*num, *den),
RationalStorage::Medium { num, den } => RationalParts::Medium(*num, *den),
RationalStorage::Large { num, den } => RationalParts::Large(*num, *den),
RationalStorage::Huge { num, den } => RationalParts::Huge(*num, *den),
#[cfg(not(feature = "embedded"))]
RationalStorage::Massive { num, den } => RationalParts::Massive(num.clone(), den.clone()),
RationalStorage::Ultra { num, den } => RationalParts::Ultra(num.clone(), den.clone()),
#[cfg(feature = "infinite-precision")]
RationalStorage::Infinite { num, den } => RationalParts::Infinite(num.clone(), den.clone()),
}
}
pub fn to_f64(&self) -> f64 {
match self.extract_native().try_as_i128() {
Some((num, den)) => {
(num as f64) / (den as f64)
}
None => {
#[cfg(feature = "infinite-precision")]
{
use num_bigint::ToBigInt;
let num_big = self.numerator();
let den_big = self.denominator();
num_big.to_f64().unwrap_or(f64::NAN) / den_big.to_f64().unwrap_or(1.0)
}
#[cfg(not(feature = "infinite-precision"))]
{
f64::NAN
}
}
}
}
}
impl RationalNumber {
pub fn try_multiply(&self, other: &Self) -> Result<Self, OverflowDetected> {
let result_tier = Self::predict_multiplication_tier(&self.storage, &other.storage);
let max_allowed_tier = RationalStorage::max_tier_for_profile();
if result_tier > max_allowed_tier {
return Err(OverflowDetected::PrecisionLimit);
}
let math_result = match (&self.storage, &other.storage) {
(RationalStorage::Tiny { num: a_num, den: a_den },
RationalStorage::Tiny { num: b_num, den: b_den }) => {
mul_i8_rational(*a_num, *a_den, *b_num, *b_den)
.map(|(num, den)| (num as i128, den as u128))
},
(RationalStorage::Small { num: a_num, den: a_den },
RationalStorage::Small { num: b_num, den: b_den }) => {
mul_i16_rational(*a_num, *a_den, *b_num, *b_den)
.map(|(num, den)| (num as i128, den as u128))
},
(RationalStorage::Medium { num: a_num, den: a_den },
RationalStorage::Medium { num: b_num, den: b_den }) => {
mul_i32_rational(*a_num, *a_den, *b_num, *b_den)
.map(|(num, den)| (num as i128, den as u128))
},
(RationalStorage::Large { num: a_num, den: a_den },
RationalStorage::Large { num: b_num, den: b_den }) => {
mul_i64_rational(*a_num, *a_den, *b_num, *b_den)
.map(|(num, den)| (num as i128, den as u128))
},
(RationalStorage::Huge { num: a_num, den: a_den },
RationalStorage::Huge { num: b_num, den: b_den }) => {
mul_i128_rational(*a_num, *a_den, *b_num, *b_den)
},
_ => {
return self.multiply_mixed_tiers(other);
}
};
match math_result {
Ok((result_num, result_den)) => {
Ok(Self::new(result_num, result_den))
},
Err(OverflowDetected::TierOverflow) => {
let (a_num, a_den) = self.extract_i128_pair()?;
let (b_num, b_den) = other.extract_i128_pair()?;
match (a_num.checked_mul(b_num), a_den.checked_mul(b_den)) {
(Some(num), Some(den)) => {
let g = gcd_unsigned(num.unsigned_abs(), den);
Ok(Self::new(num / (g as i128), den / g))
}
_ => {
use crate::fixed_point::domains::binary_fixed::i256::mul_i128_to_i256;
let den_a = i128::try_from(a_den).map_err(|_| OverflowDetected::TierOverflow)?;
let den_b = i128::try_from(b_den).map_err(|_| OverflowDetected::TierOverflow)?;
let num256 = mul_i128_to_i256(a_num, b_num);
let den256 = mul_i128_to_i256(den_a, den_b);
Ok(Self::from_i256_pair(num256, den256))
}
}
},
Err(e) => Err(e),
}
}
pub fn multiply_at_max_tier(&self, other: &Self) -> Self {
let self_promoted = self.promote_to_max_tier();
let other_promoted = other.promote_to_max_tier();
#[cfg(feature = "infinite-precision")]
{
if let (RationalStorage::Infinite { num: a_num, den: a_den },
RationalStorage::Infinite { num: b_num, den: b_den }) =
(&self_promoted.storage, &other_promoted.storage) {
let a_num_i128 = a_num.to_i128().unwrap_or(0);
let a_den_i128 = a_den.to_u128().unwrap_or(1);
let b_num_i128 = b_num.to_i128().unwrap_or(0);
let b_den_i128 = b_den.to_u128().unwrap_or(1);
match mul_i128_rational(a_num_i128, a_den_i128, b_num_i128, b_den_i128) {
Ok((result_num, result_den)) => {
Self::new(result_num, result_den)
},
Err(_) => {
let result_num = a_num * b_num;
let result_den = a_den * b_den;
Self {
storage: RationalStorage::Infinite {
num: result_num,
den: result_den
},
metadata: ShadowMetadata::new(DomainType::Symbolic, ExactnessType::Exact),
}
}
}
} else {
unreachable!("promote_to_max_tier should always return BigInt storage")
}
}
#[cfg(not(feature = "infinite-precision"))]
{
let (a_num_i128, a_den_i128) = self_promoted.extract_native().try_as_i128()
.unwrap_or((i128::MAX, 1)); let (b_num_i128, b_den_i128) = other_promoted.extract_native().try_as_i128()
.unwrap_or((i128::MAX, 1));
match mul_i128_rational(a_num_i128, a_den_i128, b_num_i128, b_den_i128) {
Ok((result_num, result_den)) => Self::new(result_num, result_den), Err(_) => {
Self::new(i128::MAX, 1)
}
}
}
}
fn multiply_mixed_tiers(&self, other: &Self) -> Result<Self, OverflowDetected> {
let self_tier = self.storage.tier_level();
let other_tier = other.storage.tier_level();
let target_tier = self_tier.max(other_tier);
if target_tier > RationalStorage::max_tier_for_profile() {
return Err(OverflowDetected::PrecisionLimit);
}
match target_tier {
1 => self.promote_both_to_tiny(other).and_then(|(a, b)| a.multiply_tiny_tier(&b)),
2 => self.promote_both_to_small(other).and_then(|(a, b)| a.multiply_small_tier(&b)),
3 => self.promote_both_to_medium(other).and_then(|(a, b)| a.multiply_medium_tier(&b)),
4 => self.promote_both_to_large(other).and_then(|(a, b)| a.multiply_large_tier(&b)),
5 => self.promote_both_to_huge(other).and_then(|(a, b)| a.multiply_huge_tier(&b)),
#[cfg(not(feature = "embedded"))]
6 => self.promote_both_to_massive(other).and_then(|(a, b)| a.multiply_massive_tier(&b)),
7 => self.promote_both_to_ultra(other).and_then(|(a, b)| a.multiply_ultra_tier(&b)),
#[cfg(feature = "infinite-precision")]
8 => Ok(self.promote_to_max_tier().multiply_at_max_tier(&other.promote_to_max_tier())),
_ => Err(OverflowDetected::PrecisionLimit)
}
}
fn add_mixed_tiers(&self, other: &Self) -> Result<Self, OverflowDetected> {
let self_tier = self.storage.tier_level();
let other_tier = other.storage.tier_level();
let target_tier = self_tier.max(other_tier);
if target_tier > RationalStorage::max_tier_for_profile() {
return Err(OverflowDetected::PrecisionLimit);
}
match target_tier {
1 => self.promote_both_to_tiny(other).and_then(|(a, b)| a.add_tiny_tier(&b)),
2 => self.promote_both_to_small(other).and_then(|(a, b)| a.add_small_tier(&b)),
3 => self.promote_both_to_medium(other).and_then(|(a, b)| a.add_medium_tier(&b)),
4 => self.promote_both_to_large(other).and_then(|(a, b)| a.add_large_tier(&b)),
5 => self.promote_both_to_huge(other).and_then(|(a, b)| a.add_huge_tier(&b)),
#[cfg(not(feature = "embedded"))]
6 => self.promote_both_to_massive(other).and_then(|(a, b)| a.add_massive_tier(&b)),
7 => self.promote_both_to_ultra(other).and_then(|(a, b)| a.add_ultra_tier(&b)),
_ => Err(OverflowDetected::PrecisionLimit)
}
}
fn subtract_mixed_tiers(&self, other: &Self) -> Result<Self, OverflowDetected> {
let self_tier = self.storage.tier_level();
let other_tier = other.storage.tier_level();
let target_tier = self_tier.max(other_tier);
if target_tier > RationalStorage::max_tier_for_profile() {
return Err(OverflowDetected::PrecisionLimit);
}
match target_tier {
1 => self.promote_both_to_tiny(other).and_then(|(a, b)| a.subtract_tiny_tier(&b)),
2 => self.promote_both_to_small(other).and_then(|(a, b)| a.subtract_small_tier(&b)),
3 => self.promote_both_to_medium(other).and_then(|(a, b)| a.subtract_medium_tier(&b)),
4 => self.promote_both_to_large(other).and_then(|(a, b)| a.subtract_large_tier(&b)),
5 => self.promote_both_to_huge(other).and_then(|(a, b)| a.subtract_huge_tier(&b)),
#[cfg(not(feature = "embedded"))]
6 => self.promote_both_to_massive(other).and_then(|(a, b)| a.subtract_massive_tier(&b)),
7 => self.promote_both_to_ultra(other).and_then(|(a, b)| a.subtract_ultra_tier(&b)),
_ => Err(OverflowDetected::PrecisionLimit)
}
}
fn divide_mixed_tiers(&self, other: &Self) -> Result<Self, OverflowDetected> {
let self_tier = self.storage.tier_level();
let other_tier = other.storage.tier_level();
let target_tier = self_tier.max(other_tier);
if target_tier > RationalStorage::max_tier_for_profile() {
return Err(OverflowDetected::PrecisionLimit);
}
let mut tier = target_tier;
loop {
let attempt = match tier {
1 => self.promote_both_to_tiny(other).and_then(|(a, b)| a.divide_tiny_tier(&b)),
2 => self.promote_both_to_small(other).and_then(|(a, b)| a.divide_small_tier(&b)),
3 => self.promote_both_to_medium(other).and_then(|(a, b)| a.divide_medium_tier(&b)),
4 => self.promote_both_to_large(other).and_then(|(a, b)| a.divide_large_tier(&b)),
5 => self.promote_both_to_huge(other).and_then(|(a, b)| a.divide_huge_tier(&b)),
#[cfg(not(feature = "embedded"))]
6 => self.promote_both_to_massive(other).and_then(|(a, b)| a.divide_massive_tier(&b)),
7 => self.promote_both_to_ultra(other).and_then(|(a, b)| a.divide_ultra_tier(&b)),
_ => Err(OverflowDetected::PrecisionLimit)
};
match attempt {
Err(OverflowDetected::TierOverflow)
if tier < 7 && tier < RationalStorage::max_tier_for_profile() =>
{
tier += 1;
#[cfg(feature = "embedded")]
if tier == 6 { tier = 7; }
}
result => return result,
}
}
}
fn predict_multiplication_tier(a: &RationalStorage, b: &RationalStorage) -> u8 {
a.tier_level().max(b.tier_level())
}
fn predict_addition_tier(a: &RationalStorage, b: &RationalStorage) -> u8 {
a.tier_level().max(b.tier_level())
}
fn predict_subtraction_tier(a: &RationalStorage, b: &RationalStorage) -> u8 {
a.tier_level().max(b.tier_level())
}
fn predict_division_tier(a: &RationalStorage, b: &RationalStorage) -> u8 {
a.tier_level().max(b.tier_level())
}
fn promote_to_max_tier(&self) -> Self {
#[cfg(feature = "infinite-precision")]
{
let num = self.numerator();
let den = self.denominator();
Self {
storage: RationalStorage::Infinite { num, den },
metadata: self.metadata,
}
}
#[cfg(not(feature = "infinite-precision"))]
{
match self.extract_native().try_as_i128() {
Some((num, den)) => Self::new(num, den), None => {
Self::new(i128::MAX, 1)
}
}
}
}
fn promote_both_to_tiny(&self, other: &Self) -> Result<(Self, Self), OverflowDetected> {
let a = self.promote_to_tier(1)?;
let b = other.promote_to_tier(1)?;
Ok((a, b))
}
fn promote_both_to_small(&self, other: &Self) -> Result<(Self, Self), OverflowDetected> {
let a = self.promote_to_tier(2)?;
let b = other.promote_to_tier(2)?;
Ok((a, b))
}
fn promote_both_to_medium(&self, other: &Self) -> Result<(Self, Self), OverflowDetected> {
let a = self.promote_to_tier(3)?;
let b = other.promote_to_tier(3)?;
Ok((a, b))
}
fn promote_both_to_large(&self, other: &Self) -> Result<(Self, Self), OverflowDetected> {
let a = self.promote_to_tier(4)?;
let b = other.promote_to_tier(4)?;
Ok((a, b))
}
fn promote_both_to_huge(&self, other: &Self) -> Result<(Self, Self), OverflowDetected> {
let a = self.promote_to_tier(5)?;
let b = other.promote_to_tier(5)?;
Ok((a, b))
}
#[cfg(not(feature = "embedded"))]
fn promote_both_to_massive(&self, other: &Self) -> Result<(Self, Self), OverflowDetected> {
let a = self.promote_to_tier(6)?;
let b = other.promote_to_tier(6)?;
Ok((a, b))
}
fn promote_both_to_ultra(&self, other: &Self) -> Result<(Self, Self), OverflowDetected> {
let a = self.promote_to_tier(7)?;
let b = other.promote_to_tier(7)?;
Ok((a, b))
}
fn promote_to_tier(&self, target_tier: u8) -> Result<Self, OverflowDetected> {
let current_tier = self.storage.tier_level();
if current_tier >= target_tier {
return Ok(self.clone()); }
let max_allowed_tier = RationalStorage::max_tier_for_profile();
if target_tier > max_allowed_tier {
return Err(OverflowDetected::PrecisionLimit);
}
let parts = self.extract_native();
#[cfg(not(feature = "embedded"))]
if target_tier == 6 {
let (num, den) = parts.try_as_i256_pair()
.ok_or(OverflowDetected::PrecisionLimit)?;
return Ok(Self::from_i256_pair(num, den));
}
if target_tier == 7 {
let (num, den) = parts.try_as_i512_pair()
.ok_or(OverflowDetected::PrecisionLimit)?;
return Ok(Self::from_i512_pair(num, den));
}
let (num_i128, den_u128) = parts.try_as_i128()
.ok_or(OverflowDetected::PrecisionLimit)?;
Ok(Self::new(num_i128, den_u128))
}
fn add_tiny_tier(&self, other: &Self) -> Result<Self, OverflowDetected> {
match (&self.storage, &other.storage) {
(RationalStorage::Tiny { num: a_num, den: a_den },
RationalStorage::Tiny { num: b_num, den: b_den }) => {
let num = *a_num as i128 * *b_den as i128 + *b_num as i128 * *a_den as i128;
let den = *a_den as u128 * *b_den as u128;
Ok(Self::new(num, den))
},
_ => Err(OverflowDetected::TierOverflow),
}
}
fn subtract_tiny_tier(&self, other: &Self) -> Result<Self, OverflowDetected> {
match (&self.storage, &other.storage) {
(RationalStorage::Tiny { num: a_num, den: a_den },
RationalStorage::Tiny { num: b_num, den: b_den }) => {
let num = *a_num as i128 * *b_den as i128 - *b_num as i128 * *a_den as i128;
let den = *a_den as u128 * *b_den as u128;
Ok(Self::new(num, den))
},
_ => Err(OverflowDetected::TierOverflow),
}
}
fn multiply_tiny_tier(&self, other: &Self) -> Result<Self, OverflowDetected> {
match (&self.storage, &other.storage) {
(RationalStorage::Tiny { num: a_num, den: a_den },
RationalStorage::Tiny { num: b_num, den: b_den }) => {
Ok(Self::new(*a_num as i128 * *b_num as i128, *a_den as u128 * *b_den as u128))
},
_ => Err(OverflowDetected::TierOverflow),
}
}
fn divide_tiny_tier(&self, other: &Self) -> Result<Self, OverflowDetected> {
match (&self.storage, &other.storage) {
(RationalStorage::Tiny { num: a_num, den: a_den },
RationalStorage::Tiny { num: b_num, den: b_den }) => {
if *b_num == 0 {
return Err(OverflowDetected::PrecisionLimit);
}
let mut num = *a_num as i128 * *b_den as i128;
if *b_num < 0 {
num = -num;
}
let den = *a_den as u128 * b_num.unsigned_abs() as u128;
Ok(Self::new(num, den))
},
_ => Err(OverflowDetected::TierOverflow),
}
}
fn extract_i128_pair(&self) -> Result<(i128, u128), OverflowDetected> {
match &self.storage {
RationalStorage::Tiny { num, den } => Ok((*num as i128, *den as u128)),
RationalStorage::Small { num, den } => Ok((*num as i128, *den as u128)),
RationalStorage::Medium { num, den } => Ok((*num as i128, *den as u128)),
RationalStorage::Large { num, den } => Ok((*num as i128, *den as u128)),
RationalStorage::Huge { num, den } => Ok((*num, *den)),
_ => Err(OverflowDetected::PrecisionLimit),
}
}
fn from_i128_result(result: Result<(i128, u128), OverflowDetected>) -> Result<Self, OverflowDetected> {
result.map(|(num, den)| Self::new(num, den))
}
fn add_small_tier(&self, other: &Self) -> Result<Self, OverflowDetected> {
let (an, ad) = self.extract_i128_pair()?;
let (bn, bd) = other.extract_i128_pair()?;
Self::from_i128_result(add_i128_rational(an, ad, bn, bd))
}
fn subtract_small_tier(&self, other: &Self) -> Result<Self, OverflowDetected> {
let (an, ad) = self.extract_i128_pair()?;
let (bn, bd) = other.extract_i128_pair()?;
Self::from_i128_result(sub_i128_rational(an, ad, bn, bd))
}
fn multiply_small_tier(&self, other: &Self) -> Result<Self, OverflowDetected> {
let (an, ad) = self.extract_i128_pair()?;
let (bn, bd) = other.extract_i128_pair()?;
Self::from_i128_result(mul_i128_rational(an, ad, bn, bd))
}
fn divide_small_tier(&self, other: &Self) -> Result<Self, OverflowDetected> {
let (an, ad) = self.extract_i128_pair()?;
let (bn, bd) = other.extract_i128_pair()?;
Self::from_i128_result(div_i128_rational(an, ad, bn, bd))
}
fn add_medium_tier(&self, other: &Self) -> Result<Self, OverflowDetected> {
let (an, ad) = self.extract_i128_pair()?;
let (bn, bd) = other.extract_i128_pair()?;
Self::from_i128_result(add_i128_rational(an, ad, bn, bd))
}
fn subtract_medium_tier(&self, other: &Self) -> Result<Self, OverflowDetected> {
let (an, ad) = self.extract_i128_pair()?;
let (bn, bd) = other.extract_i128_pair()?;
Self::from_i128_result(sub_i128_rational(an, ad, bn, bd))
}
fn multiply_medium_tier(&self, other: &Self) -> Result<Self, OverflowDetected> {
let (an, ad) = self.extract_i128_pair()?;
let (bn, bd) = other.extract_i128_pair()?;
Self::from_i128_result(mul_i128_rational(an, ad, bn, bd))
}
fn divide_medium_tier(&self, other: &Self) -> Result<Self, OverflowDetected> {
let (an, ad) = self.extract_i128_pair()?;
let (bn, bd) = other.extract_i128_pair()?;
Self::from_i128_result(div_i128_rational(an, ad, bn, bd))
}
fn add_large_tier(&self, other: &Self) -> Result<Self, OverflowDetected> {
let (an, ad) = self.extract_i128_pair()?;
let (bn, bd) = other.extract_i128_pair()?;
Self::from_i128_result(add_i128_rational(an, ad, bn, bd))
}
fn subtract_large_tier(&self, other: &Self) -> Result<Self, OverflowDetected> {
let (an, ad) = self.extract_i128_pair()?;
let (bn, bd) = other.extract_i128_pair()?;
Self::from_i128_result(sub_i128_rational(an, ad, bn, bd))
}
fn multiply_large_tier(&self, other: &Self) -> Result<Self, OverflowDetected> {
let (an, ad) = self.extract_i128_pair()?;
let (bn, bd) = other.extract_i128_pair()?;
Self::from_i128_result(mul_i128_rational(an, ad, bn, bd))
}
fn divide_large_tier(&self, other: &Self) -> Result<Self, OverflowDetected> {
let (an, ad) = self.extract_i128_pair()?;
let (bn, bd) = other.extract_i128_pair()?;
Self::from_i128_result(div_i128_rational(an, ad, bn, bd))
}
fn add_huge_tier(&self, other: &Self) -> Result<Self, OverflowDetected> {
let (an, ad) = self.extract_i128_pair()?;
let (bn, bd) = other.extract_i128_pair()?;
Self::from_i128_result(add_i128_rational(an, ad, bn, bd))
}
fn subtract_huge_tier(&self, other: &Self) -> Result<Self, OverflowDetected> {
let (an, ad) = self.extract_i128_pair()?;
let (bn, bd) = other.extract_i128_pair()?;
Self::from_i128_result(sub_i128_rational(an, ad, bn, bd))
}
fn multiply_huge_tier(&self, other: &Self) -> Result<Self, OverflowDetected> {
let (an, ad) = self.extract_i128_pair()?;
let (bn, bd) = other.extract_i128_pair()?;
Self::from_i128_result(mul_i128_rational(an, ad, bn, bd))
}
fn divide_huge_tier(&self, other: &Self) -> Result<Self, OverflowDetected> {
let (an, ad) = self.extract_i128_pair()?;
let (bn, bd) = other.extract_i128_pair()?;
Self::from_i128_result(div_i128_rational(an, ad, bn, bd))
}
#[cfg(not(feature = "embedded"))]
fn extract_i256_pair(&self) -> Result<(I256, I256), OverflowDetected> {
match &self.storage {
RationalStorage::Massive { num, den } => Ok((*num, *den)),
_ => {
let parts = self.extract_native();
if let Some((n, d)) = parts.try_as_i128() {
Ok((I256::from_i128(n), I256::from_i128(d as i128)))
} else {
Err(OverflowDetected::TierOverflow)
}
}
}
}
#[cfg(not(feature = "embedded"))]
fn add_massive_tier(&self, other: &Self) -> Result<Self, OverflowDetected> {
let (an, ad) = self.extract_i256_pair()?;
let (bn, bd) = other.extract_i256_pair()?;
let an_w = I512::from_i256(an);
let ad_w = I512::from_i256(ad);
let bn_w = I512::from_i256(bn);
let bd_w = I512::from_i256(bd);
let num = an_w * bd_w + bn_w * ad_w;
let den = ad_w * bd_w;
let g = gcd_i512(num, den);
let rn = num / g;
let rd = den / g;
if rn.fits_in_i256() && rd.fits_in_i256() {
Ok(Self::from_i256_pair(rn.as_i256(), rd.as_i256()))
} else {
Err(OverflowDetected::TierOverflow)
}
}
#[cfg(not(feature = "embedded"))]
fn subtract_massive_tier(&self, other: &Self) -> Result<Self, OverflowDetected> {
let (an, ad) = self.extract_i256_pair()?;
let (bn, bd) = other.extract_i256_pair()?;
let an_w = I512::from_i256(an);
let ad_w = I512::from_i256(ad);
let bn_w = I512::from_i256(bn);
let bd_w = I512::from_i256(bd);
let num = an_w * bd_w - bn_w * ad_w;
let den = ad_w * bd_w;
let g = gcd_i512(num, den);
let rn = num / g;
let rd = den / g;
if rn.fits_in_i256() && rd.fits_in_i256() {
Ok(Self::from_i256_pair(rn.as_i256(), rd.as_i256()))
} else {
Err(OverflowDetected::TierOverflow)
}
}
#[cfg(not(feature = "embedded"))]
fn multiply_massive_tier(&self, other: &Self) -> Result<Self, OverflowDetected> {
let (an, ad) = self.extract_i256_pair()?;
let (bn, bd) = other.extract_i256_pair()?;
let g1 = gcd_i256(an, bd);
let g2 = gcd_i256(bn, ad);
let an_r = an / g1;
let bd_r = bd / g1;
let bn_r = bn / g2;
let ad_r = ad / g2;
let an_w = I512::from_i256(an_r);
let ad_w = I512::from_i256(ad_r);
let bn_w = I512::from_i256(bn_r);
let bd_w = I512::from_i256(bd_r);
let num = an_w * bn_w;
let den = ad_w * bd_w;
let g = gcd_i512(num, den);
let rn = num / g;
let rd = den / g;
if rn.fits_in_i256() && rd.fits_in_i256() {
Ok(Self::from_i256_pair(rn.as_i256(), rd.as_i256()))
} else {
Err(OverflowDetected::TierOverflow)
}
}
#[cfg(not(feature = "embedded"))]
fn divide_massive_tier(&self, other: &Self) -> Result<Self, OverflowDetected> {
let (an, ad) = self.extract_i256_pair()?;
let (bn, bd) = other.extract_i256_pair()?;
if bn.is_zero() { return Err(OverflowDetected::TierOverflow); }
let an_w = I512::from_i256(an);
let ad_w = I512::from_i256(ad);
let bn_w = I512::from_i256(bn);
let bd_w = I512::from_i256(bd);
let num = an_w * bd_w;
let den = ad_w * bn_w;
let g = gcd_i512(num, den);
let rn = num / g;
let rd = den / g;
let (rn, rd) = if rd.is_negative() { (-rn, -rd) } else { (rn, rd) };
if rn.fits_in_i256() && rd.fits_in_i256() {
Ok(Self::from_i256_pair(rn.as_i256(), rd.as_i256()))
} else {
Err(OverflowDetected::TierOverflow)
}
}
fn extract_i512_pair(&self) -> Result<(I512, I512), OverflowDetected> {
match &self.storage {
RationalStorage::Ultra { num, den } => Ok((*num, *den)),
#[cfg(not(feature = "embedded"))]
RationalStorage::Massive { num, den } => {
Ok((I512::from_i256(*num), I512::from_i256(*den)))
}
_ => {
let parts = self.extract_native();
if let Some((n, d)) = parts.try_as_i128() {
Ok((I512::from_i128(n), I512::from_i128(d as i128)))
} else {
Err(OverflowDetected::TierOverflow)
}
}
}
}
fn add_ultra_tier(&self, other: &Self) -> Result<Self, OverflowDetected> {
let (an, ad) = self.extract_i512_pair()?;
let (bn, bd) = other.extract_i512_pair()?;
let an_w = I1024::from_i512(an);
let ad_w = I1024::from_i512(ad);
let bn_w = I1024::from_i512(bn);
let bd_w = I1024::from_i512(bd);
let num = an_w * bd_w + bn_w * ad_w;
let den = ad_w * bd_w;
let g = gcd_i1024(num, den);
let rn = num / g;
let rd = den / g;
if rn.fits_in_i512() && rd.fits_in_i512() {
Ok(Self::from_i512_pair(rn.as_i512(), rd.as_i512()))
} else {
Err(OverflowDetected::TierOverflow)
}
}
fn subtract_ultra_tier(&self, other: &Self) -> Result<Self, OverflowDetected> {
let (an, ad) = self.extract_i512_pair()?;
let (bn, bd) = other.extract_i512_pair()?;
let an_w = I1024::from_i512(an);
let ad_w = I1024::from_i512(ad);
let bn_w = I1024::from_i512(bn);
let bd_w = I1024::from_i512(bd);
let num = an_w * bd_w - bn_w * ad_w;
let den = ad_w * bd_w;
let g = gcd_i1024(num, den);
let rn = num / g;
let rd = den / g;
if rn.fits_in_i512() && rd.fits_in_i512() {
Ok(Self::from_i512_pair(rn.as_i512(), rd.as_i512()))
} else {
Err(OverflowDetected::TierOverflow)
}
}
fn multiply_ultra_tier(&self, other: &Self) -> Result<Self, OverflowDetected> {
let (an, ad) = self.extract_i512_pair()?;
let (bn, bd) = other.extract_i512_pair()?;
let g1 = gcd_i512(an, bd);
let g2 = gcd_i512(bn, ad);
let an_r = an / g1;
let bd_r = bd / g1;
let bn_r = bn / g2;
let ad_r = ad / g2;
let an_w = I1024::from_i512(an_r);
let ad_w = I1024::from_i512(ad_r);
let bn_w = I1024::from_i512(bn_r);
let bd_w = I1024::from_i512(bd_r);
let num = an_w * bn_w;
let den = ad_w * bd_w;
let g = gcd_i1024(num, den);
let rn = num / g;
let rd = den / g;
if rn.fits_in_i512() && rd.fits_in_i512() {
Ok(Self::from_i512_pair(rn.as_i512(), rd.as_i512()))
} else {
Err(OverflowDetected::TierOverflow)
}
}
fn divide_ultra_tier(&self, other: &Self) -> Result<Self, OverflowDetected> {
let (an, ad) = self.extract_i512_pair()?;
let (bn, bd) = other.extract_i512_pair()?;
if bn.is_zero() { return Err(OverflowDetected::TierOverflow); }
let an_w = I1024::from_i512(an);
let ad_w = I1024::from_i512(ad);
let bn_w = I1024::from_i512(bn);
let bd_w = I1024::from_i512(bd);
let num = an_w * bd_w;
let den = ad_w * bn_w;
let g = gcd_i1024(num, den);
let rn = num / g;
let rd = den / g;
let (rn, rd) = if (rd.words[15] as i64) < 0 { (-rn, -rd) } else { (rn, rd) };
if rn.fits_in_i512() && rd.fits_in_i512() {
Ok(Self::from_i512_pair(rn.as_i512(), rd.as_i512()))
} else {
Err(OverflowDetected::TierOverflow)
}
}
#[cfg(feature = "infinite-precision")]
pub(crate) fn extract_exact_rational(&self) -> (BigInt, BigInt) {
match &self.storage {
RationalStorage::Tiny { num, den } => (BigInt::from(*num), BigInt::from(*den)),
RationalStorage::Small { num, den } => (BigInt::from(*num), BigInt::from(*den)),
RationalStorage::Medium { num, den } => (BigInt::from(*num), BigInt::from(*den)),
RationalStorage::Large { num, den } => (BigInt::from(*num), BigInt::from(*den)),
RationalStorage::Huge { num, den } => (BigInt::from(*num), BigInt::from(*den)),
#[cfg(not(feature = "embedded"))]
RationalStorage::Massive { num, den } => {
(BigInt::from(num.as_i128()), BigInt::from(den.as_i128()))
},
RationalStorage::Ultra { num, den } => {
(BigInt::from(num.as_i128()), BigInt::from(den.as_i128()))
},
RationalStorage::Infinite { num, den } => (num.clone(), den.clone()),
}
}
pub fn try_add(&self, other: &Self) -> Result<Self, OverflowDetected> {
let result_tier = Self::predict_addition_tier(&self.storage, &other.storage);
let max_allowed_tier = RationalStorage::max_tier_for_profile();
if result_tier > max_allowed_tier {
return Err(OverflowDetected::PrecisionLimit);
}
let math_result = match (&self.storage, &other.storage) {
(RationalStorage::Tiny { num: a_num, den: a_den },
RationalStorage::Tiny { num: b_num, den: b_den }) => {
add_i8_rational(*a_num, *a_den, *b_num, *b_den)
.map(|(num, den)| (num as i128, den as u128))
},
(RationalStorage::Small { num: a_num, den: a_den },
RationalStorage::Small { num: b_num, den: b_den }) => {
add_i16_rational(*a_num, *a_den, *b_num, *b_den)
.map(|(num, den)| (num as i128, den as u128))
},
(RationalStorage::Medium { num: a_num, den: a_den },
RationalStorage::Medium { num: b_num, den: b_den }) => {
add_i32_rational(*a_num, *a_den, *b_num, *b_den)
.map(|(num, den)| (num as i128, den as u128))
},
(RationalStorage::Large { num: a_num, den: a_den },
RationalStorage::Large { num: b_num, den: b_den }) => {
add_i64_rational(*a_num, *a_den, *b_num, *b_den)
.map(|(num, den)| (num as i128, den as u128))
},
(RationalStorage::Huge { num: a_num, den: a_den },
RationalStorage::Huge { num: b_num, den: b_den }) => {
add_i128_rational(*a_num, *a_den, *b_num, *b_den)
},
_ => {
return self.add_mixed_tiers(other);
}
};
match math_result {
Ok((result_num, result_den)) => {
Ok(Self::new(result_num, result_den))
},
Err(OverflowDetected::TierOverflow) => {
let (a_num, a_den) = self.extract_i128_pair()?;
let (b_num, b_den) = other.extract_i128_pair()?;
let b_den_s = i128::try_from(b_den).map_err(|_| OverflowDetected::TierOverflow)?;
let a_den_s = i128::try_from(a_den).map_err(|_| OverflowDetected::TierOverflow)?;
let a_exp = a_num.checked_mul(b_den_s).ok_or(OverflowDetected::TierOverflow)?;
let b_exp = b_num.checked_mul(a_den_s).ok_or(OverflowDetected::TierOverflow)?;
let num = a_exp.checked_add(b_exp).ok_or(OverflowDetected::TierOverflow)?;
let den = a_den.checked_mul(b_den).ok_or(OverflowDetected::TierOverflow)?;
let g = gcd_unsigned(num.unsigned_abs(), den);
Ok(Self::new(num / (g as i128), den / g))
},
Err(e) => Err(e),
}
}
pub fn try_subtract(&self, other: &Self) -> Result<Self, OverflowDetected> {
let result_tier = Self::predict_subtraction_tier(&self.storage, &other.storage);
let max_allowed_tier = RationalStorage::max_tier_for_profile();
if result_tier > max_allowed_tier {
return Err(OverflowDetected::PrecisionLimit);
}
let math_result = match (&self.storage, &other.storage) {
(RationalStorage::Tiny { num: a_num, den: a_den },
RationalStorage::Tiny { num: b_num, den: b_den }) => {
sub_i8_rational(*a_num, *a_den, *b_num, *b_den)
.map(|(num, den)| (num as i128, den as u128))
},
(RationalStorage::Small { num: a_num, den: a_den },
RationalStorage::Small { num: b_num, den: b_den }) => {
sub_i16_rational(*a_num, *a_den, *b_num, *b_den)
.map(|(num, den)| (num as i128, den as u128))
},
(RationalStorage::Medium { num: a_num, den: a_den },
RationalStorage::Medium { num: b_num, den: b_den }) => {
sub_i32_rational(*a_num, *a_den, *b_num, *b_den)
.map(|(num, den)| (num as i128, den as u128))
},
(RationalStorage::Large { num: a_num, den: a_den },
RationalStorage::Large { num: b_num, den: b_den }) => {
sub_i64_rational(*a_num, *a_den, *b_num, *b_den)
.map(|(num, den)| (num as i128, den as u128))
},
(RationalStorage::Huge { num: a_num, den: a_den },
RationalStorage::Huge { num: b_num, den: b_den }) => {
sub_i128_rational(*a_num, *a_den, *b_num, *b_den)
},
_ => {
return self.subtract_mixed_tiers(other);
}
};
match math_result {
Ok((result_num, result_den)) => {
Ok(Self::new(result_num, result_den))
},
Err(OverflowDetected::TierOverflow) => {
let (a_num, a_den) = self.extract_i128_pair()?;
let (b_num, b_den) = other.extract_i128_pair()?;
let b_den_s = i128::try_from(b_den).map_err(|_| OverflowDetected::TierOverflow)?;
let a_den_s = i128::try_from(a_den).map_err(|_| OverflowDetected::TierOverflow)?;
let a_exp = a_num.checked_mul(b_den_s).ok_or(OverflowDetected::TierOverflow)?;
let b_exp = b_num.checked_mul(a_den_s).ok_or(OverflowDetected::TierOverflow)?;
let num = a_exp.checked_sub(b_exp).ok_or(OverflowDetected::TierOverflow)?;
let den = a_den.checked_mul(b_den).ok_or(OverflowDetected::TierOverflow)?;
let g = gcd_unsigned(num.unsigned_abs(), den);
Ok(Self::new(num / (g as i128), den / g))
},
Err(e) => Err(e),
}
}
pub fn try_divide(&self, other: &Self) -> Result<Self, OverflowDetected> {
let result_tier = Self::predict_division_tier(&self.storage, &other.storage);
let max_allowed_tier = RationalStorage::max_tier_for_profile();
if result_tier > max_allowed_tier {
return Err(OverflowDetected::PrecisionLimit);
}
let math_result = match (&self.storage, &other.storage) {
(RationalStorage::Tiny { num: a_num, den: a_den },
RationalStorage::Tiny { num: b_num, den: b_den }) => {
div_i8_rational(*a_num, *a_den, *b_num, *b_den)
.map(|(num, den)| (num as i128, den as u128))
},
(RationalStorage::Small { num: a_num, den: a_den },
RationalStorage::Small { num: b_num, den: b_den }) => {
div_i16_rational(*a_num, *a_den, *b_num, *b_den)
.map(|(num, den)| (num as i128, den as u128))
},
(RationalStorage::Medium { num: a_num, den: a_den },
RationalStorage::Medium { num: b_num, den: b_den }) => {
div_i32_rational(*a_num, *a_den, *b_num, *b_den)
.map(|(num, den)| (num as i128, den as u128))
},
(RationalStorage::Large { num: a_num, den: a_den },
RationalStorage::Large { num: b_num, den: b_den }) => {
div_i64_rational(*a_num, *a_den, *b_num, *b_den)
.map(|(num, den)| (num as i128, den as u128))
},
(RationalStorage::Huge { num: a_num, den: a_den },
RationalStorage::Huge { num: b_num, den: b_den }) => {
div_i128_rational(*a_num, *a_den, *b_num, *b_den)
},
#[cfg(not(feature = "embedded"))]
(RationalStorage::Massive { .. }, RationalStorage::Massive { .. }) => {
return self.divide_mixed_tiers(other);
},
(RationalStorage::Ultra { .. }, RationalStorage::Ultra { .. }) => {
return self.divide_mixed_tiers(other);
},
_ => {
return self.divide_mixed_tiers(other);
}
};
match math_result {
Ok((result_num, result_den)) => {
Ok(Self::new(result_num, result_den))
},
Err(OverflowDetected::TierOverflow) => {
let (a_num, a_den) = self.extract_i128_pair()?;
let (b_num, b_den) = other.extract_i128_pair()?;
if b_num == 0 { return Err(OverflowDetected::PrecisionLoss); }
let b_den_s = i128::try_from(b_den).map_err(|_| OverflowDetected::TierOverflow)?;
let mut num = a_num.checked_mul(b_den_s).ok_or(OverflowDetected::TierOverflow)?;
if b_num < 0 { num = num.checked_neg().ok_or(OverflowDetected::TierOverflow)?; }
let den = a_den.checked_mul(b_num.unsigned_abs()).ok_or(OverflowDetected::TierOverflow)?;
let g = gcd_unsigned(num.unsigned_abs(), den);
Ok(Self::new(num / (g as i128), den / g))
},
Err(e) => Err(e),
}
}
pub fn try_negate(&self) -> Result<Self, OverflowDetected> {
match &self.storage {
RationalStorage::Tiny { num, den } => {
if *num == i8::MIN {
Err(OverflowDetected::TierOverflow) } else {
Ok(Self {
storage: RationalStorage::Tiny { num: -num, den: *den },
metadata: self.metadata,
})
}
},
RationalStorage::Small { num, den } => {
if *num == i16::MIN {
Err(OverflowDetected::TierOverflow) } else {
Ok(Self {
storage: RationalStorage::Small { num: -num, den: *den },
metadata: self.metadata,
})
}
},
RationalStorage::Medium { num, den } => {
if *num == i32::MIN {
Err(OverflowDetected::TierOverflow) } else {
Ok(Self {
storage: RationalStorage::Medium { num: -num, den: *den },
metadata: self.metadata,
})
}
},
RationalStorage::Large { num, den } => {
if *num == i64::MIN {
Err(OverflowDetected::TierOverflow) } else {
Ok(Self {
storage: RationalStorage::Large { num: -num, den: *den },
metadata: self.metadata,
})
}
},
RationalStorage::Huge { num, den } => {
if *num == i128::MIN {
Err(OverflowDetected::TierOverflow) } else {
Ok(Self {
storage: RationalStorage::Huge { num: -num, den: *den },
metadata: self.metadata,
})
}
},
#[cfg(not(feature = "embedded"))]
RationalStorage::Massive { num, den } => {
Ok(Self {
storage: RationalStorage::Massive { num: -*num, den: *den },
metadata: self.metadata,
})
},
RationalStorage::Ultra { num, den } => {
Ok(Self {
storage: RationalStorage::Ultra { num: -*num, den: *den },
metadata: self.metadata,
})
},
#[cfg(feature = "infinite-precision")]
RationalStorage::Infinite { num, den } => {
Ok(Self {
storage: RationalStorage::Infinite { num: -num, den: den.clone() },
metadata: self.metadata,
})
},
}
}
}
impl fmt::Display for RationalNumber {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.storage {
RationalStorage::Tiny { num, den } => {
if *den == 1 { write!(f, "{}", num) } else { write!(f, "{}/{}", num, den) }
},
RationalStorage::Small { num, den } => {
if *den == 1 { write!(f, "{}", num) } else { write!(f, "{}/{}", num, den) }
},
RationalStorage::Medium { num, den } => {
if *den == 1 { write!(f, "{}", num) } else { write!(f, "{}/{}", num, den) }
},
RationalStorage::Large { num, den } => {
if *den == 1 { write!(f, "{}", num) } else { write!(f, "{}/{}", num, den) }
},
RationalStorage::Huge { num, den } => {
if *den == 1 { write!(f, "{}", num) } else { write!(f, "{}/{}", num, den) }
},
#[cfg(not(feature = "embedded"))]
RationalStorage::Massive { num, den } => {
let num_i128 = num.as_i128();
let den_i128 = den.as_i128();
if den_i128 == 1 { write!(f, "{}", num_i128) } else { write!(f, "{}/{}", num_i128, den_i128) }
},
RationalStorage::Ultra { num, den } => {
let num_i128 = num.as_i128();
let den_i128 = den.as_i128();
if den_i128 == 1 { write!(f, "{}", num_i128) } else { write!(f, "{}/{}", num_i128, den_i128) }
},
#[cfg(feature = "infinite-precision")]
RationalStorage::Infinite { num, den } => {
if den.is_one() { write!(f, "{}", num) } else { write!(f, "{}/{}", num, den) }
},
}
}
}
impl PartialEq for RationalNumber {
fn eq(&self, other: &Self) -> bool {
match (&self.storage, &other.storage) {
(RationalStorage::Tiny { num: a, den: ad }, RationalStorage::Tiny { num: b, den: bd }) => {
((*a as i16) * (*bd as i16)) == ((*b as i16) * (*ad as i16))
},
(RationalStorage::Small { num: a, den: ad }, RationalStorage::Small { num: b, den: bd }) => {
((*a as i32) * (*bd as i32)) == ((*b as i32) * (*ad as i32))
},
(RationalStorage::Medium { num: a, den: ad }, RationalStorage::Medium { num: b, den: bd }) => {
((*a as i64) * (*bd as i64)) == ((*b as i64) * (*ad as i64))
},
(RationalStorage::Large { num: a, den: ad }, RationalStorage::Large { num: b, den: bd }) => {
((*a as i128) * (*bd as i128)) == ((*b as i128) * (*ad as i128))
},
(RationalStorage::Huge { num: a, den: ad }, RationalStorage::Huge { num: b, den: bd }) => {
match (a.checked_mul(*bd as i128), b.checked_mul(*ad as i128)) {
(Some(lhs), Some(rhs)) => lhs == rhs,
_ => {
let lhs_i256 = I256::from_i128(*a) * I256::from_i128(*bd as i128);
let rhs_i256 = I256::from_i128(*b) * I256::from_i128(*ad as i128);
lhs_i256 == rhs_i256
}
}
},
#[cfg(not(feature = "embedded"))]
(RationalStorage::Massive { num: a, den: ad }, RationalStorage::Massive { num: b, den: bd }) => {
let lhs = I512::from_i256(*a) * I512::from_i256(*bd);
let rhs = I512::from_i256(*b) * I512::from_i256(*ad);
lhs == rhs
},
(RationalStorage::Ultra { num: a, den: ad }, RationalStorage::Ultra { num: b, den: bd }) => {
let lhs = I1024::from_i512(*a) * I1024::from_i512(*bd);
let rhs = I1024::from_i512(*b) * I1024::from_i512(*ad);
lhs == rhs
},
#[cfg(feature = "infinite-precision")]
(RationalStorage::Infinite { num: a, den: ad }, RationalStorage::Infinite { num: b, den: bd }) => {
(a * bd) == (b * ad)
},
_ => {
if let (Some((a_num, a_den)), Some((b_num, b_den))) =
(self.extract_native().try_as_i128(), other.extract_native().try_as_i128())
{
match (a_num.checked_mul(b_den as i128), b_num.checked_mul(a_den as i128)) {
(Some(lhs), Some(rhs)) => lhs == rhs,
_ => {
let lhs = I256::from_i128(a_num) * I256::from_u128(b_den);
let rhs = I256::from_i128(b_num) * I256::from_u128(a_den);
lhs == rhs
}
}
} else {
#[cfg(feature = "infinite-precision")]
{
let (a_num, a_den) = self.extract_exact_rational();
let (b_num, b_den) = other.extract_exact_rational();
(&a_num * &b_den) == (&b_num * &a_den)
}
#[cfg(not(feature = "infinite-precision"))]
{
false
}
}
}
}
}
}
impl Eq for RationalNumber {}
impl PartialOrd for RationalNumber {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for RationalNumber {
fn cmp(&self, other: &Self) -> Ordering {
match (&self.storage, &other.storage) {
(RationalStorage::Tiny { num: a, den: ad }, RationalStorage::Tiny { num: b, den: bd }) => {
let lhs = (*a as i16) * (*bd as i16);
let rhs = (*b as i16) * (*ad as i16);
lhs.cmp(&rhs)
},
(RationalStorage::Small { num: a, den: ad }, RationalStorage::Small { num: b, den: bd }) => {
let lhs = (*a as i32) * (*bd as i32);
let rhs = (*b as i32) * (*ad as i32);
lhs.cmp(&rhs)
},
(RationalStorage::Medium { num: a, den: ad }, RationalStorage::Medium { num: b, den: bd }) => {
let lhs = (*a as i64) * (*bd as i64);
let rhs = (*b as i64) * (*ad as i64);
lhs.cmp(&rhs)
},
(RationalStorage::Large { num: a, den: ad }, RationalStorage::Large { num: b, den: bd }) => {
let lhs = (*a as i128) * (*bd as i128);
let rhs = (*b as i128) * (*ad as i128);
lhs.cmp(&rhs)
},
(RationalStorage::Huge { num: a, den: ad }, RationalStorage::Huge { num: b, den: bd }) => {
match (a.checked_mul(*bd as i128), b.checked_mul(*ad as i128)) {
(Some(lhs), Some(rhs)) => lhs.cmp(&rhs),
_ => {
let lhs_i256 = I256::from_i128(*a) * I256::from_i128(*bd as i128);
let rhs_i256 = I256::from_i128(*b) * I256::from_i128(*ad as i128);
lhs_i256.cmp(&rhs_i256)
}
}
},
#[cfg(not(feature = "embedded"))]
(RationalStorage::Massive { num: a, den: ad }, RationalStorage::Massive { num: b, den: bd }) => {
let lhs = *a * *ad;
let rhs = *b * *bd;
lhs.cmp(&rhs)
},
(RationalStorage::Ultra { num: a, den: ad }, RationalStorage::Ultra { num: b, den: bd }) => {
let lhs = *a * *ad;
let rhs = *b * *bd;
lhs.cmp(&rhs)
},
#[cfg(feature = "infinite-precision")]
(RationalStorage::Infinite { num: a, den: ad }, RationalStorage::Infinite { num: b, den: bd }) => {
let lhs = a * bd;
let rhs = b * ad;
lhs.cmp(&rhs)
},
_ => {
if let (Some((a_num, a_den)), Some((b_num, b_den))) =
(self.extract_native().try_as_i128(), other.extract_native().try_as_i128())
{
match (a_num.checked_mul(b_den as i128), b_num.checked_mul(a_den as i128)) {
(Some(lhs), Some(rhs)) => lhs.cmp(&rhs),
_ => {
let lhs = I256::from_i128(a_num) * I256::from_u128(b_den);
let rhs = I256::from_i128(b_num) * I256::from_u128(a_den);
lhs.cmp(&rhs)
}
}
} else {
#[cfg(feature = "infinite-precision")]
{
let (a_num, a_den) = self.extract_exact_rational();
let (b_num, b_den) = other.extract_exact_rational();
let lhs = &a_num * &b_den;
let rhs = &b_num * &a_den;
lhs.cmp(&rhs)
}
#[cfg(not(feature = "infinite-precision"))]
{
Ordering::Equal }
}
}
}
}
}
impl std::ops::Add for RationalNumber {
type Output = Self;
fn add(self, other: Self) -> Self {
self.try_add(&other).expect("RationalNumber addition overflow - consider using try_add for fallible operations")
}
}
impl std::ops::Sub for RationalNumber {
type Output = Self;
fn sub(self, other: Self) -> Self {
self.try_subtract(&other).expect("RationalNumber subtraction overflow - consider using try_subtract for fallible operations")
}
}
impl std::ops::Mul for RationalNumber {
type Output = Self;
fn mul(self, other: Self) -> Self {
self.try_multiply(&other).expect("RationalNumber multiplication overflow - consider using try_multiply for fallible operations")
}
}
impl std::ops::Div for RationalNumber {
type Output = Self;
fn div(self, other: Self) -> Self {
self.try_divide(&other).expect("RationalNumber division error - consider using try_divide for fallible operations")
}
}
impl std::ops::Neg for RationalNumber {
type Output = Self;
fn neg(self) -> Self {
self.try_negate().expect("RationalNumber negation error - tier overflow")
}
}
#[cfg(feature = "infinite-precision")]
impl Zero for RationalNumber {
fn zero() -> Self {
Self::zero()
}
fn is_zero(&self) -> bool {
self.is_zero()
}
}
#[cfg(feature = "infinite-precision")]
impl One for RationalNumber {
fn one() -> Self {
Self::one()
}
fn is_one(&self) -> bool {
self.is_one()
}
}
impl RationalNumber {
pub fn zero() -> Self {
Self::new(0, 1)
}
pub fn is_zero(&self) -> bool {
match &self.storage {
RationalStorage::Tiny { num, .. } => *num == 0,
RationalStorage::Small { num, .. } => *num == 0,
RationalStorage::Medium { num, .. } => *num == 0,
RationalStorage::Large { num, .. } => *num == 0,
RationalStorage::Huge { num, .. } => *num == 0,
#[cfg(not(feature = "embedded"))]
RationalStorage::Massive { num, .. } => num.is_zero(),
RationalStorage::Ultra { num, .. } => num.is_zero(),
#[cfg(feature = "infinite-precision")]
RationalStorage::Infinite { num, .. } => num == &BigInt::from(0),
}
}
pub fn is_integer(&self) -> bool {
match &self.storage {
RationalStorage::Tiny { den, .. } => *den == 1,
RationalStorage::Small { den, .. } => *den == 1,
RationalStorage::Medium { den, .. } => *den == 1,
RationalStorage::Large { den, .. } => *den == 1,
RationalStorage::Huge { den, .. } => *den == 1,
#[cfg(not(feature = "embedded"))]
RationalStorage::Massive { den, .. } => den.as_i128() == 1,
RationalStorage::Ultra { den, .. } => den.as_i128() == 1,
#[cfg(feature = "infinite-precision")]
RationalStorage::Infinite { den, .. } => den == &BigInt::from(1),
}
}
pub fn one() -> Self {
Self::new(1, 1)
}
pub fn is_one(&self) -> bool {
match &self.storage {
RationalStorage::Tiny { num, den } => *num == 1 && *den == 1,
RationalStorage::Small { num, den } => *num == 1 && *den == 1,
RationalStorage::Medium { num, den } => *num == 1 && *den == 1,
RationalStorage::Large { num, den } => *num == 1 && *den == 1,
RationalStorage::Huge { num, den } => *num == 1 && *den == 1,
#[cfg(not(feature = "embedded"))]
RationalStorage::Massive { num, den } => num.as_i128() == 1 && den.as_i128() == 1,
RationalStorage::Ultra { num, den } => num.as_i128() == 1 && den.as_i128() == 1,
#[cfg(feature = "infinite-precision")]
RationalStorage::Infinite { num, den } => num == &BigInt::from(1) && den == &BigInt::from(1),
}
}
pub fn pow(self, exponent: i32) -> Self {
if exponent == 0 {
return Self::one();
}
if exponent == 1 {
return self;
}
let (base, exp) = if exponent < 0 {
let recip = Self::new(
if self.numerator_i128().unwrap_or(0) < 0 {
-(self.denominator_i128().unwrap_or(1) as i128)
} else {
self.denominator_i128().unwrap_or(1) as i128
},
self.numerator_i128().unwrap_or(1).unsigned_abs() as u128,
);
(recip, (-exponent) as u32)
} else {
(self, exponent as u32)
};
let mut result = Self::one();
let mut b = base;
let mut e = exp;
while e > 0 {
if e & 1 == 1 {
result = result.try_multiply(&b).unwrap_or(result);
}
e >>= 1;
if e > 0 {
b = b.clone().try_multiply(&b).unwrap_or(b);
}
}
result
}
}
impl UniversalTieredArithmetic for RationalNumber {
type Error = OverflowDetected;
fn try_add(&self, other: &Self) -> Result<Self, Self::Error> {
RationalNumber::try_add(self, other)
}
fn try_subtract(&self, other: &Self) -> Result<Self, Self::Error> {
RationalNumber::try_subtract(self, other)
}
fn try_multiply(&self, other: &Self) -> Result<Self, Self::Error> {
RationalNumber::try_multiply(self, other)
}
fn try_divide(&self, other: &Self) -> Result<Self, Self::Error> {
RationalNumber::try_divide(self, other)
}
fn try_negate(&self) -> Result<Self, Self::Error> {
RationalNumber::try_negate(self)
}
fn current_tier(&self) -> u8 {
self.tier_level()
}
fn can_promote_to_tier(&self, tier: u8) -> bool {
let current_tier = self.current_tier();
let max_tier = Self::max_tier();
tier <= max_tier && tier > current_tier
}
fn promote_to_tier(&self, tier: u8) -> Option<Self> {
if !self.can_promote_to_tier(tier) {
return None;
}
let parts = self.extract_native();
#[cfg(not(feature = "embedded"))]
if tier == 6 {
let (num, den) = parts.try_as_i256_pair()?;
return Some(Self::from_i256_pair(num, den));
}
if tier == 7 {
let (num, den) = parts.try_as_i512_pair()?;
return Some(Self::from_i512_pair(num, den));
}
let (num_i128, den_i128) = parts.try_as_i128()?;
Some(Self::new(num_i128, den_i128))
}
fn max_tier() -> u8 {
8 }
fn domain_type() -> UGODDomainType {
UGODDomainType::Symbolic
}
fn symbolic_tier_mapping(symbolic_tier: u8) -> u8 {
symbolic_tier }
fn can_accommodate_symbolic_tier(&self, symbolic_tier: u8) -> bool {
self.current_tier() >= symbolic_tier
}
fn max_tier_for_profile(profile: DeploymentProfile) -> u8 {
match profile {
DeploymentProfile::Realtime => 3, DeploymentProfile::Compact => 4, DeploymentProfile::Embedded => 5, DeploymentProfile::Balanced => 7, DeploymentProfile::Scientific => 7, DeploymentProfile::Custom => 8, }
}
}
pub fn symbolic_to_universal_tier(symbolic_tier: u8) -> u8 {
match symbolic_tier {
1 | 2 => 1, 3 => 2, 4 => 3, 5 => 4, 6 => 5, 7 | 8 => 6, _ => 6, }
}
pub fn universal_to_symbolic_tier(universal_tier: u8) -> u8 {
match universal_tier {
1 => 2, 2 => 3, 3 => 4, 4 => 5, 5 => 6, 6 => 7, _ => 7, }
}
#[cfg(not(feature = "embedded"))]
fn gcd_i256(a: I256, b: I256) -> I256 {
let mut a = if a.is_negative() { -a } else { a };
let mut b = if b.is_negative() { -b } else { b };
let zero = I256::zero();
while b != zero {
let t = b;
b = a % b;
a = t;
}
if a == zero { I256::from_i128(1) } else { a }
}
#[cfg(not(feature = "embedded"))]
fn gcd_i512(a: I512, b: I512) -> I512 {
let mut a = if a.is_negative() { -a } else { a };
let mut b = if b.is_negative() { -b } else { b };
let zero = I512::zero();
while b != zero {
let t = b;
b = a % b;
a = t;
}
if a == zero { I512::from_i128(1) } else { a }
}
fn gcd_i1024(a: I1024, b: I1024) -> I1024 {
let mut a = if (a.words[15] as i64) < 0 { -a } else { a };
let mut b = if (b.words[15] as i64) < 0 { -b } else { b };
let zero = I1024::zero();
while b != zero {
let t = b;
b = a % b;
a = t;
}
if a == zero { I1024::from_i128(1) } else { a }
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tier_assignment() {
let tiny = RationalNumber::new(1, 3);
let small = RationalNumber::new(355, 113);
let large = RationalNumber::new(1_000_000, 3_000_000);
assert_eq!(tiny.tier_level(), 1);
assert_eq!(small.tier_level(), 2);
assert_eq!(large.tier_level(), 3);
}
#[test]
fn test_overflow_detection() {
let large_a = RationalNumber::new(i8::MAX as i128, 1);
let large_b = RationalNumber::new(i8::MAX as i128, 1);
let result = large_a.try_multiply(&large_b);
assert!(result.is_ok(), "UGOD should promote i8 overflow to higher tier");
let product = result.unwrap();
assert_eq!(product.extract_native().try_as_i128().unwrap().0, 16129);
let max_tier_result = large_a.multiply_at_max_tier(&large_b);
assert!(max_tier_result.tier_level() >= 2); }
#[test]
fn test_exact_arithmetic() {
let a = RationalNumber::new(1, 3);
let b = RationalNumber::new(2, 6);
let c = RationalNumber::new(1, 2);
assert_eq!(a, b); assert!(a < c); assert!(c > a);
assert!(!a.is_zero());
assert!(!a.is_one());
let one = RationalNumber::one();
assert!(one.is_one());
let zero = RationalNumber::zero();
assert!(zero.is_zero());
}
#[test]
fn test_pow() {
let base = RationalNumber::new(2, 3);
assert_eq!(base.clone().pow(0), RationalNumber::one());
assert_eq!(base.clone().pow(1), base);
assert_eq!(base.clone().pow(2), RationalNumber::new(4, 9));
assert_eq!(base.clone().pow(3), RationalNumber::new(8, 27));
assert_eq!(base.clone().pow(-1), RationalNumber::new(3, 2));
assert_eq!(base.pow(-2), RationalNumber::new(9, 4));
}
}