use std::cmp::Ordering;
use crate::parser::ast::Value;
use super::compare_values;
const FLOAT_EPSILON: f64 = f64::EPSILON;
fn floats_equal(a: f64, b: f64) -> bool {
if a.is_nan() || b.is_nan() {
return false;
}
if a.is_infinite() || b.is_infinite() {
#[allow(clippy::float_cmp)]
return a == b;
}
(a - b).abs() <= FLOAT_EPSILON
}
#[must_use]
pub fn apply_equal(left: &Value, right: &Value) -> bool {
if let (Value::Float(a), Value::Float(b)) = (left, right) {
return floats_equal(*a, *b);
}
compare_values(left, right) == Some(Ordering::Equal)
}
#[must_use]
pub fn apply_not_equal(left: &Value, right: &Value) -> bool {
!apply_equal(left, right)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_apply_equal_uint_same_value() {
let left = Value::Uint(42);
let right = Value::Uint(42);
assert!(apply_equal(&left, &right));
}
#[test]
fn test_apply_equal_uint_different_value() {
let left = Value::Uint(42);
let right = Value::Uint(24);
assert!(!apply_equal(&left, &right));
}
#[test]
fn test_apply_equal_uint_zero() {
let left = Value::Uint(0);
let right = Value::Uint(0);
assert!(apply_equal(&left, &right));
}
#[test]
fn test_apply_equal_uint_max_value() {
let left = Value::Uint(u64::MAX);
let right = Value::Uint(u64::MAX);
assert!(apply_equal(&left, &right));
}
#[test]
fn test_apply_equal_int_same_value() {
let left = Value::Int(42);
let right = Value::Int(42);
assert!(apply_equal(&left, &right));
}
#[test]
fn test_apply_equal_int_different_value() {
let left = Value::Int(42);
let right = Value::Int(-42);
assert!(!apply_equal(&left, &right));
}
#[test]
fn test_apply_equal_int_negative() {
let left = Value::Int(-100);
let right = Value::Int(-100);
assert!(apply_equal(&left, &right));
}
#[test]
fn test_apply_equal_int_zero() {
let left = Value::Int(0);
let right = Value::Int(0);
assert!(apply_equal(&left, &right));
}
#[test]
fn test_apply_equal_int_extreme_values() {
let left = Value::Int(i64::MAX);
let right = Value::Int(i64::MAX);
assert!(apply_equal(&left, &right));
let left = Value::Int(i64::MIN);
let right = Value::Int(i64::MIN);
assert!(apply_equal(&left, &right));
}
#[test]
fn test_apply_equal_bytes_same_value() {
let left = Value::Bytes(vec![0x7f, 0x45, 0x4c, 0x46]);
let right = Value::Bytes(vec![0x7f, 0x45, 0x4c, 0x46]);
assert!(apply_equal(&left, &right));
}
#[test]
fn test_apply_equal_bytes_different_value() {
let left = Value::Bytes(vec![0x7f, 0x45, 0x4c, 0x46]);
let right = Value::Bytes(vec![0x50, 0x4b, 0x03, 0x04]);
assert!(!apply_equal(&left, &right));
}
#[test]
fn test_apply_equal_bytes_empty() {
let left = Value::Bytes(vec![]);
let right = Value::Bytes(vec![]);
assert!(apply_equal(&left, &right));
}
#[test]
fn test_apply_equal_bytes_different_length() {
let left = Value::Bytes(vec![0x7f, 0x45]);
let right = Value::Bytes(vec![0x7f, 0x45, 0x4c, 0x46]);
assert!(!apply_equal(&left, &right));
}
#[test]
fn test_apply_equal_bytes_single_byte() {
let left = Value::Bytes(vec![0x7f]);
let right = Value::Bytes(vec![0x7f]);
assert!(apply_equal(&left, &right));
let left = Value::Bytes(vec![0x7f]);
let right = Value::Bytes(vec![0x45]);
assert!(!apply_equal(&left, &right));
}
#[test]
fn test_apply_equal_string_same_value() {
let left = Value::String("hello".to_string());
let right = Value::String("hello".to_string());
assert!(apply_equal(&left, &right));
}
#[test]
fn test_apply_equal_string_different_value() {
let left = Value::String("hello".to_string());
let right = Value::String("world".to_string());
assert!(!apply_equal(&left, &right));
}
#[test]
fn test_apply_equal_string_empty() {
let left = Value::String(String::new());
let right = Value::String(String::new());
assert!(apply_equal(&left, &right));
}
#[test]
fn test_apply_equal_string_case_sensitive() {
let left = Value::String("Hello".to_string());
let right = Value::String("hello".to_string());
assert!(!apply_equal(&left, &right));
}
#[test]
fn test_apply_equal_string_unicode() {
let left = Value::String("\u{1f980} Rust".to_string());
let right = Value::String("\u{1f980} Rust".to_string());
assert!(apply_equal(&left, &right));
let left = Value::String("\u{1f980} Rust".to_string());
let right = Value::String("\u{1f40d} Python".to_string());
assert!(!apply_equal(&left, &right));
}
#[test]
fn test_apply_equal_string_whitespace() {
let left = Value::String("hello world".to_string());
let right = Value::String("hello world".to_string());
assert!(apply_equal(&left, &right));
let left = Value::String("hello world".to_string());
let right = Value::String("hello world".to_string()); assert!(!apply_equal(&left, &right));
}
#[test]
fn test_apply_equal_uint_vs_int() {
let left = Value::Uint(42);
let right = Value::Int(42);
assert!(apply_equal(&left, &right));
let left = Value::Uint(0);
let right = Value::Int(0);
assert!(apply_equal(&left, &right));
let left = Value::Uint(42);
let right = Value::Int(-42);
assert!(!apply_equal(&left, &right));
let left = Value::Uint(u64::MAX);
let right = Value::Int(-1);
assert!(!apply_equal(&left, &right));
}
#[test]
fn test_apply_equal_uint_vs_bytes() {
let left = Value::Uint(42);
let right = Value::Bytes(vec![42]);
assert!(!apply_equal(&left, &right));
}
#[test]
fn test_apply_equal_uint_vs_string() {
let left = Value::Uint(42);
let right = Value::String("42".to_string());
assert!(!apply_equal(&left, &right));
}
#[test]
fn test_apply_equal_int_vs_bytes() {
let left = Value::Int(-42);
let right = Value::Bytes(vec![214]); assert!(!apply_equal(&left, &right));
}
#[test]
fn test_apply_equal_int_vs_string() {
let left = Value::Int(-42);
let right = Value::String("-42".to_string());
assert!(!apply_equal(&left, &right));
}
#[test]
fn test_apply_equal_bytes_vs_string() {
let left = Value::Bytes(vec![104, 101, 108, 108, 111]); let right = Value::String("hello".to_string());
assert!(!apply_equal(&left, &right));
}
#[test]
fn test_apply_equal_all_cross_type_combinations() {
let values = [
Value::Uint(42),
Value::Int(42),
Value::Bytes(vec![42]),
Value::String("42".to_string()),
];
for (i, left) in values.iter().enumerate() {
for (j, right) in values.iter().enumerate() {
if i != j {
let result = apply_equal(left, right);
if (i <= 1) && (j <= 1) {
assert!(
result,
"Integer cross-type comparison should be true: {left:?} vs {right:?}"
);
} else {
assert!(
!result,
"Non-integer cross-type comparison should be false: {left:?} vs {right:?}"
);
}
}
}
}
}
#[test]
fn test_apply_equal_reflexivity() {
let values = vec![
Value::Uint(42),
Value::Int(-42),
Value::Bytes(vec![0x7f, 0x45, 0x4c, 0x46]),
Value::String("hello".to_string()),
];
for value in values {
assert!(
apply_equal(&value, &value),
"Value should be equal to itself: {value:?}"
);
}
}
#[test]
fn test_apply_equal_symmetry() {
let test_cases = vec![
(Value::Uint(42), Value::Uint(42)),
(Value::Int(-100), Value::Int(-100)),
(Value::Bytes(vec![1, 2, 3]), Value::Bytes(vec![1, 2, 3])),
(
Value::String("test".to_string()),
Value::String("test".to_string()),
),
];
for (left, right) in test_cases {
let left_to_right = apply_equal(&left, &right);
let right_to_left = apply_equal(&right, &left);
assert_eq!(
left_to_right, right_to_left,
"Equality should be symmetric: {left:?} vs {right:?}"
);
}
}
#[test]
fn test_apply_equal_transitivity() {
let a = Value::Uint(123);
let b = Value::Uint(123);
let c = Value::Uint(123);
assert!(apply_equal(&a, &b));
assert!(apply_equal(&b, &c));
assert!(apply_equal(&a, &c));
}
#[test]
fn test_apply_equal_edge_cases() {
let max_unsigned = Value::Uint(u64::MAX);
let max_signed = Value::Int(i64::MAX);
let min_int = Value::Int(i64::MIN);
assert!(apply_equal(&max_unsigned, &max_unsigned));
assert!(apply_equal(&max_signed, &max_signed));
assert!(apply_equal(&min_int, &min_int));
assert!(!apply_equal(&max_unsigned, &Value::Int(-1)));
assert!(apply_equal(&Value::Uint(i64::MAX as u64), &max_signed));
let empty_bytes = Value::Bytes(vec![]);
let empty_string = Value::String(String::new());
assert!(apply_equal(&empty_bytes, &empty_bytes));
assert!(apply_equal(&empty_string, &empty_string));
assert!(!apply_equal(&empty_bytes, &empty_string));
}
#[test]
fn test_apply_not_equal_uint_same_value() {
let left = Value::Uint(42);
let right = Value::Uint(42);
assert!(!apply_not_equal(&left, &right));
}
#[test]
fn test_apply_not_equal_uint_different_value() {
let left = Value::Uint(42);
let right = Value::Uint(24);
assert!(apply_not_equal(&left, &right));
}
#[test]
fn test_apply_not_equal_uint_zero() {
let left = Value::Uint(0);
let right = Value::Uint(0);
assert!(!apply_not_equal(&left, &right));
}
#[test]
fn test_apply_not_equal_uint_max_value() {
let left = Value::Uint(u64::MAX);
let right = Value::Uint(u64::MAX);
assert!(!apply_not_equal(&left, &right));
let left = Value::Uint(u64::MAX);
let right = Value::Uint(0);
assert!(apply_not_equal(&left, &right));
}
#[test]
fn test_apply_not_equal_int_same_value() {
let left = Value::Int(42);
let right = Value::Int(42);
assert!(!apply_not_equal(&left, &right));
}
#[test]
fn test_apply_not_equal_int_different_value() {
let left = Value::Int(42);
let right = Value::Int(-42);
assert!(apply_not_equal(&left, &right));
}
#[test]
fn test_apply_not_equal_int_negative() {
let left = Value::Int(-100);
let right = Value::Int(-100);
assert!(!apply_not_equal(&left, &right));
let left = Value::Int(-100);
let right = Value::Int(100);
assert!(apply_not_equal(&left, &right));
}
#[test]
fn test_apply_not_equal_int_zero() {
let left = Value::Int(0);
let right = Value::Int(0);
assert!(!apply_not_equal(&left, &right));
}
#[test]
fn test_apply_not_equal_int_extreme_values() {
let left = Value::Int(i64::MAX);
let right = Value::Int(i64::MAX);
assert!(!apply_not_equal(&left, &right));
let left = Value::Int(i64::MIN);
let right = Value::Int(i64::MIN);
assert!(!apply_not_equal(&left, &right));
let left = Value::Int(i64::MAX);
let right = Value::Int(i64::MIN);
assert!(apply_not_equal(&left, &right));
}
#[test]
fn test_apply_not_equal_bytes_same_value() {
let left = Value::Bytes(vec![0x7f, 0x45, 0x4c, 0x46]);
let right = Value::Bytes(vec![0x7f, 0x45, 0x4c, 0x46]);
assert!(!apply_not_equal(&left, &right));
}
#[test]
fn test_apply_not_equal_bytes_different_value() {
let left = Value::Bytes(vec![0x7f, 0x45, 0x4c, 0x46]);
let right = Value::Bytes(vec![0x50, 0x4b, 0x03, 0x04]);
assert!(apply_not_equal(&left, &right));
}
#[test]
fn test_apply_not_equal_bytes_empty() {
let left = Value::Bytes(vec![]);
let right = Value::Bytes(vec![]);
assert!(!apply_not_equal(&left, &right));
}
#[test]
fn test_apply_not_equal_bytes_different_length() {
let left = Value::Bytes(vec![0x7f, 0x45]);
let right = Value::Bytes(vec![0x7f, 0x45, 0x4c, 0x46]);
assert!(apply_not_equal(&left, &right));
}
#[test]
fn test_apply_not_equal_bytes_single_byte() {
let left = Value::Bytes(vec![0x7f]);
let right = Value::Bytes(vec![0x7f]);
assert!(!apply_not_equal(&left, &right));
let left = Value::Bytes(vec![0x7f]);
let right = Value::Bytes(vec![0x45]);
assert!(apply_not_equal(&left, &right));
}
#[test]
fn test_apply_not_equal_string_same_value() {
let left = Value::String("hello".to_string());
let right = Value::String("hello".to_string());
assert!(!apply_not_equal(&left, &right));
}
#[test]
fn test_apply_not_equal_string_different_value() {
let left = Value::String("hello".to_string());
let right = Value::String("world".to_string());
assert!(apply_not_equal(&left, &right));
}
#[test]
fn test_apply_not_equal_string_empty() {
let left = Value::String(String::new());
let right = Value::String(String::new());
assert!(!apply_not_equal(&left, &right));
}
#[test]
fn test_apply_not_equal_string_case_sensitive() {
let left = Value::String("Hello".to_string());
let right = Value::String("hello".to_string());
assert!(apply_not_equal(&left, &right));
}
#[test]
fn test_apply_not_equal_string_unicode() {
let left = Value::String("\u{1f980} Rust".to_string());
let right = Value::String("\u{1f980} Rust".to_string());
assert!(!apply_not_equal(&left, &right));
let left = Value::String("\u{1f980} Rust".to_string());
let right = Value::String("\u{1f40d} Python".to_string());
assert!(apply_not_equal(&left, &right));
}
#[test]
fn test_apply_not_equal_string_whitespace() {
let left = Value::String("hello world".to_string());
let right = Value::String("hello world".to_string());
assert!(!apply_not_equal(&left, &right));
let left = Value::String("hello world".to_string());
let right = Value::String("hello world".to_string()); assert!(apply_not_equal(&left, &right));
}
#[test]
fn test_apply_not_equal_uint_vs_int() {
let left = Value::Uint(42);
let right = Value::Int(42);
assert!(!apply_not_equal(&left, &right));
let left = Value::Uint(0);
let right = Value::Int(0);
assert!(!apply_not_equal(&left, &right));
let left = Value::Uint(42);
let right = Value::Int(-42);
assert!(apply_not_equal(&left, &right));
}
#[test]
fn test_apply_not_equal_uint_vs_bytes() {
let left = Value::Uint(42);
let right = Value::Bytes(vec![42]);
assert!(apply_not_equal(&left, &right));
}
#[test]
fn test_apply_not_equal_uint_vs_string() {
let left = Value::Uint(42);
let right = Value::String("42".to_string());
assert!(apply_not_equal(&left, &right));
}
#[test]
fn test_apply_not_equal_int_vs_bytes() {
let left = Value::Int(-42);
let right = Value::Bytes(vec![214]); assert!(apply_not_equal(&left, &right));
}
#[test]
fn test_apply_not_equal_int_vs_string() {
let left = Value::Int(-42);
let right = Value::String("-42".to_string());
assert!(apply_not_equal(&left, &right));
}
#[test]
fn test_apply_not_equal_bytes_vs_string() {
let left = Value::Bytes(vec![104, 101, 108, 108, 111]); let right = Value::String("hello".to_string());
assert!(apply_not_equal(&left, &right));
}
#[test]
fn test_apply_not_equal_all_cross_type_combinations() {
let values = [
Value::Uint(42),
Value::Int(42),
Value::Bytes(vec![42]),
Value::String("42".to_string()),
];
for (i, left) in values.iter().enumerate() {
for (j, right) in values.iter().enumerate() {
if i != j {
let result = apply_not_equal(left, right);
if (i <= 1) && (j <= 1) {
assert!(
!result,
"Integer cross-type not_equal should be false: {left:?} vs {right:?}"
);
} else {
assert!(
result,
"Non-integer cross-type not_equal should be true: {left:?} vs {right:?}"
);
}
}
}
}
}
#[test]
fn test_apply_not_equal_consistency_with_equal() {
let test_cases = vec![
(Value::Uint(42), Value::Uint(42)),
(Value::Uint(42), Value::Uint(24)),
(Value::Int(-100), Value::Int(-100)),
(Value::Int(-100), Value::Int(100)),
(Value::Bytes(vec![1, 2, 3]), Value::Bytes(vec![1, 2, 3])),
(Value::Bytes(vec![1, 2, 3]), Value::Bytes(vec![3, 2, 1])),
(
Value::String("test".to_string()),
Value::String("test".to_string()),
),
(
Value::String("test".to_string()),
Value::String("different".to_string()),
),
(Value::Uint(42), Value::Int(42)),
(Value::Uint(42), Value::String("42".to_string())),
(Value::Bytes(vec![42]), Value::Uint(42)),
];
for (left, right) in test_cases {
let equal_result = apply_equal(&left, &right);
let not_equal_result = apply_not_equal(&left, &right);
assert_eq!(
equal_result, !not_equal_result,
"apply_not_equal should be negation of apply_equal: {left:?} vs {right:?}"
);
}
}
#[test]
fn test_apply_not_equal_edge_cases() {
let max_unsigned = Value::Uint(u64::MAX);
let max_signed = Value::Int(i64::MAX);
let min_int = Value::Int(i64::MIN);
assert!(!apply_not_equal(&max_unsigned, &max_unsigned));
assert!(!apply_not_equal(&max_signed, &max_signed));
assert!(!apply_not_equal(&min_int, &min_int));
let empty_bytes = Value::Bytes(vec![]);
let empty_string = Value::String(String::new());
assert!(!apply_not_equal(&empty_bytes, &empty_bytes));
assert!(!apply_not_equal(&empty_string, &empty_string));
assert!(apply_not_equal(&empty_bytes, &empty_string));
}
#[test]
fn test_apply_equal_float_exact_same_value() {
assert!(apply_equal(&Value::Float(1.0), &Value::Float(1.0)));
assert!(apply_equal(&Value::Float(0.0), &Value::Float(0.0)));
assert!(apply_equal(&Value::Float(-3.125), &Value::Float(-3.125)));
}
#[test]
fn test_apply_equal_float_near_equal_within_epsilon() {
let a = 1.0_f64;
let b = a + f64::EPSILON;
assert!(
apply_equal(&Value::Float(a), &Value::Float(b)),
"values differing by f64::EPSILON should be equal"
);
}
#[test]
fn test_apply_equal_float_clearly_unequal() {
assert!(!apply_equal(&Value::Float(1.0), &Value::Float(2.0)));
assert!(!apply_equal(&Value::Float(0.0), &Value::Float(1.0)));
assert!(!apply_equal(&Value::Float(-1.0), &Value::Float(1.0)));
}
#[test]
fn test_apply_equal_float_infinity() {
let pos_inf = f64::INFINITY;
let neg_inf = f64::NEG_INFINITY;
assert!(apply_equal(&Value::Float(pos_inf), &Value::Float(pos_inf)));
assert!(apply_equal(&Value::Float(neg_inf), &Value::Float(neg_inf)));
assert!(!apply_equal(&Value::Float(pos_inf), &Value::Float(neg_inf)));
assert!(!apply_equal(&Value::Float(pos_inf), &Value::Float(1.0)));
}
#[test]
fn test_apply_equal_float_nan() {
let nan = f64::NAN;
assert!(!apply_equal(&Value::Float(nan), &Value::Float(nan)));
assert!(!apply_equal(&Value::Float(nan), &Value::Float(0.0)));
assert!(!apply_equal(&Value::Float(0.0), &Value::Float(nan)));
}
#[test]
fn test_apply_not_equal_float_exact_same_value() {
assert!(!apply_not_equal(&Value::Float(1.0), &Value::Float(1.0)));
assert!(!apply_not_equal(&Value::Float(0.0), &Value::Float(0.0)));
}
#[test]
fn test_apply_not_equal_float_near_equal_within_epsilon() {
let a = 1.0_f64;
let b = a + f64::EPSILON;
assert!(
!apply_not_equal(&Value::Float(a), &Value::Float(b)),
"values differing by f64::EPSILON should not be not-equal"
);
}
#[test]
fn test_apply_not_equal_float_clearly_unequal() {
assert!(apply_not_equal(&Value::Float(1.0), &Value::Float(2.0)));
assert!(apply_not_equal(&Value::Float(-1.0), &Value::Float(1.0)));
}
#[test]
fn test_apply_not_equal_float_nan() {
let nan = f64::NAN;
assert!(apply_not_equal(&Value::Float(nan), &Value::Float(nan)));
assert!(apply_not_equal(&Value::Float(nan), &Value::Float(0.0)));
}
#[test]
fn test_apply_not_equal_float_infinity() {
assert!(!apply_not_equal(
&Value::Float(f64::INFINITY),
&Value::Float(f64::INFINITY)
));
assert!(apply_not_equal(
&Value::Float(f64::INFINITY),
&Value::Float(f64::NEG_INFINITY)
));
}
#[test]
fn test_apply_not_equal_various_value_combinations() {
let test_cases = vec![
(Value::Uint(0), Value::Uint(1), true),
(Value::Uint(100), Value::Uint(100), false),
(Value::Uint(u64::MAX), Value::Uint(u64::MAX - 1), true),
(Value::Int(0), Value::Int(-1), true),
(Value::Int(-50), Value::Int(-50), false),
(Value::Int(i64::MIN), Value::Int(i64::MAX), true),
(Value::Bytes(vec![0]), Value::Bytes(vec![1]), true),
(
Value::Bytes(vec![255, 254]),
Value::Bytes(vec![255, 254]),
false,
),
(Value::Bytes(vec![]), Value::Bytes(vec![0]), true),
(
Value::String("a".to_string()),
Value::String("b".to_string()),
true,
),
(
Value::String("same".to_string()),
Value::String("same".to_string()),
false,
),
(
Value::String(String::new()),
Value::String("non-empty".to_string()),
true,
),
];
for (left, right, expected) in test_cases {
assert_eq!(
apply_not_equal(&left, &right),
expected,
"apply_not_equal({left:?}, {right:?}) should be {expected}"
);
}
}
}