// Copyright 2025 Gabriel Bjørnager Jensen.

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

mod seal {
	pub trait StdFloat { }
}

use crate::Float;

pub(crate) use seal::StdFloat as SealedStdFloat;

/// Extends [`Float`] with `std`-only functionality.
///
/// # Safety
///
/// Furthermore, all items must behave exactly as their standard counterparts.
#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "std")))]
pub unsafe trait StdFloat: SealedStdFloat + Float {
	/// See [`f64::sqrt`].
	#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "std")))]
	#[must_use]
	fn sqrt(self) -> Self;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

macro_rules! impl_std_float {
	{
		$($ty:ty $(= $feature:literal)?),*$(,)?
	} => {
		$(
			$(
				#[cfg(feature = $feature)]
			)?
			impl ::multitype::SealedStdFloat 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 sqrt(self) -> Self {
					self.sqrt()
				}

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

				#[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 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, other: Self) -> Self {
					self.atan2(other)
				}

				#[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)]
				#[track_caller]
				fn exp(self) -> Self {
					self.exp()
				}

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

				#[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 log2(self) -> Self {
					self.log2()
				}

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

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