reproto_core/
rp_number.rs

1use num_bigint::BigInt;
2use num_integer::Integer;
3use num_traits::Signed;
4use num_traits::cast::ToPrimitive;
5use serde;
6use std::fmt;
7use std::result;
8
9macro_rules! convert_method {
10    ($ty:ty, $method:ident) => {
11        pub fn $method(&self) -> Option<$ty> {
12            let m = self.multiple();
13            self.digits.checked_div(&m).and_then(|r| r.$method())
14        }
15    };
16}
17
18#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
19pub struct RpNumber {
20    // base digits
21    pub digits: BigInt,
22    // where the decimal point is
23    pub decimal: usize,
24}
25
26impl RpNumber {
27    convert_method!(i32, to_i32);
28    convert_method!(i64, to_i64);
29    convert_method!(u32, to_u32);
30    convert_method!(u64, to_u64);
31    convert_method!(usize, to_usize);
32
33    /// Get the decimal multiple.
34    fn multiple(&self) -> BigInt {
35        let mut multiple: BigInt = 1.into();
36
37        for _ in 0..self.decimal {
38            let ten: BigInt = 10.into();
39            multiple = multiple * ten;
40        }
41
42        multiple
43    }
44
45    /// Try to convert to bigint.
46    pub fn to_bigint(&self) -> Option<&BigInt> {
47        if self.decimal != 0 {
48            return None;
49        }
50
51        Some(&self.digits)
52    }
53
54    pub fn to_f64(&self) -> Option<f64> {
55        let multiple = self.multiple();
56        let (base, decimal) = self.digits.div_mod_floor(&multiple);
57
58        base.to_f64().and_then(|base| {
59            decimal.to_f64().and_then(|decimal| {
60                multiple
61                    .to_f64()
62                    .map(|multiple| base + (decimal / multiple))
63            })
64        })
65    }
66}
67
68impl From<u32> for RpNumber {
69    fn from(value: u32) -> RpNumber {
70        RpNumber {
71            digits: value.into(),
72            decimal: 0usize,
73        }
74    }
75}
76
77impl From<u64> for RpNumber {
78    fn from(value: u64) -> RpNumber {
79        RpNumber {
80            digits: value.into(),
81            decimal: 0usize,
82        }
83    }
84}
85
86impl From<i32> for RpNumber {
87    fn from(value: i32) -> RpNumber {
88        RpNumber {
89            digits: value.into(),
90            decimal: 0usize,
91        }
92    }
93}
94
95impl From<i64> for RpNumber {
96    fn from(value: i64) -> RpNumber {
97        RpNumber {
98            digits: value.into(),
99            decimal: 0usize,
100        }
101    }
102}
103
104impl From<BigInt> for RpNumber {
105    fn from(value: BigInt) -> RpNumber {
106        RpNumber {
107            digits: value,
108            decimal: 0usize,
109        }
110    }
111}
112
113impl fmt::Debug for RpNumber {
114    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
115        write!(f, "RpNumber({})", self)
116    }
117}
118
119impl fmt::Display for RpNumber {
120    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
121        if self.decimal == 0 {
122            return write!(f, "{}", self.digits);
123        }
124
125        let multiple = self.multiple();
126        let (base, decimal) = self.digits.abs().div_mod_floor(&multiple);
127
128        let decimal = format!("{}", decimal);
129
130        // pad leading zeros if needed
131        let decimal = if decimal.len() < self.decimal {
132            let mut s = String::new();
133
134            for _ in decimal.len()..self.decimal {
135                s.push('0');
136            }
137
138            s.push_str(&decimal);
139            s
140        } else {
141            decimal
142        };
143
144        if self.digits.is_negative() {
145            write!(f, "-{}.{}", base, decimal)
146        } else {
147            write!(f, "{}.{}", base, decimal)
148        }
149    }
150}
151
152impl serde::Serialize for RpNumber {
153    fn serialize<S>(&self, serializer: S) -> result::Result<S::Ok, S::Error>
154    where
155        S: serde::Serializer,
156    {
157        let n = self.to_f64().unwrap();
158        serializer.serialize_f64(n)
159    }
160}
161
162#[cfg(test)]
163mod test_numbers {
164    use super::*;
165
166    #[test]
167    fn test_number() {
168        let n = RpNumber {
169            digits: 104321.into(),
170            decimal: 2,
171        };
172
173        assert_eq!(Some(1043.21), n.to_f64());
174        assert_eq!(Some(1043), n.to_u32());
175        assert_eq!(Some(1043), n.to_u64());
176        assert_eq!(Some(1043), n.to_i32());
177        assert_eq!(Some(1043), n.to_i64());
178    }
179
180    #[test]
181    fn test_negative() {
182        let n = RpNumber {
183            digits: (-104321).into(),
184            decimal: 2,
185        };
186
187        assert_eq!(None, n.to_u64());
188        assert_eq!(Some(-1043.21), n.to_f64());
189        assert_eq!(Some(-1043), n.to_i32());
190        assert_eq!(Some(-1043), n.to_i64());
191        assert_eq!(None, n.to_u32());
192        assert_eq!(None, n.to_u64());
193    }
194
195    #[test]
196    fn test_display() {
197        let n = RpNumber {
198            digits: (104321).into(),
199            decimal: 2,
200        };
201
202        assert_eq!("1043.21", format!("{}", n));
203
204        let n2 = RpNumber {
205            digits: (104321).into(),
206            decimal: 0,
207        };
208
209        assert_eq!("104321", format!("{}", n2));
210
211        let n3 = RpNumber {
212            digits: (104321).into(),
213            decimal: 10,
214        };
215
216        assert_eq!("0.0000104321", format!("{}", n3));
217    }
218}