#[cfg(test)]
use super::format::FascFormat;
#[cfg(test)]
#[cfg(test)]
use crate::fixed_point::i256::I256;
#[cfg(test)]
use crate::fixed_point::i512::I512;
#[cfg(test)]
use crate::fixed_point::I1024;
#[cfg(test)]
use crate::fixed_point::domains::balanced_ternary::ternary_types::UniversalTernaryFixed;
#[cfg(test)]
use crate::fixed_point::domains::balanced_ternary::ternary_types::TernaryRaw;
use crate::fixed_point::universal::tier_types::CompactShadow;
pub(super) fn shadow_gcd(mut a: u128, mut b: u128) -> u128 {
while b != 0 {
let t = b;
b = a % b;
a = t;
}
a
}
pub(super) fn shadow_negate(s: &CompactShadow) -> CompactShadow {
match s.as_rational() {
Some((num, den)) => CompactShadow::from_rational(-num, den),
None => CompactShadow::None,
}
}
pub(super) fn shadow_add(a: &CompactShadow, b: &CompactShadow) -> CompactShadow {
let (an, ad) = match a.as_rational() {
Some(v) => v,
None => return CompactShadow::None,
};
let (bn, bd) = match b.as_rational() {
Some(v) => v,
None => return CompactShadow::None,
};
let ad_128 = ad as i128;
let bd_128 = bd as i128;
let num = an.checked_mul(bd_128).and_then(|x| bn.checked_mul(ad_128).and_then(|y| x.checked_add(y)));
let den = (ad as u128).checked_mul(bd as u128);
match (num, den) {
(Some(n), Some(d)) if d > 0 => {
let g = shadow_gcd(n.unsigned_abs(), d);
let rn = n / g as i128;
let rd = d / g;
CompactShadow::from_rational(rn, rd)
}
_ => CompactShadow::None,
}
}
pub(super) fn shadow_subtract(a: &CompactShadow, b: &CompactShadow) -> CompactShadow {
let (an, ad) = match a.as_rational() {
Some(v) => v,
None => return CompactShadow::None,
};
let (bn, bd) = match b.as_rational() {
Some(v) => v,
None => return CompactShadow::None,
};
let ad_128 = ad as i128;
let bd_128 = bd as i128;
let num = an.checked_mul(bd_128).and_then(|x| bn.checked_mul(ad_128).and_then(|y| x.checked_sub(y)));
let den = (ad as u128).checked_mul(bd as u128);
match (num, den) {
(Some(n), Some(d)) if d > 0 => {
let g = shadow_gcd(n.unsigned_abs(), d);
let rn = n / g as i128;
let rd = d / g;
CompactShadow::from_rational(rn, rd)
}
_ => CompactShadow::None,
}
}
pub(super) fn shadow_multiply(a: &CompactShadow, b: &CompactShadow) -> CompactShadow {
let (an, ad) = match a.as_rational() {
Some(v) => v,
None => return CompactShadow::None,
};
let (bn, bd) = match b.as_rational() {
Some(v) => v,
None => return CompactShadow::None,
};
let num = an.checked_mul(bn);
let den = (ad as u128).checked_mul(bd as u128);
match (num, den) {
(Some(n), Some(d)) if d > 0 => {
let g = shadow_gcd(n.unsigned_abs(), d);
let rn = n / g as i128;
let rd = d / g;
CompactShadow::from_rational(rn, rd)
}
_ => CompactShadow::None,
}
}
pub(super) fn shadow_divide(a: &CompactShadow, b: &CompactShadow) -> CompactShadow {
let (an, ad) = match a.as_rational() {
Some(v) => v,
None => return CompactShadow::None,
};
let (bn, bd) = match b.as_rational() {
Some(v) => v,
None => return CompactShadow::None,
};
if bn == 0 { return CompactShadow::None; } let bd_128 = bd as i128;
let num = an.checked_mul(bd_128);
let den = (ad as u128).checked_mul(bn.unsigned_abs());
let sign = if bn < 0 { -1i128 } else { 1i128 };
match (num, den) {
(Some(n), Some(d)) if d > 0 => {
match n.checked_mul(sign) {
Some(signed_n) => {
let g = shadow_gcd(signed_n.unsigned_abs(), d);
let rn = signed_n / g as i128;
let rd = d / g;
CompactShadow::from_rational(rn, rd)
}
None => CompactShadow::None,
}
}
_ => CompactShadow::None,
}
}
#[cfg(test)]
mod ternary_storage_arm_tests {
macro_rules! suite {
($m:ident, $S:ty, $F:literal, $W:ident) => {
mod $m {
#![allow(dead_code, unused_imports, unreachable_code)]
use super::super::*;
type FixedPoint = crate::fixed_point::Fixed<$S, $F>;
type BinaryStorage = $S;
type ComputeStorage = <$S as crate::fixed_point::Storage>::Compute;
const FRAC_BITS: u32 = $F;
const BITS: u32 = <$S as crate::fixed_point::WideInt>::BITS;
use super::super::*;
fn ternary_to_storage(
t: &UniversalTernaryFixed,
) -> Result<(u8, <FixedPoint as crate::fixed_point::fixed_num::FixedNum>::Storage), crate::fixed_point::core_types::errors::OverflowDetected> {
<FixedPoint as FascFormat>::ternary_to_storage(t)
}
fn tier4(v: i128) -> UniversalTernaryFixed {
UniversalTernaryFixed::from_tier_raw(4, TernaryRaw::Medium(I256::from_i128(v))).unwrap()
}
#[test]
fn medium_arm_small_raw_converts_everywhere() {
let (tier, _storage) = ternary_to_storage(&tier4(12_345)).expect("small Medium raw");
assert_eq!(tier, 4);
let (tier_n, _s) = ternary_to_storage(&tier4(-12_345)).expect("small negative Medium raw");
assert_eq!(tier_n, 4);
}
#[test]
fn medium_arm_oversized_raw_fails_loud_on_narrow_storage() {
let big = UniversalTernaryFixed::from_tier_raw(
4,
TernaryRaw::Medium(I256::from_i128(i128::MAX) + I256::from_i128(i128::MAX)),
)
.unwrap();
let res = ternary_to_storage(&big);
if BITS <= 128 { assert!(res.is_err(), "beyond-i128 Medium raw must be TierOverflow on narrow storage"); } else { assert!(res.is_ok(), "I256+ storage holds beyond-i128 Medium raws"); }
}
#[test]
fn large_and_xlarge_arms_small_and_oversized() {
let t5 = UniversalTernaryFixed::from_tier_raw(5, TernaryRaw::Large(I512::from_i128(-777)))
.unwrap();
assert_eq!(ternary_to_storage(&t5).expect("small Large raw").0, 5);
let t6 =
UniversalTernaryFixed::from_tier_raw(6, TernaryRaw::XLarge(I1024::from_i128(777)))
.unwrap();
assert_eq!(ternary_to_storage(&t6).expect("small XLarge raw").0, 6);
let big5 = UniversalTernaryFixed::from_tier_raw(
5,
TernaryRaw::Large(I512::from_i128(1) << 200),
)
.unwrap();
let res5 = ternary_to_storage(&big5);
if BITS <= 128 { assert!(res5.is_err()); } else { assert!(res5.is_ok()); }
let big6 = UniversalTernaryFixed::from_tier_raw(
6,
TernaryRaw::XLarge(I1024::from_i128(1) << 600),
)
.unwrap();
assert!(
ternary_to_storage(&big6).is_err(),
"beyond-I512 XLarge raw must be TierOverflow everywhere"
);
}
}
};
}
for_each_format!(suite);
}