use std::hint::black_box;

use gungraun::{library_benchmark, library_benchmark_group, main};

main!(library_benchmark_groups = benches);
library_benchmark_group!(
    name = benches;
    benchmarks = ggmath_mask, glam_mask
);

#[library_benchmark]
#[bench::arbitrary(4.0, 5.0, 6.0)]
pub fn ggmath_mask(x: f32, y: f32, z: f32) -> impl Copy {
    use ggmath::{Vec2U, Vec3, Vec3U, Vec4};

    let mut v2u: Vec2U<f32> = Vec2U::new(x, y);
    let mut v3u: Vec3U<f32> = Vec3U::new(x, y, z);
    let mut v3a: Vec3<f32> = Vec3::new(x, y, z);
    let mut v4a: Vec4<f32> = Vec4::new(x, y, z, x);

    for _ in 0..2 {
        v2u += ((v2u + Vec2U::ONE).finite_mask() & (v2u / Vec2U::splat(x)).nan_mask())
            .select(Vec2U::ONE, Vec2U::NEG_X);
        v2u += v2u.eq_mask(Vec2U::new(x, y)).select(v2u, -v2u)
            + v2u.ne_mask(Vec2U::new(x, y)).select(Vec2U::Y, Vec2U::X);
        v2u += (v2u.lt_mask(Vec2U::new(x, y)) | (v2u * Vec2U::splat(1.3)).gt_mask(v2u))
            .select(v2u, -v2u);
        v2u += (v2u.le_mask(Vec2U::new(x, y)) ^ (v2u * Vec2U::splat(1.3)).ge_mask(v2u))
            .select(v2u, -v2u);

        v3u += ((v3u + Vec3U::ONE).finite_mask() & (v3u / Vec3U::splat(x)).nan_mask())
            .select(Vec3U::ONE, Vec3U::NEG_X);
        v3u += v3u.eq_mask(Vec3U::new(x, y, z)).select(v3u, -v3u)
            + v3u.ne_mask(Vec3U::new(x, y, z)).select(Vec3U::Y, Vec3U::X);
        v3u += (v3u.lt_mask(Vec3U::new(x, y, z)) | (v3u * Vec3U::splat(1.3)).gt_mask(v3u))
            .select(v3u, -v3u);
        v3u += (v3u.le_mask(Vec3U::new(x, y, z)) ^ (v3u * Vec3U::splat(1.3)).ge_mask(v3u))
            .select(v3u, -v3u);

        v3a += ((v3a + Vec3::ONE).finite_mask() & (v3a / Vec3::splat(x)).nan_mask())
            .select(Vec3::ONE, Vec3::NEG_X);
        v3a += v3a.eq_mask(Vec3::new(x, y, z)).select(v3a, -v3a)
            + v3a.ne_mask(Vec3::new(x, y, z)).select(Vec3::Y, Vec3::X);
        v3a += (v3a.lt_mask(Vec3::new(x, y, z)) | (v3a * Vec3::splat(1.3)).gt_mask(v3a))
            .select(v3a, -v3a);
        v3a += (v3a.le_mask(Vec3::new(x, y, z)) ^ (v3a * Vec3::splat(1.3)).ge_mask(v3a))
            .select(v3a, -v3a);

        v4a += ((v4a + Vec4::ONE).finite_mask() & (v4a / Vec4::splat(x)).nan_mask())
            .select(Vec4::ONE, Vec4::NEG_X);
        v4a += v4a.eq_mask(Vec4::new(x, y, z, x)).select(v4a, -v4a)
            + v4a.ne_mask(Vec4::new(x, y, z, x)).select(Vec4::Y, Vec4::X);
        v4a += (v4a.lt_mask(Vec4::new(x, y, z, x)) | (v4a * Vec4::splat(1.3)).gt_mask(v4a))
            .select(v4a, -v4a);
        v4a += (v4a.le_mask(Vec4::new(x, y, z, x)) ^ (v4a * Vec4::splat(1.3)).ge_mask(v4a))
            .select(v4a, -v4a);
    }

    black_box((v2u, v3u, v3a, v4a))
}

#[library_benchmark]
#[bench::arbitrary(4.0, 5.0, 6.0)]
pub fn glam_mask(x: f32, y: f32, z: f32) -> impl Copy {
    use glam::{Vec2, Vec3, Vec3A, Vec4};

    let mut v2u: Vec2 = Vec2::new(x, y);
    let mut v3u: Vec3 = Vec3::new(x, y, z);
    let mut v3a: Vec3A = Vec3A::new(x, y, z);
    let mut v4a: Vec4 = Vec4::new(x, y, z, x);

    for _ in 0..2 {
        v2u += Vec2::select(
            (v2u + Vec2::ONE).is_finite_mask() & (v2u / Vec2::splat(x)).is_nan_mask(),
            Vec2::ONE,
            Vec2::NEG_X,
        );
        v2u += Vec2::select(v2u.cmpeq(Vec2::new(x, y)), v2u, -v2u)
            + Vec2::select(v2u.cmpne(Vec2::new(x, y)), Vec2::Y, Vec2::X);
        v2u += Vec2::select(
            v2u.cmplt(Vec2::new(x, y)) | (v2u * Vec2::splat(1.3)).cmpgt(v2u),
            v2u,
            -v2u,
        );
        v2u += Vec2::select(
            v2u.cmple(Vec2::new(x, y)) ^ (v2u * Vec2::splat(1.3)).cmpge(v2u),
            v2u,
            -v2u,
        );

        v3u += Vec3::select(
            (v3u + Vec3::ONE).is_finite_mask() & (v3u / Vec3::splat(x)).is_nan_mask(),
            Vec3::ONE,
            Vec3::NEG_X,
        );
        v3u += Vec3::select(v3u.cmpeq(Vec3::new(x, y, z)), v3u, -v3u)
            + Vec3::select(v3u.cmpne(Vec3::new(x, y, z)), Vec3::Y, Vec3::X);
        v3u += Vec3::select(
            v3u.cmplt(Vec3::new(x, y, z)) | (v3u * Vec3::splat(1.3)).cmpgt(v3u),
            v3u,
            -v3u,
        );
        v3u += Vec3::select(
            v3u.cmple(Vec3::new(x, y, z)) ^ (v3u * Vec3::splat(1.3)).cmpge(v3u),
            v3u,
            -v3u,
        );

        v3a += Vec3A::select(
            (v3a + Vec3A::ONE).is_finite_mask() & (v3a / Vec3A::splat(x)).is_nan_mask(),
            Vec3A::ONE,
            Vec3A::NEG_X,
        );
        v3a += Vec3A::select(v3a.cmpeq(Vec3A::new(x, y, z)), v3a, -v3a)
            + Vec3A::select(v3a.cmpne(Vec3A::new(x, y, z)), Vec3A::Y, Vec3A::X);
        v3a += Vec3A::select(
            v3a.cmplt(Vec3A::new(x, y, z)) | (v3a * Vec3A::splat(1.3)).cmpgt(v3a),
            v3a,
            -v3a,
        );
        v3a += Vec3A::select(
            v3a.cmple(Vec3A::new(x, y, z)) ^ (v3a * Vec3A::splat(1.3)).cmpge(v3a),
            v3a,
            -v3a,
        );

        v4a += Vec4::select(
            (v4a + Vec4::ONE).is_finite_mask() & (v4a / Vec4::splat(x)).is_nan_mask(),
            Vec4::ONE,
            Vec4::NEG_X,
        );
        v4a += Vec4::select(v4a.cmpeq(Vec4::new(x, y, z, x)), v4a, -v4a)
            + Vec4::select(v4a.cmpne(Vec4::new(x, y, z, x)), Vec4::Y, Vec4::X);
        v4a += Vec4::select(
            v4a.cmplt(Vec4::new(x, y, z, x)) | (v4a * Vec4::splat(1.3)).cmpgt(v4a),
            v4a,
            -v4a,
        );
        v4a += Vec4::select(
            v4a.cmple(Vec4::new(x, y, z, x)) ^ (v4a * Vec4::splat(1.3)).cmpge(v4a),
            v4a,
            -v4a,
        );
    }

    black_box((v2u, v3u, v3a, v4a))
}
