use std::cmp::Ordering;
use crate::{
fixed::{Fixed, FixedPointOps},
num::{MulDiv, Num},
};
use num_traits::{CheckedMul, One, Zero};
pub fn usd_to_market_token_amount<T>(
usd_value: T,
pool_value: T,
supply: T,
usd_to_amount_divisor: T,
) -> Option<T>
where
T: MulDiv + Num,
{
if usd_to_amount_divisor.is_zero() {
return None;
}
if supply.is_zero() && pool_value.is_zero() {
usd_value.checked_div(&usd_to_amount_divisor)
} else if supply.is_zero() && !pool_value.is_zero() {
pool_value
.checked_add(&usd_value)?
.checked_div(&usd_to_amount_divisor)
} else {
supply.checked_mul_div(&usd_value, &pool_value)
}
}
pub fn market_token_amount_to_usd<T>(amount: &T, pool_value: &T, supply: &T) -> Option<T>
where
T: MulDiv,
{
pool_value.checked_mul_div(amount, supply)
}
pub fn apply_factors<T, const DECIMALS: u8>(
value: T,
factor: T,
exponent_factor: T,
) -> crate::Result<T>
where
T: FixedPointOps<DECIMALS>,
{
Ok(apply_exponent_factor_wrapped(value, exponent_factor)
.ok_or(crate::Error::PowComputation)?
.checked_mul(&Fixed::from_inner(factor))
.ok_or(crate::Error::Overflow)?
.into_inner())
}
fn apply_exponent_factor_wrapped<T, const DECIMALS: u8>(
value: T,
exponent_factor: T,
) -> Option<Fixed<T, DECIMALS>>
where
T: FixedPointOps<DECIMALS>,
{
let unit = Fixed::ONE;
let value = Fixed::from_inner(value);
let exponent = Fixed::from_inner(exponent_factor);
let ans = match value.cmp(&unit) {
Ordering::Less => Fixed::zero(),
Ordering::Equal => unit,
Ordering::Greater => {
if exponent.is_zero() {
unit
} else if exponent.is_one() {
value
} else {
value.checked_pow(&exponent)?
}
}
};
Some(ans)
}
#[inline]
pub fn apply_exponent_factor<T, const DECIMALS: u8>(value: T, exponent_factor: T) -> Option<T>
where
T: FixedPointOps<DECIMALS>,
{
Some(apply_exponent_factor_wrapped(value, exponent_factor)?.into_inner())
}
#[inline]
pub fn apply_factor<T, const DECIMALS: u8>(value: &T, factor: &T) -> Option<T>
where
T: FixedPointOps<DECIMALS>,
{
value.checked_mul_div(factor, &FixedPointOps::UNIT)
}
#[inline]
pub fn div_to_factor<T, const DECIMALS: u8>(
value: &T,
divisor: &T,
round_up_magnitude: bool,
) -> Option<T>
where
T: FixedPointOps<DECIMALS>,
{
if divisor.is_zero() {
return Some(T::zero());
}
if round_up_magnitude {
value.checked_mul_div_ceil(&T::UNIT, divisor)
} else {
value.checked_mul_div(&T::UNIT, divisor)
}
}
#[inline]
pub fn div_to_factor_signed<T, const DECIMALS: u8>(
value: &T::Signed,
divisor: &T,
) -> Option<T::Signed>
where
T: FixedPointOps<DECIMALS>,
{
if divisor.is_zero() {
return Some(Zero::zero());
}
T::UNIT.checked_mul_div_with_signed_numerator(value, divisor)
}
#[cfg(test)]
mod tests {
#[test]
#[allow(clippy::identity_op)]
fn test_apply_factor() {
use crate::fixed::Fixed;
type U64D9 = Fixed<u64, 9>;
type U64D10 = Fixed<u64, 10>;
let x = 12 * *U64D10::ONE.get();
let y = 1 * *U64D9::ONE.get();
let res = crate::utils::apply_factor::<_, 19>(&x, &y).unwrap();
assert_eq!(res, 12u64);
let x = 100 * *U64D9::ONE.get();
let y = 1 * (*U64D9::ONE.get() / 100); let res = crate::utils::apply_factor::<_, 9>(&x, &y).unwrap();
assert_eq!(res, *U64D9::ONE.get());
let x = 100 * *U64D9::ONE.get();
let y = 1 * (*U64D9::ONE.get() + *U64D9::ONE.get() / 10); let res = crate::utils::apply_factor::<_, 9>(&x, &y).unwrap();
assert_eq!(res, 110 * *U64D9::ONE.get());
}
}