//! Tests for division and modulo operations.
//!
//! This module contains comprehensive tests for BigInt division operations
//! including division with remainder, division only, and remainder only.
//! Tests cover various input sizes, edge cases, and error conditions.

use clock_bigint::{BigInt, BigIntCore, BigIntError, div, div_rem, rem};

#[test]
fn test_div_rem_basic() {
    let a = BigInt::from_u64(10, 10);
    let b = BigInt::from_u64(3, 10);

    let (quotient, remainder) = div_rem(&a, &b).unwrap();

    assert_eq!(quotient.limbs()[0], 3); // 10 / 3 = 3
    assert_eq!(remainder.limbs()[0], 1); // 10 % 3 = 1
}

#[test]
fn test_div_rem_exact() {
    let a = BigInt::from_u64(12, 10);
    let b = BigInt::from_u64(4, 10);

    let (quotient, remainder) = div_rem(&a, &b).unwrap();

    assert_eq!(quotient.limbs()[0], 3); // 12 / 4 = 3
    assert_eq!(remainder.limbs()[0], 0); // 12 % 4 = 0
}

#[test]
fn test_div_rem_large_numbers() {
    let a = BigInt::from_limbs(&[10, 0], 4).unwrap(); // 10 + 0 * 2^64 = 10
    let b = BigInt::from_u64(3, 4);

    let (quotient, remainder) = div_rem(&a, &b).unwrap();

    // 10 / 3 = 3 remainder 1
    assert_eq!(quotient.limbs()[0], 3);
    assert_eq!(remainder.limbs()[0], 1);
}

#[test]
fn test_div_rem_by_one() {
    let a = BigInt::from_u64(42, 10);
    let b = BigInt::from_u64(1, 10);

    let (quotient, remainder) = div_rem(&a, &b).unwrap();

    assert_eq!(quotient.limbs()[0], 42);
    assert_eq!(remainder.limbs()[0], 0);
}

#[test]
fn test_div_rem_equal() {
    let a = BigInt::from_u64(7, 10);
    let b = BigInt::from_u64(7, 10);

    let (quotient, remainder) = div_rem(&a, &b).unwrap();

    assert_eq!(quotient.limbs()[0], 1);
    assert_eq!(remainder.limbs()[0], 0);
}

#[test]
fn test_div_rem_divide_by_zero() {
    let a = BigInt::from_u64(10, 10);
    let b = BigInt::from_u64(0, 10);

    let result = div_rem(&a, &b);
    assert!(matches!(result, Err(BigIntError::DivisionByZero)));
}

#[test]
fn test_div_rem_zero_dividend() {
    let a = BigInt::from_u64(0, 10);
    let b = BigInt::from_u64(5, 10);

    let (quotient, remainder) = div_rem(&a, &b).unwrap();

    assert_eq!(quotient.limbs()[0], 0);
    assert_eq!(remainder.limbs()[0], 0);
}

#[test]
fn test_div_basic() {
    let a = BigInt::from_u64(10, 10);
    let b = BigInt::from_u64(3, 10);

    let quotient = div(&a, &b).unwrap();
    assert_eq!(quotient.limbs()[0], 3); // 10 / 3 = 3
}

#[test]
fn test_div_exact() {
    let a = BigInt::from_u64(12, 10);
    let b = BigInt::from_u64(4, 10);

    let quotient = div(&a, &b).unwrap();
    assert_eq!(quotient.limbs()[0], 3); // 12 / 4 = 3
}

#[test]
fn test_div_by_zero() {
    let a = BigInt::from_u64(10, 10);
    let b = BigInt::from_u64(0, 10);

    let result = div(&a, &b);
    assert!(matches!(result, Err(BigIntError::DivisionByZero)));
}

#[test]
fn test_rem_basic() {
    let a = BigInt::from_u64(10, 10);
    let b = BigInt::from_u64(3, 10);

    let remainder = rem(&a, &b).unwrap();
    assert_eq!(remainder.limbs()[0], 1); // 10 % 3 = 1
}

#[test]
fn test_rem_exact() {
    let a = BigInt::from_u64(12, 10);
    let b = BigInt::from_u64(4, 10);

    let remainder = rem(&a, &b).unwrap();
    assert_eq!(remainder.limbs()[0], 0); // 12 % 4 = 0
}

#[test]
fn test_rem_by_zero() {
    let a = BigInt::from_u64(10, 10);
    let b = BigInt::from_u64(0, 10);

    let result = rem(&a, &b);
    assert!(matches!(result, Err(BigIntError::DivisionByZero)));
}

#[test]
fn test_rem_zero_dividend() {
    let a = BigInt::from_u64(0, 10);
    let b = BigInt::from_u64(5, 10);

    let remainder = rem(&a, &b).unwrap();
    assert_eq!(remainder.limbs()[0], 0);
}

#[test]
fn test_div_rem_consistency() {
    let a = BigInt::from_u64(17, 10);
    let b = BigInt::from_u64(5, 10);

    let (q1, r1) = div_rem(&a, &b).unwrap();
    let q2 = div(&a, &b).unwrap();
    let r2 = rem(&a, &b).unwrap();

    assert_eq!(q1.limbs()[0], q2.limbs()[0]);
    assert_eq!(r1.limbs()[0], r2.limbs()[0]);
}

#[test]
fn test_div_rem_identity() {
    // Test: a = q * b + r
    let a = BigInt::from_u64(23, 10);
    let b = BigInt::from_u64(7, 10);

    let (q, r) = div_rem(&a, &b).unwrap();

    // Compute q * b + r
    let qb = mul(&q, &b).unwrap();
    let reconstructed = add(&qb, &r).unwrap();

    assert_eq!(a.limbs()[0], reconstructed.limbs()[0]);
    assert!(r.limbs()[0] < b.limbs()[0]); // r < b
}

#[test]
fn test_div_rem_negative_divisor() {
    let a = BigInt::from_u64(10, 10);
    let mut b = BigInt::from_u64(3, 10);
    b.set_sign(true); // Make b negative

    let (_quotient, remainder) = div_rem(&a, &b).unwrap();

    // Result should maintain the invariant: a = q * b + r
    assert!(remainder.limbs()[0] < 3); // |r| < |b|
    assert!(!remainder.sign()); // remainder should be non-negative
}

#[test]
fn test_div_rem_with_carry() {
    // Test division that requires borrowing across limbs
    let a = BigInt::from_limbs(&[1, 0, 0, 0], 4).unwrap(); // 2^64
    let b = BigInt::from_u64(3, 4);

    let (quotient, remainder) = div_rem(&a, &b).unwrap();

    // Verify: a = q * b + r
    let qb = mul(&quotient, &b).unwrap();
    let reconstructed = add(&qb, &remainder).unwrap();

    assert_eq!(a.limbs(), reconstructed.limbs());
    assert!(remainder.limbs()[0] < 3); // r < b
}

fn mul(a: &BigInt, b: &BigInt) -> Result<BigInt, BigIntError> {
    use clock_bigint::mul;
    mul(a, b)
}

fn add(a: &BigInt, b: &BigInt) -> Result<BigInt, BigIntError> {
    use clock_bigint::add;
    add(a, b)
}
