use std::fmt::Debug;

use num_bigint_dig::BigUint;
use rand::{
    RngExt, SeedableRng,
    distr::{Distribution, StandardUniform},
};
use small_is_prime::IsPrime;

#[test]
fn test_fixed_8() {
    test_fixed::<u8>();
}
#[test]
fn test_fixed_16() {
    test_fixed::<u16>();
}
#[test]
fn test_fixed_32() {
    test_fixed::<u32>();
}
#[test]
fn test_fixed_64() {
    test_fixed::<u64>();
}
#[test]
fn test_8() {
    test_all(0..=u8::MAX);
}
#[test]
fn test_16() {
    test_all(0..=u16::MAX);
}
#[test]
fn test_random_32() {
    random_test::<u32>(1_000_000);
}
#[test]
fn test_random_64() {
    random_test::<u64>(1_000_000);
}

fn random_test<T>(reps: usize)
where
    StandardUniform: Distribution<T>,
    T: IsPrime + Copy,
    BigUint: From<T>,
{
    let mut rng = rand_xoshiro::Xoshiro128PlusPlus::seed_from_u64(42);
    for _ in 0..reps {
        let scrutinee = rng.random();
        assert_eq!(
            scrutinee.is_prime(),
            num_bigint_dig::prime::probably_prime(&scrutinee.into(), 1)
        );
    }
}

fn test_all<T>(it: impl Iterator<Item = T>)
where
    T: IsPrime + Copy + Debug,
    BigUint: From<T>,
{
    for scrutinee in it {
        assert_eq!(
            scrutinee.is_prime(),
            num_bigint_dig::prime::probably_prime(&scrutinee.into(), 1),
            "{scrutinee:?}"
        );
    }
}

fn test_fixed<T>()
where
    T: IsPrime + Copy + Debug + TryFrom<u64>,
    BigUint: From<T>,
{
    static TEST_VALUES: &[u64] = &[
        0,
        u8::MAX as u64,
        u16::MAX as u64,
        u32::MAX as u64,
        u64::MAX,
        1,
        2,
        3,
        4,
        5,
        6,
        7,
        30,
        31,
        32,
        33,
        37,
        41,
        43,
        47,
        49,
        51,
        57,
        61,
        77,
        100,
        121,
        169,
        289,
        325,
        561,
        961,
        1000,
        1023,
        1024,
        1025,
        1105,
        1729,
        2047,
        2465,
        2821,
        4095,
        4096,
        4097,
        6601,
        9375,
        10201,
        28178,
        65521,
        65535,
        450775,
        1000000,
        1373653,
        9780504,
        25326001,
        1234567891,
        1795265022,
        3215031751,
        4294967291,
        4294967295,
        9876543211,
        9999999967,
        9999999969,
        10000000000,
        1125899906842597,
        3825123056546413051,
        18446744073709551557,
    ];
    test_all(TEST_VALUES.iter().filter_map(|x| T::try_from(*x).ok()))
}
