defituna_client/math/
full_math.rs

1//! A custom implementation of https://github.com/sdroege/rust-muldiv to support phantom overflow resistant
2//! multiply-divide operations. This library uses U128 in place of u128 for u64 operations,
3//! and supports U128 operations.
4//!
5use uint::construct_uint;
6
7construct_uint! {
8    pub struct U128(2);
9}
10
11construct_uint! {
12    pub struct U256(4);
13}
14
15construct_uint! {
16    pub struct U512(8);
17}
18
19/// Trait for calculating `val * num / denom` with different rounding modes and overflow
20/// protection.
21///
22/// Implementations of this trait have to ensure that even if the result of the multiplication does
23/// not fit into the type, as long as it would fit after the division the correct result has to be
24/// returned instead of `None`. `None` only should be returned if the overall result does not fit
25/// into the type.
26///
27/// This specifically means that e.g. the `u64` implementation must, depending on the arguments, be
28/// able to do 128 bit integer multiplication.
29pub trait MulDiv<RHS = Self> {
30    /// Output type for the methods of this trait.
31    type Output;
32
33    fn mul_div_floor(self, num: RHS, denom: RHS) -> Option<Self::Output>;
34    fn mul_div_ceil(self, num: RHS, denom: RHS) -> Option<Self::Output>;
35
36    /// Return u64 not out of bounds
37    fn to_underflow_u64(self) -> u64;
38}
39
40pub trait Upcast256 {
41    fn as_u256(self) -> U256;
42}
43impl Upcast256 for U128 {
44    fn as_u256(self) -> U256 {
45        U256([self.0[0], self.0[1], 0, 0])
46    }
47}
48
49pub trait Downcast256 {
50    /// Unsafe cast to U128
51    /// Bits beyond the 128th position are lost
52    fn as_u128(self) -> U128;
53}
54impl Downcast256 for U256 {
55    fn as_u128(self) -> U128 {
56        U128([self.0[0], self.0[1]])
57    }
58}
59
60pub trait Upcast512 {
61    fn as_u512(self) -> U512;
62}
63impl Upcast512 for U256 {
64    fn as_u512(self) -> U512 {
65        U512([self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0])
66    }
67}
68
69pub trait Downcast512 {
70    /// Unsafe cast to U256
71    /// Bits beyond the 256th position are lost
72    fn as_u256(self) -> U256;
73}
74impl Downcast512 for U512 {
75    fn as_u256(self) -> U256 {
76        U256([self.0[0], self.0[1], self.0[2], self.0[3]])
77    }
78}
79
80impl MulDiv for u64 {
81    type Output = u64;
82
83    fn mul_div_floor(self, num: Self, denom: Self) -> Option<Self::Output> {
84        assert_ne!(denom, 0);
85        let r = (U128::from(self) * U128::from(num)) / U128::from(denom);
86        if r > U128::from(u64::MAX) {
87            None
88        } else {
89            Some(r.as_u64())
90        }
91    }
92
93    fn mul_div_ceil(self, num: Self, denom: Self) -> Option<Self::Output> {
94        assert_ne!(denom, 0);
95        let r = (U128::from(self) * U128::from(num) + U128::from(denom - 1)) / U128::from(denom);
96        if r > U128::from(u64::MAX) {
97            None
98        } else {
99            Some(r.as_u64())
100        }
101    }
102
103    fn to_underflow_u64(self) -> u64 {
104        self
105    }
106}
107
108impl MulDiv for U128 {
109    type Output = U128;
110
111    fn mul_div_floor(self, num: Self, denom: Self) -> Option<Self::Output> {
112        assert_ne!(denom, U128::default());
113        let r = ((self.as_u256()) * (num.as_u256())) / (denom.as_u256());
114        if r > U128::MAX.as_u256() {
115            None
116        } else {
117            Some(r.as_u128())
118        }
119    }
120
121    fn mul_div_ceil(self, num: Self, denom: Self) -> Option<Self::Output> {
122        assert_ne!(denom, U128::default());
123        let r = (self.as_u256() * num.as_u256() + (denom - 1).as_u256()) / denom.as_u256();
124        if r > U128::MAX.as_u256() {
125            None
126        } else {
127            Some(r.as_u128())
128        }
129    }
130
131    fn to_underflow_u64(self) -> u64 {
132        if self < U128::from(u64::MAX) {
133            self.as_u64()
134        } else {
135            0
136        }
137    }
138}
139
140impl MulDiv for U256 {
141    type Output = U256;
142
143    fn mul_div_floor(self, num: Self, denom: Self) -> Option<Self::Output> {
144        assert_ne!(denom, U256::default());
145        let r = (self.as_u512() * num.as_u512()) / denom.as_u512();
146        if r > U256::MAX.as_u512() {
147            None
148        } else {
149            Some(r.as_u256())
150        }
151    }
152
153    fn mul_div_ceil(self, num: Self, denom: Self) -> Option<Self::Output> {
154        assert_ne!(denom, U256::default());
155        let r = (self.as_u512() * num.as_u512() + (denom - 1).as_u512()) / denom.as_u512();
156        if r > U256::MAX.as_u512() {
157            None
158        } else {
159            Some(r.as_u256())
160        }
161    }
162
163    fn to_underflow_u64(self) -> u64 {
164        if self < U256::from(u64::MAX) {
165            self.as_u64()
166        } else {
167            0
168        }
169    }
170}