1use std::{convert::TryFrom, fmt};
2
3use crate::common::*;
4use crate::error::*;
5use crate::rate::*;
6
7pub use crate::common::uint::U192;
9
10#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd, Eq, Ord)]
12pub struct Decimal(pub U192);
13
14impl Decimal {
15 pub fn one() -> Self {
17 Self(Self::wad())
18 }
19
20 pub fn zero() -> Self {
22 Self(U192::zero())
23 }
24
25 fn wad() -> U192 {
27 U192::from(WAD)
28 }
29
30 fn half_wad() -> U192 {
32 U192::from(HALF_WAD)
33 }
34
35 pub fn from_percent<T>(percent: T) -> Self
37 where
38 T: Into<U192>,
39 {
40 let percent: U192 = percent.into();
41 Self(percent.checked_mul(PERCENT_SCALER.into()).unwrap())
42 }
43
44 #[deprecated(
45 since = "0.1.7",
46 note = "please use the generic `from_percent` instead"
47 )]
48 pub fn from_percent_u64(percent: u64) -> Self {
49 Self::from_percent(percent)
50 }
51
52 pub fn to_percent<T>(&self) -> Result<T, DecimalError>
54 where
55 T: TryFrom<U192>,
56 {
57 T::try_from(self.0 / PERCENT_SCALER).map_err(|_| DecimalError::MathOverflow)
58 }
59
60 pub fn to_bps<T>(&self) -> Result<T, DecimalError>
62 where
63 T: TryFrom<U192>,
64 {
65 T::try_from(self.0 / BPS_SCALER).map_err(|_| DecimalError::MathOverflow)
66 }
67
68 pub fn from_bps(bps: impl Into<U192>) -> Self {
70 let bps: U192 = bps.into();
71 Self(bps.checked_mul(BPS_SCALER.into()).unwrap())
72 }
73
74 pub fn to_scaled_val<T>(&self) -> Result<T, DecimalError>
76 where
77 T: TryFrom<U192>,
78 {
79 T::try_from(self.0).map_err(|_| DecimalError::MathOverflow)
80 }
81
82 pub fn from_scaled_val(scaled_val: impl Into<U192>) -> Self {
84 Self(scaled_val.into())
85 }
86
87 pub fn try_round<T>(&self) -> Result<T, DecimalError>
89 where
90 T: TryFrom<U192>,
91 {
92 let rounded_val = Self::half_wad()
93 .checked_add(self.0)
94 .ok_or(DecimalError::MathOverflow)?
95 .checked_div(Self::wad())
96 .ok_or(DecimalError::MathOverflow)?;
97 T::try_from(rounded_val).map_err(|_| DecimalError::MathOverflow)
98 }
99
100 #[deprecated(since = "0.1.7", note = "please use the generic `try_round` instead")]
102 pub fn try_round_u64(&self) -> Result<u64, DecimalError> {
103 self.try_round()
104 }
105
106 #[deprecated(since = "0.1.7", note = "please use the generic `try_round` instead")]
108 pub fn try_round_u128(&self) -> Result<u128, DecimalError> {
109 self.try_round()
110 }
111
112 pub fn try_ceil<T>(&self) -> Result<T, DecimalError>
114 where
115 T: TryFrom<U192>,
116 {
117 let ceil_val = Self::wad()
118 .checked_sub(U192::from(1u64))
119 .ok_or(DecimalError::MathOverflow)?
120 .checked_add(self.0)
121 .ok_or(DecimalError::MathOverflow)?
122 .checked_div(Self::wad())
123 .ok_or(DecimalError::MathOverflow)?;
124 T::try_from(ceil_val).map_err(|_| DecimalError::MathOverflow)
125 }
126
127 #[deprecated(since = "0.1.7", note = "please use the generic `try_ceil` instead")]
129 pub fn try_ceil_u64(&self) -> Result<u64, DecimalError> {
130 self.try_ceil()
131 }
132
133 #[deprecated(since = "0.1.7", note = "please use the generic `try_ceil` instead")]
135 pub fn try_ceil_u128(&self) -> Result<u128, DecimalError> {
136 self.try_ceil()
137 }
138
139 pub fn try_floor<T>(&self) -> Result<T, DecimalError>
141 where
142 T: TryFrom<U192>,
143 {
144 let ceil_val = self
145 .0
146 .checked_div(Self::wad())
147 .ok_or(DecimalError::MathOverflow)?;
148 T::try_from(ceil_val).map_err(|_| DecimalError::MathOverflow)
149 }
150
151 #[deprecated(since = "0.1.7", note = "please use the generic `try_floor` instead")]
153 pub fn try_floor_u64(&self) -> Result<u64, DecimalError> {
154 self.try_floor()
155 }
156
157 #[deprecated(since = "0.1.7", note = "please use the generic `try_floor` instead")]
159 pub fn try_floor_u128(&self) -> Result<u128, DecimalError> {
160 self.try_floor()
161 }
162}
163
164impl fmt::Display for Decimal {
165 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166 let mut scaled_val = self.0.to_string();
167 if scaled_val.len() <= SCALE {
168 scaled_val.insert_str(0, &vec!["0"; SCALE - scaled_val.len()].join(""));
169 scaled_val.insert_str(0, "0.");
170 } else {
171 scaled_val.insert(scaled_val.len() - SCALE, '.');
172 }
173 f.write_str(&scaled_val)
174 }
175}
176
177impl<T> From<T> for Decimal
178where
179 T: Into<U128>,
180{
181 fn from(val: T) -> Self {
182 let val: U128 = val.into();
183 Self(Self::wad().checked_mul(val.into()).unwrap())
185 }
186}
187
188impl From<Rate> for Decimal {
189 fn from(val: Rate) -> Self {
190 Self(val.to_scaled_val().unwrap())
191 }
192}
193
194impl TryAdd for Decimal {
195 fn try_add(self, rhs: Self) -> Result<Self, DecimalError> {
196 Ok(Self(
197 self.0
198 .checked_add(rhs.0)
199 .ok_or(DecimalError::MathOverflow)?,
200 ))
201 }
202}
203
204impl TrySub for Decimal {
205 fn try_sub(self, rhs: Self) -> Result<Self, DecimalError> {
206 Ok(Self(
207 self.0
208 .checked_sub(rhs.0)
209 .ok_or(DecimalError::MathOverflow)?,
210 ))
211 }
212}
213
214impl<T> TryDiv<T> for Decimal
215where
216 T: Into<U192>,
217{
218 fn try_div(self, rhs: T) -> Result<Self, DecimalError> {
219 Ok(Self(
220 self.0
221 .checked_div(rhs.into())
222 .ok_or(DecimalError::MathOverflow)?,
223 ))
224 }
225}
226
227impl TryDiv<Rate> for Decimal {
228 fn try_div(self, rhs: Rate) -> Result<Self, DecimalError> {
229 self.try_div(Self::from(rhs))
230 }
231}
232
233impl TryDiv<Decimal> for Decimal {
234 fn try_div(self, rhs: Self) -> Result<Self, DecimalError> {
235 Ok(Self(
236 self.0
237 .checked_mul(Self::wad())
238 .ok_or(DecimalError::MathOverflow)?
239 .checked_div(rhs.0)
240 .ok_or(DecimalError::MathOverflow)?,
241 ))
242 }
243}
244
245impl<T> TryMul<T> for Decimal
246where
247 T: Into<U192>,
248{
249 fn try_mul(self, rhs: T) -> Result<Self, DecimalError> {
250 Ok(Self(
251 self.0
252 .checked_mul(rhs.into())
253 .ok_or(DecimalError::MathOverflow)?,
254 ))
255 }
256}
257
258impl TryMul<Rate> for Decimal {
259 fn try_mul(self, rhs: Rate) -> Result<Self, DecimalError> {
260 self.try_mul(Self::from(rhs))
261 }
262}
263
264impl TryMul<Decimal> for Decimal {
265 fn try_mul(self, rhs: Self) -> Result<Self, DecimalError> {
266 Ok(Self(
267 self.0
268 .checked_mul(rhs.0)
269 .ok_or(DecimalError::MathOverflow)?
270 .checked_div(Self::wad())
271 .ok_or(DecimalError::MathOverflow)?,
272 ))
273 }
274}
275
276#[cfg(test)]
277mod test {
278 use super::*;
279
280 #[test]
281 fn test_scaler() {
282 assert_eq!(U192::exp10(SCALE), Decimal::wad());
283 }
284
285 #[test]
286 fn test_decimal_from_to_percent() {
287 let pct = 10; let x = Decimal::from_percent(pct);
289 let pct_actual = x.to_percent().unwrap();
290
291 assert_eq!(pct as u128, pct_actual);
292 }
293}