1use std::cmp::Ordering;
2
3use crate::{
4 fixed::{Fixed, FixedPointOps},
5 num::{MulDiv, Num},
6};
7
8use num_traits::{CheckedMul, One, Zero};
9
10pub fn usd_to_market_token_amount<T>(
14 usd_value: T,
15 pool_value: T,
16 supply: T,
17 usd_to_amount_divisor: T,
18) -> Option<T>
19where
20 T: MulDiv + Num,
21{
22 if usd_to_amount_divisor.is_zero() {
23 return None;
24 }
25 if supply.is_zero() && pool_value.is_zero() {
26 usd_value.checked_div(&usd_to_amount_divisor)
27 } else if supply.is_zero() && !pool_value.is_zero() {
28 pool_value
29 .checked_add(&usd_value)?
30 .checked_div(&usd_to_amount_divisor)
31 } else {
32 supply.checked_mul_div(&usd_value, &pool_value)
33 }
34}
35
36pub fn market_token_amount_to_usd<T>(amount: &T, pool_value: &T, supply: &T) -> Option<T>
40where
41 T: MulDiv,
42{
43 pool_value.checked_mul_div(amount, supply)
44}
45
46pub fn apply_factors<T, const DECIMALS: u8>(
50 value: T,
51 factor: T,
52 exponent_factor: T,
53) -> crate::Result<T>
54where
55 T: FixedPointOps<DECIMALS>,
56{
57 Ok(apply_exponent_factor_wrapped(value, exponent_factor)
58 .ok_or(crate::Error::PowComputation)?
59 .checked_mul(&Fixed::from_inner(factor))
60 .ok_or(crate::Error::Overflow)?
61 .into_inner())
62}
63
64fn apply_exponent_factor_wrapped<T, const DECIMALS: u8>(
65 value: T,
66 exponent_factor: T,
67) -> Option<Fixed<T, DECIMALS>>
68where
69 T: FixedPointOps<DECIMALS>,
70{
71 let unit = Fixed::ONE;
72 let value = Fixed::from_inner(value);
73 let exponent = Fixed::from_inner(exponent_factor);
74
75 let ans = match value.cmp(&unit) {
76 Ordering::Less => Fixed::zero(),
77 Ordering::Equal => unit,
78 Ordering::Greater => {
79 if exponent.is_zero() {
80 unit
81 } else if exponent.is_one() {
82 value
83 } else {
84 value.checked_pow(&exponent)?
85 }
86 }
87 };
88 Some(ans)
89}
90
91#[inline]
95pub fn apply_exponent_factor<T, const DECIMALS: u8>(value: T, exponent_factor: T) -> Option<T>
96where
97 T: FixedPointOps<DECIMALS>,
98{
99 Some(apply_exponent_factor_wrapped(value, exponent_factor)?.into_inner())
100}
101
102#[inline]
108pub fn apply_factor<T, const DECIMALS: u8>(value: &T, factor: &T) -> Option<T>
109where
110 T: FixedPointOps<DECIMALS>,
111{
112 value.checked_mul_div(factor, &FixedPointOps::UNIT)
113}
114
115#[inline]
120pub fn div_to_factor<T, const DECIMALS: u8>(
121 value: &T,
122 divisor: &T,
123 round_up_magnitude: bool,
124) -> Option<T>
125where
126 T: FixedPointOps<DECIMALS>,
127{
128 if divisor.is_zero() {
129 return Some(T::zero());
130 }
131
132 if round_up_magnitude {
133 value.checked_mul_div_ceil(&T::UNIT, divisor)
134 } else {
135 value.checked_mul_div(&T::UNIT, divisor)
136 }
137}
138
139#[inline]
144pub fn div_to_factor_signed<T, const DECIMALS: u8>(
145 value: &T::Signed,
146 divisor: &T,
147) -> Option<T::Signed>
148where
149 T: FixedPointOps<DECIMALS>,
150{
151 if divisor.is_zero() {
152 return Some(Zero::zero());
153 }
154
155 T::UNIT.checked_mul_div_with_signed_numerator(value, divisor)
156}
157
158#[cfg(test)]
159mod tests {
160 #[test]
161 #[allow(clippy::identity_op)]
162 fn test_apply_factor() {
163 use crate::fixed::Fixed;
164 type U64D9 = Fixed<u64, 9>;
165 type U64D10 = Fixed<u64, 10>;
166
167 let x = 12 * *U64D10::ONE.get();
168 let y = 1 * *U64D9::ONE.get();
169 let res = crate::utils::apply_factor::<_, 19>(&x, &y).unwrap();
170 assert_eq!(res, 12u64);
171
172 let x = 100 * *U64D9::ONE.get();
173 let y = 1 * (*U64D9::ONE.get() / 100); let res = crate::utils::apply_factor::<_, 9>(&x, &y).unwrap();
175 assert_eq!(res, *U64D9::ONE.get());
176
177 let x = 100 * *U64D9::ONE.get();
178 let y = 1 * (*U64D9::ONE.get() + *U64D9::ONE.get() / 10); let res = crate::utils::apply_factor::<_, 9>(&x, &y).unwrap();
180 assert_eq!(res, 110 * *U64D9::ONE.get());
181 }
182}