use crate::fixed_point::core_types::errors::OverflowDetected;
use crate::fixed_point::domains::binary_fixed::transcendental::{
exp_tier_n_plus_1::exp_q64_64_native,
ln_tier_n_plus_1::ln_q64_64_native,
sin_cos_tier_n_plus_1::{self as sincos, sincos_q64_64},
};
use crate::fixed_point::imperative::decimal_literal;
use crate::fixed_point::i256::mul_i128_to_i256;
use crate::fixed_point::{I256, I512};
pub const ONE_Q64: i128 = 1 << 64;
pub const PI_Q64: i128 = sincos::PI_Q64;
pub const TWO_PI_Q64: i128 = 115904311329233965478;
pub const PI_HALF_Q64: i128 = sincos::PI_HALF_Q64;
pub const PI_Q32: i64 = 13493037704;
pub const TWO_PI_Q32: i64 = 26986075409;
#[inline]
pub fn exp_q64(x: i128) -> i128 {
exp_q64_64_native(x)
}
#[inline]
pub fn ln_q64(x: i128) -> Option<i128> {
if x <= 0 { None } else { Some(ln_q64_64_native(x)) }
}
#[inline]
pub fn sin_q64(x: i128) -> i128 {
sincos_q64_64(x).0
}
#[inline]
pub fn cos_q64(x: i128) -> i128 {
sincos_q64_64(x).1
}
#[inline]
pub fn sincos_q64(x: i128) -> (i128, i128) {
sincos_q64_64(x)
}
pub fn try_from_str(s: &str, frac_bits: u32) -> Result<i128, OverflowDetected> {
if frac_bits > 127 {
return Err(OverflowDetected::InvalidInput);
}
match decimal_literal::parse(s, frac_bits, 128) {
Some(converted) => converted.map(|c| decimal_literal::to_i128(&c)),
None => Err(OverflowDetected::ParseError),
}
}
#[inline]
fn mul_q64(a: i128, b: i128) -> i128 {
let p = mul_i128_to_i256(a, b);
let mut q = p >> 64u32;
if p.words[0] >= 1u64 << 63 {
q = q + I256::from_i128(1);
}
q.as_i128()
}
#[inline]
fn div_unit_q64(num: u128, den: u128) -> i128 {
let (q, r) = if num == 1u128 << 64 {
let (q, r) = (u128::MAX / den, u128::MAX % den + 1);
if r == den { (q + 1, 0) } else { (q, r) }
} else {
let n = num << 64;
(n / den, n % den)
};
(if 2 * r >= den { q + 1 } else { q }) as i128
}
pub fn sigmoid_q64(x: i128) -> i128 {
let one = ONE_Q64 as u128;
if x < 0 {
let e = exp_q64_64_native(x) as u128; div_unit_q64(e, one + e)
} else {
let e = exp_q64_64_native(-x) as u128;
div_unit_q64(one, one + e)
}
}
pub fn softplus_q64(x: i128) -> i128 {
let neg_abs = if x < 0 { x } else { -x };
let e = exp_q64_64_native(neg_abs); let corr = ln_q64_64_native(ONE_Q64 + e); if x < 0 { corr } else { x.checked_add(corr).unwrap_or(i128::MAX) }
}
pub fn silu_q64(x: i128) -> i128 {
mul_q64(x, sigmoid_q64(x))
}
pub fn sqrt_q64(x: i128) -> Option<i128> {
sqrt_q64_to(x, 64)
}
pub fn sqrt_q64_to(x: i128, frac_bits: u32) -> Option<i128> {
assert!(frac_bits <= 64, "sqrt_q64_to: frac_bits above 64");
if x < 0 {
return None;
}
if x == 0 {
return Some(0);
}
let f2 = 2 * frac_bits;
let (n, down) = if f2 >= 64 {
(I256::from_u128(x as u128) << (f2 - 64) as usize, 0)
} else {
(I256::from_u128((x as u128) >> (64 - f2)), 64 - f2)
};
if n.is_zero() {
return Some(if (x as u128) >> (down - 2) != 0 { 1 } else { 0 });
}
let bits = 256 - n.words.iter().rev().enumerate().find(|(_, &w)| w != 0).map_or(256, |(i, w)| i as u32 * 64 + w.leading_zeros());
let mut r = I256::from_i128(1) << ((bits + 1) / 2) as usize; loop {
let next = (r + n / r) >> 1u32;
if next >= r {
break;
}
r = next;
}
let mid = r * r + r;
let up = if down == 0 { n > mid } else { I256::from_u128(x as u128) > (mid << down as usize) };
Some((if up { r + I256::from_i128(1) } else { r }).as_i128())
}
#[inline]
pub fn narrow_q64(v: i128, frac_bits: u32) -> i128 {
assert!(frac_bits <= 64, "narrow_q64: frac_bits above 64");
let shift = 64 - frac_bits;
if shift == 0 {
v
} else {
(v >> shift) + ((v >> (shift - 1)) & 1)
}
}
fn mul_div_parts(a: i128, b: i128, d: i128) -> Result<(I256, I256, I256), OverflowDetected> {
if d == 0 {
return Err(OverflowDetected::DivisionByZero);
}
let p = mul_i128_to_i256(a, b);
let dw = I256::from_i128(d);
let mut q = p / dw; let mut r = p - q * dw;
if !r.is_zero() && (r.is_negative() != dw.is_negative()) {
q = q - I256::from_i128(1);
r = r + dw;
}
Ok((q, r, dw))
}
pub fn mul_div_floor(a: i128, b: i128, d: i128) -> Result<i128, OverflowDetected> {
let (q, _, _) = mul_div_parts(a, b, d)?;
if q.fits_in_i128() { Ok(q.as_i128()) } else { Err(OverflowDetected::TierOverflow) }
}
pub fn mul_div_nearest(a: i128, b: i128, d: i128) -> Result<i128, OverflowDetected> {
let (q, r, dw) = mul_div_parts(a, b, d)?;
let (r_abs, d_abs) = if dw.is_negative() { (-r, -dw) } else { (r, dw) };
let q = if r_abs + r_abs >= d_abs { q + I256::from_i128(1) } else { q };
if q.fits_in_i128() { Ok(q.as_i128()) } else { Err(OverflowDetected::TierOverflow) }
}
pub fn mul_div_floor_u128(a: u128, b: u128, d: u128) -> Result<u128, OverflowDetected> {
if d == 0 {
return Err(OverflowDetected::DivisionByZero);
}
let q = (I512::from_u128(a) * I512::from_u128(b)) / I512::from_u128(d);
if q.words[2..].iter().any(|&w| w != 0) {
return Err(OverflowDetected::TierOverflow);
}
Ok(q.words[0] as u128 | (q.words[1] as u128) << 64)
}
pub fn try_ratio_from_str(s: &str) -> Result<(i128, i128), OverflowDetected> {
const PARSE: OverflowDetected = OverflowDetected::ParseError;
const RANGE: OverflowDetected = OverflowDetected::TierOverflow;
let s = s.trim().as_bytes();
let (negative, s) = match s.first() {
Some(b'-') => (true, &s[1..]),
Some(b'+') => (false, &s[1..]),
_ => (false, s),
};
let (mantissa, exponent) = match s.iter().position(|&c| c == b'e' || c == b'E') {
Some(i) => (&s[..i], Some(&s[i + 1..])),
None => (s, None),
};
let (int_digits, frac_digits) = match mantissa.iter().position(|&c| c == b'.') {
Some(i) => (&mantissa[..i], &mantissa[i + 1..]),
None => (mantissa, &mantissa[..0]),
};
let all_digits = |d: &[u8]| d.iter().all(u8::is_ascii_digit);
if int_digits.len() + frac_digits.len() == 0 || !all_digits(int_digits) || !all_digits(frac_digits) {
return Err(PARSE);
}
let mut exp: i64 = 0;
if let Some(e) = exponent {
let (e_neg, e) = match e.first() {
Some(b'-') => (true, &e[1..]),
Some(b'+') => (false, &e[1..]),
_ => (false, e),
};
if e.is_empty() || !all_digits(e) {
return Err(PARSE);
}
for &c in e {
exp = (exp * 10 + (c - b'0') as i64).min(1_000_000);
}
if e_neg {
exp = -exp;
}
}
let frac_digits = &frac_digits[..frac_digits.iter().rposition(|&c| c != b'0').map_or(0, |i| i + 1)];
let mut num: i128 = 0;
for &c in int_digits.iter().chain(frac_digits) {
num = num.checked_mul(10).and_then(|n| n.checked_add((c - b'0') as i128)).ok_or(RANGE)?;
}
if num == 0 {
return Ok((0, 1));
}
let mut scale = exp - frac_digits.len() as i64;
let mut den: i128 = 1;
while scale > 0 {
num = num.checked_mul(10).ok_or(RANGE)?;
scale -= 1;
}
while scale < 0 {
if num % 10 == 0 {
num /= 10;
} else {
den = den.checked_mul(10).ok_or(RANGE)?;
}
scale += 1;
}
let (mut a, mut b) = (num, den);
while b != 0 {
(a, b) = (b, a % b);
}
Ok((if negative { -(num / a) } else { num / a }, den / a))
}