#![allow(clippy::approx_constant)] // many false positives

macro_rules! force_eval {
    ($e:expr) => {
        unsafe { ::core::ptr::read_volatile(&$e) }
    };
}

#[cfg(not(debug_assertions))]
macro_rules! i {
    ($array:expr, $index:expr) => {
        unsafe { *$array.get_unchecked($index) }
    };
    ($array:expr, $index:expr, = , $rhs:expr) => {
        unsafe {
            *$array.get_unchecked_mut($index) = $rhs;
        }
    };
    ($array:expr, $index:expr, += , $rhs:expr) => {
        unsafe {
            *$array.get_unchecked_mut($index) += $rhs;
        }
    };
    ($array:expr, $index:expr, -= , $rhs:expr) => {
        unsafe {
            *$array.get_unchecked_mut($index) -= $rhs;
        }
    };
    ($array:expr, $index:expr, &= , $rhs:expr) => {
        unsafe {
            *$array.get_unchecked_mut($index) &= $rhs;
        }
    };
    ($array:expr, $index:expr, == , $rhs:expr) => {
        unsafe { *$array.get_unchecked_mut($index) == $rhs }
    };
}

#[cfg(debug_assertions)]
macro_rules! i {
    ($array:expr, $index:expr) => {
        *$array.get($index).unwrap()
    };
    ($array:expr, $index:expr, = , $rhs:expr) => {
        *$array.get_mut($index).unwrap() = $rhs;
    };
    ($array:expr, $index:expr, -= , $rhs:expr) => {
        *$array.get_mut($index).unwrap() -= $rhs;
    };
    ($array:expr, $index:expr, += , $rhs:expr) => {
        *$array.get_mut($index).unwrap() += $rhs;
    };
    ($array:expr, $index:expr, &= , $rhs:expr) => {
        *$array.get_mut($index).unwrap() &= $rhs;
    };
    ($array:expr, $index:expr, == , $rhs:expr) => {
        *$array.get_mut($index).unwrap() == $rhs
    };
}

// `support` may be public for testing
#[macro_use]
#[cfg(feature = "unstable-public-internals")]
pub mod support;

#[macro_use]
#[cfg(not(feature = "unstable-public-internals"))]
pub(crate) mod support;

cfg_if! {
    if #[cfg(feature = "unstable-public-internals")] {
        pub mod generic;
    } else {
        mod generic;
    }
}

// Private modules
mod arch;
mod expo2;
mod k_cos;
mod k_cosf;
mod k_expo2;
mod k_expo2f;
mod k_sin;
mod k_sinf;
mod k_tan;
mod k_tanf;
mod rem_pio2;
mod rem_pio2_large;
mod rem_pio2f;

// Private re-imports
use self::expo2::expo2;
use self::k_cos::k_cos;
use self::k_cosf::k_cosf;
use self::k_expo2::k_expo2;
use self::k_expo2f::k_expo2f;
use self::k_sin::k_sin;
use self::k_sinf::k_sinf;
use self::k_tan::k_tan;
use self::k_tanf::k_tanf;
use self::rem_pio2::rem_pio2;
use self::rem_pio2_large::rem_pio2_large;
use self::rem_pio2f::rem_pio2f;
#[allow(unused_imports)]
use self::support::{CastFrom, CastInto, DFloat, DInt, Float, HFloat, HInt, Int, IntTy, MinInt};

// Public modules
mod acos;
mod acosf;
mod acosh;
mod acoshf;
mod asin;
mod asinf;
mod asinh;
mod asinhf;
mod atan;
mod atan2;
mod atan2f;
mod atanf;
mod atanh;
mod atanhf;
mod cbrt;
mod cbrtf;
mod ceil;
mod copysign;
mod cos;
mod cosf;
mod cosh;
mod coshf;
mod erf;
mod erff;
mod exp;
mod exp10;
mod exp10f;
mod exp2;
mod exp2f;
mod expf;
mod expm1;
mod expm1f;
mod fabs;
mod fdim;
mod floor;
mod fma;
mod fmin_fmax;
mod fminimum_fmaximum;
mod fminimum_fmaximum_num;
mod fmod;
mod frexp;
mod hypot;
mod hypotf;
mod ilogb;
mod j0;
mod j0f;
mod j1;
mod j1f;
mod jn;
mod jnf;
mod ldexp;
mod lgamma;
mod lgamma_r;
mod lgammaf;
mod lgammaf_r;
mod log;
mod log10;
mod log10f;
mod log1p;
mod log1pf;
mod log2;
mod log2f;
mod logf;
mod modf;
mod modff;
mod nextafter;
mod nextafterf;
mod pow;
mod powf;
mod remainder;
mod remainderf;
mod remquo;
mod remquof;
mod rint;
mod round;
mod roundeven;
mod scalbn;
mod sin;
mod sincos;
mod sincosf;
mod sinf;
mod sinh;
mod sinhf;
mod sqrt;
mod tan;
mod tanf;
mod tanh;
mod tanhf;
mod tgamma;
mod tgammaf;
mod trunc;

// Use separated imports instead of {}-grouped imports for easier merging.
pub use self::acos::acos;
pub use self::acosf::acosf;
pub use self::acosh::acosh;
pub use self::acoshf::acoshf;
pub use self::asin::asin;
pub use self::asinf::asinf;
pub use self::asinh::asinh;
pub use self::asinhf::asinhf;
pub use self::atan::atan;
pub use self::atan2::atan2;
pub use self::atan2f::atan2f;
pub use self::atanf::atanf;
pub use self::atanh::atanh;
pub use self::atanhf::atanhf;
pub use self::cbrt::cbrt;
pub use self::cbrtf::cbrtf;
pub use self::ceil::{ceil, ceilf};
pub use self::copysign::{copysign, copysignf};
pub use self::cos::cos;
pub use self::cosf::cosf;
pub use self::cosh::cosh;
pub use self::coshf::coshf;
pub use self::erf::{erf, erfc};
pub use self::erff::{erfcf, erff};
pub use self::exp::exp;
pub use self::exp2::exp2;
pub use self::exp2f::exp2f;
pub use self::exp10::exp10;
pub use self::exp10f::exp10f;
pub use self::expf::expf;
pub use self::expm1::expm1;
pub use self::expm1f::expm1f;
pub use self::fabs::{fabs, fabsf};
pub use self::fdim::{fdim, fdimf};
pub use self::floor::{floor, floorf};
pub use self::fma::{fma, fmaf};
pub use self::fmin_fmax::{fmax, fmaxf, fmin, fminf};
pub use self::fminimum_fmaximum::{fmaximum, fmaximumf, fminimum, fminimumf};
pub use self::fminimum_fmaximum_num::{fmaximum_num, fmaximum_numf, fminimum_num, fminimum_numf};
pub use self::fmod::{fmod, fmodf};
pub use self::frexp::{frexp, frexpf};
pub use self::hypot::hypot;
pub use self::hypotf::hypotf;
pub use self::ilogb::{ilogb, ilogbf};
pub use self::j0::{j0, y0};
pub use self::j0f::{j0f, y0f};
pub use self::j1::{j1, y1};
pub use self::j1f::{j1f, y1f};
pub use self::jn::{jn, yn};
pub use self::jnf::{jnf, ynf};
pub use self::ldexp::{ldexp, ldexpf};
pub use self::lgamma::lgamma;
pub use self::lgamma_r::lgamma_r;
pub use self::lgammaf::lgammaf;
pub use self::lgammaf_r::lgammaf_r;
pub use self::log::log;
pub use self::log1p::log1p;
pub use self::log1pf::log1pf;
pub use self::log2::log2;
pub use self::log2f::log2f;
pub use self::log10::log10;
pub use self::log10f::log10f;
pub use self::logf::logf;
pub use self::modf::modf;
pub use self::modff::modff;
pub use self::nextafter::nextafter;
pub use self::nextafterf::nextafterf;
pub use self::pow::pow;
pub use self::powf::powf;
pub use self::remainder::remainder;
pub use self::remainderf::remainderf;
pub use self::remquo::remquo;
pub use self::remquof::remquof;
pub use self::rint::{rint, rintf};
pub use self::round::{round, roundf};
pub use self::roundeven::{roundeven, roundevenf};
pub use self::scalbn::{scalbn, scalbnf};
pub use self::sin::sin;
pub use self::sincos::sincos;
pub use self::sincosf::sincosf;
pub use self::sinf::sinf;
pub use self::sinh::sinh;
pub use self::sinhf::sinhf;
pub use self::sqrt::{sqrt, sqrtf};
pub use self::tan::tan;
pub use self::tanf::tanf;
pub use self::tanh::tanh;
pub use self::tanhf::tanhf;
pub use self::tgamma::tgamma;
pub use self::tgammaf::tgammaf;
pub use self::trunc::{trunc, truncf};

cfg_if! {
    if #[cfg(f16_enabled)] {
        // verify-sorted-start
        pub use self::ceil::ceilf16;
        pub use self::copysign::copysignf16;
        pub use self::fabs::fabsf16;
        pub use self::fdim::fdimf16;
        pub use self::floor::floorf16;
        pub use self::fmin_fmax::{fmaxf16, fminf16};
        pub use self::fminimum_fmaximum::{fmaximumf16, fminimumf16};
        pub use self::fminimum_fmaximum_num::{fmaximum_numf16, fminimum_numf16};
        pub use self::fmod::fmodf16;
        pub use self::frexp::frexpf16;
        pub use self::ilogb::ilogbf16;
        pub use self::ldexp::ldexpf16;
        pub use self::rint::rintf16;
        pub use self::round::roundf16;
        pub use self::roundeven::roundevenf16;
        pub use self::scalbn::scalbnf16;
        pub use self::sqrt::sqrtf16;
        pub use self::trunc::truncf16;
        // verify-sorted-end

        #[allow(unused_imports)]
        pub(crate) use self::fma::fmaf16;
    }
}

cfg_if! {
    if #[cfg(f128_enabled)] {
        // verify-sorted-start
        pub use self::ceil::ceilf128;
        pub use self::copysign::copysignf128;
        pub use self::fabs::fabsf128;
        pub use self::fdim::fdimf128;
        pub use self::floor::floorf128;
        pub use self::fma::fmaf128;
        pub use self::fmin_fmax::{fmaxf128, fminf128};
        pub use self::fminimum_fmaximum::{fmaximumf128, fminimumf128};
        pub use self::fminimum_fmaximum_num::{fmaximum_numf128, fminimum_numf128};
        pub use self::fmod::fmodf128;
        pub use self::frexp::frexpf128;
        pub use self::ilogb::ilogbf128;
        pub use self::ldexp::ldexpf128;
        pub use self::rint::rintf128;
        pub use self::round::roundf128;
        pub use self::roundeven::roundevenf128;
        pub use self::scalbn::scalbnf128;
        pub use self::sqrt::sqrtf128;
        pub use self::trunc::truncf128;
        // verify-sorted-end
    }
}

#[inline]
fn get_high_word(x: f64) -> u32 {
    (x.to_bits() >> 32) as u32
}

#[inline]
fn get_low_word(x: f64) -> u32 {
    x.to_bits() as u32
}

#[inline]
fn with_set_high_word(f: f64, hi: u32) -> f64 {
    let mut tmp = f.to_bits();
    tmp &= 0x00000000_ffffffff;
    tmp |= (hi as u64) << 32;
    f64::from_bits(tmp)
}

#[inline]
fn with_set_low_word(f: f64, lo: u32) -> f64 {
    let mut tmp = f.to_bits();
    tmp &= 0xffffffff_00000000;
    tmp |= lo as u64;
    f64::from_bits(tmp)
}

#[inline]
fn combine_words(hi: u32, lo: u32) -> f64 {
    f64::from_bits(((hi as u64) << 32) | lo as u64)
}
