use std::cmp::Ordering;
use crate::{value::dispatch_operation, Value};
impl Ord for Value {
fn cmp(&self, other: &Self) -> Ordering {
let mut left = *self;
let mut right = *other;
left.match_orders(&mut right);
match (left, right) {
(Value::UnsignedInt(l), Value::UnsignedInt(r)) => l.cmp(&r),
(Value::UnsignedBigInt(l), Value::UnsignedBigInt(r)) => l.cmp(&r),
(Value::SignedInt(l), Value::SignedInt(r)) => l.cmp(&r),
(Value::SignedBigInt(l), Value::SignedBigInt(r)) => l.cmp(&r),
(Value::Float(l), Value::Float(r)) => l.total_cmp(&r),
_ => unreachable!("both sides have equal orders because we did `match_orders`"),
}
}
}
impl PartialOrd for Value {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
let mut left = *self;
let mut right = *other;
dispatch_operation!(left, right, l, |r| l == r)
}
}
impl Eq for Value {}
impl Value {
pub fn strict_eq(self, other: Self) -> bool {
match (self, other) {
(Value::UnsignedInt(l), Value::UnsignedInt(r)) => l == r,
(Value::UnsignedBigInt(l), Value::UnsignedBigInt(r)) => l == r,
(Value::SignedInt(l), Value::SignedInt(r)) => l == r,
(Value::SignedBigInt(l), Value::SignedBigInt(r)) => l == r,
(Value::Float(l), Value::Float(r)) => l == r,
_ => false,
}
}
pub fn strict_cmp(self, other: Self) -> Ordering {
self.order()
.cmp(&other.order())
.then_with(|| self.cmp(&other))
}
}
#[cfg(test)]
mod ordering_tests {
use super::*;
use rstest::rstest;
#[rstest]
fn equality_across_variants(
#[values(42_u64, 42_u128, 42_i64, 42_i128, 42.0_f64)] left: impl Into<Value>,
) {
let left = left.into();
let right: Value = 42_u64.into();
assert_eq!(left, right);
}
#[test]
fn float_total_cmp_nan_behavior() {
let nan1: Value = f64::NAN.into();
let nan2: Value = f64::NAN.into();
assert_ne!(nan1, nan2);
assert_eq!(nan1.partial_cmp(&nan2), Some(Ordering::Equal));
}
#[rstest]
fn ordering_across_variants(
#[values(1_u64, 1_u128, 1_i64, 1_i128, 1.0_f64)] one: impl Into<Value>,
#[values(2_u64, 2_u128, 2_i64, 2_i128, 2.0_f64)] two: impl Into<Value>,
) {
let one = one.into();
let two = two.into();
assert!(one < two);
}
#[test]
fn float_ordering_total_cmp() {
let neg_zero: Value = (-0.0_f64).into();
let pos_zero: Value = 0.0_f64.into();
assert_eq!(neg_zero.cmp(&pos_zero), f64::total_cmp(&-0.0, &0.0));
}
#[test]
fn strict_eq_same_variant_same_value() {
let a: Value = 42_u64.into();
let b: Value = 42_u64.into();
assert!(a.strict_eq(b));
}
#[test]
fn strict_eq_different_variants_same_value() {
let a: Value = 42_u64.into();
let b: Value = 42_u128.into();
assert!(!a.strict_eq(b));
}
#[test]
fn strict_cmp_same_variant() {
let a: Value = 10_i64.into();
let b: Value = 20_i64.into();
assert_eq!(a.strict_cmp(b), Ordering::Less);
}
#[test]
fn strict_cmp_different_variants() {
let a: Value = 10_u64.into();
let b: Value = 10_u128.into();
assert_eq!(a.strict_cmp(b), Ordering::Less);
}
#[test]
fn equality_large_values_promote() {
let a: Value = u64::MAX.into();
let b: Value = (u64::MAX as u128).into();
assert_eq!(a, b);
}
#[test]
fn ordering_signed_vs_unsigned() {
let a: Value = (-1_i64).into();
let b: Value = 1_u64.into();
assert!(a < b);
}
#[test]
fn ordering_float_vs_int() {
let a: Value = 3.5_f64.into();
let b: Value = 4_i64.into();
assert!(a < b);
}
}