//! Test cases to verify specific values.
//!
//! Each routine can have a set of inputs and, optinoally, outputs. If an output is provided, it
//! will be used to check against. If only inputs are provided, the case will be checked against
//! a basis.
//!
//! This is useful for adding regression tests or expected failures.

use libm::hf64;
#[cfg(f128_enabled)]
use libm::hf128;

use crate::{CheckBasis, CheckCtx, GeneratorKind, MathOp, op};

pub struct TestCase<Op: MathOp> {
    pub input: Op::RustArgs,
    pub output: Option<Op::RustRet>,
}

impl<Op: MathOp> TestCase<Op> {
    #[expect(dead_code)]
    fn append_inputs(v: &mut Vec<Self>, l: &[Op::RustArgs]) {
        v.extend(l.iter().copied().map(|input| Self {
            input,
            output: None,
        }));
    }

    fn append_pairs(v: &mut Vec<Self>, l: &[(Op::RustArgs, Option<Op::RustRet>)])
    where
        Op::RustRet: Copy,
    {
        v.extend(
            l.iter()
                .copied()
                .map(|(input, output)| Self { input, output }),
        );
    }
}

fn acos_cases() -> Vec<TestCase<op::acos::Routine>> {
    vec![]
}

fn acosf_cases() -> Vec<TestCase<op::acosf::Routine>> {
    vec![]
}

fn acosh_cases() -> Vec<TestCase<op::acosh::Routine>> {
    vec![]
}

fn acoshf_cases() -> Vec<TestCase<op::acoshf::Routine>> {
    vec![]
}

fn asin_cases() -> Vec<TestCase<op::asin::Routine>> {
    vec![]
}

fn asinf_cases() -> Vec<TestCase<op::asinf::Routine>> {
    vec![]
}

fn asinh_cases() -> Vec<TestCase<op::asinh::Routine>> {
    vec![]
}

fn asinhf_cases() -> Vec<TestCase<op::asinhf::Routine>> {
    vec![]
}

fn atan_cases() -> Vec<TestCase<op::atan::Routine>> {
    vec![]
}

fn atan2_cases() -> Vec<TestCase<op::atan2::Routine>> {
    vec![]
}

fn atan2f_cases() -> Vec<TestCase<op::atan2f::Routine>> {
    vec![]
}

fn atanf_cases() -> Vec<TestCase<op::atanf::Routine>> {
    vec![]
}

fn atanh_cases() -> Vec<TestCase<op::atanh::Routine>> {
    vec![]
}

fn atanhf_cases() -> Vec<TestCase<op::atanhf::Routine>> {
    vec![]
}

fn cbrt_cases() -> Vec<TestCase<op::cbrt::Routine>> {
    vec![]
}

fn cbrtf_cases() -> Vec<TestCase<op::cbrtf::Routine>> {
    vec![]
}

fn ceil_cases() -> Vec<TestCase<op::ceil::Routine>> {
    vec![]
}

fn ceilf_cases() -> Vec<TestCase<op::ceilf::Routine>> {
    vec![]
}

#[cfg(f128_enabled)]
fn ceilf128_cases() -> Vec<TestCase<op::ceilf128::Routine>> {
    vec![]
}

#[cfg(f16_enabled)]
fn ceilf16_cases() -> Vec<TestCase<op::ceilf16::Routine>> {
    vec![]
}

fn copysign_cases() -> Vec<TestCase<op::copysign::Routine>> {
    vec![]
}

fn copysignf_cases() -> Vec<TestCase<op::copysignf::Routine>> {
    vec![]
}

#[cfg(f128_enabled)]
fn copysignf128_cases() -> Vec<TestCase<op::copysignf128::Routine>> {
    vec![]
}

#[cfg(f16_enabled)]
fn copysignf16_cases() -> Vec<TestCase<op::copysignf16::Routine>> {
    vec![]
}

fn cos_cases() -> Vec<TestCase<op::cos::Routine>> {
    vec![]
}

fn cosf_cases() -> Vec<TestCase<op::cosf::Routine>> {
    vec![]
}

fn cosh_cases() -> Vec<TestCase<op::cosh::Routine>> {
    vec![]
}

fn coshf_cases() -> Vec<TestCase<op::coshf::Routine>> {
    vec![]
}

fn erf_cases() -> Vec<TestCase<op::erf::Routine>> {
    vec![]
}

fn erfc_cases() -> Vec<TestCase<op::erfc::Routine>> {
    vec![]
}

fn erfcf_cases() -> Vec<TestCase<op::erfcf::Routine>> {
    vec![]
}

fn erff_cases() -> Vec<TestCase<op::erff::Routine>> {
    vec![]
}

fn exp_cases() -> Vec<TestCase<op::exp::Routine>> {
    vec![]
}

fn exp10_cases() -> Vec<TestCase<op::exp10::Routine>> {
    vec![]
}

fn exp10f_cases() -> Vec<TestCase<op::exp10f::Routine>> {
    vec![]
}

fn exp2_cases() -> Vec<TestCase<op::exp2::Routine>> {
    vec![]
}

fn exp2f_cases() -> Vec<TestCase<op::exp2f::Routine>> {
    vec![]
}

fn expf_cases() -> Vec<TestCase<op::expf::Routine>> {
    vec![]
}

fn expm1_cases() -> Vec<TestCase<op::expm1::Routine>> {
    vec![]
}

fn expm1f_cases() -> Vec<TestCase<op::expm1f::Routine>> {
    vec![]
}

fn fabs_cases() -> Vec<TestCase<op::fabs::Routine>> {
    vec![]
}

fn fabsf_cases() -> Vec<TestCase<op::fabsf::Routine>> {
    vec![]
}

#[cfg(f128_enabled)]
fn fabsf128_cases() -> Vec<TestCase<op::fabsf128::Routine>> {
    vec![]
}

#[cfg(f16_enabled)]
fn fabsf16_cases() -> Vec<TestCase<op::fabsf16::Routine>> {
    vec![]
}

fn fdim_cases() -> Vec<TestCase<op::fdim::Routine>> {
    vec![]
}

fn fdimf_cases() -> Vec<TestCase<op::fdimf::Routine>> {
    vec![]
}

#[cfg(f128_enabled)]
fn fdimf128_cases() -> Vec<TestCase<op::fdimf128::Routine>> {
    vec![]
}

#[cfg(f16_enabled)]
fn fdimf16_cases() -> Vec<TestCase<op::fdimf16::Routine>> {
    vec![]
}

fn floor_cases() -> Vec<TestCase<op::floor::Routine>> {
    vec![]
}

fn floorf_cases() -> Vec<TestCase<op::floorf::Routine>> {
    vec![]
}

#[cfg(f128_enabled)]
fn floorf128_cases() -> Vec<TestCase<op::floorf128::Routine>> {
    vec![]
}

#[cfg(f16_enabled)]
fn floorf16_cases() -> Vec<TestCase<op::floorf16::Routine>> {
    vec![]
}

fn fma_cases() -> Vec<TestCase<op::fma::Routine>> {
    let mut v = vec![];
    TestCase::append_pairs(
        &mut v,
        &[
            // Previous failure with incorrect sign
            ((5e-324, -5e-324, 0.0), Some(-0.0)),
        ],
    );
    v
}

fn fmaf_cases() -> Vec<TestCase<op::fmaf::Routine>> {
    let mut v = vec![];
    TestCase::append_pairs(
        &mut v,
        &[
            // Known rounding error for some implementations (notably MinGW)
            (
                (-1.9369631e13f32, 2.1513551e-7, -1.7354427e-24),
                Some(-4167095.8),
            ),
        ],
    );
    v
}

#[cfg(f128_enabled)]
fn fmaf128_cases() -> Vec<TestCase<op::fmaf128::Routine>> {
    let mut v = vec![];
    TestCase::append_pairs(
        &mut v,
        &[
            (
                // Tricky rounding case that previously failed in extensive tests
                (
                    hf128!("-0x1.1966cc01966cc01966cc01966f06p-25"),
                    hf128!("-0x1.669933fe69933fe69933fe6997c9p-16358"),
                    hf128!("-0x0.000000000000000000000000048ap-16382"),
                ),
                Some(hf128!("0x0.c5171470a3ff5e0f68d751491b18p-16382")),
            ),
            (
                // Subnormal edge case that caused a failure
                (
                    hf128!("0x0.7ffffffffffffffffffffffffff7p-16382"),
                    hf128!("0x1.ffffffffffffffffffffffffffffp-1"),
                    hf128!("0x0.8000000000000000000000000009p-16382"),
                ),
                Some(hf128!("0x1.0000000000000000000000000000p-16382")),
            ),
        ],
    );
    v
}

#[cfg(f16_enabled)]
fn fmaxf16_cases() -> Vec<TestCase<op::fmaxf16::Routine>> {
    vec![]
}

fn fmaxf_cases() -> Vec<TestCase<op::fmaxf::Routine>> {
    vec![]
}

fn fmax_cases() -> Vec<TestCase<op::fmax::Routine>> {
    vec![]
}

#[cfg(f128_enabled)]
fn fmaxf128_cases() -> Vec<TestCase<op::fmaxf128::Routine>> {
    vec![]
}

#[cfg(f16_enabled)]
fn fmaximumf16_cases() -> Vec<TestCase<op::fmaximumf16::Routine>> {
    vec![]
}

fn fmaximumf_cases() -> Vec<TestCase<op::fmaximumf::Routine>> {
    vec![]
}

fn fmaximum_cases() -> Vec<TestCase<op::fmaximum::Routine>> {
    vec![]
}

#[cfg(f128_enabled)]
fn fmaximumf128_cases() -> Vec<TestCase<op::fmaximumf128::Routine>> {
    vec![]
}

#[cfg(f16_enabled)]
fn fmaximum_numf16_cases() -> Vec<TestCase<op::fmaximum_numf16::Routine>> {
    vec![]
}

fn fmaximum_numf_cases() -> Vec<TestCase<op::fmaximum_numf::Routine>> {
    vec![]
}

fn fmaximum_num_cases() -> Vec<TestCase<op::fmaximum_num::Routine>> {
    vec![]
}

#[cfg(f128_enabled)]
fn fmaximum_numf128_cases() -> Vec<TestCase<op::fmaximum_numf128::Routine>> {
    vec![]
}

#[cfg(f16_enabled)]
fn fminf16_cases() -> Vec<TestCase<op::fminf16::Routine>> {
    vec![]
}

fn fminf_cases() -> Vec<TestCase<op::fminf::Routine>> {
    vec![]
}

fn fmin_cases() -> Vec<TestCase<op::fmin::Routine>> {
    vec![]
}

#[cfg(f128_enabled)]
fn fminf128_cases() -> Vec<TestCase<op::fminf128::Routine>> {
    vec![]
}

#[cfg(f16_enabled)]
fn fminimumf16_cases() -> Vec<TestCase<op::fminimumf16::Routine>> {
    vec![]
}

fn fminimumf_cases() -> Vec<TestCase<op::fminimumf::Routine>> {
    vec![]
}

fn fminimum_cases() -> Vec<TestCase<op::fminimum::Routine>> {
    vec![]
}

#[cfg(f128_enabled)]
fn fminimumf128_cases() -> Vec<TestCase<op::fminimumf128::Routine>> {
    vec![]
}

#[cfg(f16_enabled)]
fn fminimum_numf16_cases() -> Vec<TestCase<op::fminimum_numf16::Routine>> {
    vec![]
}

fn fminimum_numf_cases() -> Vec<TestCase<op::fminimum_numf::Routine>> {
    vec![]
}

fn fminimum_num_cases() -> Vec<TestCase<op::fminimum_num::Routine>> {
    vec![]
}

#[cfg(f128_enabled)]
fn fminimum_numf128_cases() -> Vec<TestCase<op::fminimum_numf128::Routine>> {
    vec![]
}

fn fmod_cases() -> Vec<TestCase<op::fmod::Routine>> {
    let mut v = vec![];
    TestCase::append_pairs(
        &mut v,
        &[
            // Previous failure with incorrect loop iteration
            // <https://github.com/rust-lang/libm/pull/469#discussion_r2022337272>
            ((2.1, 3.123e-320), Some(2.0696e-320)),
            ((2.1, 2.253547e-318), Some(1.772535e-318)),
        ],
    );
    v
}

fn fmodf_cases() -> Vec<TestCase<op::fmodf::Routine>> {
    let mut v = vec![];
    TestCase::append_pairs(
        &mut v,
        &[
            // Previous failure with incorrect loop iteration
            // <https://github.com/rust-lang/libm/pull/469#discussion_r2022337272>
            ((2.1, 8.858e-42), Some(8.085e-42)),
            ((2.1, 6.39164e-40), Some(6.1636e-40)),
            ((5.5, 6.39164e-40), Some(4.77036e-40)),
            ((-151.189, 6.39164e-40), Some(-5.64734e-40)),
        ],
    );
    v
}

#[cfg(f128_enabled)]
fn fmodf128_cases() -> Vec<TestCase<op::fmodf128::Routine>> {
    vec![]
}

#[cfg(f16_enabled)]
fn fmodf16_cases() -> Vec<TestCase<op::fmodf16::Routine>> {
    vec![]
}

fn frexp_cases() -> Vec<TestCase<op::frexp::Routine>> {
    vec![]
}

fn frexpf_cases() -> Vec<TestCase<op::frexpf::Routine>> {
    vec![]
}

fn hypot_cases() -> Vec<TestCase<op::hypot::Routine>> {
    vec![]
}

fn hypotf_cases() -> Vec<TestCase<op::hypotf::Routine>> {
    vec![]
}

fn ilogb_cases() -> Vec<TestCase<op::ilogb::Routine>> {
    vec![]
}

fn ilogbf_cases() -> Vec<TestCase<op::ilogbf::Routine>> {
    vec![]
}

fn j0_cases() -> Vec<TestCase<op::j0::Routine>> {
    vec![]
}

fn j0f_cases() -> Vec<TestCase<op::j0f::Routine>> {
    vec![]
}

fn j1_cases() -> Vec<TestCase<op::j1::Routine>> {
    vec![]
}

fn j1f_cases() -> Vec<TestCase<op::j1f::Routine>> {
    vec![]
}

fn jn_cases() -> Vec<TestCase<op::jn::Routine>> {
    vec![]
}

fn jnf_cases() -> Vec<TestCase<op::jnf::Routine>> {
    vec![]
}

fn ldexp_cases() -> Vec<TestCase<op::ldexp::Routine>> {
    vec![]
}

fn ldexpf_cases() -> Vec<TestCase<op::ldexpf::Routine>> {
    vec![]
}

#[cfg(f128_enabled)]
fn ldexpf128_cases() -> Vec<TestCase<op::ldexpf128::Routine>> {
    vec![]
}

#[cfg(f16_enabled)]
fn ldexpf16_cases() -> Vec<TestCase<op::ldexpf16::Routine>> {
    vec![]
}

fn lgamma_cases() -> Vec<TestCase<op::lgamma::Routine>> {
    vec![]
}

fn lgamma_r_cases() -> Vec<TestCase<op::lgamma_r::Routine>> {
    vec![]
}

fn lgammaf_cases() -> Vec<TestCase<op::lgammaf::Routine>> {
    vec![]
}

fn lgammaf_r_cases() -> Vec<TestCase<op::lgammaf_r::Routine>> {
    vec![]
}

fn log_cases() -> Vec<TestCase<op::log::Routine>> {
    vec![]
}

fn log10_cases() -> Vec<TestCase<op::log10::Routine>> {
    vec![]
}

fn log10f_cases() -> Vec<TestCase<op::log10f::Routine>> {
    vec![]
}

fn log1p_cases() -> Vec<TestCase<op::log1p::Routine>> {
    vec![]
}

fn log1pf_cases() -> Vec<TestCase<op::log1pf::Routine>> {
    vec![]
}

fn log2_cases() -> Vec<TestCase<op::log2::Routine>> {
    vec![]
}

fn log2f_cases() -> Vec<TestCase<op::log2f::Routine>> {
    vec![]
}

fn logf_cases() -> Vec<TestCase<op::logf::Routine>> {
    vec![]
}

fn modf_cases() -> Vec<TestCase<op::modf::Routine>> {
    vec![]
}

fn modff_cases() -> Vec<TestCase<op::modff::Routine>> {
    vec![]
}

fn nextafter_cases() -> Vec<TestCase<op::nextafter::Routine>> {
    vec![]
}

fn nextafterf_cases() -> Vec<TestCase<op::nextafterf::Routine>> {
    vec![]
}

fn pow_cases() -> Vec<TestCase<op::pow::Routine>> {
    vec![]
}

fn powf_cases() -> Vec<TestCase<op::powf::Routine>> {
    vec![]
}

fn remainder_cases() -> Vec<TestCase<op::remainder::Routine>> {
    vec![]
}

fn remainderf_cases() -> Vec<TestCase<op::remainderf::Routine>> {
    vec![]
}

fn remquo_cases() -> Vec<TestCase<op::remquo::Routine>> {
    vec![]
}

fn remquof_cases() -> Vec<TestCase<op::remquof::Routine>> {
    vec![]
}

fn rint_cases() -> Vec<TestCase<op::rint::Routine>> {
    let mut v = vec![];
    TestCase::append_pairs(
        &mut v,
        &[
            // Known failure on i586
            #[cfg(not(x86_no_sse))]
            (
                (hf64!("-0x1.e3f13ff995ffcp+38"),),
                Some(hf64!("-0x1.e3f13ff994000p+38")),
            ),
            #[cfg(x86_no_sse)]
            (
                (hf64!("-0x1.e3f13ff995ffcp+38"),),
                Some(hf64!("-0x1.e3f13ff998000p+38")),
            ),
        ],
    );
    v
}

fn rintf_cases() -> Vec<TestCase<op::rintf::Routine>> {
    vec![]
}

#[cfg(f128_enabled)]
fn rintf128_cases() -> Vec<TestCase<op::rintf128::Routine>> {
    vec![]
}

#[cfg(f16_enabled)]
fn rintf16_cases() -> Vec<TestCase<op::rintf16::Routine>> {
    vec![]
}

#[cfg(f16_enabled)]
fn roundf16_cases() -> Vec<TestCase<op::roundf16::Routine>> {
    vec![]
}

fn round_cases() -> Vec<TestCase<op::round::Routine>> {
    vec![]
}

fn roundf_cases() -> Vec<TestCase<op::roundf::Routine>> {
    vec![]
}

#[cfg(f128_enabled)]
fn roundf128_cases() -> Vec<TestCase<op::roundf128::Routine>> {
    vec![]
}

#[cfg(f16_enabled)]
fn roundevenf16_cases() -> Vec<TestCase<op::roundevenf16::Routine>> {
    vec![]
}

fn roundeven_cases() -> Vec<TestCase<op::roundeven::Routine>> {
    let mut v = vec![];
    TestCase::append_pairs(
        &mut v,
        &[
            // Known failure on i586
            #[cfg(not(x86_no_sse))]
            (
                (hf64!("-0x1.e3f13ff995ffcp+38"),),
                Some(hf64!("-0x1.e3f13ff994000p+38")),
            ),
            #[cfg(x86_no_sse)]
            (
                (hf64!("-0x1.e3f13ff995ffcp+38"),),
                Some(hf64!("-0x1.e3f13ff998000p+38")),
            ),
        ],
    );
    v
}

fn roundevenf_cases() -> Vec<TestCase<op::roundevenf::Routine>> {
    vec![]
}

#[cfg(f128_enabled)]
fn roundevenf128_cases() -> Vec<TestCase<op::roundevenf128::Routine>> {
    vec![]
}

fn scalbn_cases() -> Vec<TestCase<op::scalbn::Routine>> {
    vec![]
}

fn scalbnf_cases() -> Vec<TestCase<op::scalbnf::Routine>> {
    vec![]
}

#[cfg(f128_enabled)]
fn scalbnf128_cases() -> Vec<TestCase<op::scalbnf128::Routine>> {
    vec![]
}

#[cfg(f16_enabled)]
fn scalbnf16_cases() -> Vec<TestCase<op::scalbnf16::Routine>> {
    vec![]
}

fn sin_cases() -> Vec<TestCase<op::sin::Routine>> {
    vec![]
}

fn sincos_cases() -> Vec<TestCase<op::sincos::Routine>> {
    vec![]
}

fn sincosf_cases() -> Vec<TestCase<op::sincosf::Routine>> {
    vec![]
}

fn sinf_cases() -> Vec<TestCase<op::sinf::Routine>> {
    vec![]
}

fn sinh_cases() -> Vec<TestCase<op::sinh::Routine>> {
    vec![]
}

fn sinhf_cases() -> Vec<TestCase<op::sinhf::Routine>> {
    vec![]
}

fn sqrt_cases() -> Vec<TestCase<op::sqrt::Routine>> {
    vec![]
}

fn sqrtf_cases() -> Vec<TestCase<op::sqrtf::Routine>> {
    vec![]
}

#[cfg(f128_enabled)]
fn sqrtf128_cases() -> Vec<TestCase<op::sqrtf128::Routine>> {
    vec![]
}

#[cfg(f16_enabled)]
fn sqrtf16_cases() -> Vec<TestCase<op::sqrtf16::Routine>> {
    vec![]
}

fn tan_cases() -> Vec<TestCase<op::tan::Routine>> {
    vec![]
}

fn tanf_cases() -> Vec<TestCase<op::tanf::Routine>> {
    vec![]
}

fn tanh_cases() -> Vec<TestCase<op::tanh::Routine>> {
    vec![]
}

fn tanhf_cases() -> Vec<TestCase<op::tanhf::Routine>> {
    vec![]
}

fn tgamma_cases() -> Vec<TestCase<op::tgamma::Routine>> {
    vec![]
}

fn tgammaf_cases() -> Vec<TestCase<op::tgammaf::Routine>> {
    vec![]
}

fn trunc_cases() -> Vec<TestCase<op::trunc::Routine>> {
    vec![]
}

fn truncf_cases() -> Vec<TestCase<op::truncf::Routine>> {
    vec![]
}

#[cfg(f128_enabled)]
fn truncf128_cases() -> Vec<TestCase<op::truncf128::Routine>> {
    vec![]
}

#[cfg(f16_enabled)]
fn truncf16_cases() -> Vec<TestCase<op::truncf16::Routine>> {
    vec![]
}

fn y0_cases() -> Vec<TestCase<op::y0::Routine>> {
    vec![]
}

fn y0f_cases() -> Vec<TestCase<op::y0f::Routine>> {
    vec![]
}

fn y1_cases() -> Vec<TestCase<op::y1::Routine>> {
    vec![]
}

fn y1f_cases() -> Vec<TestCase<op::y1f::Routine>> {
    vec![]
}

fn yn_cases() -> Vec<TestCase<op::yn::Routine>> {
    vec![]
}

fn ynf_cases() -> Vec<TestCase<op::ynf::Routine>> {
    vec![]
}

pub trait CaseListInput: MathOp + Sized {
    fn get_cases() -> Vec<TestCase<Self>>;
}

macro_rules! impl_case_list {
    (
        fn_name: $fn_name:ident,
        attrs: [$($attr:meta),*],
    ) => {
        paste::paste! {
            $(#[$attr])*
            impl CaseListInput for crate::op::$fn_name::Routine {
                fn get_cases() -> Vec<TestCase<Self>> {
                    [< $fn_name _cases >]()
                }
            }
        }
    };
}

libm_macros::for_each_function! {
    callback: impl_case_list,
}

/// This is the test generator for standalone tests, i.e. those with no basis. For this, it
/// only extracts tests with a known output.
pub fn get_test_cases_standalone<Op>(
    ctx: &CheckCtx,
) -> impl Iterator<Item = (Op::RustArgs, Op::RustRet)> + use<'_, Op>
where
    Op: MathOp + CaseListInput,
{
    assert_eq!(ctx.basis, CheckBasis::None);
    assert_eq!(ctx.gen_kind, GeneratorKind::List);
    Op::get_cases()
        .into_iter()
        .filter_map(|x| x.output.map(|o| (x.input, o)))
}

/// Opposite of the above; extract only test cases that don't have a known output, to be run
/// against a basis.
pub fn get_test_cases_basis<Op>(
    ctx: &CheckCtx,
) -> (impl Iterator<Item = Op::RustArgs> + use<'_, Op>, u64)
where
    Op: MathOp + CaseListInput,
{
    assert_ne!(ctx.basis, CheckBasis::None);
    assert_eq!(ctx.gen_kind, GeneratorKind::List);

    let cases = Op::get_cases();
    let count: u64 = cases
        .iter()
        .filter(|case| case.output.is_none())
        .count()
        .try_into()
        .unwrap();

    (
        cases
            .into_iter()
            .filter(|x| x.output.is_none())
            .map(|x| x.input),
        count,
    )
}
