reda_unit/
number.rs

1use std::fmt;
2use std::ops::{Add, Div, Mul, Neg, Rem, Sub};
3use std::str::FromStr;
4use serde::{Serialize, Serializer, Deserialize, Deserializer};
5use std::cmp::Ordering;
6
7#[derive(Debug, Clone, Copy, PartialEq)]
8pub enum Suffix {
9    Giga,  // 1e9
10    Mega,  // 1e6
11    Kilo,  // 1e3
12    None,  // 1.0
13    Milli, // 1e-3
14    Micro, // 1e-6
15    Nano,  // 1e-9
16    Pico,  // 1e-12
17}
18
19#[derive(Debug, Clone, Copy, PartialEq)]
20pub struct Number {
21    pub value: f64,
22    pub suffix: Suffix,
23}
24
25impl Suffix {
26    pub const fn factor(&self) -> f64 {
27        match self {
28            Suffix::Giga => 1e9,
29            Suffix::Mega => 1e6,
30            Suffix::Kilo => 1e3,
31            Suffix::None => 1.0,
32            Suffix::Milli => 1e-3,
33            Suffix::Micro => 1e-6,
34            Suffix::Nano => 1e-9,
35            Suffix::Pico => 1e-12,
36        }
37    }
38
39    pub const fn name(&self) -> &'static str {
40        match self {
41            Suffix::Giga => "G",
42            Suffix::Mega => "M",
43            Suffix::Kilo => "K",
44            Suffix::None => "",
45            Suffix::Milli => "m",
46            Suffix::Micro => "u",
47            Suffix::Nano => "n",
48            Suffix::Pico => "p",
49        }
50    }
51}
52
53impl FromStr for Suffix {
54    type Err = ();
55
56    fn from_str(s: &str) -> Result<Self, Self::Err> {
57        match s {
58            "G" => Ok(Suffix::Giga),
59            "M" => Ok(Suffix::Mega),
60            "K" => Ok(Suffix::Kilo),
61            "" => Ok(Suffix::None),
62            "m" => Ok(Suffix::Milli),
63            "u" => Ok(Suffix::Micro),
64            "n" => Ok(Suffix::Nano),
65            "p" => Ok(Suffix::Pico),
66            _ => Err(())
67        }
68    }
69}
70
71const PREFIX_VALUE_TABLE: [(Suffix, f64); 8] = [
72    (Suffix::Giga, 1e9),
73    (Suffix::Mega, 1e6),
74    (Suffix::Kilo, 1e3),
75    (Suffix::None, 1.0),
76    (Suffix::Milli, 1e-3),
77    (Suffix::Micro, 1e-6),
78    (Suffix::Nano, 1e-9),
79    (Suffix::Pico, 1e-12),
80];
81
82const PREFIX_TABLE: [(Suffix, &'static str); 8] = [
83    (Suffix::Giga, "G"),
84    (Suffix::Mega, "M"),
85    (Suffix::Kilo, "K"),
86    (Suffix::Kilo, "k"),
87    (Suffix::Milli, "m"),
88    (Suffix::Micro, "u"),
89    (Suffix::Nano, "n"),
90    (Suffix::Pico, "p"),
91];
92
93impl Number {
94    pub const fn new(value: f64, suffix: Suffix) -> Self {
95        Number { value, suffix }
96    }
97
98    pub const fn to_f64(&self) -> f64 {
99        self.value * self.suffix.factor()
100    }
101
102    pub fn from_f64<F: Into<f64>>(val: F) -> Self {
103        let val = val.into();
104        let abs = val.abs();
105        for (suffix, factor) in PREFIX_VALUE_TABLE.iter() {
106            if abs >= *factor {
107                return Number::new(val / factor, *suffix);
108            }
109        }
110
111        Number::new(val, Suffix::None)
112    }
113
114    pub fn zero() -> Self {
115        Self::new(0.0, Suffix::None)
116    }
117
118    pub fn is_zero(&self) -> bool {
119        self.value == 0.
120    }
121
122    pub fn is_nan(self) -> bool {
123        self.value.is_nan()
124    }
125
126    pub fn is_finite(self) -> bool {
127        self.value.is_finite()
128    }
129
130    pub fn powf(self, exp: f64) -> Self {
131        Self::new(self.value.powf(exp), self.suffix)
132    }
133
134    pub fn atan2(self, other: Number) -> Self {
135        Self::new(self.to_f64().atan2(other.to_f64()), self.suffix)
136    }
137}
138
139macro_rules! impl_f64_like_method {
140    ($f:ident) => {
141        #[inline]
142        pub fn $f(&self) -> Self {
143            Self::new(self.value.$f(), self.suffix)
144        }
145    };
146}
147
148impl Number {
149    impl_f64_like_method!(abs);
150    impl_f64_like_method!(ceil);
151    impl_f64_like_method!(floor);
152    impl_f64_like_method!(round);
153    impl_f64_like_method!(trunc);
154    impl_f64_like_method!(fract);
155
156    impl_f64_like_method!(sqrt);
157    impl_f64_like_method!(exp);
158    impl_f64_like_method!(ln);
159    impl_f64_like_method!(log10);
160    impl_f64_like_method!(log2);
161    impl_f64_like_method!(recip);
162    
163    impl_f64_like_method!(sin);
164    impl_f64_like_method!(cos);
165    impl_f64_like_method!(tan);
166    impl_f64_like_method!(asin);
167    impl_f64_like_method!(acos);
168    impl_f64_like_method!(atan);
169
170    impl_f64_like_method!(sinh);
171    impl_f64_like_method!(cosh);
172    impl_f64_like_method!(tanh);
173
174    impl_f64_like_method!(to_degrees);
175    impl_f64_like_method!(to_radians);
176}
177
178impl fmt::Display for Number {
179    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
180        if let Some(p) = f.precision() {
181            write!(f, "{:.*}{}", p, self.value, self.suffix.name())
182        } else {
183            write!(f, "{}{}", self.value, self.suffix.name())
184        }
185    }
186}
187
188impl FromStr for Number {
189    type Err = String;
190    fn from_str(s: &str) -> Result<Self, Self::Err> {
191        let s = s.trim();
192        for (suffix, suffix_str) in PREFIX_TABLE.iter() {
193            if s.ends_with(suffix_str) {
194                let num_str = &s[..s.len() - suffix_str.len()];
195                let val: f64 = num_str.trim().parse()
196                    .map_err(|e| format!("Parse number '{}' error for '{}'", num_str, e))?;
197                return Ok(Number::new(val, *suffix));
198            }
199        }
200        // As none suffix
201        let val: f64 = s.parse().map_err(|e| format!("Parse number '{}' error for '{}'", s, e))?;
202        return Ok(Number::new(val, Suffix::None));
203    }
204}
205
206
207macro_rules! impl_from {
208    ($t:ty) => {
209        impl From<$t> for Number {
210            fn from(value: $t) -> Self {
211                Self {
212                    value: value as f64,
213                    suffix: Suffix::None
214                }
215            }
216        }
217    };
218}
219
220impl_from!(f64);
221impl_from!(f32);
222impl_from!(u32);
223impl_from!(i32);
224
225impl Add for Number {
226    type Output = Number;
227    fn add(self, rhs: Number) -> Number {
228        Number::from_f64(self.to_f64() + rhs.to_f64())
229    }
230}
231
232impl Sub for Number {
233    type Output = Number;
234    fn sub(self, rhs: Number) -> Number {
235        Number::from_f64(self.to_f64() - rhs.to_f64())
236    }
237}
238
239impl Mul for Number {
240    type Output = Number;
241    fn mul(self, rhs: Number) -> Number {
242        Number::from_f64(self.to_f64() * rhs.to_f64())
243    }
244}
245
246impl Div for Number {
247    type Output = Number;
248    fn div(self, rhs: Number) -> Number {
249        Number::from_f64(self.to_f64() / rhs.to_f64())
250    }
251}
252
253impl Add<f64> for Number {
254    type Output = Number;
255    fn add(self, rhs: f64) -> Number {
256        Number::from_f64(self.to_f64() + rhs)
257    }
258}
259
260impl Sub<f64> for Number {
261    type Output = Number;
262    fn sub(self, rhs: f64) -> Number {
263        Number::from_f64(self.to_f64() - rhs)
264    }
265}
266
267impl Mul<f64> for Number {
268    type Output = Number;
269    fn mul(self, rhs: f64) -> Number {
270        Number::from_f64(self.to_f64() * rhs)
271    }
272}
273
274impl Div<f64> for Number {
275    type Output = Number;
276    fn div(self, rhs: f64) -> Number {
277        Number::from_f64(self.to_f64() / rhs)
278    }
279}
280
281impl Add<Number> for f64 {
282    type Output = Number;
283    fn add(self, rhs: Number) -> Number {
284        Number::from_f64(self + rhs.to_f64())
285    }
286}
287
288impl Sub<Number> for f64 {
289    type Output = Number;
290    fn sub(self, rhs: Number) -> Number {
291        Number::from_f64(self - rhs.to_f64())
292    }
293}
294
295impl Mul<Number> for f64 {
296    type Output = Number;
297    fn mul(self, rhs: Number) -> Number {
298        Number::from_f64(self * rhs.to_f64())
299    }
300}
301
302impl Div<Number> for f64 {
303    type Output = Number;
304    fn div(self, rhs: Number) -> Number {
305        Number::from_f64(self / rhs.to_f64())
306    }
307}
308
309impl Rem<Number> for Number {
310    type Output = Number;
311    fn rem(self, rhs: Number) -> Self::Output {
312        Number::from_f64(self.to_f64() % rhs.to_f64())
313    }
314}
315
316impl Rem<f64> for Number {
317    type Output = Number;
318    fn rem(self, rhs: f64) -> Self::Output {
319        Number::from_f64(self.to_f64() % rhs)
320    }
321}
322
323impl Rem<Number> for f64 {
324    type Output = Number;
325    fn rem(self, rhs: Number) -> Self::Output {
326        Number::from_f64(self % rhs.to_f64())
327    }
328}
329
330impl Neg for Number {
331    type Output = Self;
332    fn neg(self) -> Self::Output {
333        Self {
334            value: -self.value,
335            suffix: self.suffix
336        }
337    }
338}
339
340impl PartialEq<f64> for Number {
341    fn eq(&self, other: &f64) -> bool {
342        self.to_f64() == *other
343    }
344}
345
346impl PartialEq<Number> for f64 {
347    fn eq(&self, other: &Number) -> bool {
348        other == self
349    }
350}
351
352impl PartialOrd for Number {
353    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
354        self.to_f64().partial_cmp(&other.to_f64())
355    }
356}
357
358impl PartialOrd<f64> for Number {
359    fn partial_cmp(&self, other: &f64) -> Option<Ordering> {
360        self.to_f64().partial_cmp(other)
361    }
362}
363
364impl PartialOrd<Number> for f64 {
365    fn partial_cmp(&self, other: &Number) -> Option<Ordering> {
366        self.partial_cmp(&other.to_f64())
367    }
368}
369
370impl Eq for Number {}
371
372impl Ord for Number {
373    fn cmp(&self, other: &Self) -> Ordering {
374        self.partial_cmp(other).unwrap()
375    }
376}
377
378impl Serialize for Number {
379    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
380    where
381        S: Serializer,
382    {
383        let s = self.to_string();
384        serializer.serialize_str(&s)
385    }
386}
387
388impl<'de> Deserialize<'de> for Number {
389    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
390    where
391        D: Deserializer<'de>,
392    {
393        let s = String::deserialize(deserializer)?;
394        Number::from_str(&s).map_err(serde::de::Error::custom)
395    }
396}
397
398#[cfg(test)]
399mod tests {
400    use crate::num;
401
402    use super::*;
403    use std::str::FromStr;
404
405    #[test]
406    fn test_prefix_factor_and_suffix() {
407        assert_eq!(Suffix::Giga.factor(), 1e9);
408        assert_eq!(Suffix::Micro.name(), "u");
409        assert_eq!(Suffix::from_str("K"), Ok(Suffix::Kilo));
410        assert_eq!(Suffix::from_str("z"), Err(()));
411    }
412
413    #[test]
414    fn test_number_new_and_to_f64() {
415        let n = Number::new(3.3, Suffix::Kilo);
416        assert_eq!(n.to_f64(), 3300.0);
417    }
418
419    #[test]
420    fn test_number_from_f64() {
421        let n = Number::from_f64(1e-6);
422        assert_eq!(n.suffix, Suffix::Micro);
423        assert!((n.value - 1.0).abs() < 1e-6);
424
425        let n2 = Number::from_f64(1e3);
426        assert_eq!(n2.suffix, Suffix::Kilo);
427    }
428
429    #[test]
430    fn test_number_from_str() {
431        let a = Number::from_str("3.3K").unwrap();
432        assert_eq!(a.suffix, Suffix::Kilo);
433        assert!((a.value - 3.3).abs() < 1e-6);
434
435        let b = Number::from_str("2.2u").unwrap();
436        assert_eq!(b.suffix, Suffix::Micro);
437        assert!((b.value - 2.2).abs() < 1e-6);
438
439        let c = Number::from_str("100").unwrap();
440        assert_eq!(c.suffix, Suffix::None);
441        assert_eq!(c.value, 100.0);
442
443        assert!(Number::from_str("3.3X").is_err());
444    }
445
446    #[test]
447    fn test_display() {
448        let a = Number::new(1.23456, Suffix::Milli);
449        let s = format!("{}", a);
450        assert_eq!(s, "1.23456m");
451    }
452
453    #[test]
454    fn test_number_arithmetic() {
455        let a = Number::new(3.3, Suffix::Kilo); // 3300
456        let b = Number::new(2.2, Suffix::Micro); // 2.2e-6
457
458        let c = a + b;
459        assert!((c.to_f64() - 3300.0000022).abs() < 1e-6);
460
461        let d = a - b;
462        assert!((d.to_f64() - 3299.9999978).abs() < 1e-6);
463
464        let e = a * b;
465        assert!((e.to_f64() - 3300.0 * 2.2e-6).abs() < 1e-9);
466
467        let f = a / b;
468        assert!((f.to_f64() - (3300.0 / 2.2e-6)).abs() < 1e-3);
469
470        let g = num!(7.3) % num!(2.0);
471        assert_eq!(g, 7.3 % 2.0);
472    }
473
474    #[test]
475    fn test_number_f64_arithmetic() {
476        let a = Number::new(3.3, Suffix::Kilo); // 3300
477        let b = a + 1.0;
478        assert!((b.to_f64() - 3301.0).abs() < 1e-6);
479    }
480
481    #[test]
482    fn test_num_macro() {
483        use crate::num;
484        let a = num!(3.3 k);
485        assert_eq!(a.suffix, Suffix::Kilo);
486        assert_eq!(a.value, 3.3);
487
488        let b = num!(1.0 u);
489        assert_eq!(b.suffix, Suffix::Micro);
490        assert_eq!(b.value, 1.0);
491
492        let c = num!(100);
493        assert_eq!(c.suffix, Suffix::None);
494        assert_eq!(c.value, 100.0);
495
496        let c = 100;
497        let c = num!(c);
498        assert_eq!(c.value, 100.0);
499    }
500
501    use serde_json;
502
503    #[test]
504    fn test_serialize_number() {
505        let n = Number::new(1.5, Suffix::Milli);
506        let json = serde_json::to_string(&n).unwrap();
507        assert_eq!(json, "\"1.5m\"");
508    }
509
510    #[test]
511    fn test_deserialize_number() {
512        let json = "\"2.2u\"";
513        let n: Number = serde_json::from_str(json).unwrap();
514        assert_eq!(n, Number::new(2.2, Suffix::Micro));
515    }
516
517    #[test]
518    fn test_serialize_deserialize_roundtrip() {
519        let original = Number::new(3.3, Suffix::Nano);
520        let json = serde_json::to_string(&original).unwrap();
521        let parsed: Number = serde_json::from_str(&json).unwrap();
522        assert_eq!(original, parsed);
523    }
524
525    #[test]
526    fn test_deserialize_invalid_number() {
527        let json = "\"bad_number\"";
528        let result: Result<Number, _> = serde_json::from_str(json);
529        assert!(result.is_err());
530    }
531
532    #[test]
533    fn test_deserialize_number_no_suffix() {
534        let json = "\"42.0\"";
535        let n: Number = serde_json::from_str(json).unwrap();
536        assert_eq!(n, Number::new(42.0, Suffix::None));
537    }
538}