uninum 0.1.1

A robust, ergonomic unified number type for Rust with automatic overflow handling, type promotion, and cross-type consistency.
Documentation
//! Tests for negation operations

use uninum::Number;

/// Tests basic negation operations
#[test]
fn test_basic_negation() {
    // Unsigned to signed promotion
    assert_eq!(-Number::from(100u64), Number::from(-100i64));

    // Signed negation
    assert_eq!(-Number::from(-100i64), Number::from(100i64));
    assert_eq!(-Number::from(42i64), Number::from(-42i64));

    // Float negation
    assert_eq!(-Number::from(2.72), Number::from(-2.72));
    assert_eq!(-Number::from(-3.16), Number::from(3.16));
}

/// Tests negation of zero values
#[test]
fn test_zero_negation() {
    // Unsigned zero becomes signed zero
    assert_eq!(-Number::from(0u64), Number::from(0i64));

    // Signed zero stays zero
    assert_eq!(-Number::from(0i64), Number::from(0i64));

    // Float zero preserves sign
    let neg_zero = -Number::from(0.0);
    if let Some(f) = neg_zero.try_get_f64() {
        assert!(f.is_sign_negative() && f == 0.0);
    }

    // Negative zero becomes positive zero
    let pos_zero = -Number::from(-0.0);
    if let Some(f) = pos_zero.try_get_f64() {
        assert!(f.is_sign_positive() && f == 0.0);
    }
}

/// Tests negation overflow behavior
#[test]
fn test_overflow_negation() {
    // Negating i64::MIN causes overflow
    let result = -Number::from(i64::MIN);
    assert!(result.try_get_i64().is_none()); // Overflowed
    assert!(result.is_finite());

    // Large unsigned values promote when negated
    let result = -Number::from(u64::MAX);
    assert!(result.try_get_i64().is_none()); // Too large for i64
    assert!(result.try_get_u64().is_none()); // Negative, can't be u64
}

/// Tests special float value negation
#[test]
fn test_special_float_negation() {
    // Infinity negation
    assert_eq!(
        -Number::from(f64::INFINITY),
        Number::from(f64::NEG_INFINITY)
    );
    assert_eq!(
        -Number::from(f64::NEG_INFINITY),
        Number::from(f64::INFINITY)
    );

    // NaN negation (NaN stays NaN)
    let neg_nan = -Number::from(f64::NAN);
    assert!(neg_nan.is_nan());
}

#[cfg(feature = "decimal")]
#[test]
fn test_decimal_negation() {
    use rust_decimal::Decimal;

    let pos = Number::from(Decimal::new(12345, 2)); // 123.45
    let neg = -pos;

    if let Some(d) = neg.try_get_decimal() {
        assert_eq!(d.to_string(), "-123.45");
    }

    // Negating negative decimal
    let neg_dec = Number::from(Decimal::new(-54321, 3)); // -54.321
    let pos_dec = -neg_dec;

    if let Some(d) = pos_dec.try_get_decimal() {
        assert_eq!(d.to_string(), "54.321");
    }
}

/// Tests double negation
#[test]
fn test_double_negation() {
    let original = Number::from(42);
    let double_neg = -(-original.clone());
    assert_eq!(double_neg, original);

    // With float
    let float_orig = Number::from(3.16);
    let float_double = -(-float_orig.clone());
    assert_eq!(float_double, float_orig);

    // Special case: double negation of zero
    let zero = Number::from(0);
    assert_eq!(-(-zero.clone()), zero);
}

/// Tests negation with extreme values
#[test]
fn test_extreme_value_negation() {
    // Very small positive becomes very small negative
    let tiny = Number::from(f64::MIN_POSITIVE);
    let neg_tiny = -tiny;
    if let Some(f) = neg_tiny.try_get_f64() {
        assert_eq!(f, -f64::MIN_POSITIVE);
    }

    // Very large positive becomes very large negative
    let huge = Number::from(f64::MAX);
    let neg_huge = -huge;
    if let Some(f) = neg_huge.try_get_f64() {
        assert_eq!(f, -f64::MAX);
    }
}