use std::ops::Shr;
use ethers::types::{I256, U256};
use eyre::{bail, Result};
use crate::{int256, uint256};
pub fn u256_from_str(s: &str) -> Result<U256> {
let mut found_dot = false;
let mut found_e = false;
let mut mantissa = ethers::types::U256::zero();
let mut exponent = ethers::types::U256::zero();
let mut decimals = 0;
for digit in s.chars() {
if digit.is_ascii_digit() {
let d = digit.to_digit(10).unwrap();
if !found_e {
mantissa = mantissa * 10 + d;
} else {
exponent = exponent * 10 + d;
}
if found_dot && !found_e {
decimals += 1;
}
} else if digit == 'e' && !found_e {
found_e = true;
} else if digit == '.' && !found_dot {
found_dot = true;
} else if digit != '_' {
bail!("Unexpected character in U256: {digit}");
}
}
let decimals = ethers::types::U256::from(decimals);
if exponent < decimals {
bail!("Exponent {exponent} is too small for U256: {s}");
}
Ok(mantissa * ethers::types::U256::from(10).pow(exponent - decimals))
}
pub fn i256_from_str(s: &str) -> Result<I256> {
let mut sign = ethers::types::I256::one();
let mut found_dot = false;
let mut found_e = false;
let mut mantissa = ethers::types::I256::zero();
let mut exponent = 0;
let mut decimals = 0;
for digit in s.chars() {
if digit.is_ascii_digit() {
let d = digit.to_digit(10).unwrap();
if !found_e {
mantissa = mantissa * 10 + d;
} else {
exponent = exponent * 10 + d;
}
if found_dot && !found_e {
decimals += 1;
}
} else if digit == '-' {
sign = -ethers::types::I256::one();
} else if digit == 'e' && !found_e {
found_e = true;
} else if digit == '.' && !found_dot {
found_dot = true;
} else if digit != '_' {
bail!("Unexpected character in I256: {digit}");
}
}
if exponent < decimals {
bail!("Exponent {exponent} is too small for I256: {s}");
}
Ok(sign * mantissa * ethers::types::I256::from(10).pow(exponent - decimals))
}
pub fn exp(mut x: I256) -> Result<I256> {
if x <= I256::from(-42139678854452767551_i128) {
return Ok(I256::zero());
}
if x >= int256!(135305999368893231589) {
bail!("invalid exponent {x}");
}
x = x.wrapping_shl(78) / int256!(5).pow(18);
let k = ((x.wrapping_shl(96) / int256!(54916777467707473351141471128))
.wrapping_add(int256!(2).pow(95)))
.asr(96);
x = x.wrapping_sub(k.wrapping_mul(54916777467707473351141471128_u128.into()));
let mut y = x.wrapping_add(1346386616545796478920950773328_u128.into());
y = y
.wrapping_mul(x)
.asr(96)
.wrapping_add(57155421227552351082224309758442_u128.into());
let mut p = y
.wrapping_add(x)
.wrapping_sub(94201549194550492254356042504812_u128.into());
p = p
.wrapping_mul(y)
.asr(96)
.wrapping_add(28719021644029726153956944680412240_u128.into());
p = p
.wrapping_mul(x)
.wrapping_add(int256!(4385272521454847904659076985693276).wrapping_shl(96));
let mut q = x.wrapping_sub(2855989394907223263936484059900_u128.into());
q = q
.wrapping_mul(x)
.asr(96)
.wrapping_add(50020603652535783019961831881945_u128.into());
q = q
.wrapping_mul(x)
.asr(96)
.wrapping_sub(533845033583426703283633433725380_u128.into());
q = q
.wrapping_mul(x)
.asr(96)
.wrapping_add(3604857256930695427073651918091429_u128.into());
q = q
.wrapping_mul(x)
.asr(96)
.wrapping_sub(14423608567350463180887372962807573_u128.into());
q = q
.wrapping_mul(x)
.asr(96)
.wrapping_add(26449188498355588339934803723976023_u128.into());
let mut r = p.wrapping_div(q);
r = I256::from_raw(
(r.into_raw()
.overflowing_mul(uint256!(3822833074963236453042738258902158003155416615667))
.0)
.shr(int256!(195).wrapping_sub(k).low_usize()),
);
Ok(r)
}
pub fn ln(mut x: I256) -> Result<I256> {
if x <= I256::zero() {
bail!("Cannot calculate ln of of negative number or zero.");
}
let mut r = I256::from((x > I256::from(0xffffffffffffffffffffffffffffffff_u128)) as u128)
.wrapping_shl(7);
r = r | I256::from((x.asr(r.as_usize()) > I256::from(0xffffffffffffffff_u128)) as u128)
.wrapping_shl(6);
r = r | I256::from((x.asr(r.as_usize()) > I256::from(0xffffffff_u128)) as u128).wrapping_shl(5);
r = r | I256::from((x.asr(r.as_usize()) > I256::from(0xffff_u128)) as u128).wrapping_shl(4);
r = r | I256::from((x.asr(r.as_usize()) > I256::from(0xff_u128)) as u128).wrapping_shl(3);
r = r | I256::from((x.asr(r.as_usize()) > I256::from(0xf_u128)) as u128).wrapping_shl(2);
r = r | I256::from((x.asr(r.as_usize()) > I256::from(0x3_u128)) as u128).wrapping_shl(1);
r = r | I256::from((x.asr(r.as_usize()) > I256::from(0x1_u128)) as u128);
let k = r.wrapping_sub(int256!(96));
x = x.wrapping_shl(int256!(159).wrapping_sub(k).as_usize());
x = I256::from_raw(x.into_raw().shr(159));
let mut p = x.wrapping_add(int256!(3273285459638523848632254066296));
p = ((p.wrapping_mul(x)).asr(96)).wrapping_add(int256!(24828157081833163892658089445524));
p = ((p.wrapping_mul(x)).asr(96)).wrapping_add(int256!(43456485725739037958740375743393));
p = ((p.wrapping_mul(x)).asr(96)).wrapping_sub(int256!(11111509109440967052023855526967));
p = ((p.wrapping_mul(x)).asr(96)).wrapping_sub(int256!(45023709667254063763336534515857));
p = ((p.wrapping_mul(x)).asr(96)).wrapping_sub(int256!(14706773417378608786704636184526));
p = p
.wrapping_mul(x)
.wrapping_sub(int256!(795164235651350426258249787498).wrapping_shl(96));
let mut q = x.wrapping_add(int256!(5573035233440673466300451813936));
q = (q.wrapping_mul(x).asr(96)).wrapping_add(int256!(71694874799317883764090561454958));
q = q
.wrapping_mul(x)
.asr(96)
.wrapping_add(int256!(283447036172924575727196451306956));
q = q
.wrapping_mul(x)
.asr(96)
.wrapping_add(int256!(401686690394027663651624208769553));
q = q
.wrapping_mul(x)
.asr(96)
.wrapping_add(int256!(204048457590392012362485061816622));
q = q
.wrapping_mul(x)
.asr(96)
.wrapping_add(int256!(31853899698501571402653359427138));
q = q
.wrapping_mul(x)
.asr(96)
.wrapping_add(int256!(909429971244387300277376558375));
r = p.wrapping_div(q);
r = r.wrapping_mul(int256!(1677202110996718588342820967067443963516166));
r = r.wrapping_add(
int256!(16597577552685614221487285958193947469193820559219878177908093499208371)
.wrapping_mul(k),
);
r = r.wrapping_add(int256!(
600920179829731861736702779321621459595472258049074101567377883020018308
));
r = r.asr(174);
Ok(r)
}
#[cfg(test)]
mod tests {
use ethers::signers::Signer;
use hyperdrive_wrappers::wrappers::mock_fixed_point_math::MockFixedPointMath;
use rand::{thread_rng, Rng};
use test_utils::{chain::Chain, constants::DEPLOYER};
use super::*;
use crate::{fixed, uint256, FixedPoint};
#[tokio::test]
async fn fuzz_exp_narrow() -> Result<()> {
let chain = Chain::connect(None, None).await?;
chain.deal(DEPLOYER.address(), uint256!(100_000e18)).await?;
let client = chain.client(DEPLOYER.clone()).await?;
let mock_fixed_point_math = MockFixedPointMath::deploy(client, ())?.send().await?;
let mut rng = thread_rng();
for _ in 0..10_000 {
let x: FixedPoint<I256> = rng.gen_range(fixed!(0)..=fixed!(1e18));
let actual = ln(x.raw());
match mock_fixed_point_math.ln(x.raw()).call().await {
Ok(expected) => assert_eq!(actual.unwrap(), expected),
Err(_) => assert!(actual.is_err()),
}
}
Ok(())
}
#[tokio::test]
async fn fuzz_exp() -> Result<()> {
let chain = Chain::connect(None, None).await?;
chain.deal(DEPLOYER.address(), uint256!(100_000e18)).await?;
let client = chain.client(DEPLOYER.clone()).await?;
let mock_fixed_point_math = MockFixedPointMath::deploy(client, ())?.send().await?;
let mut rng = thread_rng();
for _ in 0..10_000 {
let x: FixedPoint<I256> = rng.gen_range(fixed!(0)..FixedPoint::<I256>::MAX);
let actual = exp(x.raw());
match mock_fixed_point_math.exp(x.raw()).call().await {
Ok(expected) => assert_eq!(actual.unwrap(), expected),
Err(_) => assert!(actual.is_err()),
}
}
Ok(())
}
#[tokio::test]
async fn fuzz_ln_narrow() -> Result<()> {
let chain = Chain::connect(None, None).await?;
chain.deal(DEPLOYER.address(), uint256!(100_000e18)).await?;
let client = chain.client(DEPLOYER.clone()).await?;
let mock_fixed_point_math = MockFixedPointMath::deploy(client, ())?.send().await?;
let mut rng = thread_rng();
for _ in 0..10_000 {
let x: FixedPoint<I256> = rng.gen_range(fixed!(0)..=fixed!(1e18));
let actual = ln(x.raw());
match mock_fixed_point_math.ln(x.raw()).call().await {
Ok(expected) => assert_eq!(actual.unwrap(), expected),
Err(_) => assert!(actual.is_err()),
}
}
Ok(())
}
#[tokio::test]
async fn fuzz_ln() -> Result<()> {
let chain = Chain::connect(None, None).await?;
chain.deal(DEPLOYER.address(), uint256!(100_000e18)).await?;
let client = chain.client(DEPLOYER.clone()).await?;
let mock_fixed_point_math = MockFixedPointMath::deploy(client, ())?.send().await?;
let mut rng = thread_rng();
for _ in 0..10_000 {
let x: FixedPoint<I256> = rng.gen_range(fixed!(0)..FixedPoint::<I256>::MAX);
let actual = ln(x.raw());
match mock_fixed_point_math.ln(x.raw()).call().await {
Ok(expected) => assert_eq!(actual.unwrap(), expected),
Err(_) => assert!(actual.is_err()),
}
}
Ok(())
}
}