Skip to main content

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}