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}