Skip to main content

nanvm_lib/vm/bigint/
cmp.rs

1use crate::vm::{IVm, bigint::BigInt};
2use std::cmp::Ordering;
3
4impl<A: IVm> PartialOrd for BigInt<A> {
5    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
6        Some(self.cmp(other))
7    }
8}
9
10/// The single owner of how two `BigInt`s relate: [`PartialEq`] (in
11/// `partial_eq.rs`) and [`PartialOrd`] both delegate here.
12///
13/// Invariant: both operands are normalized — no leading (most-significant)
14/// zero words, and zero is always `Sign::Positive` with no words (no negative
15/// zero). That is what makes the sign order sound (a negative value is always
16/// below a positive one) and what `abs_cmp_vec` asserts; a non-normalized
17/// operand panics.
18impl<A: IVm> Ord for BigInt<A> {
19    fn cmp(&self, rhs: &Self) -> Ordering {
20        use crate::sign::Sign;
21
22        let sign = self.sign();
23        sign.cmp(&rhs.sign()).then_with(|| match sign {
24            Sign::Positive => self.clone().abs_cmp_vec(rhs.clone()),
25            Sign::Negative => rhs.clone().abs_cmp_vec(self.clone()),
26        })
27    }
28}
29
30#[cfg(test)]
31mod tests {
32    use crate::vm::bigint::test_util::{T, int, neg, pos};
33    use std::cmp::Ordering;
34
35    #[test]
36    fn cmp_positive_vs_negative() {
37        let lhs: T = 1u64.into();
38        let rhs: T = (-1i64).into();
39        assert_eq!(lhs.cmp(&rhs), Ordering::Greater);
40        assert_eq!(rhs.cmp(&lhs), Ordering::Less);
41    }
42
43    #[test]
44    fn cmp_positive_by_absolute_value() {
45        let lhs: T = 5u64.into();
46        let rhs: T = 6u64.into();
47        assert_eq!(lhs.cmp(&rhs), Ordering::Less);
48        assert_eq!(rhs.cmp(&lhs), Ordering::Greater);
49    }
50
51    #[test]
52    fn cmp_negative_reverses_absolute_order() {
53        let lhs: T = (-5i64).into();
54        let rhs: T = (-6i64).into();
55        assert_eq!(lhs.cmp(&rhs), Ordering::Greater);
56        assert_eq!(rhs.cmp(&lhs), Ordering::Less);
57    }
58
59    #[test]
60    fn cmp_equal_values() {
61        let lhs: T = 0u64.into();
62        let rhs: T = 0u64.into();
63        assert_eq!(lhs.cmp(&rhs), Ordering::Equal);
64    }
65
66    /// Values covering the sign/zero cases: zero, ±1, single- and multiword
67    /// magnitudes of both signs, and pairs of equal magnitude with opposite
68    /// signs. Called twice so equal values come from distinct allocations.
69    fn cases() -> [T; 9] {
70        [
71            int(0),
72            int(1),
73            int(-1),
74            int(5),
75            int(-5),
76            pos([0, 1]),
77            neg([0, 1]),
78            pos([7, 1]),
79            neg([7, 1]),
80        ]
81    }
82
83    #[test]
84    fn eq_agrees_with_cmp() {
85        for (i, a) in cases().iter().enumerate() {
86            for (j, b) in cases().iter().enumerate() {
87                assert_eq!(a == b, a.cmp(b) == Ordering::Equal, "{a:?} vs {b:?}");
88                assert_eq!(a == b, i == j, "{a:?} vs {b:?}");
89                assert_eq!(a.partial_cmp(b), Some(a.cmp(b)), "{a:?} vs {b:?}");
90                assert_eq!(a.cmp(b), b.cmp(a).reverse(), "{a:?} vs {b:?}");
91            }
92        }
93        // Negating zero yields zero, not a negative zero.
94        let negated_zero = -int(0);
95        assert_eq!(negated_zero.cmp(&int(0)), Ordering::Equal);
96        assert!(negated_zero == int(0));
97    }
98}