use crate::constants::*;
use crate::error::SolMathError;
use crate::arithmetic::{fp_mul, fp_mul_i, fp_mul_i_round, fp_div_i, fp_sqrt};
use crate::overflow::checked_mul_div_u;
use crate::hp::pow_fixed_hp;
pub fn ln_fixed_i(x: u128) -> Result<i128, SolMathError> {
if x == 0 {
return Err(SolMathError::DomainError);
}
let mut m = x;
let mut k: i32 = 0;
while m < SCALE {
m = m.checked_mul(2).ok_or(SolMathError::Overflow)?;
k -= 1;
}
while m >= 2 * SCALE {
m /= 2;
k += 1;
}
let k_i = k as i128;
let m_i = m as i128;
let offset = m - SCALE;
if offset < LN_TABLE_HALF_STEP {
let t_num = m_i - SCALE_I;
let t_den = m_i + SCALE_I;
let t = (t_num * SCALE_I + t_den / 2) / t_den;
let u = fp_mul_i_round(t, t)?;
let p = fp_mul_i_round(LN_REMEZ_W3, u)? + LN_REMEZ_W2;
let p = fp_mul_i_round(p, u)? + LN_REMEZ_W1;
let p = fp_mul_i_round(p, u)? + LN_REMEZ_W0;
let series_result = fp_mul_i_round(2 * t, p)?;
let ln2_raw = k_i * LN2_LO;
let ln2_correction = if ln2_raw >= 0 {
(ln2_raw + SCALE_I / 2) / SCALE_I
} else {
(ln2_raw - SCALE_I / 2) / SCALE_I
};
return Ok(series_result + k_i * LN2_I + ln2_correction);
}
let j = (offset / LN_TABLE_STEP) as usize;
let j = j.min(15);
let m_j = SCALE + (2 * j as u128 + 1) * LN_TABLE_HALF_STEP;
let ln_m_j = LN_TABLE_16[j];
let ln_m_j_lo = LN_TABLE_LO_16[j];
let m_j_i = m_j as i128;
let t_num = m_i - m_j_i;
let t_den = m_i + m_j_i;
let p_val = t_num * SCALE_I;
let t = (p_val + t_den / 2) / t_den;
let t_rem = p_val - t * t_den; let t_lo = t_rem * SCALE_I / t_den;
let u = fp_mul_i_round(t, t)?;
let p = fp_mul_i_round(LN_REMEZ_W3, u)? + LN_REMEZ_W2;
let p = fp_mul_i_round(p, u)? + LN_REMEZ_W1;
let p = fp_mul_i_round(p, u)? + LN_REMEZ_W0;
let series_result = fp_mul_i_round(2 * t, p)?;
let combined_lo = ln_m_j_lo + k_i * LN2_LO + t_lo;
let correction = if combined_lo >= 0 {
(combined_lo + SCALE_I / 2) / SCALE_I
} else {
(combined_lo - SCALE_I / 2) / SCALE_I
};
Ok(series_result + ln_m_j + k_i * LN2_I + correction)
}
pub fn exp_fixed_i(x: i128) -> Result<i128, SolMathError> {
let max_x = 40 * SCALE_I;
if x <= -max_x { return Ok(0); }
if x >= max_x { return Err(SolMathError::Overflow); }
if x == 0 { return Ok(SCALE_I); }
let mut k = x / LN2_I;
let ln2_correction = {
let raw = k * LN2_LO;
if raw >= 0 { (raw + SCALE_I / 2) / SCALE_I } else { (raw - SCALE_I / 2) / SCALE_I }
};
let mut r = x - k * LN2_I - ln2_correction;
let half_ln2 = LN2_I / 2;
if r > half_ln2 {
k += 1;
r -= LN2_I;
} else if r < -half_ln2 {
k -= 1;
r += LN2_I;
}
let xx = fp_mul_i_round(r, r)?;
let poly = fp_mul_i_round(xx, EXP_REMEZ_P5)? + EXP_REMEZ_P4;
let poly = fp_mul_i_round(xx, poly)? + EXP_REMEZ_P3;
let poly = fp_mul_i_round(xx, poly)? + EXP_REMEZ_P2;
let poly = fp_mul_i_round(xx, poly)? + EXP_REMEZ_P1;
let c = r - fp_mul_i_round(poly, xx)?;
let rc = fp_mul_i_round(r, c)?;
let sum = SCALE_I + r + fp_div_i(rc, 2 * SCALE_I - c)?;
if k >= 0 {
sum.checked_shl(k as u32).ok_or(SolMathError::Overflow)
} else {
Ok(sum >> (-k) as u32)
}
}
pub fn pow_fixed(base: u128, exponent: u128) -> Result<u128, SolMathError> {
if base == 0 && exponent == 0 {
return Err(SolMathError::DomainError); }
if exponent == 0 {
return Ok(SCALE); }
if base == 0 {
return Ok(0); }
if exponent == SCALE {
return Ok(base); }
if base == SCALE {
return Ok(SCALE); }
if exponent == 2 * SCALE {
return fp_mul(base, base);
}
if exponent == SCALE / 2 {
return fp_sqrt(base);
}
if exponent % SCALE == 0 {
let n = exponent / SCALE;
if n >= 1 && n <= 20 {
return pow_fixed_hp(base, exponent);
}
}
let ln_base = ln_fixed_i(base)?; let exp_i = exponent as i128;
let product = fp_mul_i(exp_i, ln_base)?; let result = exp_fixed_i(product)?;
Ok(if result <= 0 {
0
} else {
result as u128
})
}
pub fn pow_int(base: u128, n: u128) -> Result<u128, SolMathError> {
match n {
0 => Ok(SCALE),
1 => Ok(base),
2 => fp_mul(base, base),
3 => Ok(fp_mul(fp_mul(base, base)?, base)?),
4 => { let x2 = fp_mul(base, base)?; fp_mul(x2, x2) },
_ => {
let ln_base = ln_fixed_i(base)?;
let total = match (n as i128).checked_mul(ln_base) {
Some(v) => v,
None => if ln_base > 0 { return Err(SolMathError::Overflow) } else { return Ok(0) },
};
if total.abs() < 39 * SCALE_I {
pow_fixed_hp(base, n * SCALE)
} else {
let half = pow_int(base, n / 2)?;
let mut result = checked_mul_div_u(half, half, SCALE)
.ok_or(SolMathError::Overflow)?;
if n % 2 == 1 {
result = checked_mul_div_u(result, base, SCALE)
.ok_or(SolMathError::Overflow)?;
}
Ok(result)
}
}
}
}
pub fn pow_fixed_i(base: i128, exponent: i128) -> Result<i128, SolMathError> {
if base == 0 && exponent == 0 {
return Err(SolMathError::DomainError); }
if exponent == 0 {
return Ok(SCALE_I); }
if base == 0 {
if exponent < 0 {
return Err(SolMathError::Overflow); }
return Ok(0); }
if exponent == SCALE_I {
return Ok(base); }
if base == SCALE_I {
return Ok(SCALE_I); }
if exponent < 0 {
let pos_result = pow_fixed_i(base, -exponent)?;
if pos_result == 0 {
return Err(SolMathError::Overflow); }
return fp_div_i(SCALE_I, pos_result);
}
if base < 0 {
if exponent % SCALE_I != 0 {
return Err(SolMathError::DomainError); }
let n = exponent / SCALE_I;
let abs_base = base.unsigned_abs();
let abs_result = pow_fixed(abs_base, exponent as u128)?;
if abs_result > i128::MAX as u128 {
return Err(SolMathError::Overflow);
}
Ok(if n % 2 == 0 {
abs_result as i128
} else {
-(abs_result as i128)
})
} else {
let result = pow_fixed(base as u128, exponent as u128)?;
if result > i128::MAX as u128 {
return Err(SolMathError::Overflow);
}
Ok(result as i128)
}
}
pub fn expm1_fixed(x: i128) -> Result<i128, SolMathError> {
let half = SCALE_I / 2;
if x > half || x < -half {
return Ok(exp_fixed_i(x)? - SCALE_I);
}
if x == 0 { return Ok(0); }
const C11: i128 = 25_052;
const C10: i128 = 275_573;
const C9: i128 = 2_755_732;
const C8: i128 = 24_801_587;
const C7: i128 = 198_412_698;
const C6: i128 = 1_388_888_889;
const C5: i128 = 8_333_333_333;
const C4: i128 = 41_666_666_667;
const C3: i128 = 166_666_666_667;
const C2: i128 = 500_000_000_000;
const C1: i128 = SCALE_I;
let p = fp_mul_i_round(x, C11)? + C10;
let p = fp_mul_i_round(x, p)? + C9;
let p = fp_mul_i_round(x, p)? + C8;
let p = fp_mul_i_round(x, p)? + C7;
let p = fp_mul_i_round(x, p)? + C6;
let p = fp_mul_i_round(x, p)? + C5;
let p = fp_mul_i_round(x, p)? + C4;
let p = fp_mul_i_round(x, p)? + C3;
let p = fp_mul_i_round(x, p)? + C2;
let p = fp_mul_i_round(x, p)? + C1;
Ok(fp_mul_i_round(x, p)?)
}