#[allow(unused_imports)]
use super::{BinaryStorage, ComputeStorage};
#[cfg(table_format = "q16_16")]
use crate::fixed_point::frac_config;
#[allow(unused_imports)]
use crate::fixed_point::i256::I256;
#[allow(unused_imports)]
use crate::fixed_point::i512::I512;
#[allow(unused_imports)]
use crate::fixed_point::I1024;
#[cfg(table_format = "q256_256")]
use crate::fixed_point::I2048;
use crate::fixed_point::domains::symbolic::rational::rational_number::OverflowDetected;
#[allow(unused_imports)]
#[cfg(any(table_format = "q64_64", table_format = "q128_128", table_format = "q256_256", table_format = "q32_32", table_format = "q16_16"))]
use crate::fixed_point::domains::binary_fixed::transcendental::{
sqrt_binary_i256, sqrt_binary_i512,
};
#[allow(unused_imports)]
#[cfg(table_format = "q256_256")]
use crate::fixed_point::domains::binary_fixed::transcendental::ln_binary_i1024;
#[cfg(table_format = "q256_256")]
use crate::fixed_point::domains::binary_fixed::transcendental::sin_cos_tier_n_plus_1::{
sin_compute_tier_i1024, cos_compute_tier_i1024, sincos_compute_tier_i1024, pi_half_i1024,
};
#[cfg(table_format = "q128_128")]
use crate::fixed_point::domains::binary_fixed::transcendental::sin_cos_tier_n_plus_1::{
sin_compute_tier_i512, cos_compute_tier_i512, sincos_compute_tier_i512,
};
#[cfg(table_format = "q64_64")]
use crate::fixed_point::domains::binary_fixed::transcendental::sin_cos_tier_n_plus_1::{
sin_compute_tier_i256, cos_compute_tier_i256, sincos_compute_tier_i256,
};
#[cfg(table_format = "q16_16")]
use crate::fixed_point::domains::binary_fixed::transcendental::sin_cos_tier_n_plus_1::{
sin_compute_tier_i64, cos_compute_tier_i64, sincos_compute_tier_i64,
};
#[cfg(table_format = "q256_256")]
use crate::fixed_point::domains::binary_fixed::transcendental::atan_tier_n_plus_1::{
atan_compute_tier_i1024, atan2_compute_tier_i1024,
};
#[cfg(table_format = "q128_128")]
use crate::fixed_point::domains::binary_fixed::transcendental::atan_tier_n_plus_1::{
atan_compute_tier_i512, atan2_compute_tier_i512,
};
#[cfg(table_format = "q64_64")]
use crate::fixed_point::domains::binary_fixed::transcendental::atan_tier_n_plus_1::{
atan_compute_tier_i256, atan2_compute_tier_i256,
};
#[cfg(table_format = "q16_16")]
use crate::fixed_point::domains::binary_fixed::transcendental::atan_tier_n_plus_1::{
atan_compute_tier_i64, atan2_compute_tier_i64,
};
#[cfg(table_format = "q256_256")]
use crate::fixed_point::domains::binary_fixed::transcendental::sqrt_tier_n_plus_1::sqrt_binary_i1024;
#[cfg(table_format = "q16_16")]
use crate::fixed_point::domains::binary_fixed::transcendental::sqrt_tier_n_plus_1::sqrt_binary_i64;
#[cfg(table_format = "q32_32")]
use crate::fixed_point::domains::binary_fixed::transcendental::{
sin_binary_i128, cos_binary_i128, sqrt_binary_i128,
atan_binary_i128, atan2_binary_i128,
};
#[cfg(table_format = "q32_32")]
use crate::fixed_point::domains::binary_fixed::transcendental::sin_cos_tier_n_plus_1::pi_half_i128;
pub(crate) fn upscale_to_compute(val: BinaryStorage) -> ComputeStorage {
#[cfg(table_format = "q256_256")]
{
I1024::from_i512(val) << 256
}
#[cfg(table_format = "q128_128")]
{
I512::from_i256(val) << 128
}
#[cfg(table_format = "q64_64")]
{
I256::from_i128(val) << 64
}
#[cfg(table_format = "q32_32")]
{
(val as i128) << 32
}
#[cfg(table_format = "q16_16")]
{
(val as i64) << frac_config::FRAC_BITS
}
}
#[inline]
pub(crate) fn downscale_to_storage(val: ComputeStorage) -> Result<BinaryStorage, OverflowDetected> {
#[cfg(table_format = "q256_256")]
{
let round_bit = (val & (I1024::from_i128(1) << 255)) != I1024::zero();
let mut shifted = val >> 256;
if round_bit { shifted = shifted + I1024::from_i128(1); }
if !shifted.fits_in_i512() {
return Err(OverflowDetected::TierOverflow);
}
Ok(shifted.as_i512())
}
#[cfg(table_format = "q128_128")]
{
let round_bit = (val & (I512::from_i128(1) << 127)) != I512::zero();
let mut shifted = val >> 128;
if round_bit { shifted = shifted + I512::from_i128(1); }
if !shifted.fits_in_i256() {
return Err(OverflowDetected::TierOverflow);
}
Ok(shifted.as_i256())
}
#[cfg(table_format = "q64_64")]
{
let round_bit = (val & (I256::from_i128(1) << 63)) != I256::zero();
let mut shifted = val >> 64;
if round_bit { shifted = shifted + I256::from_i128(1); }
if !shifted.fits_in_i128() {
return Err(OverflowDetected::TierOverflow);
}
Ok(shifted.as_i128())
}
#[cfg(table_format = "q32_32")]
{
let round_bit = (val & (1i128 << 31)) != 0;
let shifted = (val >> 32) + round_bit as i128;
i64::try_from(shifted).map_err(|_| OverflowDetected::TierOverflow)
}
#[cfg(table_format = "q16_16")]
{
let round_bit = (val & (1i64 << frac_config::FRAC_ROUND_BIT)) != 0;
let shifted = (val >> frac_config::FRAC_BITS) + round_bit as i64;
i32::try_from(shifted).map_err(|_| OverflowDetected::TierOverflow)
}
}
#[inline]
pub(crate) fn downscale_to_storage_floor(val: ComputeStorage) -> Result<BinaryStorage, OverflowDetected> {
#[cfg(table_format = "q256_256")]
{
let shifted = val >> 256;
if !shifted.fits_in_i512() {
return Err(OverflowDetected::TierOverflow);
}
Ok(shifted.as_i512())
}
#[cfg(table_format = "q128_128")]
{
let shifted = val >> 128;
if !shifted.fits_in_i256() {
return Err(OverflowDetected::TierOverflow);
}
Ok(shifted.as_i256())
}
#[cfg(table_format = "q64_64")]
{
let shifted = val >> 64;
if !shifted.fits_in_i128() {
return Err(OverflowDetected::TierOverflow);
}
Ok(shifted.as_i128())
}
#[cfg(table_format = "q32_32")]
{
let shifted = val >> 32;
if shifted > i64::MAX as i128 || shifted < i64::MIN as i128 {
return Err(OverflowDetected::TierOverflow);
}
Ok(shifted as i64)
}
#[cfg(table_format = "q16_16")]
{
let shifted = val >> frac_config::FRAC_BITS;
if shifted > i32::MAX as i64 || shifted < i32::MIN as i64 {
return Err(OverflowDetected::TierOverflow);
}
Ok(shifted as i32)
}
}
#[inline]
pub(crate) fn downscale_to_storage_ceil(val: ComputeStorage) -> Result<BinaryStorage, OverflowDetected> {
#[cfg(table_format = "q256_256")]
{
let mask = (I1024::from_i128(1) << 256) - I1024::from_i128(1);
let inexact = (val & mask) != I1024::zero();
let shifted = val >> 256;
if !shifted.fits_in_i512() {
return Err(OverflowDetected::TierOverflow);
}
let floor = shifted.as_i512();
if inexact {
floor.checked_add(I512::from_i128(1)).ok_or(OverflowDetected::TierOverflow)
} else {
Ok(floor)
}
}
#[cfg(table_format = "q128_128")]
{
let mask = (I512::from_i128(1) << 128) - I512::from_i128(1);
let inexact = (val & mask) != I512::zero();
let shifted = val >> 128;
if !shifted.fits_in_i256() {
return Err(OverflowDetected::TierOverflow);
}
let floor = shifted.as_i256();
if inexact {
floor.checked_add(I256::from_i128(1)).ok_or(OverflowDetected::TierOverflow)
} else {
Ok(floor)
}
}
#[cfg(table_format = "q64_64")]
{
let mask = (I256::from_i128(1) << 64) - I256::from_i128(1);
let inexact = (val & mask) != I256::zero();
let shifted = val >> 64;
if !shifted.fits_in_i128() {
return Err(OverflowDetected::TierOverflow);
}
let floor = shifted.as_i128();
if inexact {
floor.checked_add(1).ok_or(OverflowDetected::TierOverflow)
} else {
Ok(floor)
}
}
#[cfg(table_format = "q32_32")]
{
let inexact = (val & ((1i128 << 32) - 1)) != 0;
let shifted = val >> 32;
if shifted > i64::MAX as i128 || shifted < i64::MIN as i128 {
return Err(OverflowDetected::TierOverflow);
}
let floor = shifted as i64;
if inexact {
floor.checked_add(1).ok_or(OverflowDetected::TierOverflow)
} else {
Ok(floor)
}
}
#[cfg(table_format = "q16_16")]
{
let inexact = (val & ((1i64 << frac_config::FRAC_BITS) - 1)) != 0;
let shifted = val >> frac_config::FRAC_BITS;
if shifted > i32::MAX as i64 || shifted < i32::MIN as i64 {
return Err(OverflowDetected::TierOverflow);
}
let floor = shifted as i32;
if inexact {
floor.checked_add(1).ok_or(OverflowDetected::TierOverflow)
} else {
Ok(floor)
}
}
}
#[inline]
pub(super) fn decimal_to_compute_storage(decimals: u8, scaled: BinaryStorage) -> Result<ComputeStorage, OverflowDetected> {
if decimals == 0 {
#[cfg(table_format = "q256_256")]
{ return Ok(I1024::from_i512(scaled) << 512); }
#[cfg(table_format = "q128_128")]
{ return Ok(I512::from_i256(scaled) << 256); }
#[cfg(table_format = "q64_64")]
{ return Ok(I256::from_i128(scaled) << 128); }
#[cfg(table_format = "q32_32")]
{ return Ok((scaled as i128) << 64); }
#[cfg(table_format = "q16_16")]
{
let v = (scaled as i128) << frac_config::COMPUTE_FRAC_BITS;
return i64::try_from(v).map_err(|_| OverflowDetected::TierOverflow);
}
}
let den = pow10_compute(decimals)?;
#[cfg(table_format = "q256_256")]
{
let num = I1024::from_i512(scaled) << 512;
Ok(num / den)
}
#[cfg(table_format = "q128_128")]
{
let num = I512::from_i256(scaled) << 256;
Ok(num / den)
}
#[cfg(table_format = "q64_64")]
{
let num = I256::from_i128(scaled) << 128;
Ok(num / den)
}
#[cfg(table_format = "q32_32")]
{
let num = (scaled as i128) << 64;
Ok(num / den)
}
#[cfg(table_format = "q16_16")]
{
let num = (scaled as i128) << frac_config::COMPUTE_FRAC_BITS;
i64::try_from(num / den as i128).map_err(|_| OverflowDetected::TierOverflow)
}
}
#[inline]
pub(super) fn pow10_compute(exp: u8) -> Result<ComputeStorage, OverflowDetected> {
#[cfg(table_format = "q64_64")]
{
const TABLE: [I256; 39] = pow10_table_i256();
if (exp as usize) < TABLE.len() {
Ok(TABLE[exp as usize])
} else if exp <= 76 {
Ok(pow10_compute_fallback_i256(exp))
} else {
Err(OverflowDetected::Overflow)
}
}
#[cfg(table_format = "q128_128")]
{
const TABLE: [I512; 77] = pow10_table_i512();
if (exp as usize) < TABLE.len() {
Ok(TABLE[exp as usize])
} else if exp <= 153 {
Ok(pow10_compute_fallback_i512(exp))
} else {
Err(OverflowDetected::Overflow)
}
}
#[cfg(table_format = "q256_256")]
{
const TABLE: [I1024; 155] = pow10_table_i1024();
if (exp as usize) < TABLE.len() {
Ok(TABLE[exp as usize])
} else {
Ok(pow10_compute_fallback_i1024(exp))
}
}
#[cfg(table_format = "q32_32")]
{
const TABLE: [i128; 39] = pow10_table_i128();
if (exp as usize) < TABLE.len() {
Ok(TABLE[exp as usize])
} else {
Err(OverflowDetected::Overflow)
}
}
#[cfg(table_format = "q16_16")]
{
const TABLE: [i64; 19] = pow10_table_i64();
if (exp as usize) < TABLE.len() {
Ok(TABLE[exp as usize])
} else {
Err(OverflowDetected::Overflow)
}
}
}
const fn mul10_words<const N: usize>(words: [u64; N]) -> [u64; N] {
let mut result = [0u64; N];
let mut carry: u64 = 0;
let mut i = 0;
while i < N {
let prod = words[i] as u128 * 10 + carry as u128;
result[i] = prod as u64;
carry = (prod >> 64) as u64;
i += 1;
}
result
}
#[allow(dead_code)]
pub(super) const fn pow10_table_i256() -> [I256; 39] {
let mut table = [I256::from_words([0; 4]); 39];
let mut words = [0u64; 4];
words[0] = 1; table[0] = I256::from_words(words);
let mut i = 1;
while i < 39 {
words = mul10_words(words);
table[i] = I256::from_words(words);
i += 1;
}
table
}
#[cfg(any(table_format = "q128_128", table_format = "q256_256"))]
pub(super) const fn pow10_table_i512() -> [I512; 77] {
let mut table = [I512::from_words([0; 8]); 77];
let mut words = [0u64; 8];
words[0] = 1;
table[0] = I512::from_words(words);
let mut i = 1;
while i < 77 {
words = mul10_words(words);
table[i] = I512::from_words(words);
i += 1;
}
table
}
#[cfg(table_format = "q256_256")]
pub(super) const fn pow10_table_i1024() -> [I1024; 155] {
let mut table = [I1024::from_words([0; 16]); 155];
let mut words = [0u64; 16];
words[0] = 1;
table[0] = I1024::from_words(words);
let mut i = 1;
while i < 155 {
words = mul10_words(words);
table[i] = I1024::from_words(words);
i += 1;
}
table
}
#[cfg(table_format = "q32_32")]
pub(super) const fn pow10_table_i128() -> [i128; 39] {
let mut table = [0i128; 39];
table[0] = 1;
let mut i = 1;
while i < 39 {
table[i] = table[i - 1] * 10;
i += 1;
}
table
}
#[cfg(table_format = "q16_16")]
pub(super) const fn pow10_table_i64() -> [i64; 19] {
let mut table = [0i64; 19];
table[0] = 1;
let mut i = 1;
while i < 19 {
table[i] = table[i - 1] * 10;
i += 1;
}
table
}
#[cold]
#[allow(dead_code)]
fn pow10_compute_fallback_i256(exp: u8) -> I256 {
let mut result = I256::from_i128(1);
let ten = I256::from_i128(10);
for _ in 0..exp { result = result * ten; }
result
}
#[cold]
#[cfg(table_format = "q128_128")]
fn pow10_compute_fallback_i512(exp: u8) -> I512 {
let mut result = I512::from_i128(1);
let ten = I512::from_i128(10);
for _ in 0..exp { result = result * ten; }
result
}
#[cold]
#[cfg(table_format = "q256_256")]
fn pow10_compute_fallback_i1024(exp: u8) -> I1024 {
let mut result = I1024::from_i128(1);
let ten = I1024::from_i128(10);
for _ in 0..exp { result = result * ten; }
result
}
#[inline]
pub(super) fn symbolic_to_compute_storage(num: i128, den: i128) -> Result<ComputeStorage, OverflowDetected> {
#[cfg(table_format = "q256_256")]
{
let n = I1024::from_i512(I512::from_i256(I256::from_i128(num))) << 512;
let d = I1024::from_i512(I512::from_i256(I256::from_i128(den)));
Ok(n / d)
}
#[cfg(table_format = "q128_128")]
{
let n = I512::from_i256(I256::from_i128(num)) << 256;
let d = I512::from_i256(I256::from_i128(den));
Ok(n / d)
}
#[cfg(table_format = "q64_64")]
{
let n = I256::from_i128(num) << 128;
let d = I256::from_i128(den);
Ok(n / d)
}
#[cfg(table_format = "q32_32")]
{
let n = I256::from_i128(num) << 64;
let d = I256::from_i128(den);
let q = n / d;
if !q.fits_in_i128() { return Err(OverflowDetected::TierOverflow); }
Ok(q.as_i128())
}
#[cfg(table_format = "q16_16")]
{
let n = I256::from_i128(num) << (frac_config::COMPUTE_FRAC_BITS as usize);
let q = n / I256::from_i128(den);
if !q.fits_in_i128() { return Err(OverflowDetected::TierOverflow); }
i64::try_from(q.as_i128()).map_err(|_| OverflowDetected::TierOverflow)
}
}
pub(super) fn symbolic_wide_to_compute_storage(
rational: &crate::fixed_point::domains::symbolic::rational::RationalNumber,
) -> Result<ComputeStorage, OverflowDetected> {
let parts = rational.extract_native();
#[cfg(table_format = "q128_128")]
{
if let Some((num_i256, den_i256)) = parts.try_as_i256_pair() {
let n = I512::from_i256(num_i256) << 256;
let d = I512::from_i256(den_i256);
return Ok(n / d);
}
if let Some((num_i512, den_i512)) = parts.try_as_i512_pair() {
let n = I1024::from_i512(num_i512) << 256;
let d = I1024::from_i512(den_i512);
let q = n / d;
if !q.fits_in_i512() { return Err(OverflowDetected::TierOverflow); }
return Ok(q.as_i512());
}
}
#[cfg(table_format = "q256_256")]
{
if let Some((num_i512, den_i512)) = parts.try_as_i512_pair() {
let n = I1024::from_i512(num_i512) << 512;
let d = I1024::from_i512(den_i512);
return Ok(n / d);
}
}
#[cfg(table_format = "q64_64")]
{
if let Some((num_i256, den_i256)) = parts.try_as_i256_pair() {
let q = (I512::from_i256(num_i256) << 128) / I512::from_i256(den_i256);
if !q.fits_in_i256() { return Err(OverflowDetected::TierOverflow); }
return Ok(q.as_i256());
}
if let Some((num_i512, den_i512)) = parts.try_as_i512_pair() {
let n = I1024::from_i512(num_i512) << 128;
let d = I1024::from_i512(den_i512);
let q = n / d;
if !q.fits_in_i256() { return Err(OverflowDetected::TierOverflow); }
return Ok(q.as_i256());
}
}
#[cfg(table_format = "q32_32")]
{
if let Some((num_i256, den_i256)) = parts.try_as_i256_pair() {
let q = (I512::from_i256(num_i256) << 64) / I512::from_i256(den_i256);
if !q.fits_in_i128() { return Err(OverflowDetected::TierOverflow); }
return Ok(q.as_i128());
}
if let Some((num_i512, den_i512)) = parts.try_as_i512_pair() {
let n = I1024::from_i512(num_i512) << 64;
let d = I1024::from_i512(den_i512);
let q = n / d;
if !q.fits_in_i128() { return Err(OverflowDetected::TierOverflow); }
return Ok(q.as_i512().as_i256().as_i128());
}
}
#[cfg(table_format = "q16_16")]
{
if let Some((num_i256, den_i256)) = parts.try_as_i256_pair() {
let q = (I512::from_i256(num_i256) << (frac_config::COMPUTE_FRAC_BITS as usize)) / I512::from_i256(den_i256);
if !q.fits_in_i128() { return Err(OverflowDetected::TierOverflow); }
return i64::try_from(q.as_i128()).map_err(|_| OverflowDetected::TierOverflow);
}
if let Some((num_i512, den_i512)) = parts.try_as_i512_pair() {
let n = I1024::from_i512(num_i512) << (frac_config::COMPUTE_FRAC_BITS as usize);
let d = I1024::from_i512(den_i512);
let q = n / d;
if !q.fits_in_i128() { return Err(OverflowDetected::TierOverflow); }
return i64::try_from(q.as_i128()).map_err(|_| OverflowDetected::TierOverflow);
}
}
Err(OverflowDetected::TierOverflow)
}
#[inline]
pub(crate) fn compute_add(a: ComputeStorage, b: ComputeStorage) -> ComputeStorage {
a.checked_add(b).expect("compute tier: addition overflow")
}
#[inline]
pub(crate) fn compute_checked_add(
a: ComputeStorage,
b: ComputeStorage,
) -> Result<ComputeStorage, OverflowDetected> {
a.checked_add(b).ok_or(OverflowDetected::TierOverflow)
}
pub(crate) fn compute_checked_subtract(
a: ComputeStorage,
b: ComputeStorage,
) -> Result<ComputeStorage, OverflowDetected> {
compute_checked_add(a, compute_checked_negate(b)?)
}
pub(crate) fn compute_checked_negate(a: ComputeStorage) -> Result<ComputeStorage, OverflowDetected> {
#[cfg(not(table_format = "q256_256"))]
{ a.checked_neg().ok_or(OverflowDetected::TierOverflow) }
#[cfg(table_format = "q256_256")]
{ if a == I1024::min_value() { Err(OverflowDetected::TierOverflow) } else { Ok(-a) } }
}
#[inline]
pub(crate) fn compute_ceiling() -> ComputeStorage {
#[cfg(table_format = "q16_16")]
{ i64::MAX }
#[cfg(table_format = "q32_32")]
{ i128::MAX }
#[cfg(table_format = "q64_64")]
{ I256::max_value() }
#[cfg(table_format = "q128_128")]
{ I512::max_value() }
#[cfg(table_format = "q256_256")]
{ I1024::max_value() }
}
#[inline]
pub(crate) fn exp_sentinel_reached(v: &ComputeStorage) -> bool {
#[cfg(table_format = "q16_16")]
{ *v == i64::MAX }
#[cfg(table_format = "q32_32")]
{ *v == i128::MAX }
#[cfg(table_format = "q64_64")]
{ *v >= I256::from_i128(i128::MAX) << 128usize }
#[cfg(table_format = "q128_128")]
{ *v >= I512::from_i256(I256::from_i128(i128::MAX)) << 256usize }
#[cfg(table_format = "q256_256")]
{ *v == I1024::max_value() }
}
#[inline]
pub(crate) fn compute_subtract(a: ComputeStorage, b: ComputeStorage) -> ComputeStorage {
#[cfg(not(table_format = "q256_256"))]
{ a.checked_sub(b).expect("compute tier: subtraction overflow") }
#[cfg(table_format = "q256_256")]
{ a.checked_add(compute_negate(b)).expect("compute tier: subtraction overflow") }
}
#[inline]
pub(crate) fn compute_negate(a: ComputeStorage) -> ComputeStorage {
#[cfg(not(table_format = "q256_256"))]
{ a.checked_neg().expect("compute tier: negation overflow") }
#[cfg(table_format = "q256_256")]
{
assert!(a != I1024::min_value(), "compute tier: negation overflow");
-a
}
}
pub(crate) fn compute_multiply(a: ComputeStorage, b: ComputeStorage) -> ComputeStorage {
compute_checked_multiply(a, b).expect("compute tier: multiplication overflow")
}
pub(crate) fn compute_checked_multiply(a: ComputeStorage, b: ComputeStorage) -> Result<ComputeStorage, OverflowDetected> {
const OVERFLOW: OverflowDetected = OverflowDetected::TierOverflow;
#[cfg(table_format = "q256_256")]
{
let a_negative = a < I1024::zero();
let b_negative = b < I1024::zero();
let abs_a = if a_negative { I1024::zero() - a } else { a };
let abs_b = if b_negative { I1024::zero() - b } else { b };
let product = abs_a.mul_to_i2048(abs_b);
let rounded = (product + (I2048::from_i128(1) << 511)) >> 512;
if !(rounded.fits_in_i1024()) { return Err(OVERFLOW); }
let magnitude = rounded.as_i1024();
Ok(if a_negative != b_negative { I1024::zero() - magnitude } else { magnitude })
}
#[cfg(table_format = "q128_128")]
{
let product = I1024::from_i512(a) * I1024::from_i512(b);
let round_bit = (product & (I1024::from_i128(1) << 255)) != I1024::zero();
let mut shifted = product >> 256;
if round_bit { shifted = shifted + I1024::from_i128(1); }
if !(shifted.fits_in_i512()) { return Err(OVERFLOW); }
Ok(shifted.as_i512())
}
#[cfg(table_format = "q64_64")]
{
let product = I512::from_i256(a) * I512::from_i256(b);
let round_bit = (product & (I512::from_i128(1) << 127)) != I512::zero();
let mut shifted = product >> 128;
if round_bit { shifted = shifted + I512::from_i128(1); }
if !(shifted.fits_in_i256()) { return Err(OVERFLOW); }
Ok(shifted.as_i256())
}
#[cfg(table_format = "q32_32")]
{
let product = I256::from_i128(a) * I256::from_i128(b);
let round_bit = (product & (I256::from_i128(1) << 63)) != I256::zero();
let mut shifted = product >> 64;
if round_bit { shifted = shifted + I256::from_i128(1); }
if !(shifted.fits_in_i128()) { return Err(OVERFLOW); }
Ok(shifted.as_i128())
}
#[cfg(table_format = "q16_16")]
{
let product = (a as i128) * (b as i128);
let round_bit = ((product >> frac_config::COMPUTE_ROUND_BIT) & 1) as i128;
i64::try_from((product >> frac_config::COMPUTE_FRAC_BITS) + round_bit).map_err(|_| OVERFLOW)
}
}
#[inline]
pub(crate) fn compute_divide(a: ComputeStorage, b: ComputeStorage) -> Result<ComputeStorage, OverflowDetected> {
compute_checked_divide(a, b)
}
#[inline]
pub(crate) fn compute_checked_divide(a: ComputeStorage, b: ComputeStorage) -> Result<ComputeStorage, OverflowDetected> {
macro_rules! nearest {
($n:expr, $d:expr, $q:expr, $r:expr, $zero:expr, $one:expr) => {{
let (n, d, q, r) = ($n, $d, $q, $r);
let positive = (n < $zero) == (d < $zero);
let r_abs = if r < $zero { $zero - r } else { r };
let d_abs = if d < $zero { $zero - d } else { d };
let rest = d_abs - r_abs; let bump = if positive { r_abs >= rest } else { r_abs > rest };
if !bump { q } else if positive { q + $one } else { q - $one }
}};
}
#[cfg(table_format = "q256_256")]
{
if b == I1024::zero() { return Err(OverflowDetected::DivisionByZero); }
use crate::fixed_point::domains::binary_fixed::i2048::i2048_divmod;
let (n, d) = (I2048::from_i1024(a) << 512, I2048::from_i1024(b));
let (q, r) = i2048_divmod(n, d);
let q = nearest!(n, d, q, r, I2048::zero(), I2048::from_i128(1));
if !q.fits_in_i1024() { return Err(OverflowDetected::TierOverflow); }
Ok(q.as_i1024())
}
#[cfg(table_format = "q128_128")]
{
if b == I512::zero() { return Err(OverflowDetected::DivisionByZero); }
let (n, d) = (I1024::from_i512(a) << 256, I1024::from_i512(b));
let q = n / d;
let q = nearest!(n, d, q, n - q * d, I1024::zero(), I1024::from_i128(1));
if !q.fits_in_i512() { return Err(OverflowDetected::TierOverflow); }
Ok(q.as_i512())
}
#[cfg(table_format = "q64_64")]
{
if b == I256::zero() { return Err(OverflowDetected::DivisionByZero); }
let (n, d) = (I512::from_i256(a) << 128, I512::from_i256(b));
let q = n / d;
let q = nearest!(n, d, q, n - q * d, I512::zero(), I512::from_i128(1));
if !q.fits_in_i256() { return Err(OverflowDetected::TierOverflow); }
Ok(q.as_i256())
}
#[cfg(table_format = "q32_32")]
{
if b == 0i128 { return Err(OverflowDetected::DivisionByZero); }
let (n, d) = (I256::from_i128(a) << 64, I256::from_i128(b));
let q = n / d;
let q = nearest!(n, d, q, n - q * d, I256::zero(), I256::from_i128(1));
if !q.fits_in_i128() { return Err(OverflowDetected::TierOverflow); }
Ok(q.as_i128())
}
#[cfg(table_format = "q16_16")]
{
if b == 0i64 { return Err(OverflowDetected::DivisionByZero); }
let (n, d) = ((a as i128) << frac_config::COMPUTE_FRAC_BITS, b as i128);
let q = nearest!(n, d, n / d, n % d, 0i128, 1i128);
if q > i64::MAX as i128 || q < i64::MIN as i128 { return Err(OverflowDetected::TierOverflow); }
Ok(q as i64)
}
}
#[inline]
pub(crate) fn compute_halve(a: ComputeStorage) -> ComputeStorage {
a >> 1
}
#[allow(dead_code)]
#[inline]
pub(crate) fn make_compute_int(n: i64) -> ComputeStorage {
#[cfg(table_format = "q256_256")]
{ I1024::from_i128(n as i128) << 512 }
#[cfg(table_format = "q128_128")]
{ I512::from_i128(n as i128) << 256 }
#[cfg(table_format = "q64_64")]
{ I256::from_i128(n as i128) << 128 }
#[cfg(table_format = "q32_32")]
{ (n as i128) << 64 }
#[cfg(table_format = "q16_16")]
{
n.checked_mul(1i64 << frac_config::COMPUTE_FRAC_BITS)
.expect("make_compute_int: integer outside the realtime compute range")
}
}
pub(crate) fn compute_mul_div_int(c: ComputeStorage, num: i64, den: i64) -> Result<ComputeStorage, OverflowDetected> {
if den <= 0 { return Err(OverflowDetected::DivisionByZero); }
#[cfg(table_format = "q16_16")]
{
let p = (c as i128) * (num as i128);
let d = den as i128;
let q = (2 * p + d).div_euclid(2 * d);
i64::try_from(q).map_err(|_| OverflowDetected::TierOverflow)
}
#[cfg(not(table_format = "q16_16"))]
{
compute_divide(compute_multiply(c, make_compute_int(num)), make_compute_int(den))
}
}
pub(crate) fn compute_div_count(c: ComputeStorage, n: usize) -> Result<ComputeStorage, OverflowDetected> {
if n == 0 { return Err(OverflowDetected::DivisionByZero); }
#[cfg(table_format = "q16_16")]
{
let n = i64::try_from(n).map_err(|_| OverflowDetected::TierOverflow)?;
compute_mul_div_int(c, 1, n)
}
#[cfg(not(table_format = "q16_16"))]
{
compute_divide(c, make_compute_int(n as i64))
}
}
#[inline]
pub(crate) fn compute_is_zero(a: &ComputeStorage) -> bool {
#[cfg(table_format = "q256_256")]
{ *a == I1024::zero() }
#[cfg(table_format = "q128_128")]
{ *a == I512::zero() }
#[cfg(table_format = "q64_64")]
{ *a == I256::zero() }
#[cfg(table_format = "q32_32")]
{ *a == 0i128 }
#[cfg(table_format = "q16_16")]
{ *a == 0i64 }
}
#[inline]
pub(crate) fn compute_is_negative(a: &ComputeStorage) -> bool {
#[cfg(table_format = "q256_256")]
{ (a.words[15] & 0x8000_0000_0000_0000) != 0 }
#[cfg(table_format = "q128_128")]
{ *a < I512::zero() }
#[cfg(table_format = "q64_64")]
{ *a < I256::zero() }
#[cfg(table_format = "q32_32")]
{ *a < 0i128 }
#[cfg(table_format = "q16_16")]
{ *a < 0i64 }
}
#[inline]
pub(super) fn sin_at_compute_tier(x: ComputeStorage) -> ComputeStorage {
#[cfg(table_format = "q256_256")]
{ sin_compute_tier_i1024(x) }
#[cfg(table_format = "q128_128")]
{ sin_compute_tier_i512(x) }
#[cfg(table_format = "q64_64")]
{ sin_compute_tier_i256(x) }
#[cfg(table_format = "q32_32")]
{
sin_binary_i128(x)
}
#[cfg(table_format = "q16_16")]
{
sin_compute_tier_i64(x)
}
}
#[inline]
pub(super) fn cos_at_compute_tier(x: ComputeStorage) -> ComputeStorage {
#[cfg(table_format = "q256_256")]
{ cos_compute_tier_i1024(x) }
#[cfg(table_format = "q128_128")]
{ cos_compute_tier_i512(x) }
#[cfg(table_format = "q64_64")]
{ cos_compute_tier_i256(x) }
#[cfg(table_format = "q32_32")]
{
cos_binary_i128(x)
}
#[cfg(table_format = "q16_16")]
{
cos_compute_tier_i64(x)
}
}
#[inline]
pub(crate) fn sinhcosh_at_compute_tier(x: ComputeStorage) -> (ComputeStorage, ComputeStorage) {
let ep = exp_at_compute_tier(x);
let en = exp_at_compute_tier(compute_negate(x));
let sinh_c = compute_halve(compute_subtract(ep, en));
let cosh_sum = compute_checked_add(ep, en).unwrap_or_else(|_| compute_ceiling());
let cosh_c = compute_halve(cosh_sum);
(sinh_c, cosh_c)
}
#[inline]
pub(crate) fn sincos_at_compute_tier(x: ComputeStorage) -> (ComputeStorage, ComputeStorage) {
#[cfg(table_format = "q256_256")]
{ sincos_compute_tier_i1024(x) }
#[cfg(table_format = "q128_128")]
{ sincos_compute_tier_i512(x) }
#[cfg(table_format = "q64_64")]
{ sincos_compute_tier_i256(x) }
#[cfg(table_format = "q32_32")]
{
(sin_binary_i128(x), cos_binary_i128(x))
}
#[cfg(table_format = "q16_16")]
{
sincos_compute_tier_i64(x)
}
}
#[inline]
pub(super) fn atan_at_compute_tier(x: ComputeStorage) -> ComputeStorage {
#[cfg(table_format = "q256_256")]
{ atan_compute_tier_i1024(x) }
#[cfg(table_format = "q128_128")]
{ atan_compute_tier_i512(x) }
#[cfg(table_format = "q64_64")]
{ atan_compute_tier_i256(x) }
#[cfg(table_format = "q32_32")]
{
atan_binary_i128(x)
}
#[cfg(table_format = "q16_16")]
{
atan_compute_tier_i64(x)
}
}
#[inline]
pub(super) fn atan2_at_compute_tier(y: ComputeStorage, x: ComputeStorage) -> ComputeStorage {
#[cfg(table_format = "q256_256")]
{ atan2_compute_tier_i1024(y, x) }
#[cfg(table_format = "q128_128")]
{ atan2_compute_tier_i512(y, x) }
#[cfg(table_format = "q64_64")]
{ atan2_compute_tier_i256(y, x) }
#[cfg(table_format = "q32_32")]
{
atan2_binary_i128(y, x)
}
#[cfg(table_format = "q16_16")]
{
atan2_compute_tier_i64(y, x)
}
}
#[inline]
pub(crate) fn exp_at_compute_tier(x: ComputeStorage) -> ComputeStorage {
#[cfg(table_format = "q256_256")]
{
use crate::fixed_point::domains::binary_fixed::transcendental::exp_binary_i1024;
exp_binary_i1024(x)
}
#[cfg(table_format = "q128_128")]
{
use crate::fixed_point::domains::binary_fixed::transcendental::exp_binary_i512;
exp_binary_i512(x)
}
#[cfg(table_format = "q64_64")]
{
use crate::fixed_point::domains::binary_fixed::transcendental::exp_binary_i256;
exp_binary_i256(x)
}
#[cfg(table_format = "q32_32")]
{
use crate::fixed_point::domains::binary_fixed::transcendental::exp_binary_i128;
exp_binary_i128(x)
}
#[cfg(table_format = "q16_16")]
{
use crate::fixed_point::domains::binary_fixed::transcendental::exp_binary_i64;
exp_binary_i64(x)
}
}
#[inline]
pub(crate) fn sqrt_at_compute_tier(x: ComputeStorage) -> ComputeStorage {
#[cfg(table_format = "q256_256")]
{ sqrt_binary_i1024(x) }
#[cfg(table_format = "q128_128")]
{
sqrt_binary_i512(x)
}
#[cfg(table_format = "q64_64")]
{
sqrt_binary_i256(x)
}
#[cfg(table_format = "q32_32")]
{
sqrt_binary_i128(x)
}
#[cfg(table_format = "q16_16")]
{
sqrt_binary_i64(x)
}
}
#[allow(dead_code)]
#[inline]
pub(crate) fn ln_at_compute_tier(x: ComputeStorage) -> ComputeStorage {
#[cfg(table_format = "q256_256")]
{
use crate::fixed_point::domains::binary_fixed::transcendental::ln_binary_i1024;
ln_binary_i1024(x)
}
#[cfg(table_format = "q128_128")]
{
use crate::fixed_point::domains::binary_fixed::transcendental::ln_binary_i512;
ln_binary_i512(x)
}
#[cfg(table_format = "q64_64")]
{
use crate::fixed_point::domains::binary_fixed::transcendental::ln_binary_i256;
ln_binary_i256(x)
}
#[cfg(table_format = "q32_32")]
{
use crate::fixed_point::domains::binary_fixed::transcendental::ln_binary_i128;
ln_binary_i128(x)
}
#[cfg(table_format = "q16_16")]
{
use crate::fixed_point::domains::binary_fixed::transcendental::ln_binary_i64;
ln_binary_i64(x)
}
}
#[inline]
pub(super) fn pi_half_at_compute_tier() -> ComputeStorage {
#[cfg(table_format = "q256_256")]
{
pi_half_i1024()
}
#[cfg(table_format = "q128_128")]
{
use crate::fixed_point::domains::binary_fixed::transcendental::sin_cos_tier_n_plus_1::PI_HALF_Q256;
I512::from_words(PI_HALF_Q256)
}
#[cfg(table_format = "q64_64")]
{
use crate::fixed_point::domains::binary_fixed::transcendental::sin_cos_tier_n_plus_1::PI_HALF_Q128;
I256::from_words(PI_HALF_Q128)
}
#[cfg(table_format = "q32_32")]
{
pi_half_i128()
}
#[cfg(table_format = "q16_16")]
{
use crate::fixed_point::domains::binary_fixed::transcendental::sin_cos_tier_n_plus_1::PI_HALF_Q64;
(PI_HALF_Q64 >> frac_config::NATIVE_UPSHIFT) as i64
}
}