//! Arithmetic operation tests with property-based testing.

use clock_bigint::{BigInt, BigIntCore, U256, add, mul, sub};
use proptest::prelude::*;

proptest! {
    #[test]
    fn test_add_commutative(a in 0u64..1000, b in 0u64..1000) {
        let a_bi = BigInt::from_u64(a, 10);
        let b_bi = BigInt::from_u64(b, 10);

        let sum1 = add(&a_bi, &b_bi).unwrap();
        let sum2 = add(&b_bi, &a_bi).unwrap();

        assert_eq!(sum1.limbs()[0], sum2.limbs()[0]);
    }

    #[test]
    fn test_add_identity(a in 0u64..1000) {
        let a_bi = BigInt::from_u64(a, 10);
        let zero = BigInt::from_u64(0, 10);

        let sum = add(&a_bi, &zero).unwrap();
        assert_eq!(sum.limbs()[0], a);
    }

    #[test]
    fn test_sub_inverse(a in 0u64..1000, b in 0u64..1000) {
        let a_bi = BigInt::from_u64(a, 10);
        let b_bi = BigInt::from_u64(b, 10);

        let diff = sub(&a_bi, &b_bi).unwrap();
        let restored = add(&diff, &b_bi).unwrap();

        // If a >= b, restored should equal a
        if a >= b {
            assert_eq!(restored.limbs()[0], a);
        }
    }

    #[test]
    fn test_mul_commutative(a in 0u64..100, b in 0u64..100) {
        let a_bi = BigInt::from_u64(a, 10);
        let b_bi = BigInt::from_u64(b, 10);

        let prod1 = mul(&a_bi, &b_bi).unwrap();
        let prod2 = mul(&b_bi, &a_bi).unwrap();

        assert_eq!(prod1.limbs()[0], prod2.limbs()[0]);
    }

    #[test]
    fn test_mul_zero(a in 0u64..1000) {
        let a_bi = BigInt::from_u64(a, 10);
        let zero = BigInt::from_u64(0, 10);

        let prod = mul(&a_bi, &zero).unwrap();
        assert!(prod.is_zero());
    }

    #[test]
    fn test_mul_identity(a in 0u64..1000) {
        let a_bi = BigInt::from_u64(a, 10);
        let one = BigInt::from_u64(1, 10);

        let prod = mul(&a_bi, &one).unwrap();
        assert_eq!(prod.limbs()[0], a);
    }
}

#[test]
fn test_add_simple() {
    let a = BigInt::from_u64(10, 10);
    let b = BigInt::from_u64(20, 10);
    let sum = add(&a, &b).unwrap();
    assert_eq!(sum.limbs()[0], 30);
}

#[test]
fn test_sub_simple() {
    let a = BigInt::from_u64(20, 10);
    let b = BigInt::from_u64(10, 10);
    let diff = sub(&a, &b).unwrap();
    assert_eq!(diff.limbs()[0], 10);
}

#[test]
fn test_mul_simple() {
    let a = BigInt::from_u64(6, 10);
    let b = BigInt::from_u64(7, 10);
    let prod = mul(&a, &b).unwrap();
    assert_eq!(prod.limbs()[0], 42);
}

#[test]
fn test_add_with_carry() {
    let a = BigInt::from_u64(u64::MAX, 10);
    let b = BigInt::from_u64(1, 10);
    // This should require 2 limbs
    let _sum = add(&a, &b);
    // Result depends on implementation - may overflow for single-limb BigInt
}

#[test]
fn test_u256_operations() {
    let a = U256::from_u64(10);
    let b = U256::from_u64(20);
    // U256 operations would need to be implemented for fixed-length types
    // For now, we test the type creation
    assert_eq!(a.limbs()[0], 10);
    assert_eq!(b.limbs()[0], 20);
}
