rust_fixed_point_decimal/binops/
cmp.rs

1// ---------------------------------------------------------------------------
2// Copyright:   (c) 2021 ff. Michael Amrhein (michael@adrhinum.de)
3// License:     This program is part of a larger application. For license
4//              details please read the file LICENSE.TXT provided together
5//              with the application.
6// ---------------------------------------------------------------------------
7// $Source: src/binops/cmp.rs $
8// $Revision: 2021-10-27T16:55:16+02:00 $
9
10use std::cmp::Ordering;
11
12use rust_fixed_point_decimal_core::{checked_adjust_prec, checked_mul_pow_ten};
13
14use crate::{
15    prec_constraints::{PrecLimitCheck, True},
16    Decimal, MAX_PREC,
17};
18
19impl<const P: u8, const Q: u8> PartialEq<Decimal<Q>> for Decimal<P>
20where
21    PrecLimitCheck<{ P <= MAX_PREC }>: True,
22    PrecLimitCheck<{ Q <= MAX_PREC }>: True,
23{
24    fn eq(&self, other: &Decimal<Q>) -> bool {
25        match checked_adjust_prec(self.coeff, P, other.coeff, Q) {
26            (Some(a), Some(b)) => a == b,
27            _ => false,
28        }
29    }
30}
31
32impl<const P: u8, const Q: u8> PartialOrd<Decimal<Q>> for Decimal<P>
33where
34    PrecLimitCheck<{ P <= MAX_PREC }>: True,
35    PrecLimitCheck<{ Q <= MAX_PREC }>: True,
36{
37    fn partial_cmp(&self, other: &Decimal<Q>) -> Option<Ordering> {
38        match checked_adjust_prec(self.coeff, P, other.coeff, Q) {
39            (Some(a), Some(b)) => a.partial_cmp(&b),
40            (None, Some(_)) => {
41                if self.coeff > 0 {
42                    Some(Ordering::Greater)
43                } else {
44                    Some(Ordering::Less)
45                }
46            }
47            (Some(_), None) => {
48                if other.coeff < 0 {
49                    Some(Ordering::Greater)
50                } else {
51                    Some(Ordering::Less)
52                }
53            }
54            // Should never happen:
55            (None, None) => None,
56        }
57    }
58}
59
60impl<const P: u8> Decimal<P>
61where
62    PrecLimitCheck<{ P <= MAX_PREC }>: True,
63{
64    /// Returns true if self is equal to zero.
65    #[inline(always)]
66    pub fn eq_zero(&self) -> bool {
67        self.coeff == 0
68    }
69
70    /// Returns true if self is equal to one.
71    #[inline(always)]
72    pub fn eq_one(&self) -> bool {
73        self.coeff == Self::ONE.coeff
74    }
75
76    /// Returns true if self is less than zero.
77    #[inline(always)]
78    pub fn is_negative(&self) -> bool {
79        self.coeff < 0
80    }
81
82    /// Returns true if self is greater than zero.
83    #[inline(always)]
84    pub fn is_positive(&self) -> bool {
85        self.coeff > 0
86    }
87}
88
89#[cfg(test)]
90mod cmp_decimals_tests {
91    use std::cmp::{max, min, Ordering};
92
93    use crate::Decimal;
94
95    #[test]
96    fn test_eq_same_prec() {
97        let x = Decimal::<1>::new_raw(178);
98        assert!(x.eq(&x));
99        let y = x.clone();
100        assert!(x.eq(&y));
101        assert_eq!(x, y);
102        assert_eq!(y, x);
103        assert!(!(y.ne(&x)));
104    }
105
106    #[test]
107    fn test_eq_different_prec() {
108        let x = Decimal::<1>::new_raw(178);
109        let y = Decimal::<4>::new_raw(178000);
110        assert!(x.eq(&y));
111        assert_eq!(x, y);
112        assert_eq!(y, x);
113        assert!(!(y.ne(&x)));
114    }
115
116    #[test]
117    fn test_ne_same_prec() {
118        let x = Decimal::<7>::new_raw(-178000);
119        let y = Decimal::<7>::new_raw(178000);
120        assert_ne!(x, y);
121        assert_eq!(x.partial_cmp(&y), Some(Ordering::Less));
122        assert_eq!(x.cmp(&y), Ordering::Less);
123        assert!(x < y);
124        assert!(y > x);
125    }
126
127    #[test]
128    fn test_ne_different_prec() {
129        let x = Decimal::<7>::new_raw(178001);
130        let y = Decimal::<4>::new_raw(178);
131        assert_ne!(x, y);
132        assert_eq!(x.partial_cmp(&y), Some(Ordering::Greater));
133        assert!(x > y);
134        assert!(y < x);
135    }
136
137    #[test]
138    fn test_cmp_max() {
139        assert_eq!(Decimal::<5>::MAX, Decimal::<5>::MAX);
140        assert_ne!(Decimal::<2>::MAX, Decimal::<9>::MAX);
141        assert!(Decimal::<2>::MAX > Decimal::<3>::MAX);
142        assert!(Decimal::<6>::MAX < Decimal::<4>::MAX);
143    }
144
145    #[test]
146    fn test_cmp_min() {
147        assert_eq!(Decimal::<5>::MIN, Decimal::<5>::MIN);
148        assert_ne!(Decimal::<2>::MIN, Decimal::<9>::MIN);
149        assert!(Decimal::<2>::MIN < Decimal::<3>::MIN);
150        assert!(Decimal::<6>::MIN > Decimal::<4>::MIN);
151    }
152
153    #[test]
154    fn test_min_max() {
155        let x = Decimal::<2>::new_raw(12345);
156        let y = Decimal::<2>::new_raw(12344);
157        assert_eq!(min(x, y), y);
158        assert_eq!(min(x, x), x);
159        assert_eq!(max(x, y), x);
160        assert_eq!(max(x, x), x);
161    }
162
163    #[test]
164    fn test_eq_zero() {
165        assert!(Decimal::<0>::eq_zero(&Decimal::<0>::ZERO));
166        assert!(Decimal::<1>::eq_zero(&Decimal::<1>::ZERO));
167        assert!(Decimal::<2>::eq_zero(&Decimal::<2>::ZERO));
168        assert!(Decimal::<3>::eq_zero(&Decimal::<3>::ZERO));
169        assert!(Decimal::<4>::eq_zero(&Decimal::<4>::ZERO));
170        assert!(Decimal::<5>::eq_zero(&Decimal::<5>::ZERO));
171        assert!(Decimal::<6>::eq_zero(&Decimal::<6>::ZERO));
172        assert!(Decimal::<7>::eq_zero(&Decimal::<7>::ZERO));
173        assert!(Decimal::<8>::eq_zero(&Decimal::<8>::ZERO));
174        assert!(Decimal::<9>::eq_zero(&Decimal::<9>::ZERO));
175        assert!(!Decimal::<0>::eq_zero(&Decimal::<0>::ONE));
176        assert!(!Decimal::<1>::eq_zero(&Decimal::<1>::ONE));
177        assert!(!Decimal::<2>::eq_zero(&Decimal::<2>::ONE));
178        assert!(!Decimal::<3>::eq_zero(&Decimal::<3>::ONE));
179        assert!(!Decimal::<4>::eq_zero(&Decimal::<4>::ONE));
180        assert!(!Decimal::<5>::eq_zero(&Decimal::<5>::ONE));
181        assert!(!Decimal::<6>::eq_zero(&Decimal::<6>::ONE));
182        assert!(!Decimal::<7>::eq_zero(&Decimal::<7>::ONE));
183        assert!(!Decimal::<8>::eq_zero(&Decimal::<8>::ONE));
184        assert!(!Decimal::<9>::eq_zero(&Decimal::<9>::ONE));
185    }
186
187    #[test]
188    fn test_eq_one() {
189        assert!(Decimal::<0>::eq_one(&Decimal::<0>::ONE));
190        assert!(Decimal::<1>::eq_one(&Decimal::<1>::ONE));
191        assert!(Decimal::<2>::eq_one(&Decimal::<2>::ONE));
192        assert!(Decimal::<3>::eq_one(&Decimal::<3>::ONE));
193        assert!(Decimal::<4>::eq_one(&Decimal::<4>::ONE));
194        assert!(Decimal::<5>::eq_one(&Decimal::<5>::ONE));
195        assert!(Decimal::<6>::eq_one(&Decimal::<6>::ONE));
196        assert!(Decimal::<7>::eq_one(&Decimal::<7>::ONE));
197        assert!(Decimal::<8>::eq_one(&Decimal::<8>::ONE));
198        assert!(Decimal::<9>::eq_one(&Decimal::<9>::ONE));
199        assert!(!Decimal::<0>::eq_one(&Decimal::<0>::ZERO));
200        assert!(!Decimal::<1>::eq_one(&Decimal::<1>::ZERO));
201        assert!(!Decimal::<2>::eq_one(&Decimal::<2>::ZERO));
202        assert!(!Decimal::<3>::eq_one(&Decimal::<3>::ZERO));
203        assert!(!Decimal::<4>::eq_one(&Decimal::<4>::ZERO));
204        assert!(!Decimal::<5>::eq_one(&Decimal::<5>::ZERO));
205        assert!(!Decimal::<6>::eq_one(&Decimal::<6>::ZERO));
206        assert!(!Decimal::<7>::eq_one(&Decimal::<7>::ZERO));
207        assert!(!Decimal::<8>::eq_one(&Decimal::<8>::ZERO));
208        assert!(!Decimal::<9>::eq_one(&Decimal::<9>::ZERO));
209    }
210}
211
212// Implementing the symmetric comparison using the following macros also for
213// `u8` causes a compiler error[E0391]:
214// cycle detected when building an abstract representation for the const
215// argument ...
216// So for now it's left out.
217// TODO: check with next rustc version!
218
219macro_rules! impl_decimal_eq_uint {
220    () => {
221        impl_decimal_eq_uint!(u16, u32, u64);
222    };
223    ($($t:ty),*) => {
224        $(
225        impl<const P: u8> PartialEq<$t> for Decimal<P>
226        where
227            PrecLimitCheck<{ P <= MAX_PREC }>: True,
228        {
229            #[inline(always)]
230            fn eq(&self, other: &$t) -> bool {
231                if self.is_negative() {
232                    return false;
233                }
234                match checked_mul_pow_ten((*other) as i128, P) {
235                    Some(coeff) => self.coeff == coeff,
236                    None => false,
237                }
238            }
239        }
240        )*
241    }
242}
243
244impl_decimal_eq_uint!();
245
246macro_rules! impl_decimal_eq_signed_int {
247    () => {
248        impl_decimal_eq_signed_int!(i8, i16, i32, i64, i128);
249    };
250    ($($t:ty),*) => {
251        $(
252        impl<const P: u8> PartialEq<$t> for Decimal<P>
253        where
254            PrecLimitCheck<{ P <= MAX_PREC }>: True,
255        {
256            #[inline(always)]
257            fn eq(&self, other: &$t) -> bool {
258                match checked_mul_pow_ten((*other) as i128, P) {
259                    Some(coeff) => self.coeff == coeff,
260                    None => false,
261                }
262            }
263        }
264        )*
265    }
266}
267
268impl_decimal_eq_signed_int!();
269
270macro_rules! impl_int_eq_decimal {
271    () => {
272        impl_int_eq_decimal!(i8, u16, i16, u32, i32, u64, i64, i128);
273    };
274    ($($t:ty),*) => {
275        $(
276        impl<const Q: u8> PartialEq<Decimal<Q>> for $t
277        where
278            PrecLimitCheck<{ Q <= MAX_PREC }>: True,
279            Decimal<Q>: PartialEq<$t>,
280        {
281            #[inline(always)]
282            fn eq(&self, other: &Decimal<Q>) -> bool {
283                PartialEq::eq(other, self)
284            }
285        }
286        )*
287    }
288}
289
290impl_int_eq_decimal!();
291
292macro_rules! impl_decimal_cmp_signed_int {
293    () => {
294        impl_decimal_cmp_signed_int!(i8, i16, i32, i64, i128);
295    };
296    ($($t:ty),*) => {
297        $(
298        impl<const P: u8> PartialOrd<$t> for Decimal<P>
299        where
300            PrecLimitCheck<{ P <= MAX_PREC }>: True,
301        {
302            #[inline(always)]
303            fn partial_cmp(&self, other: &$t) -> Option<Ordering> {
304                match checked_mul_pow_ten((*other) as i128, P) {
305                    Some(coeff) => self.coeff.partial_cmp(&coeff),
306                    None => {
307                        if *other >= 0 {
308                            Some(Ordering::Less)
309                        } else {
310                            Some(Ordering::Greater)
311                        }
312                    },
313                }
314            }
315        }
316        )*
317    }
318}
319
320impl_decimal_cmp_signed_int!();
321
322macro_rules! impl_signed_int_cmp_decimal {
323    () => {
324        impl_signed_int_cmp_decimal!(i8, i16, i32, i64, i128);
325    };
326    ($($t:ty),*) => {
327        $(
328        impl<const Q: u8> PartialOrd<Decimal<Q>> for $t
329        where
330            PrecLimitCheck<{ Q <= MAX_PREC }>: True,
331        {
332            #[inline(always)]
333            fn partial_cmp(&self, other: &Decimal<Q>) -> Option<Ordering> {
334                match checked_mul_pow_ten((*self) as i128, Q) {
335                    Some(coeff) => coeff.partial_cmp(&other.coeff),
336                    None => {
337                        if *self < 0 {
338                            Some(Ordering::Less)
339                        } else {
340                            Some(Ordering::Greater)
341                        }
342                    },
343                }
344            }
345        }
346        )*
347    }
348}
349
350impl_signed_int_cmp_decimal!();
351
352macro_rules! impl_decimal_cmp_uint {
353    () => {
354        impl_decimal_cmp_uint!(u16, u32, u64);
355    };
356    ($($t:ty),*) => {
357        $(
358        impl<const P: u8> PartialOrd<$t> for Decimal<P>
359        where
360            PrecLimitCheck<{ P <= MAX_PREC }>: True,
361        {
362            #[inline(always)]
363            fn partial_cmp(&self, other: &$t) -> Option<Ordering> {
364                if self.is_negative() {
365                    return Some(Ordering::Less);
366                }
367                match checked_mul_pow_ten((*other) as i128, P) {
368                    Some(coeff) => self.coeff.partial_cmp(&coeff),
369                    None => Some(Ordering::Less),
370                }
371            }
372        }
373        )*
374    }
375}
376
377impl_decimal_cmp_uint!();
378
379macro_rules! impl_uint_cmp_decimal {
380    () => {
381        impl_uint_cmp_decimal!(u16, u32, u64);
382    };
383    ($($t:ty),*) => {
384        $(
385        impl<const Q: u8> PartialOrd<Decimal<Q>> for $t
386        where
387            PrecLimitCheck<{ Q <= MAX_PREC }>: True,
388        {
389            #[inline(always)]
390            fn partial_cmp(&self, other: &Decimal<Q>) -> Option<Ordering> {
391                if other.is_negative() {
392                    return Some(Ordering::Greater);
393                }
394                match checked_mul_pow_ten((*self) as i128, Q) {
395                    Some(coeff) => coeff.partial_cmp(&other.coeff),
396                    None => Some(Ordering::Greater),
397                }
398            }
399        }
400        )*
401    }
402}
403
404impl_uint_cmp_decimal!();
405
406#[cfg(test)]
407mod cmp_decimals_and_ints_tests {
408    use std::cmp::Ordering;
409
410    use crate::Decimal;
411
412    #[test]
413    fn test_eq() {
414        let x = Decimal::<1>::new_raw(170);
415        assert!(x.eq(&x));
416        let y = 17_u32;
417        assert!(x.eq(&y));
418        assert!(y.eq(&x));
419        let y = 17_i128;
420        assert_eq!(x, y);
421        assert_eq!(y, x);
422    }
423
424    #[test]
425    fn test_ne() {
426        let x = Decimal::<7>::new_raw(-178000);
427        let y = 0_i8;
428        assert_ne!(x, y);
429        assert_eq!(x.partial_cmp(&y), Some(Ordering::Less));
430        assert!(x < y);
431        assert!(y > x);
432        let y = 1_u32;
433        assert_ne!(x, y);
434        assert_eq!(x.partial_cmp(&y), Some(Ordering::Less));
435        assert!(x < y);
436        assert!(y > x);
437        let x = Decimal::<1>::new_raw(178);
438        let y = 18_u64;
439        assert_ne!(x, y);
440        assert!(x <= y);
441        assert!(y >= x);
442    }
443}