use crate::common::errors::PoolError;
use alloy_primitives::{uint, I256, U256};
use std::str::FromStr;
pub const ONE_18: I256 = I256::from_raw(uint!(1000000000000000000_U256));
pub const MAX_NATURAL_EXPONENT: I256 = I256::from_raw(uint!(130000000000000000000_U256));
pub const LN_36_LOWER_BOUND: I256 = I256::from_raw(uint!(900000000000000000_U256));
pub const LN_36_UPPER_BOUND: I256 = I256::from_raw(uint!(1100000000000000000_U256));
pub const MILD_EXPONENT_BOUND: U256 =
uint!(289480223093290488558927462521719769633174961664101410098_U256);
pub const RAY: I256 = I256::from_raw(uint!(1000000000000000000000000000000000000_U256));
pub const X0: I256 = I256::from_raw(uint!(128000000000000000000_U256)); pub const A0: I256 = I256::from_raw(uint!(
38877084059945950922200000000000000000000000000000000000_U256
)); pub const X1: I256 = I256::from_raw(uint!(64000000000000000000_U256)); pub const A1: I256 = I256::from_raw(uint!(6235149080811616882910000000_U256));
pub const X2: I256 = I256::from_raw(uint!(3200000000000000000000_U256)); pub const A2: I256 = I256::from_raw(uint!(7896296018268069516100000000000000_U256)); pub const X3: I256 = I256::from_raw(uint!(1600000000000000000000_U256)); pub const A3: I256 = I256::from_raw(uint!(888611052050787263676000000_U256)); pub const X4: I256 = I256::from_raw(uint!(800000000000000000000_U256)); pub const A4: I256 = I256::from_raw(uint!(298095798704172827474000_U256)); pub const X5: I256 = I256::from_raw(uint!(400000000000000000000_U256)); pub const A5: I256 = I256::from_raw(uint!(5459815003314423907810_U256)); pub const X6: I256 = I256::from_raw(uint!(200000000000000000000_U256)); pub const A6: I256 = I256::from_raw(uint!(738905609893065022723_U256)); pub const X7: I256 = I256::from_raw(uint!(100000000000000000000_U256)); pub const A7: I256 = I256::from_raw(uint!(271828182845904523536_U256)); pub const X8: I256 = I256::from_raw(uint!(50000000000000000000_U256)); pub const A8: I256 = I256::from_raw(uint!(164872127070012814685_U256)); pub const X9: I256 = I256::from_raw(uint!(25000000000000000000_U256)); pub const A9: I256 = I256::from_raw(uint!(128402541668774148407_U256)); pub const X10: I256 = I256::from_raw(uint!(12500000000000000000_U256)); pub const A10: I256 = I256::from_raw(uint!(113314845306682631683_U256)); pub const X11: I256 = I256::from_raw(uint!(6250000000000000000_U256)); pub const A11: I256 = I256::from_raw(uint!(106449445891785942956_U256));
pub const ONE_20: I256 = I256::from_raw(uint!(100000000000000000000_U256));
const MIN_NATURAL_EXPONENT_ABS: U256 = uint!(41000000000000000000_U256);
pub const MIN_NATURAL_EXPONENT: I256 =
I256::from_raw(U256::ZERO.wrapping_sub(MIN_NATURAL_EXPONENT_ABS));
pub fn pow(x: &U256, y: &U256) -> Result<U256, PoolError> {
if y.is_zero() {
return Ok(ONE_18.into_raw());
}
if x.is_zero() {
return Ok(U256::ZERO);
}
let x_int256 = I256::from_raw(*x);
if y >= &MILD_EXPONENT_BOUND {
return Err(PoolError::MathOverflow);
}
let y_int256 = I256::from_raw(*y);
let logx_times_y = if x_int256 > LN_36_LOWER_BOUND && x_int256 < LN_36_UPPER_BOUND {
let ln_36_x = ln_36(&x_int256)?;
(ln_36_x / ONE_18) * y_int256 + ((ln_36_x % ONE_18) * y_int256) / ONE_18
} else {
ln(&x_int256)? * y_int256
};
let logx_times_y = logx_times_y / ONE_18;
if logx_times_y < MIN_NATURAL_EXPONENT || logx_times_y > MAX_NATURAL_EXPONENT {
return Err(PoolError::MathOverflow);
}
exp(&logx_times_y).map(|result| result.into_raw())
}
fn exp(x: &I256) -> Result<I256, PoolError> {
if x < &MIN_NATURAL_EXPONENT || x > &MAX_NATURAL_EXPONENT {
return Err(PoolError::MathOverflow);
}
if x.is_negative() {
return Ok((ONE_18 * ONE_18) / exp(&(-*x))?);
}
let mut x = *x;
let first_an = if x >= X0 {
x -= X0;
A0
} else if x >= X1 {
x -= X1;
A1
} else {
I256::ONE
};
x *= I256::from_str("100").unwrap();
let mut product = ONE_20;
if x >= X2 {
x -= X2;
product = (product * A2) / ONE_20;
}
if x >= X3 {
x -= X3;
product = (product * A3) / ONE_20;
}
if x >= X4 {
x -= X4;
product = (product * A4) / ONE_20;
}
if x >= X5 {
x -= X5;
product = (product * A5) / ONE_20;
}
if x >= X6 {
x -= X6;
product = (product * A6) / ONE_20;
}
if x >= X7 {
x -= X7;
product = (product * A7) / ONE_20;
}
if x >= X8 {
x -= X8;
product = (product * A8) / ONE_20;
}
if x >= X9 {
x -= X9;
product = (product * A9) / ONE_20;
}
let mut series_sum = ONE_20; let mut term = x;
series_sum += term;
term = (term * x) / ONE_20 / I256::from_str("2").unwrap();
series_sum += term;
term = (term * x) / ONE_20 / I256::from_str("3").unwrap();
series_sum += term;
term = (term * x) / ONE_20 / I256::from_str("4").unwrap();
series_sum += term;
term = (term * x) / ONE_20 / I256::from_str("5").unwrap();
series_sum += term;
term = (term * x) / ONE_20 / I256::from_str("6").unwrap();
series_sum += term;
term = (term * x) / ONE_20 / I256::from_str("7").unwrap();
series_sum += term;
term = (term * x) / ONE_20 / I256::from_str("8").unwrap();
series_sum += term;
term = (term * x) / ONE_20 / I256::from_str("9").unwrap();
series_sum += term;
term = (term * x) / ONE_20 / I256::from_str("10").unwrap();
series_sum += term;
term = (term * x) / ONE_20 / I256::from_str("11").unwrap();
series_sum += term;
term = (term * x) / ONE_20 / I256::from_str("12").unwrap();
series_sum += term;
let result = ((product * series_sum) / ONE_20) * first_an / I256::from_str("100").unwrap();
Ok(result)
}
fn ln(x: &I256) -> Result<I256, PoolError> {
let mut a = *x;
if a < ONE_18 {
return Ok(I256::MINUS_ONE * ln(&((ONE_18 * ONE_18) / a))?);
}
let mut sum = I256::ZERO;
if a >= (A0 * ONE_18) {
a /= A0; sum += X0;
}
if a >= (A1 * ONE_18) {
a /= A1; sum += X1;
}
sum *= I256::from_str("100").unwrap();
a *= I256::from_str("100").unwrap();
if a >= A2 {
a = (a * ONE_20) / A2;
sum += X2;
}
if a >= A3 {
a = (a * ONE_20) / A3;
sum += X3;
}
if a >= A4 {
a = (a * ONE_20) / A4;
sum += X4;
}
if a >= A5 {
a = (a * ONE_20) / A5;
sum += X5;
}
if a >= A6 {
a = (a * ONE_20) / A6;
sum += X6;
}
if a >= A7 {
a = (a * ONE_20) / A7;
sum += X7;
}
if a >= A8 {
a = (a * ONE_20) / A8;
sum += X8;
}
if a >= A9 {
a = (a * ONE_20) / A9;
sum += X9;
}
if a >= A10 {
a = (a * ONE_20) / A10;
sum += X10;
}
if a >= A11 {
a = (a * ONE_20) / A11;
sum += X11;
}
let z = ((a - ONE_20) * ONE_20) / (a + ONE_20);
let z_squared = (z * z) / ONE_20;
let mut num = z;
let mut series_sum = num;
num = (num * z_squared) / ONE_20;
series_sum += num / I256::from_str("3").unwrap();
num = (num * z_squared) / ONE_20;
series_sum += num / I256::from_str("5").unwrap();
num = (num * z_squared) / ONE_20;
series_sum += num / I256::from_str("7").unwrap();
num = (num * z_squared) / ONE_20;
series_sum += num / I256::from_str("9").unwrap();
num = (num * z_squared) / ONE_20;
series_sum += num / I256::from_str("11").unwrap();
series_sum *= I256::from_str("2").unwrap();
Ok((sum + series_sum) / I256::from_str("100").unwrap())
}
fn ln_36(x: &I256) -> Result<I256, PoolError> {
let mut x = *x;
x *= ONE_18;
let z = ((x - RAY) * RAY) / (x + RAY);
let z_squared = (z * z) / RAY;
let mut num = z;
let mut series_sum = num;
num = (num * z_squared) / RAY;
series_sum += num / I256::from_str("3").unwrap();
num = (num * z_squared) / RAY;
series_sum += num / I256::from_str("5").unwrap();
num = (num * z_squared) / RAY;
series_sum += num / I256::from_str("7").unwrap();
num = (num * z_squared) / RAY;
series_sum += num / I256::from_str("9").unwrap();
num = (num * z_squared) / RAY;
series_sum += num / I256::from_str("11").unwrap();
num = (num * z_squared) / RAY;
series_sum += num / I256::from_str("13").unwrap();
num = (num * z_squared) / RAY;
series_sum += num / I256::from_str("15").unwrap();
Ok(series_sum * I256::from_str("2").unwrap())
}