use super::decimal_compute::{ComputeStorage, DECIMAL_COMPUTE_DP, decimal_compute_neg};
use crate::fixed_point::domains::symbolic::rational::rational_number::OverflowDetected;
#[cfg(any(table_format = "q16_16", table_format = "q32_32"))]
pub(super) use crate::fixed_point::i256::I256 as Hp;
#[cfg(table_format = "q64_64")]
pub(super) use crate::fixed_point::i512::I512 as Hp;
#[cfg(table_format = "q128_128")]
pub(super) use crate::fixed_point::I1024 as Hp;
#[cfg(table_format = "q256_256")]
pub(super) use crate::fixed_point::I2048 as Hp;
#[cfg(table_format = "q16_16")]
pub(super) const HP_DP: u32 = 28;
#[cfg(table_format = "q32_32")]
pub(super) const HP_DP: u32 = 38;
#[cfg(table_format = "q64_64")]
pub(super) const HP_DP: u32 = 60;
#[cfg(table_format = "q128_128")]
pub(super) const HP_DP: u32 = 90;
#[cfg(table_format = "q256_256")]
pub(super) const HP_DP: u32 = 170;
#[cfg(any(table_format = "q16_16", table_format = "q32_32"))]
pub(super) const HP_BITS: u32 = 256;
#[cfg(table_format = "q64_64")]
pub(super) const HP_BITS: u32 = 512;
#[cfg(table_format = "q128_128")]
pub(super) const HP_BITS: u32 = 1024;
#[cfg(table_format = "q256_256")]
pub(super) const HP_BITS: u32 = 2048;
#[cfg(table_format = "q16_16")]
pub(super) const COMPUTE_BITS: i64 = 64;
#[cfg(table_format = "q32_32")]
pub(super) const COMPUTE_BITS: i64 = 128;
#[cfg(table_format = "q64_64")]
pub(super) const COMPUTE_BITS: i64 = 256;
#[cfg(table_format = "q128_128")]
pub(super) const COMPUTE_BITS: i64 = 512;
#[cfg(table_format = "q256_256")]
pub(super) const COMPUTE_BITS: i64 = 1024;
#[inline]
pub(super) fn hp_small(v: u64) -> Hp {
Hp::from_i128(v as i128)
}
#[inline]
pub(super) fn hp_is_negative(v: &Hp) -> bool {
v.words[v.words.len() - 1] >> 63 == 1
}
#[inline]
pub(super) fn hp_is_zero(v: &Hp) -> bool {
v.words.iter().all(|&w| w == 0)
}
pub(super) fn hp_bit_length(v: &Hp) -> u32 {
for i in (0..v.words.len()).rev() {
if v.words[i] != 0 {
return i as u32 * 64 + (64 - v.words[i].leading_zeros());
}
}
0
}
pub(super) fn hp_mul_small(mut v: Hp, m: u64) -> Hp {
let mut carry: u128 = 0;
for w in v.words.iter_mut() {
let cur = (*w as u128) * (m as u128) + carry;
*w = cur as u64;
carry = cur >> 64;
}
assert!(carry == 0 && !hp_is_negative(&v), "decimal exp: working value outside its tier");
v
}
pub(super) fn hp_divmod_small(mut v: Hp, d: u64) -> (Hp, u64) {
let mut rem: u128 = 0;
for w in v.words.iter_mut().rev() {
let cur = (rem << 64) | *w as u128;
*w = (cur / d as u128) as u64;
rem = cur % d as u128;
}
(v, rem as u64)
}
pub(super) fn hp_mul_pow10(mut v: Hp, mut e: u32) -> Hp {
while e > 0 {
let k = e.min(19);
v = hp_mul_small(v, 10u64.pow(k));
e -= k;
}
v
}
pub(super) fn hp_div_pow10(mut v: Hp, mut e: u32) -> Hp {
while e > 0 {
let k = e.min(19);
v = hp_divmod_small(v, 10u64.pow(k)).0;
e -= k;
}
v
}
pub(super) fn hp_pow10(e: u32) -> Hp {
hp_mul_pow10(hp_small(1), e)
}
pub(super) fn hp_shl(v: Hp, k: u32) -> Hp {
let mut out = hp_small(0);
let (words, bits) = ((k / 64) as usize, k % 64);
let n = v.words.len();
for i in (words..n).rev() {
let lo = v.words[i - words];
let carry = if bits > 0 && i > words { v.words[i - words - 1] >> (64 - bits) } else { 0 };
out.words[i] = if bits > 0 { (lo << bits) | carry } else { lo };
}
out
}
pub(super) fn hp_shr(v: Hp, k: u32) -> Hp {
let mut out = hp_small(0);
let (words, bits) = ((k / 64) as usize, k % 64);
let n = v.words.len();
for i in 0..n.saturating_sub(words) {
let hi = if bits > 0 && i + words + 1 < n { v.words[i + words + 1] << (64 - bits) } else { 0 };
out.words[i] = if bits > 0 { (v.words[i + words] >> bits) | hi } else { v.words[i + words] };
}
out
}
pub(super) fn hp_to_i128(v: &Hp) -> i128 {
assert!(hp_bit_length(v) <= 126, "decimal exp: small value out of range");
(v.words[0] as i128) | ((v.words[1] as i128) << 64)
}
pub(super) fn hp_mul(a: Hp, b: Hp, half: Hp) -> Hp {
hp_div_pow10(a * b + half, HP_DP)
}
pub(super) fn hp_div_small_round(v: Hp, n: u64) -> Hp {
hp_divmod_small(v + hp_small(n / 2), n).0
}
pub(super) fn hp_div_round_half_even(t: Hp, e: u32, k: u32) -> Hp {
let d = hp_shl(hp_pow10(e), k);
let q = hp_shr(hp_div_pow10(t, e), k);
let rem = t - hp_mul_pow10(hp_shl(q, k), e);
match rem.cmp(&(d - rem)) {
std::cmp::Ordering::Less => q,
std::cmp::Ordering::Greater => q + hp_small(1),
std::cmp::Ordering::Equal => if q.words[0] & 1 == 1 { q + hp_small(1) } else { q },
}
}
pub(super) fn compute_to_hp(x: ComputeStorage) -> Hp {
#[cfg(table_format = "q16_16")]
{ Hp::from_i128(x as i128) }
#[cfg(table_format = "q32_32")]
{ Hp::from_i128(x) }
#[cfg(table_format = "q64_64")]
{ Hp::from_i256(x) }
#[cfg(table_format = "q128_128")]
{ Hp::from_i512(x) }
#[cfg(table_format = "q256_256")]
{ Hp::from_i1024(x) }
}
pub(super) fn hp_to_compute(v: &Hp) -> Result<ComputeStorage, OverflowDetected> {
if hp_bit_length(v) > COMPUTE_BITS as u32 - 1 {
return Err(OverflowDetected::TierOverflow);
}
let w = &v.words;
#[cfg(table_format = "q16_16")]
{ Ok(w[0] as i64) }
#[cfg(table_format = "q32_32")]
{ Ok((w[0] as i128) | ((w[1] as i128) << 64)) }
#[cfg(table_format = "q64_64")]
{ Ok(crate::fixed_point::i256::I256::from_words([w[0], w[1], w[2], w[3]])) }
#[cfg(table_format = "q128_128")]
{
let mut words = [0u64; 8];
words.copy_from_slice(&w[..8]);
Ok(crate::fixed_point::i512::I512::from_words(words))
}
#[cfg(table_format = "q256_256")]
{
let mut words = [0u64; 16];
words.copy_from_slice(&w[..16]);
Ok(crate::fixed_point::I1024::from_words(words))
}
}
pub(super) fn hp_signed_to_compute(v: Hp) -> Result<ComputeStorage, OverflowDetected> {
let negative = hp_is_negative(&v);
let magnitude = if negative { -v } else { v };
let q = hp_to_compute(&hp_div_round_half_even(magnitude, HP_DP - DECIMAL_COMPUTE_DP as u32, 0))?;
Ok(if negative { decimal_compute_neg(q) } else { q })
}
pub(super) fn hp_div(a: Hp, b: Hp) -> Hp {
#[cfg(table_format = "q256_256")]
{ crate::fixed_point::domains::binary_fixed::i2048::i2048_div(a, b) }
#[cfg(not(table_format = "q256_256"))]
{ a / b }
}