//! Benchmarks for division and modulo operations.
//!
//! This module contains performance benchmarks for BigInt division operations
//! including division with remainder, division only, and remainder only.
//! Benchmarks cover various input sizes and edge cases.

use clock_bigint::{BigInt, div, div_rem, rem};
use criterion::{Criterion, criterion_group, criterion_main};

fn bench_division_u256(c: &mut Criterion) {
    let mut group = c.benchmark_group("u256_division");

    // Benchmark 256-bit division with remainder
    group.bench_function("u256_div_rem", |bencher| {
        let a = BigInt::from_limbs(
            &[
                0x123456789ABCDEF0,
                0xFEDCBA9876543210,
                0x1111111111111111,
                0x2222222222222222,
            ],
            8,
        )
        .unwrap();
        let b = BigInt::from_limbs(
            &[
                0x3333333333333333,
                0x4444444444444444,
                0x5555555555555555,
                0x6666666666666666,
            ],
            8,
        )
        .unwrap();

        bencher.iter(|| {
            std::hint::black_box(div_rem(&a, &b)).unwrap();
        });
    });

    // Benchmark 256-bit division only
    group.bench_function("u256_div", |bencher| {
        let a = BigInt::from_limbs(
            &[
                0x123456789ABCDEF0,
                0xFEDCBA9876543210,
                0x1111111111111111,
                0x2222222222222222,
            ],
            8,
        )
        .unwrap();
        let b = BigInt::from_limbs(
            &[
                0x3333333333333333,
                0x4444444444444444,
                0x5555555555555555,
                0x6666666666666666,
            ],
            8,
        )
        .unwrap();

        bencher.iter(|| {
            std::hint::black_box(div(&a, &b)).unwrap();
        });
    });

    // Benchmark 256-bit remainder only
    group.bench_function("u256_rem", |bencher| {
        let a = BigInt::from_limbs(
            &[
                0x123456789ABCDEF0,
                0xFEDCBA9876543210,
                0x1111111111111111,
                0x2222222222222222,
            ],
            8,
        )
        .unwrap();
        let b = BigInt::from_limbs(
            &[
                0x3333333333333333,
                0x4444444444444444,
                0x5555555555555555,
                0x6666666666666666,
            ],
            8,
        )
        .unwrap();

        bencher.iter(|| {
            std::hint::black_box(rem(&a, &b)).unwrap();
        });
    });

    group.finish();
}

fn bench_division_u2048(c: &mut Criterion) {
    let mut group = c.benchmark_group("u2048_division");

    // Benchmark 2048-bit division with remainder
    group.bench_function("u2048_div_rem", |bencher| {
        let a = BigInt::from_limbs(
            &[
                0x123456789ABCDEF0,
                0xFEDCBA9876543210,
                0x1111111111111111,
                0x2222222222222222,
                0x3333333333333333,
                0x4444444444444444,
                0x5555555555555555,
                0x6666666666666666,
                0x7777777777777777,
                0x8888888888888888,
                0x9999999999999999,
                0xAAAAAAAAAAAAAAAA,
                0xBBBBBBBBBBBBBBBB,
                0xCCCCCCCCCCCCCCCC,
                0xDDDDDDDDDDDDDDDD,
                0xEEEEEEEEEEEEEEEE,
                0xFFFFFFFFFFFFFFFF,
                0x0000000000000000,
                0x1111111111111111,
                0x2222222222222222,
                0x3333333333333333,
                0x4444444444444444,
                0x5555555555555555,
                0x6666666666666666,
                0x7777777777777777,
                0x8888888888888888,
                0x9999999999999999,
                0xAAAAAAAAAAAAAAAA,
                0xBBBBBBBBBBBBBBBB,
                0xCCCCCCCCCCCCCCCC,
                0xDDDDDDDDDDDDDDDD,
                0xEEEEEEEEEEEEEEEE,
            ],
            64,
        )
        .unwrap();

        let b = BigInt::from_limbs(
            &[
                0xAAAAAAAAAAAAAAAA,
                0xBBBBBBBBBBBBBBBB,
                0xCCCCCCCCCCCCCCCC,
                0xDDDDDDDDDDDDDDDD,
                0xEEEEEEEEEEEEEEEE,
                0xFFFFFFFFFFFFFFFF,
                0x0000000000000000,
                0x1111111111111111,
                0x2222222222222222,
                0x3333333333333333,
                0x4444444444444444,
                0x5555555555555555,
                0x6666666666666666,
                0x7777777777777777,
                0x8888888888888888,
                0x9999999999999999,
                0xAAAAAAAAAAAAAAAA,
                0xBBBBBBBBBBBBBBBB,
                0xCCCCCCCCCCCCCCCC,
                0xDDDDDDDDDDDDDDDD,
                0xEEEEEEEEEEEEEEEE,
                0xFFFFFFFFFFFFFFFF,
                0x0000000000000000,
                0x1111111111111111,
                0x2222222222222222,
                0x3333333333333333,
                0x4444444444444444,
                0x5555555555555555,
                0x6666666666666666,
                0x7777777777777777,
                0x8888888888888888,
                0x9999999999999999,
            ],
            64,
        )
        .unwrap();

        bencher.iter(|| {
            std::hint::black_box(div_rem(&a, &b)).unwrap();
        });
    });

    group.finish();
}

fn bench_division_edge_cases(c: &mut Criterion) {
    let mut group = c.benchmark_group("division_edge_cases");

    // Benchmark division by power of 2
    group.bench_function("div_by_power_of_2", |bencher| {
        let a = BigInt::from_limbs(
            &[
                0xFFFFFFFFFFFFFFFF,
                0xFFFFFFFFFFFFFFFF,
                0xFFFFFFFFFFFFFFFF,
                0xFFFFFFFFFFFFFFFF,
            ],
            8,
        )
        .unwrap();
        let b = BigInt::from_u64(65536, 8); // 2^16

        bencher.iter(|| {
            std::hint::black_box(div_rem(&a, &b)).unwrap();
        });
    });

    // Benchmark division by 1
    group.bench_function("div_by_one", |bencher| {
        let a = BigInt::from_limbs(
            &[
                0x123456789ABCDEF0,
                0xFEDCBA9876543210,
                0x1111111111111111,
                0x2222222222222222,
            ],
            8,
        )
        .unwrap();
        let b = BigInt::from_u64(1, 8);

        bencher.iter(|| {
            std::hint::black_box(div_rem(&a, &b)).unwrap();
        });
    });

    // Benchmark division where result is 1
    group.bench_function("div_equal", |bencher| {
        let a = BigInt::from_limbs(&[0x123456789ABCDEF0, 0xFEDCBA9876543210], 4).unwrap();
        let b = BigInt::from_limbs(&[0x123456789ABCDEF0, 0xFEDCBA9876543210], 4).unwrap();

        bencher.iter(|| {
            std::hint::black_box(div_rem(&a, &b)).unwrap();
        });
    });

    group.finish();
}

criterion_group!(
    benches,
    bench_division_u256,
    bench_division_u2048,
    bench_division_edge_cases
);
criterion_main!(benches);
