scratch_test_value/comparisons/ordering.rs
1use itertools::Itertools;
2
3use crate::{QuirkSink, SNumber, SValue, conversions::SNumberToFloatQ, quirks::SValueToNumberQ};
4
5impl SValue {
6 /// different unicode encodings of similar looking characters?
7 pub fn q_lt<Q>(&self, other: &SValue, sink: &mut Q) -> bool
8 where
9 Q: QuirkSink<SNumberToFloatQ> + QuirkSink<SValueToNumberQ>,
10 {
11 if (self.is_number() || self.is_bool_but_not_as_text())
12 && (other.is_number() || other.is_bool_but_not_as_text())
13 {
14 self.q_as_number(sink).q_lt(&other.q_as_number(sink), sink)
15 } else {
16 use itertools::EitherOrBoth as E;
17 let a = self.as_text();
18 let b = other.as_text();
19 for z in a.chars().zip_longest(b.chars()) {
20 match z {
21 E::Both(l, r) => {
22 let l = l.to_lowercase().to_string();
23 let r = r.to_lowercase().to_string();
24 if l != r {
25 return l < r;
26 }
27 }
28 E::Left(_) => return false,
29 E::Right(_) => return true,
30 }
31 }
32 false
33 }
34 }
35
36 /// like [`Self::q_lt`] with swapped arguments
37 pub fn q_gt<Q>(&self, other: &SValue, sink: &mut Q) -> bool
38 where
39 Q: QuirkSink<SNumberToFloatQ> + QuirkSink<SValueToNumberQ>,
40 {
41 other.q_lt(self, sink)
42 }
43}
44
45impl SNumber {
46 /// Compares two numbers and checks if the first is smaller than the second
47 ///
48 /// The ordering seems to be: `-∞` < number < `+∞` < `NaN` < `NaN`
49 /// (`NaN` is smaller than itself)
50 ///
51 /// - `[t]` `NaN` < `NaN` → true
52 /// - `[f]` `NaN` < `±∞` → false
53 /// - `[f]` `NaN` < number → false
54 /// - `[t]` `-∞` < `NaN` → true
55 /// - `[f]` `-∞` < `-∞` → false
56 /// - `[t]` `-∞` < `+∞` → true
57 /// - `[t]` `-∞` < number → true
58 /// - `[t]` `+∞` < `NaN` → true
59 /// - `[f]` `+∞` < other than NaN → false
60 /// - `[t]` number < `NaN` → true
61 /// - `[t]` number < `+∞` → true
62 /// - `[f]` number < `-∞` → false
63 ///
64 /// (short value in `[]` for easy reference)
65 pub fn q_lt<Q>(&self, other: &SNumber, sink: &mut Q) -> bool
66 where
67 Q: QuirkSink<SNumberToFloatQ>,
68 {
69 if let (Self::Int(a), Self::Int(b)) = (self, other) {
70 a < b
71 } else {
72 let a = self.q_as_float(sink);
73 let b = other.q_as_float(sink);
74 if a.is_nan() {
75 return if b.is_nan() {
76 true
77 } else if b.is_infinite() {
78 false
79 } else {
80 assert!(b.is_finite());
81 false
82 };
83 }
84 // a is finite or infinite
85 if a.is_infinite() && a.is_sign_negative() {
86 return if b.is_nan() {
87 true
88 } else if b.is_infinite() {
89 a.is_sign_positive()
90 } else {
91 assert!(b.is_finite());
92 true
93 };
94 } else if a.is_infinite() && a.is_sign_positive() {
95 // only NaN is bigger than +∞
96 return b.is_nan();
97 }
98 // a is finite
99 if b.is_nan() {
100 true
101 } else if b.is_infinite() {
102 b.is_sign_positive()
103 } else {
104 a < b
105 }
106 }
107 }
108 /// like [`Self::q_lt`] with swapped arguments
109 pub fn q_gt<Q>(&self, other: &SNumber, sink: &mut Q) -> bool
110 where
111 Q: QuirkSink<SNumberToFloatQ>,
112 {
113 other.q_lt(self, sink)
114 }
115}