use super::ternary_types::{
TernaryTier, TernaryTier1, TernaryTier2, TernaryTier3, TernaryTier4, TernaryTier5, TernaryTier6,
TernaryValue, UniversalTernaryFixed,
SCALE_TQ10_10, SCALE_TQ20_20, SCALE_TQ40_40, scale_tq160_160_i1024, scale_tq320_320,
};
use crate::fixed_point::{I256, I512, I1024, I2048};
use crate::fixed_point::core_types::errors::OverflowDetected;
#[inline]
pub fn divide_ternary_tq10_10(a: i32, b: i32) -> Result<i32, OverflowDetected> {
if b == 0 {
return Err(OverflowDetected::DivisionByZero);
}
match (a as i64).checked_mul(SCALE_TQ10_10 as i64) {
Some(scaled_a) => {
let den = b as i64;
let mut result = scaled_a / den;
let rem2 = (scaled_a - result * den).unsigned_abs() << 1;
let dabs = den.unsigned_abs();
let positive = (scaled_a < 0) == (den < 0);
if if positive { rem2 >= dabs } else { rem2 > dabs } {
result += if positive { 1 } else { -1 };
}
if result >= i32::MIN as i64 && result <= i32::MAX as i64 {
Ok(result as i32)
} else {
Err(OverflowDetected::TierOverflow)
}
}
None => Err(OverflowDetected::TierOverflow),
}
}
#[inline]
pub fn divide_ternary_tq20_20(a: i64, b: i64) -> Result<i64, OverflowDetected> {
if b == 0 {
return Err(OverflowDetected::DivisionByZero);
}
match (a as i128).checked_mul(SCALE_TQ20_20 as i128) {
Some(scaled_a) => {
let den = b as i128;
let mut result = scaled_a / den;
let rem2 = (scaled_a - result * den).unsigned_abs() << 1;
let dabs = den.unsigned_abs();
let positive = (scaled_a < 0) == (den < 0);
if if positive { rem2 >= dabs } else { rem2 > dabs } {
result += if positive { 1 } else { -1 };
}
if result >= i64::MIN as i128 && result <= i64::MAX as i128 {
Ok(result as i64)
} else {
Err(OverflowDetected::TierOverflow)
}
}
None => Err(OverflowDetected::TierOverflow),
}
}
#[inline]
pub fn divide_ternary_tq40_40(a: i128, b: i128) -> Result<i128, OverflowDetected> {
if b == 0 {
return Err(OverflowDetected::DivisionByZero);
}
let a_extended = I256::from_i128(a);
let b_extended = I256::from_i128(b);
let scale = I256::from_i128(SCALE_TQ40_40);
let scaled_a = a_extended * scale;
let (mut result, rem) =
crate::fixed_point::domains::binary_fixed::i256::divmod_i256_by_i256(scaled_a, b_extended);
let rem_abs = if rem.is_negative() { -rem } else { rem };
let den_abs = if b_extended.is_negative() { -b_extended } else { b_extended };
let positive = scaled_a.is_negative() == b_extended.is_negative();
let rem2 = rem_abs + rem_abs;
if if positive { rem2 >= den_abs } else { rem2 > den_abs } {
result = if positive { result + I256::from_i128(1) } else { result - I256::from_i128(1) };
}
if result.fits_in_i128() {
Ok(result.as_i128())
} else {
Err(OverflowDetected::TierOverflow)
}
}
#[inline]
pub fn divide_ternary_tq80_80(a: I256, b: I256) -> I256 {
if b.is_zero() {
return I256::signed_infinity(a.is_negative());
}
let a_extended = I512::from_i256(a);
let b_extended = I512::from_i256(b);
let scale = compute_3_pow_80_i512();
let scaled_a = a_extended * scale;
let (mut result, rem) =
crate::fixed_point::domains::binary_fixed::i512::divmod_i512_by_i512(scaled_a, b_extended);
let rem_abs = if rem.is_negative() { -rem } else { rem };
let den_abs = if b_extended.is_negative() { -b_extended } else { b_extended };
let positive = scaled_a.is_negative() == b_extended.is_negative();
let rem2 = rem_abs + rem_abs;
if if positive { rem2 >= den_abs } else { rem2 > den_abs } {
result = if positive { result + I512::from_i128(1) } else { result - I512::from_i128(1) };
}
result.as_i256_saturating()
}
#[inline]
pub fn divide_ternary_tq80_80_checked(a: I256, b: I256) -> Result<I256, OverflowDetected> {
if b.is_zero() {
return Err(OverflowDetected::DivisionByZero);
}
let a_extended = I512::from_i256(a);
let b_extended = I512::from_i256(b);
let scale = compute_3_pow_80_i512();
let scaled_a = a_extended * scale;
let (mut result, rem) =
crate::fixed_point::domains::binary_fixed::i512::divmod_i512_by_i512(scaled_a, b_extended);
let rem_abs = if rem.is_negative() { -rem } else { rem };
let den_abs = if b_extended.is_negative() { -b_extended } else { b_extended };
let positive = scaled_a.is_negative() == b_extended.is_negative();
let rem2 = rem_abs + rem_abs;
if if positive { rem2 >= den_abs } else { rem2 > den_abs } {
result = if positive { result + I512::from_i128(1) } else { result - I512::from_i128(1) };
}
if result.fits_in_i256() {
Ok(result.as_i256())
} else {
Err(OverflowDetected::TierOverflow)
}
}
#[inline]
pub fn divide_ternary_tq160_160(a: I512, b: I512) -> Result<I512, OverflowDetected> {
if b.is_zero() {
return Err(OverflowDetected::DivisionByZero);
}
let a_extended = I1024::from_i512(a);
let b_extended = I1024::from_i512(b);
let scale = scale_tq160_160_i1024();
let scaled_a = a_extended * scale;
let mut result = scaled_a / b_extended;
let rem = scaled_a % b_extended;
let rem_neg = (rem.words[15] as i64) < 0;
let den_neg = (b_extended.words[15] as i64) < 0;
let sa_neg = (scaled_a.words[15] as i64) < 0;
let rem_abs = if rem_neg { -rem } else { rem };
let den_abs = if den_neg { -b_extended } else { b_extended };
let positive = sa_neg == den_neg;
let rem2 = rem_abs + rem_abs;
if if positive { rem2 >= den_abs } else { rem2 > den_abs } {
result = if positive { result + I1024::from_i128(1) } else { result - I1024::from_i128(1) };
}
if result.fits_in_i512() {
Ok(result.as_i512())
} else {
Err(OverflowDetected::TierOverflow)
}
}
#[inline]
pub fn divide_ternary_tq320_320(a: I1024, b: I1024) -> I1024 {
let b_i512 = b.as_i512();
if b_i512.is_zero() && b.fits_in_i512() {
return I1024::max_value();
}
let a_ext = I2048::from_i1024(a);
let scale = I2048::from_i1024(scale_tq320_320());
let scaled_a = a_ext * scale;
let q = i2048_div_by_i1024(scaled_a, b);
let sa_neg = (scaled_a.words[31] as i64) < 0;
let b_neg = (b.words[15] as i64) < 0;
let positive = sa_neg == b_neg;
let abs_sa = if sa_neg { -scaled_a } else { scaled_a };
let q_neg = (q.words[31] as i64) < 0;
let abs_q = if q_neg { -q } else { q };
let abs_b = if b_neg { -b } else { b };
let qb = abs_q.as_i1024().mul_to_i2048(abs_b); let rem = abs_sa - qb;
let rem2 = rem + rem;
let abs_b_wide = I2048::from_i1024(abs_b);
let one = I2048::from_i1024(I1024::from_i128(1));
let bumped = if if positive { rem2 >= abs_b_wide } else { rem2 > abs_b_wide } {
if positive { q + one } else { q - one }
} else {
q
};
bumped.as_i1024()
}
pub(super) fn compute_3_pow_80_i512() -> I512 {
I512::from_i128(147_808_829_414_345_923_316_083_210_206_383_297_601_i128)
}
fn i2048_div_by_i1024(dividend: I2048, divisor: I1024) -> I2048 {
let dividend_neg = {
let words = dividend_words(÷nd);
(words[31] & 0x8000_0000_0000_0000) != 0
};
let divisor_neg = {
let ds = divisor.as_i512();
if divisor.fits_in_i512() {
ds.is_negative()
} else {
let dw = i1024_words(&divisor);
(dw[15] & 0x8000_0000_0000_0000) != 0
}
};
let abs_dividend = if dividend_neg { -dividend } else { dividend };
let abs_divisor = if divisor_neg { -divisor } else { divisor };
let div_ext = I2048::from_i1024(abs_divisor);
let mut quotient = I2048::zero();
let mut remainder = I2048::zero();
for i in (0..2048).rev() {
remainder = remainder << 1;
let word_idx = i / 64;
let bit_idx = i % 64;
let dw = dividend_words(&abs_dividend);
if (dw[word_idx] >> bit_idx) & 1 == 1 {
remainder = remainder + I2048::one();
}
if remainder >= div_ext {
remainder = remainder - div_ext;
let qw = dividend_words_mut_via_rebuild("ient, word_idx, bit_idx);
quotient = qw;
}
}
let result_neg = dividend_neg != divisor_neg;
if result_neg { -quotient } else { quotient }
}
fn dividend_words(val: &I2048) -> [u64; 32] {
val.words
}
fn i1024_words(val: &I1024) -> [u64; 16] {
val.words
}
fn dividend_words_mut_via_rebuild(val: &I2048, word_idx: usize, bit_idx: usize) -> I2048 {
let mut words = val.words;
words[word_idx] |= 1u64 << bit_idx;
I2048::from_words(words)
}
impl UniversalTernaryFixed {
pub fn divide(&self, other: &Self) -> Result<Self, OverflowDetected> {
let (aligned_self, aligned_other) = self.align_to_common_tier(other);
match (&aligned_self.value, &aligned_other.value) {
(TernaryValue::Tier1(a), TernaryValue::Tier1(b)) => {
match divide_ternary_tq10_10(a.raw(), b.raw()) {
Ok(result) => Ok(Self { value: TernaryValue::Tier1(TernaryTier1::from_raw(result)), current_tier: TernaryTier::Tier1 }),
Err(OverflowDetected::DivisionByZero) => Err(OverflowDetected::DivisionByZero),
Err(_) => {
let p_self = aligned_self.promote_to_tier2()?;
let p_other = aligned_other.promote_to_tier2()?;
p_self.divide(&p_other)
}
}
}
(TernaryValue::Tier2(a), TernaryValue::Tier2(b)) => {
match divide_ternary_tq20_20(a.raw(), b.raw()) {
Ok(result) => Ok(Self { value: TernaryValue::Tier2(TernaryTier2::from_raw(result)), current_tier: TernaryTier::Tier2 }),
Err(OverflowDetected::DivisionByZero) => Err(OverflowDetected::DivisionByZero),
Err(_) => {
let p_self = aligned_self.promote_to_tier3()?;
let p_other = aligned_other.promote_to_tier3()?;
p_self.divide(&p_other)
}
}
}
(TernaryValue::Tier3(a), TernaryValue::Tier3(b)) => {
match divide_ternary_tq40_40(a.raw(), b.raw()) {
Ok(result) => Ok(Self { value: TernaryValue::Tier3(TernaryTier3::from_raw(result)), current_tier: TernaryTier::Tier3 }),
Err(OverflowDetected::DivisionByZero) => Err(OverflowDetected::DivisionByZero),
Err(_) => {
let p_self = aligned_self.promote_to_tier4();
let p_other = aligned_other.promote_to_tier4();
p_self.divide(&p_other)
}
}
}
(TernaryValue::Tier4(a), TernaryValue::Tier4(b)) => {
match divide_ternary_tq80_80_checked(a.raw().clone(), b.raw().clone()) {
Ok(result) => Ok(Self { value: TernaryValue::Tier4(TernaryTier4::from_raw(result)), current_tier: TernaryTier::Tier4 }),
Err(OverflowDetected::DivisionByZero) => Err(OverflowDetected::DivisionByZero),
Err(_) => {
let p_self = aligned_self.promote_to_tier5();
let p_other = aligned_other.promote_to_tier5();
p_self.divide(&p_other)
}
}
}
(TernaryValue::Tier5(a), TernaryValue::Tier5(b)) => {
match divide_ternary_tq160_160(a.raw().clone(), b.raw().clone()) {
Ok(result) => Ok(Self { value: TernaryValue::Tier5(TernaryTier5::from_raw(result)), current_tier: TernaryTier::Tier5 }),
Err(OverflowDetected::DivisionByZero) => Err(OverflowDetected::DivisionByZero),
Err(_) => {
let p_self = aligned_self.promote_to_tier6();
let p_other = aligned_other.promote_to_tier6();
p_self.divide(&p_other)
}
}
}
(TernaryValue::Tier6(a), TernaryValue::Tier6(b)) => {
if b.raw().clone() == I1024::zero() {
return Err(OverflowDetected::DivisionByZero);
}
let result = divide_ternary_tq320_320(a.raw().clone(), b.raw().clone());
Ok(Self { value: TernaryValue::Tier6(TernaryTier6::from_raw(result)), current_tier: TernaryTier::Tier6 })
}
_ => unreachable!("align_to_common_tier should ensure matching tiers")
}
}
}