Skip to main content

libmagic_rs/evaluator/operators/
equality.rs

1// Copyright (c) 2025-2026 the libmagic-rs contributors
2// SPDX-License-Identifier: Apache-2.0
3
4//! Equality and inequality operators for magic rule evaluation
5
6use std::cmp::Ordering;
7
8use crate::parser::ast::Value;
9
10use super::compare_values;
11
12/// Machine-epsilon threshold used when comparing `Value::Float` operands for
13/// equality.  Two floats are considered equal when `|a - b| <= FLOAT_EPSILON`.
14/// Special values (NaN, infinity) are handled explicitly before the epsilon
15/// check.
16const FLOAT_EPSILON: f64 = f64::EPSILON;
17
18/// Return `true` when two `f64` values are considered equal under
19/// epsilon-aware semantics.
20///
21/// * **NaN**: NaN is never equal to anything (including itself).
22/// * **Infinity**: positive/negative infinity are only equal to the same sign
23///   of infinity.
24/// * **Finite**: `|a - b| <= FLOAT_EPSILON`.
25fn floats_equal(a: f64, b: f64) -> bool {
26    if a.is_nan() || b.is_nan() {
27        return false;
28    }
29    // Infinities: equal only when same sign (inf - inf = NaN, so must check first).
30    // Exact comparison is correct here -- infinities have precise IEEE 754 bit patterns.
31    if a.is_infinite() || b.is_infinite() {
32        #[allow(clippy::float_cmp)]
33        return a == b;
34    }
35    (a - b).abs() <= FLOAT_EPSILON
36}
37
38/// Apply equality comparison between two values
39///
40/// Compares two `Value` instances for equality, handling proper type matching.
41/// Cross-type integer comparisons (`Uint` vs `Int`) are supported via `i128`
42/// coercion.  Float comparisons use epsilon-aware equality
43/// (`|a - b| <= f64::EPSILON`).  Incompatible types (e.g., string vs integer)
44/// are considered unequal.
45///
46/// # Arguments
47///
48/// * `left` - The left-hand side value (typically from file data)
49/// * `right` - The right-hand side value (typically from magic rule)
50///
51/// # Returns
52///
53/// `true` if the values are equal (including cross-type integer coercion and
54/// epsilon-aware float comparison), `false` otherwise
55///
56/// # Examples
57///
58/// ```
59/// use libmagic_rs::parser::ast::Value;
60/// use libmagic_rs::evaluator::operators::apply_equal;
61///
62/// // Same type, same value
63/// assert!(apply_equal(&Value::Uint(42), &Value::Uint(42)));
64///
65/// // Same type, different value
66/// assert!(!apply_equal(&Value::Uint(42), &Value::Uint(24)));
67///
68/// // Cross-type integer coercion
69/// assert!(apply_equal(&Value::Uint(42), &Value::Int(42)));
70///
71/// // String comparison
72/// assert!(apply_equal(
73///     &Value::String("hello".to_string()),
74///     &Value::String("hello".to_string())
75/// ));
76///
77/// // Float epsilon-aware equality
78/// assert!(apply_equal(&Value::Float(1.0), &Value::Float(1.0)));
79/// ```
80#[must_use]
81pub fn apply_equal(left: &Value, right: &Value) -> bool {
82    if let (Value::Float(a), Value::Float(b)) = (left, right) {
83        return floats_equal(*a, *b);
84    }
85    compare_values(left, right) == Some(Ordering::Equal)
86}
87
88/// Apply inequality comparison between two values
89///
90/// Compares two `Value` instances for inequality, implementing the negation
91/// of equality comparison logic. Returns `true` if the values are not equal
92/// or are of different types.
93///
94/// # Arguments
95///
96/// * `left` - The left-hand side value (typically from file data)
97/// * `right` - The right-hand side value (typically from magic rule)
98///
99/// # Returns
100///
101/// `true` if the values are not equal or of different types, `false` if they are equal
102///
103/// # Examples
104///
105/// ```
106/// use libmagic_rs::parser::ast::Value;
107/// use libmagic_rs::evaluator::operators::apply_not_equal;
108///
109/// // Same type, different value
110/// assert!(apply_not_equal(&Value::Uint(42), &Value::Uint(24)));
111///
112/// // Same type, same value
113/// assert!(!apply_not_equal(&Value::Uint(42), &Value::Uint(42)));
114///
115/// // Cross-type integers with same numeric value (equal via coercion)
116/// assert!(!apply_not_equal(&Value::Uint(42), &Value::Int(42)));
117///
118/// // String comparison
119/// assert!(apply_not_equal(
120///     &Value::String("hello".to_string()),
121///     &Value::String("world".to_string())
122/// ));
123/// ```
124#[must_use]
125pub fn apply_not_equal(left: &Value, right: &Value) -> bool {
126    !apply_equal(left, right)
127}
128
129#[cfg(test)]
130mod tests {
131    use super::*;
132
133    #[test]
134    fn test_apply_equal_uint_same_value() {
135        let left = Value::Uint(42);
136        let right = Value::Uint(42);
137        assert!(apply_equal(&left, &right));
138    }
139
140    #[test]
141    fn test_apply_equal_uint_different_value() {
142        let left = Value::Uint(42);
143        let right = Value::Uint(24);
144        assert!(!apply_equal(&left, &right));
145    }
146
147    #[test]
148    fn test_apply_equal_uint_zero() {
149        let left = Value::Uint(0);
150        let right = Value::Uint(0);
151        assert!(apply_equal(&left, &right));
152    }
153
154    #[test]
155    fn test_apply_equal_uint_max_value() {
156        let left = Value::Uint(u64::MAX);
157        let right = Value::Uint(u64::MAX);
158        assert!(apply_equal(&left, &right));
159    }
160
161    #[test]
162    fn test_apply_equal_int_same_value() {
163        let left = Value::Int(42);
164        let right = Value::Int(42);
165        assert!(apply_equal(&left, &right));
166    }
167
168    #[test]
169    fn test_apply_equal_int_different_value() {
170        let left = Value::Int(42);
171        let right = Value::Int(-42);
172        assert!(!apply_equal(&left, &right));
173    }
174
175    #[test]
176    fn test_apply_equal_int_negative() {
177        let left = Value::Int(-100);
178        let right = Value::Int(-100);
179        assert!(apply_equal(&left, &right));
180    }
181
182    #[test]
183    fn test_apply_equal_int_zero() {
184        let left = Value::Int(0);
185        let right = Value::Int(0);
186        assert!(apply_equal(&left, &right));
187    }
188
189    #[test]
190    fn test_apply_equal_int_extreme_values() {
191        let left = Value::Int(i64::MAX);
192        let right = Value::Int(i64::MAX);
193        assert!(apply_equal(&left, &right));
194
195        let left = Value::Int(i64::MIN);
196        let right = Value::Int(i64::MIN);
197        assert!(apply_equal(&left, &right));
198    }
199
200    #[test]
201    fn test_apply_equal_bytes_same_value() {
202        let left = Value::Bytes(vec![0x7f, 0x45, 0x4c, 0x46]);
203        let right = Value::Bytes(vec![0x7f, 0x45, 0x4c, 0x46]);
204        assert!(apply_equal(&left, &right));
205    }
206
207    #[test]
208    fn test_apply_equal_bytes_different_value() {
209        let left = Value::Bytes(vec![0x7f, 0x45, 0x4c, 0x46]);
210        let right = Value::Bytes(vec![0x50, 0x4b, 0x03, 0x04]);
211        assert!(!apply_equal(&left, &right));
212    }
213
214    #[test]
215    fn test_apply_equal_bytes_empty() {
216        let left = Value::Bytes(vec![]);
217        let right = Value::Bytes(vec![]);
218        assert!(apply_equal(&left, &right));
219    }
220
221    #[test]
222    fn test_apply_equal_bytes_different_length() {
223        let left = Value::Bytes(vec![0x7f, 0x45]);
224        let right = Value::Bytes(vec![0x7f, 0x45, 0x4c, 0x46]);
225        assert!(!apply_equal(&left, &right));
226    }
227
228    #[test]
229    fn test_apply_equal_bytes_single_byte() {
230        let left = Value::Bytes(vec![0x7f]);
231        let right = Value::Bytes(vec![0x7f]);
232        assert!(apply_equal(&left, &right));
233
234        let left = Value::Bytes(vec![0x7f]);
235        let right = Value::Bytes(vec![0x45]);
236        assert!(!apply_equal(&left, &right));
237    }
238
239    #[test]
240    fn test_apply_equal_string_same_value() {
241        let left = Value::String("hello".to_string());
242        let right = Value::String("hello".to_string());
243        assert!(apply_equal(&left, &right));
244    }
245
246    #[test]
247    fn test_apply_equal_string_different_value() {
248        let left = Value::String("hello".to_string());
249        let right = Value::String("world".to_string());
250        assert!(!apply_equal(&left, &right));
251    }
252
253    #[test]
254    fn test_apply_equal_string_empty() {
255        let left = Value::String(String::new());
256        let right = Value::String(String::new());
257        assert!(apply_equal(&left, &right));
258    }
259
260    #[test]
261    fn test_apply_equal_string_case_sensitive() {
262        let left = Value::String("Hello".to_string());
263        let right = Value::String("hello".to_string());
264        assert!(!apply_equal(&left, &right));
265    }
266
267    #[test]
268    fn test_apply_equal_string_unicode() {
269        let left = Value::String("\u{1f980} Rust".to_string());
270        let right = Value::String("\u{1f980} Rust".to_string());
271        assert!(apply_equal(&left, &right));
272
273        let left = Value::String("\u{1f980} Rust".to_string());
274        let right = Value::String("\u{1f40d} Python".to_string());
275        assert!(!apply_equal(&left, &right));
276    }
277
278    #[test]
279    fn test_apply_equal_string_whitespace() {
280        let left = Value::String("hello world".to_string());
281        let right = Value::String("hello world".to_string());
282        assert!(apply_equal(&left, &right));
283
284        let left = Value::String("hello world".to_string());
285        let right = Value::String("hello  world".to_string()); // Extra space
286        assert!(!apply_equal(&left, &right));
287    }
288
289    // Cross-type comparison tests (should all return false)
290    #[test]
291    fn test_apply_equal_uint_vs_int() {
292        // Same numeric value across types should match
293        let left = Value::Uint(42);
294        let right = Value::Int(42);
295        assert!(apply_equal(&left, &right));
296
297        let left = Value::Uint(0);
298        let right = Value::Int(0);
299        assert!(apply_equal(&left, &right));
300
301        // Negative Int cannot equal Uint
302        let left = Value::Uint(42);
303        let right = Value::Int(-42);
304        assert!(!apply_equal(&left, &right));
305
306        // Large Uint that doesn't fit in i64 cannot equal Int
307        let left = Value::Uint(u64::MAX);
308        let right = Value::Int(-1);
309        assert!(!apply_equal(&left, &right));
310    }
311
312    #[test]
313    fn test_apply_equal_uint_vs_bytes() {
314        let left = Value::Uint(42);
315        let right = Value::Bytes(vec![42]);
316        assert!(!apply_equal(&left, &right));
317    }
318
319    #[test]
320    fn test_apply_equal_uint_vs_string() {
321        let left = Value::Uint(42);
322        let right = Value::String("42".to_string());
323        assert!(!apply_equal(&left, &right));
324    }
325
326    #[test]
327    fn test_apply_equal_int_vs_bytes() {
328        let left = Value::Int(-42);
329        let right = Value::Bytes(vec![214]); // -42 as u8
330        assert!(!apply_equal(&left, &right));
331    }
332
333    #[test]
334    fn test_apply_equal_int_vs_string() {
335        let left = Value::Int(-42);
336        let right = Value::String("-42".to_string());
337        assert!(!apply_equal(&left, &right));
338    }
339
340    #[test]
341    fn test_apply_equal_bytes_vs_string() {
342        let left = Value::Bytes(vec![104, 101, 108, 108, 111]); // "hello" as bytes
343        let right = Value::String("hello".to_string());
344        assert!(!apply_equal(&left, &right));
345    }
346
347    #[test]
348    fn test_apply_equal_all_cross_type_combinations() {
349        let values = [
350            Value::Uint(42),
351            Value::Int(42),
352            Value::Bytes(vec![42]),
353            Value::String("42".to_string()),
354        ];
355
356        // Test cross-type comparisons
357        for (i, left) in values.iter().enumerate() {
358            for (j, right) in values.iter().enumerate() {
359                if i != j {
360                    let result = apply_equal(left, right);
361                    // Uint(42) and Int(42) should be equal (cross-type coercion)
362                    if (i <= 1) && (j <= 1) {
363                        assert!(
364                            result,
365                            "Integer cross-type comparison should be true: {left:?} vs {right:?}"
366                        );
367                    } else {
368                        assert!(
369                            !result,
370                            "Non-integer cross-type comparison should be false: {left:?} vs {right:?}"
371                        );
372                    }
373                }
374            }
375        }
376    }
377
378    #[test]
379    fn test_apply_equal_reflexivity() {
380        let values = vec![
381            Value::Uint(42),
382            Value::Int(-42),
383            Value::Bytes(vec![0x7f, 0x45, 0x4c, 0x46]),
384            Value::String("hello".to_string()),
385        ];
386
387        // Test that all values are equal to themselves
388        for value in values {
389            assert!(
390                apply_equal(&value, &value),
391                "Value should be equal to itself: {value:?}"
392            );
393        }
394    }
395
396    #[test]
397    fn test_apply_equal_symmetry() {
398        let test_cases = vec![
399            (Value::Uint(42), Value::Uint(42)),
400            (Value::Int(-100), Value::Int(-100)),
401            (Value::Bytes(vec![1, 2, 3]), Value::Bytes(vec![1, 2, 3])),
402            (
403                Value::String("test".to_string()),
404                Value::String("test".to_string()),
405            ),
406        ];
407
408        // Test that equality is symmetric: a == b implies b == a
409        for (left, right) in test_cases {
410            let left_to_right = apply_equal(&left, &right);
411            let right_to_left = apply_equal(&right, &left);
412            assert_eq!(
413                left_to_right, right_to_left,
414                "Equality should be symmetric: {left:?} vs {right:?}"
415            );
416        }
417    }
418
419    #[test]
420    fn test_apply_equal_transitivity() {
421        // Test transitivity: if a == b and b == c, then a == c
422        let a = Value::Uint(123);
423        let b = Value::Uint(123);
424        let c = Value::Uint(123);
425
426        assert!(apply_equal(&a, &b));
427        assert!(apply_equal(&b, &c));
428        assert!(apply_equal(&a, &c));
429    }
430
431    #[test]
432    fn test_apply_equal_edge_cases() {
433        // Test with maximum values
434        let max_unsigned = Value::Uint(u64::MAX);
435        let max_signed = Value::Int(i64::MAX);
436        let min_int = Value::Int(i64::MIN);
437
438        assert!(apply_equal(&max_unsigned, &max_unsigned));
439        assert!(apply_equal(&max_signed, &max_signed));
440        assert!(apply_equal(&min_int, &min_int));
441
442        // Cross-type edge cases
443        // u64::MAX != -1 in i64 (different mathematical values)
444        assert!(!apply_equal(&max_unsigned, &Value::Int(-1)));
445        // i64::MAX can be represented as u64, so should match
446        assert!(apply_equal(&Value::Uint(i64::MAX as u64), &max_signed));
447
448        // Test with empty collections
449        let empty_bytes = Value::Bytes(vec![]);
450        let empty_string = Value::String(String::new());
451
452        assert!(apply_equal(&empty_bytes, &empty_bytes));
453        assert!(apply_equal(&empty_string, &empty_string));
454        assert!(!apply_equal(&empty_bytes, &empty_string));
455    }
456
457    // Tests for apply_not_equal function
458    #[test]
459    fn test_apply_not_equal_uint_same_value() {
460        let left = Value::Uint(42);
461        let right = Value::Uint(42);
462        assert!(!apply_not_equal(&left, &right));
463    }
464
465    #[test]
466    fn test_apply_not_equal_uint_different_value() {
467        let left = Value::Uint(42);
468        let right = Value::Uint(24);
469        assert!(apply_not_equal(&left, &right));
470    }
471
472    #[test]
473    fn test_apply_not_equal_uint_zero() {
474        let left = Value::Uint(0);
475        let right = Value::Uint(0);
476        assert!(!apply_not_equal(&left, &right));
477    }
478
479    #[test]
480    fn test_apply_not_equal_uint_max_value() {
481        let left = Value::Uint(u64::MAX);
482        let right = Value::Uint(u64::MAX);
483        assert!(!apply_not_equal(&left, &right));
484
485        let left = Value::Uint(u64::MAX);
486        let right = Value::Uint(0);
487        assert!(apply_not_equal(&left, &right));
488    }
489
490    #[test]
491    fn test_apply_not_equal_int_same_value() {
492        let left = Value::Int(42);
493        let right = Value::Int(42);
494        assert!(!apply_not_equal(&left, &right));
495    }
496
497    #[test]
498    fn test_apply_not_equal_int_different_value() {
499        let left = Value::Int(42);
500        let right = Value::Int(-42);
501        assert!(apply_not_equal(&left, &right));
502    }
503
504    #[test]
505    fn test_apply_not_equal_int_negative() {
506        let left = Value::Int(-100);
507        let right = Value::Int(-100);
508        assert!(!apply_not_equal(&left, &right));
509
510        let left = Value::Int(-100);
511        let right = Value::Int(100);
512        assert!(apply_not_equal(&left, &right));
513    }
514
515    #[test]
516    fn test_apply_not_equal_int_zero() {
517        let left = Value::Int(0);
518        let right = Value::Int(0);
519        assert!(!apply_not_equal(&left, &right));
520    }
521
522    #[test]
523    fn test_apply_not_equal_int_extreme_values() {
524        let left = Value::Int(i64::MAX);
525        let right = Value::Int(i64::MAX);
526        assert!(!apply_not_equal(&left, &right));
527
528        let left = Value::Int(i64::MIN);
529        let right = Value::Int(i64::MIN);
530        assert!(!apply_not_equal(&left, &right));
531
532        let left = Value::Int(i64::MAX);
533        let right = Value::Int(i64::MIN);
534        assert!(apply_not_equal(&left, &right));
535    }
536
537    #[test]
538    fn test_apply_not_equal_bytes_same_value() {
539        let left = Value::Bytes(vec![0x7f, 0x45, 0x4c, 0x46]);
540        let right = Value::Bytes(vec![0x7f, 0x45, 0x4c, 0x46]);
541        assert!(!apply_not_equal(&left, &right));
542    }
543
544    #[test]
545    fn test_apply_not_equal_bytes_different_value() {
546        let left = Value::Bytes(vec![0x7f, 0x45, 0x4c, 0x46]);
547        let right = Value::Bytes(vec![0x50, 0x4b, 0x03, 0x04]);
548        assert!(apply_not_equal(&left, &right));
549    }
550
551    #[test]
552    fn test_apply_not_equal_bytes_empty() {
553        let left = Value::Bytes(vec![]);
554        let right = Value::Bytes(vec![]);
555        assert!(!apply_not_equal(&left, &right));
556    }
557
558    #[test]
559    fn test_apply_not_equal_bytes_different_length() {
560        let left = Value::Bytes(vec![0x7f, 0x45]);
561        let right = Value::Bytes(vec![0x7f, 0x45, 0x4c, 0x46]);
562        assert!(apply_not_equal(&left, &right));
563    }
564
565    #[test]
566    fn test_apply_not_equal_bytes_single_byte() {
567        let left = Value::Bytes(vec![0x7f]);
568        let right = Value::Bytes(vec![0x7f]);
569        assert!(!apply_not_equal(&left, &right));
570
571        let left = Value::Bytes(vec![0x7f]);
572        let right = Value::Bytes(vec![0x45]);
573        assert!(apply_not_equal(&left, &right));
574    }
575
576    #[test]
577    fn test_apply_not_equal_string_same_value() {
578        let left = Value::String("hello".to_string());
579        let right = Value::String("hello".to_string());
580        assert!(!apply_not_equal(&left, &right));
581    }
582
583    #[test]
584    fn test_apply_not_equal_string_different_value() {
585        let left = Value::String("hello".to_string());
586        let right = Value::String("world".to_string());
587        assert!(apply_not_equal(&left, &right));
588    }
589
590    #[test]
591    fn test_apply_not_equal_string_empty() {
592        let left = Value::String(String::new());
593        let right = Value::String(String::new());
594        assert!(!apply_not_equal(&left, &right));
595    }
596
597    #[test]
598    fn test_apply_not_equal_string_case_sensitive() {
599        let left = Value::String("Hello".to_string());
600        let right = Value::String("hello".to_string());
601        assert!(apply_not_equal(&left, &right));
602    }
603
604    #[test]
605    fn test_apply_not_equal_string_unicode() {
606        let left = Value::String("\u{1f980} Rust".to_string());
607        let right = Value::String("\u{1f980} Rust".to_string());
608        assert!(!apply_not_equal(&left, &right));
609
610        let left = Value::String("\u{1f980} Rust".to_string());
611        let right = Value::String("\u{1f40d} Python".to_string());
612        assert!(apply_not_equal(&left, &right));
613    }
614
615    #[test]
616    fn test_apply_not_equal_string_whitespace() {
617        let left = Value::String("hello world".to_string());
618        let right = Value::String("hello world".to_string());
619        assert!(!apply_not_equal(&left, &right));
620
621        let left = Value::String("hello world".to_string());
622        let right = Value::String("hello  world".to_string()); // Extra space
623        assert!(apply_not_equal(&left, &right));
624    }
625
626    // Cross-type comparison tests for not_equal (should all return true)
627    #[test]
628    fn test_apply_not_equal_uint_vs_int() {
629        // Same numeric value across types should be equal (not not-equal)
630        let left = Value::Uint(42);
631        let right = Value::Int(42);
632        assert!(!apply_not_equal(&left, &right));
633
634        let left = Value::Uint(0);
635        let right = Value::Int(0);
636        assert!(!apply_not_equal(&left, &right));
637
638        // Different numeric values should be not-equal
639        let left = Value::Uint(42);
640        let right = Value::Int(-42);
641        assert!(apply_not_equal(&left, &right));
642    }
643
644    #[test]
645    fn test_apply_not_equal_uint_vs_bytes() {
646        let left = Value::Uint(42);
647        let right = Value::Bytes(vec![42]);
648        assert!(apply_not_equal(&left, &right));
649    }
650
651    #[test]
652    fn test_apply_not_equal_uint_vs_string() {
653        let left = Value::Uint(42);
654        let right = Value::String("42".to_string());
655        assert!(apply_not_equal(&left, &right));
656    }
657
658    #[test]
659    fn test_apply_not_equal_int_vs_bytes() {
660        let left = Value::Int(-42);
661        let right = Value::Bytes(vec![214]); // -42 as u8
662        assert!(apply_not_equal(&left, &right));
663    }
664
665    #[test]
666    fn test_apply_not_equal_int_vs_string() {
667        let left = Value::Int(-42);
668        let right = Value::String("-42".to_string());
669        assert!(apply_not_equal(&left, &right));
670    }
671
672    #[test]
673    fn test_apply_not_equal_bytes_vs_string() {
674        let left = Value::Bytes(vec![104, 101, 108, 108, 111]); // "hello" as bytes
675        let right = Value::String("hello".to_string());
676        assert!(apply_not_equal(&left, &right));
677    }
678
679    #[test]
680    fn test_apply_not_equal_all_cross_type_combinations() {
681        let values = [
682            Value::Uint(42),
683            Value::Int(42),
684            Value::Bytes(vec![42]),
685            Value::String("42".to_string()),
686        ];
687
688        // Test cross-type comparisons for not_equal
689        for (i, left) in values.iter().enumerate() {
690            for (j, right) in values.iter().enumerate() {
691                if i != j {
692                    let result = apply_not_equal(left, right);
693                    // Uint(42) and Int(42) should be equal, so not_equal is false
694                    if (i <= 1) && (j <= 1) {
695                        assert!(
696                            !result,
697                            "Integer cross-type not_equal should be false: {left:?} vs {right:?}"
698                        );
699                    } else {
700                        assert!(
701                            result,
702                            "Non-integer cross-type not_equal should be true: {left:?} vs {right:?}"
703                        );
704                    }
705                }
706            }
707        }
708    }
709
710    #[test]
711    fn test_apply_not_equal_consistency_with_equal() {
712        let test_cases = vec![
713            (Value::Uint(42), Value::Uint(42)),
714            (Value::Uint(42), Value::Uint(24)),
715            (Value::Int(-100), Value::Int(-100)),
716            (Value::Int(-100), Value::Int(100)),
717            (Value::Bytes(vec![1, 2, 3]), Value::Bytes(vec![1, 2, 3])),
718            (Value::Bytes(vec![1, 2, 3]), Value::Bytes(vec![3, 2, 1])),
719            (
720                Value::String("test".to_string()),
721                Value::String("test".to_string()),
722            ),
723            (
724                Value::String("test".to_string()),
725                Value::String("different".to_string()),
726            ),
727            // Cross-type cases
728            (Value::Uint(42), Value::Int(42)),
729            (Value::Uint(42), Value::String("42".to_string())),
730            (Value::Bytes(vec![42]), Value::Uint(42)),
731        ];
732
733        // Test that apply_not_equal is always the negation of apply_equal
734        for (left, right) in test_cases {
735            let equal_result = apply_equal(&left, &right);
736            let not_equal_result = apply_not_equal(&left, &right);
737            assert_eq!(
738                equal_result, !not_equal_result,
739                "apply_not_equal should be negation of apply_equal: {left:?} vs {right:?}"
740            );
741        }
742    }
743
744    #[test]
745    fn test_apply_not_equal_edge_cases() {
746        // Test with maximum values
747        let max_unsigned = Value::Uint(u64::MAX);
748        let max_signed = Value::Int(i64::MAX);
749        let min_int = Value::Int(i64::MIN);
750
751        assert!(!apply_not_equal(&max_unsigned, &max_unsigned));
752        assert!(!apply_not_equal(&max_signed, &max_signed));
753        assert!(!apply_not_equal(&min_int, &min_int));
754
755        // Test with empty collections
756        let empty_bytes = Value::Bytes(vec![]);
757        let empty_string = Value::String(String::new());
758
759        assert!(!apply_not_equal(&empty_bytes, &empty_bytes));
760        assert!(!apply_not_equal(&empty_string, &empty_string));
761        assert!(apply_not_equal(&empty_bytes, &empty_string));
762    }
763
764    // ============================================================
765    // Float epsilon-aware equality / inequality tests
766    // ============================================================
767
768    #[test]
769    fn test_apply_equal_float_exact_same_value() {
770        assert!(apply_equal(&Value::Float(1.0), &Value::Float(1.0)));
771        assert!(apply_equal(&Value::Float(0.0), &Value::Float(0.0)));
772        assert!(apply_equal(&Value::Float(-3.125), &Value::Float(-3.125)));
773    }
774
775    #[test]
776    fn test_apply_equal_float_near_equal_within_epsilon() {
777        // Values that differ by exactly f64::EPSILON should be considered equal
778        let a = 1.0_f64;
779        let b = a + f64::EPSILON;
780        assert!(
781            apply_equal(&Value::Float(a), &Value::Float(b)),
782            "values differing by f64::EPSILON should be equal"
783        );
784    }
785
786    #[test]
787    fn test_apply_equal_float_clearly_unequal() {
788        assert!(!apply_equal(&Value::Float(1.0), &Value::Float(2.0)));
789        assert!(!apply_equal(&Value::Float(0.0), &Value::Float(1.0)));
790        assert!(!apply_equal(&Value::Float(-1.0), &Value::Float(1.0)));
791    }
792
793    #[test]
794    fn test_apply_equal_float_infinity() {
795        let pos_inf = f64::INFINITY;
796        let neg_inf = f64::NEG_INFINITY;
797
798        assert!(apply_equal(&Value::Float(pos_inf), &Value::Float(pos_inf)));
799        assert!(apply_equal(&Value::Float(neg_inf), &Value::Float(neg_inf)));
800        assert!(!apply_equal(&Value::Float(pos_inf), &Value::Float(neg_inf)));
801        assert!(!apply_equal(&Value::Float(pos_inf), &Value::Float(1.0)));
802    }
803
804    #[test]
805    fn test_apply_equal_float_nan() {
806        let nan = f64::NAN;
807        assert!(!apply_equal(&Value::Float(nan), &Value::Float(nan)));
808        assert!(!apply_equal(&Value::Float(nan), &Value::Float(0.0)));
809        assert!(!apply_equal(&Value::Float(0.0), &Value::Float(nan)));
810    }
811
812    #[test]
813    fn test_apply_not_equal_float_exact_same_value() {
814        assert!(!apply_not_equal(&Value::Float(1.0), &Value::Float(1.0)));
815        assert!(!apply_not_equal(&Value::Float(0.0), &Value::Float(0.0)));
816    }
817
818    #[test]
819    fn test_apply_not_equal_float_near_equal_within_epsilon() {
820        let a = 1.0_f64;
821        let b = a + f64::EPSILON;
822        assert!(
823            !apply_not_equal(&Value::Float(a), &Value::Float(b)),
824            "values differing by f64::EPSILON should not be not-equal"
825        );
826    }
827
828    #[test]
829    fn test_apply_not_equal_float_clearly_unequal() {
830        assert!(apply_not_equal(&Value::Float(1.0), &Value::Float(2.0)));
831        assert!(apply_not_equal(&Value::Float(-1.0), &Value::Float(1.0)));
832    }
833
834    #[test]
835    fn test_apply_not_equal_float_nan() {
836        let nan = f64::NAN;
837        // NaN != NaN should be true (NaN is never equal to anything)
838        assert!(apply_not_equal(&Value::Float(nan), &Value::Float(nan)));
839        assert!(apply_not_equal(&Value::Float(nan), &Value::Float(0.0)));
840    }
841
842    #[test]
843    fn test_apply_not_equal_float_infinity() {
844        assert!(!apply_not_equal(
845            &Value::Float(f64::INFINITY),
846            &Value::Float(f64::INFINITY)
847        ));
848        assert!(apply_not_equal(
849            &Value::Float(f64::INFINITY),
850            &Value::Float(f64::NEG_INFINITY)
851        ));
852    }
853
854    #[test]
855    fn test_apply_not_equal_various_value_combinations() {
856        // Test various combinations to ensure comprehensive coverage
857        let test_cases = vec![
858            // Uint variations
859            (Value::Uint(0), Value::Uint(1), true),
860            (Value::Uint(100), Value::Uint(100), false),
861            (Value::Uint(u64::MAX), Value::Uint(u64::MAX - 1), true),
862            // Int variations
863            (Value::Int(0), Value::Int(-1), true),
864            (Value::Int(-50), Value::Int(-50), false),
865            (Value::Int(i64::MIN), Value::Int(i64::MAX), true),
866            // Bytes variations
867            (Value::Bytes(vec![0]), Value::Bytes(vec![1]), true),
868            (
869                Value::Bytes(vec![255, 254]),
870                Value::Bytes(vec![255, 254]),
871                false,
872            ),
873            (Value::Bytes(vec![]), Value::Bytes(vec![0]), true),
874            // String variations
875            (
876                Value::String("a".to_string()),
877                Value::String("b".to_string()),
878                true,
879            ),
880            (
881                Value::String("same".to_string()),
882                Value::String("same".to_string()),
883                false,
884            ),
885            (
886                Value::String(String::new()),
887                Value::String("non-empty".to_string()),
888                true,
889            ),
890        ];
891
892        for (left, right, expected) in test_cases {
893            assert_eq!(
894                apply_not_equal(&left, &right),
895                expected,
896                "apply_not_equal({left:?}, {right:?}) should be {expected}"
897            );
898        }
899    }
900}