//! Arithmetic operations for `DataValue`.
//!
//! This module implements arithmetic operators (`Add`, `Sub`, `Mul`, `Div`) and their
//! assignment variants (`AddAssign`, `SubAssign`, `MulAssign`, `DivAssign`) for `DataValue`.
//!
//! # Type Coercion
//!
//! Operations between different numeric types automatically coerce to a compatible type:
//! - Integer + Integer → Integer (promoting to larger type if needed)
//! - Integer + Float → Float
//! - Float + Float → Float
//!
//! Special cases:
//! - String + String → Concatenated string
//! - Bytes + Bytes → Concatenated byte array
//! - Incompatible operations return `DataValue::Null`
//!
//! # Examples
//!
//! ```rust
//! use trs_data_value::DataValue;
//!
//! let a = DataValue::I32(10);
//! let b = DataValue::I32(5);
//! assert_eq!(a + b, DataValue::I32(15));
//!
//! let x = DataValue::String("Hello, ".into());
//! let y = DataValue::String("World!".into());
//! assert_eq!(x + y, DataValue::String("Hello, World!".into()));
//! ```

use tracing::warn;

use super::DataValue;

/// Addition operator for `DataValue` references.
///
/// Supports addition between numeric types (with automatic type promotion),
/// string concatenation, and byte array concatenation.
impl std::ops::Add<&DataValue> for &DataValue {
    type Output = DataValue;
    fn add(self, other: &DataValue) -> DataValue {
        use DataValue::*;
        match self {
            U8(i) => match other {
                U8(j) => U8(i + j),
                I32(j) => I32(*i as i32 + j),
                I64(j) => I64(*i as i64 + j),
                F32(j) => F32(*i as f32 + j),
                F64(j) => F64(*i as f64 + j),
                U128(j) => U128(*i as u128 + *j),
                I128(j) => I128(*i as i128 + *j),
                _ => Null,
            },
            I32(i) => match other {
                U8(j) => I32(i + *j as i32),
                I32(j) => I32(i + j),
                U32(j) => I32(i + *j as i32),
                U64(j) => U64(*i as u64 + j),
                I64(j) => I64(*i as i64 + j),
                F32(j) => F32(*i as f32 + j),
                F64(j) => F64(*i as f64 + j),
                U128(j) => U128(*i as u128 + *j),
                I128(j) => I128(*i as i128 + *j),
                _ => Null,
            },
            I64(i) => match other {
                U8(j) => I64(i + *j as i64),
                I32(j) => I64(i + *j as i64),
                U32(j) => I64(i + *j as i64),
                U64(j) => I64(i + *j as i64),
                I64(j) => I64(i + j),
                F32(j) => F32(*i as f32 + j),
                F64(j) => F64(*i as f64 + j),
                U128(j) => U128(*i as u128 + *j),
                I128(j) => I128(*i as i128 + *j),
                _ => Null,
            },
            U32(i) => match other {
                U8(j) => U32(i + *j as u32),
                I32(j) => U32(i + *j as u32),
                U32(j) => U32(i + j),
                U64(j) => U64(*i as u64 + j),
                I64(j) => I64(*i as i64 + j),
                F32(j) => F32(*i as f32 + j),
                F64(j) => F64(*i as f64 + j),
                U128(j) => U128(*i as u128 + *j),
                I128(j) => I128(*i as i128 + *j),
                _ => Null,
            },
            U64(i) => match other {
                U8(j) => U64(i + *j as u64),
                I32(j) => U64(i + *j as u64),
                U32(j) => U64(i + *j as u64),
                U64(j) => U64(i + j),
                F32(j) => F32(*i as f32 + j),
                F64(j) => F64(*i as f64 + j),
                U128(j) => U128(*i as u128 + *j),
                I128(j) => I128(*i as i128 + *j),
                _ => Null,
            },
            F32(i) => match other {
                I32(j) => F32(i + *j as f32),
                U32(j) => F32(i + *j as f32),
                F32(j) => F32(i + j),
                F64(j) => F64(*i as f64 + j),
                U128(j) => U128(*i as u128 + *j),
                I128(j) => I128(*i as i128 + *j),
                _ => Null,
            },
            F64(i) => match other {
                U8(j) => F64(i + *j as f64),
                I32(j) => F64(i + *j as f64),
                U32(j) => F64(i + *j as f64),
                U64(j) => F64(i + *j as f64),
                F32(j) => F64(i + *j as f64),
                F64(j) => F64(i + j),
                U128(j) => U128(*i as u128 + *j),
                I128(j) => I128(*i as i128 + *j),
                _ => Null,
            },
            U128(i) => match other {
                U8(j) => U128(i + *j as u128),
                I32(j) => U128(i + *j as u128),
                U32(j) => U128(i + *j as u128),
                U64(j) => U128(i + *j as u128),
                F32(j) => U128(i + *j as u128),
                F64(j) => U128(i + *j as u128),
                U128(j) => U128(*i + *j),
                I128(j) => I128(*i as i128 + *j),
                _ => Null,
            },
            I128(i) => match other {
                U8(j) => I128(i + *j as i128),
                I32(j) => I128(i + *j as i128),
                U32(j) => I128(i + *j as i128),
                U64(j) => I128(i + *j as i128),
                F32(j) => I128(i + *j as i128),
                F64(j) => I128(i + *j as i128),
                U128(j) => U128(*i as u128 + *j),
                I128(j) => I128(*i + *j),
                _ => Null,
            },
            String(i) => match other {
                String(j) => String(format!("{}{}", i, j).into()),
                _ => Null,
            },
            Bytes(i) => match other {
                String(j) => Bytes([i, j.as_bytes()].concat()),
                Bytes(j) => Bytes([i.as_slice(), j.as_slice()].concat()),
                _ => Null,
            },
            e => {
                warn!("Cannot add {e:?} with {other:?}");
                Null
            }
        }
    }
}

impl std::ops::Add<DataValue> for DataValue {
    type Output = DataValue;
    fn add(self, other: DataValue) -> DataValue {
        &self + &other
    }
}

/// Subtraction operator for `DataValue` references.
///
/// Supports subtraction between numeric types with automatic type promotion.
/// Returns `Null` for incompatible types.
impl std::ops::Sub<&DataValue> for &DataValue {
    type Output = DataValue;
    fn sub(self, other: &DataValue) -> DataValue {
        use DataValue::*;
        match self {
            U8(i) => match other {
                U8(j) => F32(*i as f32 - *j as f32),
                U32(j) => F32(*i as f32 - *j as f32),
                I32(j) => F32(*i as f32 - *j as f32),
                I64(j) => F64(*i as f64 - *j as f64),
                U64(j) => F64(*i as f64 - *j as f64),
                F32(j) => F32(*i as f32 - j),
                F64(j) => F64(*i as f64 - j),
                U128(j) => U128((*i as u128).saturating_sub(*j)),
                I128(j) => I128(*i as i128 - *j),
                _ => Null,
            },
            I32(i) => match other {
                U8(j) => F32(*i as f32 - *j as f32),
                U32(j) => F32(*i as f32 - *j as f32),
                I32(j) => F32(*i as f32 - *j as f32),
                I64(j) => F64(*i as f64 - *j as f64),
                U64(j) => F64(*i as f64 - *j as f64),
                F32(j) => F32(*i as f32 - j),
                F64(j) => F64(*i as f64 - j),
                U128(j) => U128((*i as u128).saturating_sub(*j)),
                I128(j) => I128(*i as i128 - *j),
                _ => Null,
            },
            I64(i) => match other {
                U8(j) => F32(*i as f32 - *j as f32),
                U32(j) => F32(*i as f32 - *j as f32),
                I32(j) => F32(*i as f32 - *j as f32),
                I64(j) => F64(*i as f64 - *j as f64),
                U64(j) => F64(*i as f64 - *j as f64),
                F32(j) => F32(*i as f32 - j),
                F64(j) => F64(*i as f64 - j),
                U128(j) => U128((*i as u128).saturating_sub(*j)),
                I128(j) => I128(*i as i128 - *j),
                _ => Null,
            },
            U32(i) => match other {
                U8(j) => F32(*i as f32 - *j as f32),
                U32(j) => F32(*i as f32 - *j as f32),
                I32(j) => F32(*i as f32 - *j as f32),
                I64(j) => F64(*i as f64 - *j as f64),
                U64(j) => F64(*i as f64 - *j as f64),
                F32(j) => F32(*i as f32 - j),
                F64(j) => F64(*i as f64 - j),
                U128(j) => U128((*i as u128).saturating_sub(*j)),
                I128(j) => I128(*i as i128 - *j),
                _ => Null,
            },
            U64(i) => match other {
                U8(j) => F32(*i as f32 - *j as f32),
                U32(j) => F32(*i as f32 - *j as f32),
                I32(j) => F32(*i as f32 - *j as f32),
                I64(j) => F64(*i as f64 - *j as f64),
                U64(j) => F64(*i as f64 - *j as f64),
                F32(j) => F32(*i as f32 - j),
                F64(j) => F64(*i as f64 - j),
                U128(j) => U128((*i as u128).saturating_sub(*j)),
                I128(j) => I128(*i as i128 - *j),
                _ => Null,
            },
            F32(i) => match other {
                U8(j) => F32(*i - *j as f32),
                U32(j) => F32(*i - *j as f32),
                I32(j) => F32(*i - *j as f32),
                I64(j) => F64(*i as f64 - *j as f64),
                U64(j) => F64(*i as f64 - *j as f64),
                F32(j) => F32(i - j),
                F64(j) => F64(*i as f64 - j),
                U128(j) => U128((*i as u128).saturating_sub(*j)),
                I128(j) => I128(*i as i128 - *j),
                _ => Null,
            },
            F64(i) => match other {
                U8(j) => F32(*i as f32 - *j as f32),
                U32(j) => F32(*i as f32 - *j as f32),
                I32(j) => F32(*i as f32 - *j as f32),
                I64(j) => F64(*i - *j as f64),
                U64(j) => F64(*i - *j as f64),
                F32(j) => F32(*i as f32 - j),
                F64(j) => F64(i - j),
                U128(j) => U128((*i as u128).saturating_sub(*j)),
                I128(j) => I128(*i as i128 - *j),
                _ => Null,
            },
            U128(i) => match other {
                U8(j) => U128(i - *j as u128),
                I32(j) => U128(i - *j as u128),
                U32(j) => U128(i - *j as u128),
                U64(j) => U128(i - *j as u128),
                F32(j) => U128(i - *j as u128),
                F64(j) => U128(i - *j as u128),
                U128(j) => U128((*i).saturating_sub(*j)),
                I128(j) => I128(*i as i128 - *j),
                _ => Null,
            },
            I128(i) => match other {
                U8(j) => I128(i - *j as i128),
                I32(j) => I128(i - *j as i128),
                U32(j) => I128(i - *j as i128),
                U64(j) => I128(i - *j as i128),
                F32(j) => I128(i - *j as i128),
                F64(j) => I128(i - *j as i128),
                U128(j) => U128((*i as u128).saturating_sub(*j)),
                I128(j) => I128(*i - *j),
                _ => Null,
            },
            e => {
                warn!("Cannot sub {e:?} with {other:?}");
                Null
            }
        }
    }
}

impl std::ops::Sub<DataValue> for DataValue {
    type Output = DataValue;
    fn sub(self, other: DataValue) -> DataValue {
        &self - &other
    }
}

/// Multiplication operator for `DataValue` references.
///
/// Supports multiplication between numeric types with automatic type promotion.
/// Returns `Null` for incompatible types.
impl std::ops::Mul<&DataValue> for &DataValue {
    type Output = DataValue;
    fn mul(self, other: &DataValue) -> DataValue {
        use DataValue::*;
        match self {
            U8(i) => match other {
                U8(j) => F32(*i as f32 * *j as f32),
                U32(j) => F32(*i as f32 * *j as f32),
                I32(j) => F32(*i as f32 * *j as f32),
                I64(j) => F64(*i as f64 * *j as f64),
                U64(j) => F64(*i as f64 * *j as f64),
                F32(j) => F32(*i as f32 * j),
                F64(j) => F64(*i as f64 * j),
                U128(j) => F64(*i as f64 * *j as f64),
                I128(j) => F64(*i as f64 * *j as f64),
                _ => Null,
            },
            I32(i) => match other {
                U8(j) => F32(*i as f32 * *j as f32),
                U32(j) => F32(*i as f32 * *j as f32),
                I32(j) => F32(*i as f32 * *j as f32),
                I64(j) => F64(*i as f64 * *j as f64),
                U64(j) => F64(*i as f64 * *j as f64),
                F32(j) => F32(*i as f32 * j),
                F64(j) => F64(*i as f64 * j),
                U128(j) => F64(*i as f64 * *j as f64),
                I128(j) => F64(*i as f64 * *j as f64),
                _ => Null,
            },
            I64(i) => match other {
                U8(j) => F32(*i as f32 * *j as f32),
                U32(j) => F32(*i as f32 * *j as f32),
                I32(j) => F32(*i as f32 * *j as f32),
                I64(j) => F64(*i as f64 * *j as f64),
                U64(j) => F64(*i as f64 * *j as f64),
                F32(j) => F32(*i as f32 * j),
                F64(j) => F64(*i as f64 * j),
                U128(j) => F64(*i as f64 * *j as f64),
                I128(j) => F64(*i as f64 * *j as f64),
                _ => Null,
            },
            U32(i) => match other {
                U8(j) => F32(*i as f32 * *j as f32),
                U32(j) => F32(*i as f32 * *j as f32),
                I32(j) => F32(*i as f32 * *j as f32),
                I64(j) => F64(*i as f64 * *j as f64),
                U64(j) => F64(*i as f64 * *j as f64),
                F32(j) => F32(*i as f32 * j),
                F64(j) => F64(*i as f64 * j),
                U128(j) => F64(*i as f64 * *j as f64),
                I128(j) => F64(*i as f64 * *j as f64),
                _ => Null,
            },
            U64(i) => match other {
                U8(j) => F32(*i as f32 * *j as f32),
                U32(j) => F32(*i as f32 * *j as f32),
                I32(j) => F32(*i as f32 * *j as f32),
                I64(j) => F64(*i as f64 * *j as f64),
                U64(j) => F64(*i as f64 * *j as f64),
                F32(j) => F32(*i as f32 * j),
                F64(j) => F64(*i as f64 * j),
                U128(j) => F64(*i as f64 * *j as f64),
                I128(j) => F64(*i as f64 * *j as f64),
                _ => Null,
            },
            F32(i) => match other {
                U8(j) => F32(*i * *j as f32),
                U32(j) => F32(*i * *j as f32),
                I32(j) => F32(*i * *j as f32),
                I64(j) => F64(*i as f64 * *j as f64),
                U64(j) => F64(*i as f64 * *j as f64),
                F32(j) => F32(i * j),
                F64(j) => F64(*i as f64 * j),
                U128(j) => F64(*i as f64 * *j as f64),
                I128(j) => F64(*i as f64 * *j as f64),
                _ => Null,
            },
            F64(i) => match other {
                U8(j) => F32(*i as f32 * *j as f32),
                U32(j) => F32(*i as f32 * *j as f32),
                I32(j) => F32(*i as f32 * *j as f32),
                I64(j) => F64(*i * *j as f64),
                U64(j) => F64(*i * *j as f64),
                F32(j) => F32(*i as f32 * j),
                F64(j) => F64(i * j),
                U128(j) => F64(*i * *j as f64),
                I128(j) => F64(*i * *j as f64),
                _ => Null,
            },
            U128(i) => match other {
                U8(j) => F64(*i as f64 * *j as f64),
                I32(j) => F64(*i as f64 * *j as f64),
                U32(j) => F64(*i as f64 * *j as f64),
                U64(j) => F64(*i as f64 * *j as f64),
                F32(j) => F64(*i as f64 * *j as f64),
                F64(j) => F64(*i as f64 * *j),
                U128(j) => F64(*i as f64 * *j as f64),
                I128(j) => F64(*i as f64 * *j as f64),
                _ => Null,
            },
            I128(i) => match other {
                U8(j) => F64(*i as f64 * *j as f64),
                I32(j) => F64(*i as f64 * *j as f64),
                U32(j) => F64(*i as f64 * *j as f64),
                U64(j) => F64(*i as f64 * *j as f64),
                F32(j) => F64(*i as f64 * *j as f64),
                F64(j) => F64(*i as f64 * *j),
                U128(j) => F64(*i as f64 * *j as f64),
                I128(j) => F64(*i as f64 * *j as f64),
                _ => Null,
            },
            e => {
                warn!("Cannot sub {e:?} with {other:?}");
                Null
            }
        }
    }
}

impl std::ops::Mul<DataValue> for DataValue {
    type Output = DataValue;
    fn mul(self, other: DataValue) -> DataValue {
        &self * &other
    }
}

/// Division operator for `DataValue` references.
///
/// Supports division between numeric types with automatic type promotion.
/// Returns `Null` for incompatible types.
impl std::ops::Div<&DataValue> for &DataValue {
    type Output = DataValue;
    fn div(self, other: &DataValue) -> DataValue {
        use DataValue::*;
        match self {
            U8(i) => match other {
                U8(j) => F32(*i as f32 / *j as f32),
                U32(j) => F32(*i as f32 / *j as f32),
                I32(j) => F32(*i as f32 / *j as f32),
                I64(j) => F64(*i as f64 / *j as f64),
                U64(j) => F64(*i as f64 / *j as f64),
                F32(j) => F32(*i as f32 / j),
                F64(j) => F64(*i as f64 / j),
                U128(j) => F64(*i as f64 * *j as f64),
                I128(j) => F64(*i as f64 * *j as f64),
                _ => Null,
            },
            I32(i) => match other {
                U8(j) => F32(*i as f32 / *j as f32),
                U32(j) => F32(*i as f32 / *j as f32),
                I32(j) => F32(*i as f32 / *j as f32),
                I64(j) => F64(*i as f64 / *j as f64),
                U64(j) => F64(*i as f64 / *j as f64),
                F32(j) => F32(*i as f32 / j),
                F64(j) => F64(*i as f64 / j),
                U128(j) => F64(*i as f64 * *j as f64),
                I128(j) => F64(*i as f64 * *j as f64),
                _ => Null,
            },
            I64(i) => match other {
                U8(j) => F32(*i as f32 / *j as f32),
                U32(j) => F32(*i as f32 / *j as f32),
                I32(j) => F32(*i as f32 / *j as f32),
                I64(j) => F64(*i as f64 / *j as f64),
                U64(j) => F64(*i as f64 / *j as f64),
                F32(j) => F32(*i as f32 / j),
                F64(j) => F64(*i as f64 / j),
                U128(j) => F64(*i as f64 * *j as f64),
                I128(j) => F64(*i as f64 * *j as f64),
                _ => Null,
            },
            U32(i) => match other {
                U8(j) => F32(*i as f32 / *j as f32),
                U32(j) => F32(*i as f32 / *j as f32),
                I32(j) => F32(*i as f32 / *j as f32),
                I64(j) => F64(*i as f64 / *j as f64),
                U64(j) => F64(*i as f64 / *j as f64),
                F32(j) => F32(*i as f32 / j),
                F64(j) => F64(*i as f64 / j),
                U128(j) => F64(*i as f64 * *j as f64),
                I128(j) => F64(*i as f64 * *j as f64),
                _ => Null,
            },
            U64(i) => match other {
                U8(j) => F32(*i as f32 / *j as f32),
                U32(j) => F32(*i as f32 / *j as f32),
                I32(j) => F32(*i as f32 / *j as f32),
                I64(j) => F64(*i as f64 / *j as f64),
                U64(j) => F64(*i as f64 / *j as f64),
                F32(j) => F32(*i as f32 / j),
                F64(j) => F64(*i as f64 / j),
                U128(j) => F64(*i as f64 * *j as f64),
                I128(j) => F64(*i as f64 * *j as f64),
                _ => Null,
            },
            F32(i) => match other {
                U8(j) => F32(*i / *j as f32),
                U32(j) => F32(*i / *j as f32),
                I32(j) => F32(*i / *j as f32),
                I64(j) => F64(*i as f64 / *j as f64),
                U64(j) => F64(*i as f64 / *j as f64),
                F32(j) => F32(i / j),
                F64(j) => F64(*i as f64 / j),
                U128(j) => F64(*i as f64 * *j as f64),
                I128(j) => F64(*i as f64 * *j as f64),
                _ => Null,
            },
            F64(i) => match other {
                U8(j) => F32(*i as f32 / *j as f32),
                U32(j) => F32(*i as f32 / *j as f32),
                I32(j) => F32(*i as f32 / *j as f32),
                I64(j) => F64(*i / *j as f64),
                U64(j) => F64(*i / *j as f64),
                F32(j) => F32(*i as f32 / j),
                F64(j) => F64(i / j),
                U128(j) => F64(*i * *j as f64),
                I128(j) => F64(*i * *j as f64),
                _ => Null,
            },
            U128(i) => match other {
                U8(j) => F64(*i as f64 / *j as f64),
                I32(j) => F64(*i as f64 / *j as f64),
                U32(j) => F64(*i as f64 / *j as f64),
                U64(j) => F64(*i as f64 / *j as f64),
                F32(j) => F64(*i as f64 / *j as f64),
                F64(j) => F64(*i as f64 / *j),
                U128(j) => F64(*i as f64 / *j as f64),
                I128(j) => F64(*i as f64 / *j as f64),
                _ => Null,
            },
            I128(i) => match other {
                U8(j) => F64(*i as f64 / *j as f64),
                I32(j) => F64(*i as f64 / *j as f64),
                U32(j) => F64(*i as f64 / *j as f64),
                U64(j) => F64(*i as f64 / *j as f64),
                F32(j) => F64(*i as f64 / *j as f64),
                F64(j) => F64(*i as f64 / *j),
                U128(j) => F64(*i as f64 / *j as f64),
                I128(j) => F64(*i as f64 / *j as f64),
                _ => Null,
            },
            e => {
                warn!("Cannot sub {e:?} with {other:?}");
                Null
            }
        }
    }
}

impl std::ops::Div<DataValue> for DataValue {
    type Output = DataValue;
    fn div(self, other: DataValue) -> DataValue {
        &self / &other
    }
}

impl std::ops::AddAssign<&DataValue> for DataValue {
    fn add_assign(&mut self, other: &DataValue) {
        *self = &*self + other;
    }
}

impl std::ops::SubAssign<&DataValue> for DataValue {
    fn sub_assign(&mut self, other: &DataValue) {
        *self = &*self - other;
    }
}

impl std::ops::MulAssign<&DataValue> for DataValue {
    fn mul_assign(&mut self, other: &DataValue) {
        *self = &*self * other;
    }
}

impl std::ops::DivAssign<&DataValue> for DataValue {
    fn div_assign(&mut self, other: &DataValue) {
        *self = &*self / other;
    }
}

#[cfg(test)]
mod test {
    use super::*;
    #[test]
    fn add_datavalue_same() {
        let a = DataValue::U32(1);
        let b = DataValue::U32(2);
        let c = a + b;
        assert_eq!(c, DataValue::U32(3));
        let a = DataValue::U64(1);
        let b = DataValue::U64(2);
        let c = a + b;
        assert_eq!(c, DataValue::U64(3));
        let a = DataValue::F32(1f32);
        let b = DataValue::F32(2f32);
        let c = a + b;
        assert_eq!(c, DataValue::F32(3f32));
        let a = DataValue::F64(1f64);
        let b = DataValue::F64(2f64);
        let c = a + b;
        assert_eq!(c, DataValue::F64(3f64));
        let a = DataValue::I32(1);
        let b = DataValue::I32(2);
        let c = a + b;
        assert_eq!(c, DataValue::I32(3));
        let a = DataValue::I64(1);
        let b = DataValue::I64(2);
        let c = a + b;
        assert_eq!(c, DataValue::I64(3));
        let a = DataValue::String("1".into());
        let b = DataValue::String("2".into());
        let c = a + b;
        assert_eq!(c, DataValue::String("12".into()));
        let a = DataValue::Bytes(vec![1u8]);
        let b = DataValue::Bytes(vec![2u8]);
        let c = a + b;
        assert_eq!(c, DataValue::Bytes(vec![1u8, 2u8]));
        let a = DataValue::I128(1);
        let b = DataValue::I128(2);
        let c = a + b;
        assert_eq!(c, DataValue::I128(3));
        let a = DataValue::U128(1);
        let b = DataValue::U128(2);
        let c = b + a;
        assert_eq!(c, DataValue::U128(3));
    }

    #[test]
    fn sub_datavalue_same() {
        let a = DataValue::U32(1);
        let b = DataValue::U32(2);
        let c = a - b;
        assert_eq!(c, DataValue::F32(-1f32));
        let a = DataValue::U64(1);
        let b = DataValue::U64(2);
        let c = a - b;
        assert_eq!(c, DataValue::F64(-1f64));
        let a = DataValue::F32(1f32);
        let b = DataValue::F32(2f32);
        let c = a - b;
        assert_eq!(c, DataValue::F32(-1f32));
        let a = DataValue::F64(1f64);
        let b = DataValue::F64(2f64);
        let c = a - b;
        assert_eq!(c, DataValue::F64(-1f64));
        let a = DataValue::I32(1);
        let b = DataValue::I32(2);
        let c = a - b;
        assert_eq!(c, DataValue::F32(-1f32));
        let a = DataValue::I64(1);
        let b = DataValue::I64(2);
        let c = a - b;
        assert_eq!(c, DataValue::F64(-1f64));
        let a = DataValue::String("1".into());
        let b = DataValue::String("2".into());
        let c = a - b;
        assert_eq!(c, DataValue::Null);
        let a = DataValue::Bytes(vec![1u8]);
        let b = DataValue::Bytes(vec![2u8]);
        let c = a - b;
        assert_eq!(c, DataValue::Null);
        let a = DataValue::I128(1);
        let b = DataValue::I128(2);
        let c = a - b;
        assert_eq!(c, DataValue::I128(-1));
        let a = DataValue::U128(1);
        let b = DataValue::U128(2);
        let c = b - a;
        assert_eq!(c, DataValue::U128(1));
    }

    #[test]
    fn div_datavalue_same() {
        let a = DataValue::U32(1);
        let b = DataValue::U32(2);
        let c = a / b;
        assert_eq!(c, DataValue::F32(0.5f32));
        let a = DataValue::U64(1);
        let b = DataValue::U64(2);
        let c = a / b;
        assert_eq!(c, DataValue::F64(0.5f64));
        let a = DataValue::F32(1f32);
        let b = DataValue::F32(2f32);
        let c = a / b;
        assert_eq!(c, DataValue::F32(0.5f32));
        let a = DataValue::F64(1f64);
        let b = DataValue::F64(2f64);
        let c = a / b;
        assert_eq!(c, DataValue::F64(0.5f64));
        let a = DataValue::I32(1);
        let b = DataValue::I32(2);
        let c = a / b;
        assert_eq!(c, DataValue::F32(0.5f32));
        let a = DataValue::I64(1);
        let b = DataValue::I64(2);
        let c = a / b;
        assert_eq!(c, DataValue::F64(0.5f64));
        let a = DataValue::String("1".into());
        let b = DataValue::String("2".into());
        let c = a / b;
        assert_eq!(c, DataValue::Null);
        let a = DataValue::Bytes(vec![1u8]);
        let b = DataValue::Bytes(vec![2u8]);
        let c = a / b;
        assert_eq!(c, DataValue::Null);
        let a = DataValue::I128(1);
        let b = DataValue::I128(2);
        let c = a / b;
        assert_eq!(c, DataValue::F64(0.5f64));
        let a = DataValue::U128(1);
        let b = DataValue::U128(2);
        let c = a / b;
        assert_eq!(c, DataValue::F64(0.5f64));
    }

    #[test]
    fn mul_datavalue_same() {
        let a = DataValue::U32(1);
        let b = DataValue::U32(2);
        let c = a * b;
        assert_eq!(c, DataValue::F32(2f32));
        let a = DataValue::U64(1);
        let b = DataValue::U64(2);
        let c = a * b;
        assert_eq!(c, DataValue::F64(2f64));
        let a = DataValue::F32(1f32);
        let b = DataValue::F32(2f32);
        let c = a * b;
        assert_eq!(c, DataValue::F32(2f32));
        let a = DataValue::F64(1f64);
        let b = DataValue::F64(2f64);
        let c = a * b;
        assert_eq!(c, DataValue::F64(2f64));
        let a = DataValue::I32(1);
        let b = DataValue::I32(2);
        let c = a * b;
        assert_eq!(c, DataValue::F32(2f32));
        let a = DataValue::I64(1);
        let b = DataValue::I64(2);
        let c = a * b;
        assert_eq!(c, DataValue::F64(2f64));
        let a = DataValue::String("1".into());
        let b = DataValue::String("2".into());
        let c = a * b;
        assert_eq!(c, DataValue::Null);
        let a = DataValue::Bytes(vec![1u8]);
        let b = DataValue::Bytes(vec![2u8]);
        let c = a * b;
        assert_eq!(c, DataValue::Null);
        let a = DataValue::I128(1);
        let b = DataValue::I128(2);
        let c = a * b;
        assert_eq!(c, DataValue::F64(2f64));
        let a = DataValue::U128(1);
        let b = DataValue::U128(2);
        let c = a * b;
        assert_eq!(c, DataValue::F64(2f64));
    }
    use rstest::*;
    #[rstest]
    #[case(
        vec![
            DataValue::U32(1),
            DataValue::I32(2),
            DataValue::U64(3),
            DataValue::I64(4),
            DataValue::F32(5f32),
            DataValue::F64(6f64),
            DataValue::U128(7),
            DataValue::I128(8),
            DataValue::U8(1)
        ],
        vec![
            DataValue::U32(1),
            DataValue::I32(2),
            DataValue::U64(3),
            DataValue::I64(4),
            DataValue::F32(5f32),
            DataValue::F64(6f64),
            DataValue::U128(7),
            DataValue::I128(8),
            DataValue::U8(1)
        ]
    )]
    fn operations(#[case] a: Vec<DataValue>, #[case] b: Vec<DataValue>) {
        for a_in in a.iter() {
            for b_in in b.iter() {
                assert_eq!(a_in + b_in, a_in + b_in);
                assert_eq!(a_in - b_in, a_in - b_in);
                assert_eq!(a_in * b_in, a_in * b_in);
                assert_eq!(a_in / b_in, a_in / b_in);

                let mut aa_in = a_in.clone();
                aa_in += b_in;
                assert_eq!(aa_in, a_in + b_in);
                let mut aa_in = a_in.clone();
                aa_in -= b_in;
                assert_eq!(aa_in, a_in - b_in);
                let mut aa_in = a_in.clone();
                aa_in *= b_in;
                assert_eq!(aa_in, a_in * b_in);
                let mut aa_in = a_in.clone();
                aa_in /= b_in;
                assert_eq!(aa_in, a_in / b_in);
            }
        }
    }
}
