1use crate::raw_term::{RawTermGeneralType, RawTermType};
2use crate::RawTerm;
3use crate::Term;
4
5use std::cmp::{Ord, Ordering};
6
7use num_bigint::BigInt;
8
9impl Eq for Term {}
10
11impl Ord for Term {
12 fn cmp(&self, other: &Self) -> Ordering {
13 self.partial_cmp(other).expect("total order not found")
14 }
15}
16
17impl PartialOrd for Term {
18 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
19 if self == other {
20 return Some(Ordering::Equal);
21 }
22
23 let left_type = self.as_general_type();
24 let right_type = other.as_general_type();
25
26 if left_type != right_type {
27 return left_type.partial_cmp(&right_type);
28 }
29
30 RawTerm::from(self.clone()).partial_cmp(&RawTerm::from(other.clone()))
31 }
32}
33
34impl RawTermType {
35 pub fn type_cmp(&self, other: &Self) -> Option<Ordering> {
39 if self == other {
40 return Some(Ordering::Equal);
41 }
42
43 let left_type = RawTermGeneralType::from(self);
44 let right_type = RawTermGeneralType::from(other);
45
46 left_type.partial_cmp(&right_type)
47 }
48}
49
50impl Eq for RawTerm {}
51
52impl Ord for RawTerm {
53 fn cmp(&self, other: &Self) -> Ordering {
54 self.partial_cmp(other).expect("total order not found")
55 }
56}
57
58impl PartialOrd for RawTerm {
59 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
60 if self == other {
61 return Some(Ordering::Equal);
62 }
63
64 let left_type = self.as_general_type();
65 let right_type = other.as_general_type();
66
67 if left_type != right_type {
68 return left_type.partial_cmp(&right_type);
69 }
70
71 match left_type {
72 RawTermGeneralType::Atom => {
73 atom_cmp(self, other).or_else(|| atom_cmp(other, self).map(Ordering::reverse))
74 }
75 RawTermGeneralType::Number => {
76 number_cmp(self, other).or_else(|| number_cmp(other, self).map(Ordering::reverse))
77 }
78 RawTermGeneralType::Reference => ref_cmp(self, other),
79 RawTermGeneralType::Pid => pid_cmp(self, other),
80 RawTermGeneralType::Port => port_cmp(self, other),
81 RawTermGeneralType::BitString => bitstring_cmp(self, other),
82 RawTermGeneralType::List => list_cmp(self, other),
83 RawTermGeneralType::Tuple => tuple_cmp(self, other),
84 RawTermGeneralType::Map => map_cmp(self, other),
85 RawTermGeneralType::Fun => func_cmp(self, other),
86 RawTermGeneralType::Nil => Some(Ordering::Equal),
87 _ => panic!(),
88 }
89 }
90}
91
92fn atom_cmp(left: &RawTerm, right: &RawTerm) -> Option<Ordering> {
93 use RawTerm::*;
94
95 match (left, right) {
96 (Atom(a), Atom(b)) => a.partial_cmp(b),
97 (Atom(a), SmallAtom(b)) => a.partial_cmp(b),
98 (Atom(a), AtomDeprecated(b)) => a.partial_cmp(b),
99 (Atom(a), SmallAtomDeprecated(b)) => a.partial_cmp(b),
100 (SmallAtom(a), SmallAtom(b)) => a.partial_cmp(b),
101 (SmallAtom(a), AtomDeprecated(b)) => a.partial_cmp(b),
102 (SmallAtom(a), SmallAtomDeprecated(b)) => a.partial_cmp(b),
103 (AtomDeprecated(a), AtomDeprecated(b)) => a.partial_cmp(b),
104 (AtomDeprecated(a), SmallAtomDeprecated(b)) => a.partial_cmp(b),
105 (SmallAtomDeprecated(a), SmallAtomDeprecated(b)) => a.partial_cmp(b),
106 _ => None,
107 }
108}
109
110fn number_cmp(left: &RawTerm, right: &RawTerm) -> Option<Ordering> {
111 use std::convert::TryInto;
112 use RawTerm::*;
113
114 match (left, right) {
115 (SmallInt(a), SmallInt(b)) => a.partial_cmp(b),
116
117 (SmallInt(_a), Int(b)) if b > &(u8::MAX as i32) => Some(Ordering::Less),
118 (SmallInt(_a), Int(b)) if b < &(u8::MIN as i32) => Some(Ordering::Greater),
119 (SmallInt(a), Int(b)) => a.partial_cmp(&(*b as u8)),
120
121 (SmallInt(_a), SmallBigInt(b)) if b > &BigInt::from(u8::MAX) => Some(Ordering::Less),
122 (SmallInt(_a), SmallBigInt(b)) if b < &BigInt::from(u8::MIN) => Some(Ordering::Greater),
123 (SmallInt(a), SmallBigInt(b)) => a.partial_cmp(&(b.clone().try_into().unwrap())),
124
125 (SmallInt(_a), LargeBigInt(b)) if b > &BigInt::from(u8::MAX) => Some(Ordering::Less),
126 (SmallInt(_a), LargeBigInt(b)) if b < &BigInt::from(u8::MIN) => Some(Ordering::Greater),
127 (SmallInt(a), LargeBigInt(b)) => a.partial_cmp(&(b.clone().try_into().unwrap())),
128
129 (Int(a), Int(b)) => a.partial_cmp(b),
130
131 (Int(_a), SmallBigInt(b)) if b > &BigInt::from(i32::MAX) => Some(Ordering::Less),
132 (Int(_a), SmallBigInt(b)) if b < &BigInt::from(i32::MIN) => Some(Ordering::Greater),
133 (Int(a), SmallBigInt(b)) => a.partial_cmp(&(b.clone().try_into().unwrap())),
134
135 (Int(_a), LargeBigInt(b)) if b > &BigInt::from(i32::MAX) => Some(Ordering::Less),
136 (Int(_a), LargeBigInt(b)) if b < &BigInt::from(i32::MIN) => Some(Ordering::Greater),
137 (Int(a), LargeBigInt(b)) => a.partial_cmp(&(b.clone().try_into().unwrap())),
138
139 (SmallBigInt(a), SmallBigInt(b)) => a.partial_cmp(b),
140 (SmallBigInt(a), LargeBigInt(b)) => a.partial_cmp(b),
141 (LargeBigInt(a), LargeBigInt(b)) => a.partial_cmp(b),
142
143 (Float(a), Float(b)) => a.partial_cmp(b),
144 _ => None,
145 }
146}
147
148fn tuple_cmp(left: &RawTerm, right: &RawTerm) -> Option<Ordering> {
149 use RawTerm::*;
150
151 match (left, right) {
152 (SmallTuple(a), SmallTuple(b)) => a.partial_cmp(b),
153 (LargeTuple(a), SmallTuple(b)) => a.partial_cmp(b),
154 (SmallTuple(a), LargeTuple(b)) => a.partial_cmp(b),
155 (LargeTuple(a), LargeTuple(b)) => a.partial_cmp(b),
156 _ => None,
157 }
158}
159
160fn ref_cmp(left: &RawTerm, right: &RawTerm) -> Option<Ordering> {
161 use RawTerm::*;
162
163 match (left, right) {
164 (
165 Ref {
166 creation: creation_a,
167 id: id_a,
168 ..
169 },
170 Ref {
171 creation: creation_b,
172 id: id_b,
173 ..
174 },
175 ) => (creation_a, id_a).partial_cmp(&(creation_b, id_b)),
176 (
177 NewerRef {
178 creation: creation_a,
179 id: id_a,
180 ..
181 },
182 NewerRef {
183 creation: creation_b,
184 id: id_b,
185 ..
186 },
187 ) => (creation_a, id_a).partial_cmp(&(creation_b, id_b)),
188 (
189 NewerRef {
190 creation: creation_a,
191 id: id_a,
192 ..
193 },
194 Ref {
195 creation: creation_b,
196 id: id_b,
197 ..
198 },
199 ) => (creation_a, id_a).partial_cmp(&(&(*creation_b as u32), id_b)),
200 (
201 Ref {
202 creation: creation_a,
203 id: id_a,
204 ..
205 },
206 NewerRef {
207 creation: creation_b,
208 id: id_b,
209 ..
210 },
211 ) => (&(*creation_a as u32), id_a).partial_cmp(&(creation_b, id_b)),
212 _ => None,
213 }
214}
215
216fn pid_cmp(left: &RawTerm, right: &RawTerm) -> Option<Ordering> {
217 use RawTerm::*;
218
219 match (left, right) {
220 (
221 NewPid {
222 id: id_a,
223 serial: serial_a,
224 creation: creation_a,
225 ..
226 },
227 NewPid {
228 id: id_b,
229 serial: serial_b,
230 creation: creation_b,
231 ..
232 },
233 ) => (creation_a, id_a, serial_a).partial_cmp(&(creation_b, id_b, serial_b)),
234 (
235 Pid {
236 id: id_a,
237 serial: serial_a,
238 creation: creation_a,
239 ..
240 },
241 Pid {
242 id: id_b,
243 serial: serial_b,
244 creation: creation_b,
245 ..
246 },
247 ) => (creation_a, id_a, serial_a).partial_cmp(&(creation_b, id_b, serial_b)),
248 (
249 NewPid {
250 id: id_a,
251 serial: serial_a,
252 creation: creation_a,
253 ..
254 },
255 Pid {
256 id: id_b,
257 serial: serial_b,
258 creation: creation_b,
259 ..
260 },
261 ) => (creation_a, id_a, serial_a).partial_cmp(&(&(*creation_b as u32), id_b, serial_b)),
262 (
263 Pid {
264 id: id_a,
265 serial: serial_a,
266 creation: creation_a,
267 ..
268 },
269 NewPid {
270 id: id_b,
271 serial: serial_b,
272 creation: creation_b,
273 ..
274 },
275 ) => (&(*creation_a as u32), id_a, serial_a).partial_cmp(&(creation_b, id_b, serial_b)),
276 _ => None,
277 }
278}
279
280fn port_cmp(left: &RawTerm, right: &RawTerm) -> Option<Ordering> {
281 use RawTerm::*;
282
283 match (left, right) {
284 (
285 NewPort {
286 id: id_a,
287 creation: creation_a,
288 ..
289 },
290 NewPort {
291 id: id_b,
292 creation: creation_b,
293 ..
294 },
295 ) => (creation_a, id_a).partial_cmp(&(creation_b, id_b)),
296 (
297 Port {
298 id: id_a,
299 creation: creation_a,
300 ..
301 },
302 Port {
303 id: id_b,
304 creation: creation_b,
305 ..
306 },
307 ) => (creation_a, id_a).partial_cmp(&(creation_b, id_b)),
308 (
309 NewPort {
310 id: id_a,
311 creation: creation_a,
312 ..
313 },
314 Port {
315 id: id_b,
316 creation: creation_b,
317 ..
318 },
319 ) => (creation_a, id_a).partial_cmp(&(&(*creation_b as u32), id_b)),
320 (
321 Port {
322 id: id_a,
323 creation: creation_a,
324 ..
325 },
326 NewPort {
327 id: id_b,
328 creation: creation_b,
329 ..
330 },
331 ) => (&(*creation_a as u32), id_a).partial_cmp(&(creation_b, id_b)),
332 _ => None,
333 }
334}
335
336fn bitstring_cmp(left: &RawTerm, right: &RawTerm) -> Option<Ordering> {
337 use RawTerm::*;
338
339 match (left, right) {
340 (String(a), String(b)) => a.partial_cmp(b),
341 (Binary(a), String(b)) => a.partial_cmp(b),
342 (Binary(a), Binary(b)) => a.partial_cmp(b),
343 (String(a), Binary(b)) => a.partial_cmp(b),
344 _ => None,
345 }
346}
347
348fn list_cmp(left: &RawTerm, right: &RawTerm) -> Option<Ordering> {
349 use RawTerm::*;
350
351 match (left, right) {
352 (List(a), List(b)) => a.partial_cmp(b),
353 _ => None,
354 }
355}
356
357fn map_cmp(left: &RawTerm, right: &RawTerm) -> Option<Ordering> {
358 use RawTerm::*;
359
360 match (left, right) {
361 (Map(a), Map(b)) => a.partial_cmp(b),
362 _ => None,
363 }
364}
365
366fn func_cmp(left: &RawTerm, right: &RawTerm) -> Option<Ordering> {
367 match (left, right) {
370 (
371 RawTerm::Function {
372 size: a1,
373 arity: b1,
374 uniq: c1,
375 index: d1,
376 module: e1,
377 old_index: f1,
378 old_uniq: g1,
379 pid: h1,
380 free_var: i1,
381 },
382 RawTerm::Function {
383 size: a2,
384 arity: b2,
385 uniq: c2,
386 index: d2,
387 module: e2,
388 old_index: f2,
389 old_uniq: g2,
390 pid: h2,
391 free_var: i2,
392 },
393 ) => {
394 (a1, b1, c1, d1, e1, f1, g1, h1, i1).partial_cmp(&(a2, b2, c2, d2, e2, f2, g2, h2, i2))
395 }
396
397 _ => None,
398 }
399}
400
401#[test]
402fn compare_pids() {
403 let a = RawTerm::NewPid {
404 creation: 1,
405 id: 2,
406 serial: 3,
407 node: Box::new(RawTerm::AtomDeprecated("test".to_string())),
408 };
409 let b = RawTerm::Pid {
410 creation: 1,
411 id: 2,
412 serial: 4,
413 node: Box::new(RawTerm::AtomDeprecated("test".to_string())),
414 };
415 let c = RawTerm::NewPid {
416 creation: 1,
417 id: 3,
418 serial: 3,
419 node: Box::new(RawTerm::AtomDeprecated("test".to_string())),
420 };
421 let d = RawTerm::Pid {
422 creation: 4,
423 id: 2,
424 serial: 4,
425 node: Box::new(RawTerm::AtomDeprecated("test".to_string())),
426 };
427
428 assert_eq!(Some(Ordering::Less), a.partial_cmp(&b));
429 assert_eq!(Some(Ordering::Greater), c.partial_cmp(&a));
430 assert_eq!(Some(Ordering::Greater), d.partial_cmp(&b));
431}
432
433#[test]
434fn ordering_test() {
435 let mut map = std::collections::BTreeMap::new();
436
437 map.insert(
438 RawTerm::Int(2),
439 RawTerm::List(vec![RawTerm::Atom(String::from("test"))]),
440 );
441 map.insert(
442 RawTerm::Int(4),
443 RawTerm::List(vec![RawTerm::Atom(String::from("testing"))]),
444 );
445 map.insert(
446 RawTerm::Binary(vec![123, 50, 90]),
447 RawTerm::List(vec![RawTerm::Atom(String::from("ordering"))]),
448 );
449 map.insert(
450 RawTerm::NewPort {
451 creation: 123,
452 id: 1,
453 node: Box::new(RawTerm::AtomDeprecated(String::from("localhost"))),
454 },
455 RawTerm::List(vec![RawTerm::Atom(String::from("ordering"))]),
456 );
457
458 assert_eq!(
459 &RawTerm::List(vec![RawTerm::Atom(String::from("ordering"))]),
460 map.get(&RawTerm::Binary(vec![123, 50, 90])).unwrap()
461 )
462}