use crate::constants::BASIS_POINT_MAX;
use ruint::aliases::U256;
pub const PRECISION: u128 = 1_000_000_000_000;
pub const SCALE_OFFSET: u8 = 64;
const MAX_EXPONENTIAL: u32 = 0x80000;
pub const ONE: u128 = 1u128 << SCALE_OFFSET;
pub fn pow(base: u128, exp: i32) -> Option<u128> {
let mut invert = exp.is_negative();
if exp == 0 {
return Some(1u128 << 64);
}
let exp: u32 = if invert { exp.abs() as u32 } else { exp as u32 };
if exp >= MAX_EXPONENTIAL {
return None;
}
let mut squared_base = base;
let mut result = ONE;
if squared_base >= result {
squared_base = u128::MAX.checked_div(squared_base)?;
invert = !invert;
}
if exp & 0x1 > 0 {
result = (result.checked_mul(squared_base)?) >> SCALE_OFFSET
}
squared_base = (squared_base.checked_mul(squared_base)?) >> SCALE_OFFSET;
if exp & 0x2 > 0 {
result = (result.checked_mul(squared_base)?) >> SCALE_OFFSET
}
squared_base = (squared_base.checked_mul(squared_base)?) >> SCALE_OFFSET;
if exp & 0x4 > 0 {
result = (result.checked_mul(squared_base)?) >> SCALE_OFFSET
}
squared_base = (squared_base.checked_mul(squared_base)?) >> SCALE_OFFSET;
if exp & 0x8 > 0 {
result = (result.checked_mul(squared_base)?) >> SCALE_OFFSET
}
squared_base = (squared_base.checked_mul(squared_base)?) >> SCALE_OFFSET;
if exp & 0x10 > 0 {
result = (result.checked_mul(squared_base)?) >> SCALE_OFFSET
}
squared_base = (squared_base.checked_mul(squared_base)?) >> SCALE_OFFSET;
if exp & 0x20 > 0 {
result = (result.checked_mul(squared_base)?) >> SCALE_OFFSET
}
squared_base = (squared_base.checked_mul(squared_base)?) >> SCALE_OFFSET;
if exp & 0x40 > 0 {
result = (result.checked_mul(squared_base)?) >> SCALE_OFFSET
}
squared_base = (squared_base.checked_mul(squared_base)?) >> SCALE_OFFSET;
if exp & 0x80 > 0 {
result = (result.checked_mul(squared_base)?) >> SCALE_OFFSET
}
squared_base = (squared_base.checked_mul(squared_base)?) >> SCALE_OFFSET;
if exp & 0x100 > 0 {
result = (result.checked_mul(squared_base)?) >> SCALE_OFFSET
}
squared_base = (squared_base.checked_mul(squared_base)?) >> SCALE_OFFSET;
if exp & 0x200 > 0 {
result = (result.checked_mul(squared_base)?) >> SCALE_OFFSET
}
squared_base = (squared_base.checked_mul(squared_base)?) >> SCALE_OFFSET;
if exp & 0x400 > 0 {
result = (result.checked_mul(squared_base)?) >> SCALE_OFFSET
}
squared_base = (squared_base.checked_mul(squared_base)?) >> SCALE_OFFSET;
if exp & 0x800 > 0 {
result = (result.checked_mul(squared_base)?) >> SCALE_OFFSET
}
squared_base = (squared_base.checked_mul(squared_base)?) >> SCALE_OFFSET;
if exp & 0x1000 > 0 {
result = (result.checked_mul(squared_base)?) >> SCALE_OFFSET
}
squared_base = (squared_base.checked_mul(squared_base)?) >> SCALE_OFFSET;
if exp & 0x2000 > 0 {
result = (result.checked_mul(squared_base)?) >> SCALE_OFFSET
}
squared_base = (squared_base.checked_mul(squared_base)?) >> SCALE_OFFSET;
if exp & 0x4000 > 0 {
result = (result.checked_mul(squared_base)?) >> SCALE_OFFSET
}
squared_base = (squared_base.checked_mul(squared_base)?) >> SCALE_OFFSET;
if exp & 0x8000 > 0 {
result = (result.checked_mul(squared_base)?) >> SCALE_OFFSET
}
squared_base = (squared_base.checked_mul(squared_base)?) >> SCALE_OFFSET;
if exp & 0x10000 > 0 {
result = (result.checked_mul(squared_base)?) >> SCALE_OFFSET
}
squared_base = (squared_base.checked_mul(squared_base)?) >> SCALE_OFFSET;
if exp & 0x20000 > 0 {
result = (result.checked_mul(squared_base)?) >> SCALE_OFFSET
}
squared_base = (squared_base.checked_mul(squared_base)?) >> SCALE_OFFSET;
if exp & 0x40000 > 0 {
result = (result.checked_mul(squared_base)?) >> SCALE_OFFSET
}
if result == 0 {
return None;
}
if invert {
result = u128::MAX.checked_div(result)?;
}
Some(result)
}
pub fn to_decimal(value: u128) -> Option<u128> {
let value = U256::from(value);
let precision = U256::from(PRECISION);
let scaled_value = value.checked_mul(precision)?;
let (scaled_down_value, _) = scaled_value.overflowing_shr(SCALE_OFFSET.into());
scaled_down_value.try_into().ok()
}
pub fn from_decimal(value: u128) -> Option<u128> {
let value = U256::from(value);
let precision = U256::from(PRECISION);
let (q_value, _) = value.overflowing_shl(SCALE_OFFSET.into());
let fp_value = q_value.checked_div(precision)?;
fp_value.try_into().ok()
}
pub fn get_base(bin_step: u32) -> Option<u128> {
let quotient = u128::from(bin_step).checked_shl(SCALE_OFFSET.into())?;
let fraction = quotient.checked_div(BASIS_POINT_MAX as u128)?;
ONE.checked_add(fraction)
}