i256/shared/
ord.rs

1//! Partial and total ordering.
2
3// Due to wide comparison optimizations, this can be done in 1
4// comparison on `x86_64` with the `u128` type, so we can
5// optimize this quite nicely.
6macro_rules! define {
7    (
8        @ord
9        $lhs:ident,
10        $rhs:ident,
11        low_type => $lo_t:ty,
12        high_type => $hi_t:ty,
13        op1 => $op1:tt ,
14        op2 => $op2:tt $(,)?
15    ) => {{
16        // The implied methods that are identical between short and non-circuiting options.
17        let lhs = $lhs.to_ne_wide();
18        let rhs = $rhs.to_ne_wide();
19
20        let mut i = Self::WIDE - 1;
21        let lhs_0 = ne_index!(lhs[i]) as $hi_t;
22        let rhs_0 = ne_index!(rhs[i]) as $hi_t;
23        let mut is_ord = lhs_0 $op1 rhs_0;
24        let mut is_eq = lhs_0 == rhs_0;
25
26        while i > 0 && !is_ord && is_eq {
27            i -= 1;
28            let lhs_i = ne_index!(lhs[i]) as $lo_t;
29            let rhs_i = ne_index!(rhs[i]) as $lo_t;
30            // NOTE: `<=/>=` OP only should be done in the last loop
31            is_ord = if i == 0 {
32                lhs_i $op2 rhs_i
33            } else {
34                lhs_i $op1 rhs_i
35            };
36            is_eq = lhs_i == rhs_i;
37        }
38        is_ord
39    }};
40
41    (
42        low_type => $lo_t:ty,
43        high_type => $hi_t:ty $(,)?
44    ) => {
45        /// Non-short circuiting const implementation of [`Eq`].
46        #[inline(always)]
47        const fn eq_branchless(self, rhs: Self) -> bool {
48            let lhs = self.to_ne_wide();
49            let rhs = rhs.to_ne_wide();
50            let mut is_eq = true;
51            let mut i = 0;
52            while i < Self::WIDE {
53                // NOTE: This can be in any order
54                is_eq &= (lhs[i] == rhs[i]);
55                i += 1;
56            }
57            is_eq
58        }
59
60        /// Short-circuiting const implementation of [`Eq`].
61        #[inline(always)]
62        pub const fn eq_branched(self, rhs: Self) -> bool {
63            let lhs = self.to_ne_wide();
64            let rhs = rhs.to_ne_wide();
65            let mut is_eq = true;
66            let mut i = 0;
67            while i < Self::WIDE && is_eq {
68                is_eq &= (lhs[i] == rhs[i]);
69                i += 1;
70            }
71            is_eq
72        }
73
74        /// Non-short circuiting const implementation of [`Eq`].
75        #[inline(always)]
76        pub const fn eq_const(self, rhs: Self) -> bool {
77            if Self::BITS < 4096 {
78                self.eq_branchless(rhs)
79            } else {
80                self.eq_branched(rhs)
81            }
82        }
83
84        // NOTE: Because of two's complement, these comparisons are always normal.
85        // This can always be implemented in terms of the highest wide bit, then the
86        // rest as low.
87
88        /// Non-short circuiting const implementation of [`PartialOrd::lt`].
89        #[inline(always)]
90        #[must_use]
91        pub const fn lt_const(self, rhs: Self) -> bool {
92            $crate::shared::ord::define!(
93                @ord
94                self,
95                rhs,
96                low_type => $lo_t,
97                high_type => $hi_t,
98                op1 => <,
99                op2 => <,
100            )
101        }
102
103        /// Non-short circuiting const implementation of [`PartialOrd::le`].
104        #[must_use]
105        #[inline(always)]
106        pub const fn le_const(self, rhs: Self) -> bool {
107            $crate::shared::ord::define!(
108                @ord
109                self,
110                rhs,
111                low_type => $lo_t,
112                high_type => $hi_t,
113                op1 => <,
114                op2 => <=,
115            )
116        }
117
118        /// Non-short circuiting const implementation of [`PartialOrd::gt`].
119        #[inline(always)]
120        pub const fn gt_const(self, rhs: Self) -> bool {
121            !self.le_const(rhs)
122        }
123
124        /// Non-short circuiting const implementation of [`PartialOrd::ge`].
125        #[inline(always)]
126        pub const fn ge_const(self, rhs: Self) -> bool {
127            !self.lt_const(rhs)
128        }
129
130        /// Non-short circuiting const implementation of [`Ord::cmp`].
131        #[inline(always)]
132        pub const fn cmp_const(self, rhs: Self) -> core::cmp::Ordering {
133            let lhs = self.to_ne_wide();
134            let rhs = rhs.to_ne_wide();
135
136            let mut i = Self::WIDE - 1;
137            let lhs_0 = ne_index!(lhs[i]) as $hi_t;
138            let rhs_0 = ne_index!(rhs[i]) as $hi_t;
139            let mut is_eq = lhs_0 == rhs_0;
140            let mut is_lt = lhs_0 < rhs_0;
141            let mut is_gt = lhs_0 > rhs_0;
142
143            while i > 0 && !is_lt && !is_gt && is_eq {
144                i -= 1;
145                let lhs_i = ne_index!(lhs[i]) as $lo_t;
146                let rhs_i = ne_index!(rhs[i]) as $lo_t;
147                is_eq = lhs_i == rhs_i;
148                is_lt = lhs_i < rhs_i;
149                is_gt = lhs_i > rhs_i;
150            }
151
152            if is_lt {
153                core::cmp::Ordering::Less
154            } else if is_gt {
155                core::cmp::Ordering::Greater
156            } else {
157                core::cmp::Ordering::Equal
158            }
159        }
160    };
161}
162
163pub(crate) use define;
164
165macro_rules! traits {
166    ($t:ty $(,)?) => {
167        impl core::cmp::Ord for $t {
168            #[inline(always)]
169            fn cmp(&self, other: &Self) -> core::cmp::Ordering {
170                self.cmp_const(*other)
171            }
172        }
173
174        impl core::cmp::PartialOrd for $t {
175            #[inline(always)]
176            fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
177                Some(self.cmp(other))
178            }
179
180            #[inline(always)]
181            fn lt(&self, other: &Self) -> bool {
182                self.lt_const(*other)
183            }
184
185            #[inline(always)]
186            fn le(&self, other: &Self) -> bool {
187                self.le_const(*other)
188            }
189
190            #[inline(always)]
191            fn gt(&self, other: &Self) -> bool {
192                self.gt_const(*other)
193            }
194
195            #[inline(always)]
196            fn ge(&self, other: &Self) -> bool {
197                self.ge_const(*other)
198            }
199        }
200    };
201}
202
203pub(crate) use traits;