use super::binary_types::*;
use crate::fixed_point::{I256, I512, I1024};
use crate::fixed_point::core_types::errors::OverflowDetected;
impl BinaryTier1 {
pub fn checked_div(&self, other: &Self) -> Option<Self> {
let fb: u32 = {
#[cfg(table_format = "q16_16")]
{ crate::fixed_point::frac_config::FRAC_BITS as u32 }
#[cfg(not(table_format = "q16_16"))]
{ 16 }
};
if other.value == 0 { return None; }
let a_wide = (self.value as i64) << fb;
let b_wide = other.value as i64;
let quotient = a_wide / b_wide;
let remainder = a_wide % b_wide;
let abs_2rem = (remainder.wrapping_abs() as u64) << 1;
let abs_div = b_wide.unsigned_abs();
let result = if abs_2rem >= abs_div {
if quotient < 0 { quotient - 1 } else { quotient + 1 }
} else {
quotient
};
let result_i32 = result as i32;
if result_i32 as i64 == result { Some(Self { value: result_i32 }) } else { None }
}
}
impl BinaryTier2 {
pub fn checked_div(&self, other: &Self) -> Option<Self> {
if other.value == 0 { return None; }
let a_wide = (self.value as i128) << 32;
let b_wide = other.value as i128;
let quotient = a_wide / b_wide;
let remainder = a_wide % b_wide;
let abs_2rem = (remainder.unsigned_abs()) << 1;
let abs_div = b_wide.unsigned_abs();
let result = if abs_2rem >= abs_div {
if quotient < 0 { quotient - 1 } else { quotient + 1 }
} else {
quotient
};
let result_i64 = result as i64;
if result_i64 as i128 == result { Some(Self { value: result_i64 }) } else { None }
}
}
impl BinaryTier3 {
pub fn checked_div(&self, other: &Self) -> Option<Self> {
if other.value == 0 { return None; }
let a_wide = I256::from_i128(self.value) << 64;
let b_wide = I256::from_i128(other.value);
let (quotient, remainder) = crate::fixed_point::domains::binary_fixed::i256::divmod_i256_by_i256(a_wide, b_wide);
let abs_2rem = if remainder.is_negative() { (-remainder) << 1 } else { remainder << 1 };
let abs_div = if b_wide.is_negative() { -b_wide } else { b_wide };
let result = if abs_2rem >= abs_div {
if quotient.is_negative() { quotient - I256::from_i128(1) } else { quotient + I256::from_i128(1) }
} else {
quotient
};
if result.fits_in_i128() { Some(Self { value: result.as_i128() }) } else { None }
}
}
impl BinaryTier4 {
pub fn div(&self, other: &Self) -> Option<Self> {
if other.value.is_zero() { return None; }
let a_wide = I512::from_i256(self.value) << 128;
let b_wide = I512::from_i256(other.value);
let (quotient, remainder) = crate::fixed_point::domains::binary_fixed::i512::divmod_i512_by_i512(a_wide, b_wide);
let abs_2rem = if remainder.is_negative() { (-remainder) << 1 } else { remainder << 1 };
let abs_div = if b_wide.is_negative() { -b_wide } else { b_wide };
let result = if abs_2rem >= abs_div {
if quotient.is_negative() { quotient - I512::from_i128(1) } else { quotient + I512::from_i128(1) }
} else {
quotient
};
if result.fits_in_i256() { Some(Self { value: result.as_i256() }) } else { None }
}
}
impl BinaryTier5 {
pub fn div(&self, other: &Self) -> Option<Self> {
if other.value.is_zero() { return None; }
let a_wide = I1024::from_i512(self.value) << 256;
let b_wide = I1024::from_i512(other.value);
let quotient = a_wide / b_wide;
let remainder = a_wide % b_wide;
let rem_neg = (remainder.words[15] as i64) < 0;
let div_neg = (b_wide.words[15] as i64) < 0;
let quot_neg = (quotient.words[15] as i64) < 0;
let abs_2rem = if rem_neg { (-remainder) << 1 } else { remainder << 1 };
let abs_div = if div_neg { -b_wide } else { b_wide };
let result = if abs_2rem >= abs_div {
if quot_neg { quotient - I1024::from_i128(1) } else { quotient + I1024::from_i128(1) }
} else {
quotient
};
if result.fits_in_i512() { Some(Self { value: result.as_i512() }) } else { None }
}
}
impl BinaryTier6 {
pub fn div(&self, other: &Self) -> Option<Self> {
if other.value == I1024::zero() { return None; }
use crate::fixed_point::domains::binary_fixed::i2048::{I2048, i2048_div};
let a_wide = I2048::from_i1024(self.value) << 512;
let b_wide = I2048::from_i1024(other.value);
let quotient = i2048_div(a_wide, b_wide);
Some(Self { value: quotient.as_i1024() })
}
}
impl UniversalBinaryFixed {
pub fn divide(&self, other: &Self) -> Result<Self, OverflowDetected> {
let (a, b) = self.align_to_common_tier(other);
match (&a.value, &b.value) {
(BinaryValue::Tier1(x), BinaryValue::Tier1(y)) => {
match x.checked_div(y) {
Some(r) => Ok(Self { value: BinaryValue::Tier1(r), current_tier: 1 }),
None => {
let x2 = x.to_tier2();
let y2 = y.to_tier2();
match x2.checked_div(&y2) {
Some(r) => Ok(Self { value: BinaryValue::Tier2(r), current_tier: 2 }),
None => {
let x3 = x.to_tier2().to_tier3();
let y3 = y.to_tier2().to_tier3();
match x3.checked_div(&y3) {
Some(r) => Ok(Self { value: BinaryValue::Tier3(r), current_tier: 3 }),
None => {
match x3.to_tier4().div(&y3.to_tier4()) {
Some(r) => Ok(Self { value: BinaryValue::Tier4(r), current_tier: 4 }),
None => Err(OverflowDetected::DivisionByZero),
}
}
}
}
}
}
}
}
(BinaryValue::Tier2(x), BinaryValue::Tier2(y)) => {
match x.checked_div(y) {
Some(r) => Ok(Self { value: BinaryValue::Tier2(r), current_tier: 2 }),
None => {
let x3 = x.to_tier3();
let y3 = y.to_tier3();
match x3.checked_div(&y3) {
Some(r) => Ok(Self { value: BinaryValue::Tier3(r), current_tier: 3 }),
None => {
match x3.to_tier4().div(&y3.to_tier4()) {
Some(r) => Ok(Self { value: BinaryValue::Tier4(r), current_tier: 4 }),
None => Err(OverflowDetected::DivisionByZero),
}
}
}
}
}
}
(BinaryValue::Tier3(x), BinaryValue::Tier3(y)) => {
match x.checked_div(y) {
Some(r) => Ok(Self { value: BinaryValue::Tier3(r), current_tier: 3 }),
None => {
match x.to_tier4().div(&y.to_tier4()) {
Some(r) => Ok(Self { value: BinaryValue::Tier4(r), current_tier: 4 }),
None => Err(OverflowDetected::DivisionByZero),
}
}
}
}
(BinaryValue::Tier4(x), BinaryValue::Tier4(y)) => {
match x.div(y) {
Some(r) => Ok(Self { value: BinaryValue::Tier4(r), current_tier: 4 }),
None => {
match x.to_tier5().div(&y.to_tier5()) {
Some(r) => Ok(Self { value: BinaryValue::Tier5(r), current_tier: 5 }),
None => Err(OverflowDetected::DivisionByZero),
}
}
}
}
(BinaryValue::Tier5(x), BinaryValue::Tier5(y)) => {
match x.div(y) {
Some(r) => Ok(Self { value: BinaryValue::Tier5(r), current_tier: 5 }),
None => {
match x.to_tier6().div(&y.to_tier6()) {
Some(r) => Ok(Self { value: BinaryValue::Tier6(r), current_tier: 6 }),
None => Err(OverflowDetected::DivisionByZero),
}
}
}
}
(BinaryValue::Tier6(x), BinaryValue::Tier6(y)) => {
match x.div(y) {
Some(r) => Ok(Self { value: BinaryValue::Tier6(r), current_tier: 6 }),
None => Err(OverflowDetected::DivisionByZero),
}
}
_ => Err(OverflowDetected::InvalidInput),
}
}
}