Skip to main content

scratch_test_value/comparisons/
equality.rs

1use itertools::Itertools;
2
3use crate::{
4    QuirkSink, SNumber, SValue,
5    quirks::{SNumberToFloatQ, SValueToNumberQ},
6};
7
8impl SValue {
9    /// Compares two Scratch values for equality
10    ///
11    /// If any of the two values is a non-numeric text
12    /// (it can't be parsed as float or int) or a boolean,
13    /// both values will be converted to strings.
14    /// Those strings will then be compared for equality.
15    ///
16    /// Otherwise, the numeric equality is used as in [`SNumber::q_eq`].
17    pub fn q_eq<Q>(&self, other: &SValue, sink: &mut Q) -> bool
18    where
19        Q: QuirkSink<SValueToNumberQ> + QuirkSink<SNumberToFloatQ>,
20    {
21        if (self.is_number() || self.is_bool_but_not_as_text())
22            && (other.is_number() || other.is_bool_but_not_as_text())
23        {
24            let a = self.q_as_number(sink);
25            let b = other.q_as_number(sink);
26            a.q_eq(&b, sink)
27        } else {
28            let a = self.as_text();
29            let b = other.as_text();
30
31            for z in a.chars().zip_longest(b.chars()) {
32                match z {
33                    itertools::EitherOrBoth::Both(l, r) => {
34                        let l = l.to_lowercase().to_string();
35                        let r = r.to_lowercase().to_string();
36                        if l != r {
37                            return false;
38                        }
39                    }
40                    _ => return false,
41                }
42            }
43            true
44        }
45    }
46}
47
48impl SNumber {
49    /// Compares two Scratch numbers for equality
50    ///
51    /// - `[t]` `-∞`  == `-∞`         → true
52    /// - `[f]` `-∞`  == `+∞`         → false
53    /// - `[f]` `+∞`  == `-∞`         → false
54    /// - `[t]` `+∞`  == `+∞`         → true
55    /// - `[t]` `NaN` == `NaN`        → true
56    /// - `[f]` `NaN` == number       → false
57    /// - `[f]` number == `NaN`       → false
58    ///
59    /// If both values are *integers*, integer equality is used.
60    /// If both values are *finite floating point numbers*,
61    /// floating point equality is used.
62    ///
63    /// `NaN` is only equal to `NaN`.
64    ///
65    /// (short value in `[]` for easy reference)
66    pub fn q_eq<Q>(&self, other: &SNumber, sink: &mut Q) -> bool
67    where
68        Q: QuirkSink<SNumberToFloatQ>,
69    {
70        if let (SNumber::Int(a), SNumber::Int(b)) = (self, other) {
71            return a == b;
72        }
73        let a = self.q_as_float(sink);
74        let b = other.q_as_float(sink);
75
76        if a.is_infinite() || b.is_infinite() {
77            return a.is_infinite()
78                && b.is_infinite()
79                && a.is_sign_positive() == b.is_sign_positive();
80        }
81        if a.is_nan() && b.is_nan() {
82            return true;
83        }
84        if a.is_nan() ^ b.is_nan() {
85            return false;
86        }
87        assert!(a.is_finite() && b.is_finite());
88        a == b
89    }
90}