// Copyright 2025 Gabriel Bjørnager Jensen.

#![cfg(feature = "std")]

mod test;

use crate::Float;

/// Extends [`Float`] with [`std`]-only functionality.
///
/// Note that as `core_float_math` stabilises (see [`#137578`](https://github.com/rust-lang/rust/issues/137578/)), users of this trait should prepare for its removal.
///
/// # Safety
///
/// All items must behave exactly as their standard counterparts.
#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "std")))]
pub unsafe trait StdFloat: crate::seal::StdFloat + Float {
	/// See <code>[f64]::[div_euclid](f64::div_euclid)</code>.
	#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "std")))]
	#[must_use]
	fn div_euclid(self, rhs: Self) -> Self;

	/// See <code>[f64]::[rem_euclid](f64::rem_euclid)</code>.
	#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "std")))]
	#[must_use]
	fn rem_euclid(self, rhs: Self) -> Self;

	/// See <code>[f64]::[mul_add](f64::mul_add)</code>.
	#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "std")))]
	#[must_use]
	fn mul_add(self, a: Self, b: Self) -> Self;

	/// See <code>[f64]::[powf](f64::powf)</code>.
	#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "std")))]
	#[must_use]
	fn powf(self, exp: Self) -> Self;

	/// See <code>[f64]::[powi](f64::powi)</code>.
	#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "std")))]
	#[must_use]
	fn powi(self, exp: i32) -> Self;

	/// See <code>[f64]::[exp](f64::exp)</code>.
	#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "std")))]
	#[must_use]
	fn exp(self) -> Self;

	/// See <code>[f64]::[exp_m1](f64::exp_m1)</code>.
	#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "std")))]
	#[must_use]
	fn exp_m1(self) -> Self;

	/// See <code>[f64]::[exp2](f64::exp2)</code>.
	#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "std")))]
	#[must_use]
	fn exp2(self) -> Self;

	/// See <code>[f64]::[sqrt](f64::sqrt)</code>.
	#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "std")))]
	#[must_use]
	fn sqrt(self) -> Self;

	/// See <code>[f64]::[cbrt](f64::cbrt)</code>.
	#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "std")))]
	#[must_use]
	fn cbrt(self) -> Self;

	/// See <code>[f64]::[log](f64::log)</code>.
	#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "std")))]
	#[must_use]
	fn log(self, base: Self) -> Self;

	/// See <code>[f64]::[ln](f64::ln)</code>.
	#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "std")))]
	#[must_use]
	fn ln(self) -> Self;

	/// See <code>[f64]::[ln_1p](f64::ln_1p)</code>.
	#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "std")))]
	#[must_use]
	fn ln_1p(self) -> Self;

	/// See <code>[f64]::[log2](f64::log2)</code>.
	#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "std")))]
	#[must_use]
	fn log2(self) -> Self;

	/// See <code>[f64]::[log10](f64::log10)</code>.
	#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "std")))]
	#[must_use]
	fn log10(self) -> Self;

	/// See <code>[f64]::[hypot](f64::hypot)</code>.
	#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "std")))]
	#[must_use]
	fn hypot(self, rhs: Self) -> Self;

	/// See <code>[f64]::[sin](f64::sin)</code>.
	#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "std")))]
	#[must_use]
	fn sin(self) -> Self;

	/// See <code>[f64]::[cos](f64::cos)</code>.
	#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "std")))]
	#[must_use]
	fn cos(self) -> Self;

	/// See <code>[f64]::[tan](f64::tan)</code>.
	#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "std")))]
	#[must_use]
	fn tan(self) -> Self;

	/// See <code>[f64]::[sin_cos](f64::sin_cos)</code>.
	#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "std")))]
	#[must_use]
	fn sin_cos(self) -> (Self, Self);

	/// See <code>[f64]::[asin](f64::asin)</code>.
	#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "std")))]
	#[must_use]
	fn asin(self) -> Self;

	/// See <code>[f64]::[acos](f64::acos)</code>.
	#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "std")))]
	#[must_use]
	fn acos(self) -> Self;

	/// See <code>[f64]::[atan](f64::atan)</code>.
	#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "std")))]
	#[must_use]
	fn atan(self) -> Self;

	/// See <code>[f64]::[atan2](f64::atan2)</code>.
	#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "std")))]
	#[must_use]
	fn atan2(self, rhs: Self) -> Self;

	/// See <code>[f64]::[sinh](f64::sinh)</code>.
	#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "std")))]
	#[must_use]
	fn sinh(self) -> Self;

	/// See <code>[f64]::[cosh](f64::cosh)</code>.
	#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "std")))]
	#[must_use]
	fn cosh(self) -> Self;

	/// See <code>[f64]::[tanh](f64::tanh)</code>.
	#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "std")))]
	#[must_use]
	fn tanh(self) -> Self;

	/// See <code>[f64]::[asinh](f64::asinh)</code>.
	#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "std")))]
	#[must_use]
	fn asinh(self) -> Self;

	/// See <code>[f64]::[acosh](f64::acosh)</code>.
	#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "std")))]
	#[must_use]
	fn acosh(self) -> Self;

	/// See <code>[f64]::[atanh](f64::atanh)</code>.
	#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "std")))]
	#[must_use]
	fn atanh(self) -> Self;

	/// See <code>[f64]::[round](f64::round)</code>.
	#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "std")))]
	#[must_use]
	fn round(self) -> Self;

	/// See <code>[f64]::[round_ties_even](f64::round_ties_even)</code>.
	#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "std")))]
	#[must_use]
	fn round_ties_even(self) -> Self;

	/// See <code>[f64]::[floor](f64::floor)</code>.
	#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "std")))]
	#[must_use]
	fn floor(self) -> Self;

	/// See <code>[f64]::[ceil](f64::ceil)</code>.
	#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "std")))]
	#[must_use]
	fn ceil(self) -> Self;

	/// See <code>[f64]::[trunc](f64::trunc)</code>.
	#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "std")))]
	#[must_use]
	fn trunc(self) -> Self;

	/// See <code>[f64]::[fract](f64::fract)</code>.
	#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "std")))]
	#[must_use]
	fn fract(self) -> Self;
}

macro_rules! impl_std_float {
	{
		$($Ty:ty $(= $feature:literal)?),*$(,)?
	} => {
		$(
			$(#[cfg(feature = $feature)])?
			impl ::multitype::seal::StdFloat for $Ty {}

			$(#[cfg(feature = $feature)] )?
			#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "std")))]
			$(#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = $feature)))] )?
			unsafe impl ::multitype::StdFloat for $Ty {
				#[inline(always)]
				#[track_caller]
				fn div_euclid(self, rhs: Self) -> Self {
					self.div_euclid(rhs)
				}

				#[inline(always)]
				#[track_caller]
				fn rem_euclid(self, rhs: Self) -> Self {
					self.rem_euclid(rhs)
				}

				#[inline(always)]
				#[track_caller]
				fn mul_add(self, a: Self, b: Self) -> Self {
					self.mul_add(a, b)
				}

				#[inline(always)]
				#[track_caller]
				fn powf(self, exp: Self) -> Self {
					self.powf(exp)
				}

				#[inline(always)]
				#[track_caller]
				fn powi(self, exp: ::core::primitive::i32) -> Self {
					self.powi(exp)
				}

				#[inline(always)]
				#[track_caller]
				fn exp(self) -> Self {
					self.exp()
				}

				#[inline(always)]
				#[track_caller]
				fn exp_m1(self) -> Self {
					self.exp_m1()
				}

				#[inline(always)]
				#[track_caller]
				fn exp2(self) -> Self {
					self.exp2()
				}

				#[inline(always)]
				#[track_caller]
				fn sqrt(self) -> Self {
					self.sqrt()
				}

				#[inline(always)]
				#[track_caller]
				fn cbrt(self) -> Self {
					self.cbrt()
				}

				#[inline(always)]
				#[track_caller]
				fn log(self, base: Self) -> Self {
					self.log(base)
				}

				#[inline(always)]
				#[track_caller]
				fn ln(self) -> Self {
					self.ln()
				}

				#[inline(always)]
				#[track_caller]
				fn ln_1p(self) -> Self {
					self.ln_1p()
				}

				#[inline(always)]
				#[track_caller]
				fn log2(self) -> Self {
					self.log2()
				}

				#[inline(always)]
				#[track_caller]
				fn log10(self) -> Self {
					self.log10()
				}

				#[inline(always)]
				#[track_caller]
				fn hypot(self, rhs: Self) -> Self {
					self.hypot(rhs)
				}

				#[inline(always)]
				#[track_caller]
				fn sin(self) -> Self {
					self.sin()
				}

				#[inline(always)]
				#[track_caller]
				fn cos(self) -> Self {
					self.cos()
				}

				#[inline(always)]
				#[track_caller]
				fn tan(self) -> Self {
					self.tan()
				}

				#[inline(always)]
				#[track_caller]
				fn sin_cos(self) -> (Self, Self) {
					self.sin_cos()
				}

				#[inline(always)]
				#[track_caller]
				fn asin(self) -> Self {
					self.asin()
				}

				#[inline(always)]
				#[track_caller]
				fn acos(self) -> Self {
					self.acos()
				}

				#[inline(always)]
				#[track_caller]
				fn atan(self) -> Self {
					self.atan()
				}

				#[inline(always)]
				#[track_caller]
				fn atan2(self, rhs: Self) -> Self {
					self.atan2(rhs)
				}

				#[inline(always)]
				#[track_caller]
				fn sinh(self) -> Self {
					self.sinh()
				}

				#[inline(always)]
				#[track_caller]
				fn cosh(self) -> Self {
					self.cosh()
				}

				#[inline(always)]
				#[track_caller]
				fn tanh(self) -> Self {
					self.tanh()
				}

				#[inline(always)]
				#[track_caller]
				fn asinh(self) -> Self {
					self.asinh()
				}

				#[inline(always)]
				#[track_caller]
				fn acosh(self) -> Self {
					self.acosh()
				}

				#[inline(always)]
				#[track_caller]
				fn atanh(self) -> Self {
					self.atanh()
				}

				#[inline(always)]
				fn round(self) -> Self {
					self.round()
				}

				#[inline(always)]
				fn round_ties_even(self) -> Self {
					self.round_ties_even()
				}

				#[inline(always)]
				fn floor(self) -> Self {
					self.floor()
				}

				#[inline(always)]
				fn ceil(self) -> Self {
					self.ceil()
				}

				#[inline(always)]
				fn trunc(self) -> Self {
					self.trunc()
				}

				#[inline(always)]
				fn fract(self) -> Self {
					self.fract()
				}
			}
		)*
	};
}

impl_std_float! {
	f16  = "f16",
	f32,
	f64,
	f128 = "f128",
}
