use super::ternary_types::{
TernaryTier, TernaryTier1, TernaryTier2, TernaryTier3, TernaryTier4, TernaryTier5, TernaryTier6,
TernaryValue, UniversalTernaryFixed,
SCALE_TQ8_8, SCALE_TQ16_16, SCALE_TQ32_32, scale_tq128_128_i1024, scale_tq256_256,
};
use crate::fixed_point::{I256, I512, I1024, I2048};
use crate::fixed_point::core_types::errors::OverflowDetected;
#[inline]
pub fn divide_ternary_tq8_8(a: i32, b: i32) -> Result<i32, OverflowDetected> {
if b == 0 {
return Err(OverflowDetected::DivisionByZero);
}
match (a as i64).checked_mul(SCALE_TQ8_8 as i64) {
Some(scaled_a) => {
let result = scaled_a / (b as i64);
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_tq16_16(a: i64, b: i64) -> Result<i64, OverflowDetected> {
if b == 0 {
return Err(OverflowDetected::DivisionByZero);
}
match (a as i128).checked_mul(SCALE_TQ16_16 as i128) {
Some(scaled_a) => {
let result = scaled_a / (b as i128);
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_tq32_32(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_TQ32_32);
let scaled_a = a_extended * scale;
let result = scaled_a / b_extended;
if result.fits_in_i128() {
Ok(result.as_i128())
} else {
Err(OverflowDetected::TierOverflow)
}
}
#[inline]
pub fn divide_ternary_tq64_64(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_64_i512();
let scaled_a = a_extended * scale;
let result = scaled_a / b_extended;
result.as_i256_saturating()
}
#[inline]
pub fn divide_ternary_tq64_64_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_64_i512();
let scaled_a = a_extended * scale;
let result = scaled_a / b_extended;
if result.fits_in_i256() {
Ok(result.as_i256())
} else {
Err(OverflowDetected::TierOverflow)
}
}
#[inline]
pub fn divide_ternary_tq128_128(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_tq128_128_i1024();
let scaled_a = a_extended * scale;
let result = scaled_a / b_extended;
if result.fits_in_i512() {
Ok(result.as_i512())
} else {
Err(OverflowDetected::TierOverflow)
}
}
#[inline]
pub fn divide_ternary_tq256_256(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_tq256_256());
let scaled_a = a_ext * scale;
i2048_div_by_i1024(scaled_a, b).as_i1024()
}
pub(super) fn compute_3_pow_64_i512() -> I512 {
I512::from_i128(3_433_683_820_292_512_484_657_849_089_281_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_tq8_8(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_tq16_16(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_tq32_32(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_tq64_64_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_tq128_128(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_tq256_256(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")
}
}
}