// Copyright 2025 Gabriel Bjørnager Jensen.

mod test;

use crate::{Array, FloatToInt, Uint};

use core::any::Any;
use core::cmp::Ordering;
use core::fmt::Debug;
use core::num::FpCategory;
use core::ops::{
	Add,
	AddAssign,
	Div,
	DivAssign,
	Mul,
	MulAssign,
	Neg,
	Rem,
	RemAssign,
	Sub,
	SubAssign,
};
use core::panic::{RefUnwindSafe, UnwindSafe};

/// A generic, floating-point real.
///
/// This trait abstracts the interfaces defined by [`f16`], [`f32`], [`f64`], and [`f128`] so that these may be used as a single type.
///
/// The following traits are not required due to some floating-point types not implementing them:
/// * [`Display`](core::fmt::Display): Not implement by `f128`;
/// * <code>[From](core::fmt::Display)&lt;f16&gt;</code>: Not implement by `f32`;
/// * [`FromStr`](core::str::FromStr): Not implement by `f128`;
/// * [`LowerExp`](core::fmt::LowerExp): Not implement by `f128`;
/// * [`Product`](core::iter::Product): Not implement by `f16` or `f128`;
/// * [`Sum`](core::iter::Sum): Not implement by `f16` or `f128`;
/// * [`UpperExp`](core::fmt::UpperExp): Not implement by `f128`.
///
/// These trait will be required when standard support arrives.
///
#[cfg_attr(feature = "f128",      doc = "See also [`FloatLeast32`](crate::FloatLeast32) and [`FloatLeast64`](crate::FloatLeast64).\n")]
#[cfg_attr(not(feature = "f128"), doc = "See also [`FloatLeast32`](crate::FloatLeast32).\n")]
#[cfg_attr(feature = "std",       doc = "See also [`StdFloat`](crate::StdFloat).\n")]
///
/// # Safety
///
/// [`Bits`](Self::Bits) must be an unsigned, integral type of the same width as `Self` so that transmutations between the two is always valid.
///
/// Furthermore, all items must behave exactly as their standard counterparts.
pub unsafe trait Float
where
	Self:
		crate::SealedFloat
		+ 'static
		+ Any
		+ Copy
		+ Debug
		+ Default
		//+ From<f16> // Noobs say that `f32` can't handle it.
		+ From<i8>
		+ From<u8>
		+ PartialEq
		+ PartialOrd
		+ RefUnwindSafe
		+ Send
		+ Sync
		+ Unpin
		+ UnwindSafe
		// Arithmetic operators:
		+ Add<Output = Self>
		+ Div<Output = Self>
		+ Mul<Output = Self>
		+ Neg<Output = Self>
		+ Rem<Output = Self>
		+ Sub<Output = Self>
		// Assigning arithmetic operators:
		+ AddAssign
		+ DivAssign
		+ MulAssign
		+ RemAssign
		+ SubAssign,
{
	/// The array type that represents the bytes of this type.
	type Bytes: Array<Scalar = u8>;

	/// The scalar type that represents the bits of this type.
	type Bits: Uint;

	/// See [`f64::RADIX`].
	const RADIX:           u32;

	/// See [`f64::MANTISSA_DIGITS`].
	const MANTISSA_DIGITS: u32;

	/// See [`f64::DIGITS`].
	const DIGITS:          u32;

	/// See [`f64::EPSILON`].
	const EPSILON:         Self;

	/// See [`f64::MIN`].
	const MIN:             Self;

	/// See [`f64::MIN_POSITIVE`].
	const MIN_POSITIVE:    Self;

	/// See [`f64::MAX`].
	const MAX:             Self;

	/// See [`f64::INFINITY`].
	const INFINITY:        Self;

	/// See [`f64::NEG_INFINITY`].
	const NEG_INFINITY:    Self;

	/// See [`f64::NAN`].
	const NAN:             Self;

	/// See [`f64::E`].
	const E:               Self;

	/// See [`f64::PI`].
	const PI:              Self;

	/// See [`f64::TAU`].
	const TAU:             Self;

	/// See [`f64::LOG2_10`].
	const LOG2_10:         Self;

	/// See [`f64::LOG2_E`].
	const LOG2_E:          Self;

	/// See [`f64::LN_2`].
	const LN_2:            Self;

	/// See [`f64::LN_10`].
	const LN_10:           Self;

	/// See [`f64::LOG10_2`].
	const LOG10_2:         Self;

	/// See [`f64::LOG10_E`].
	const LOG10_E:         Self;

	/// See [`f64::SQRT_2`].
	const SQRT_2:          Self;

	/// See [`f64::FRAC_1_PI`].
	const FRAC_1_PI:       Self;

	/// See [`f64::FRAC_1_SQRT_2`].
	const FRAC_1_SQRT_2:   Self;

	/// See [`f64::FRAC_2_PI`].
	const FRAC_2_PI:       Self;

	/// See [`f64::FRAC_2_SQRT_PI`].
	const FRAC_2_SQRT_PI:  Self;

	/// See [`f64::FRAC_PI_2`].
	const FRAC_PI_2:       Self;

	/// See [`f64::FRAC_PI_3`].
	const FRAC_PI_3:       Self;

	/// See [`f64::FRAC_PI_4`].
	const FRAC_PI_4:       Self;

	/// See [`f64::FRAC_PI_6`].
	const FRAC_PI_6:       Self;

	/// See [`f64::FRAC_PI_8`].
	const FRAC_PI_8:       Self;

	/// Losslessly constructs a new, floating-point real from an [`f16`] value.
	#[cfg(feature = "f16")]
	#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "f16")))]
	#[must_use]
	fn from_f16(value: f16) -> Self;

	/// Lossily constructs a new, floating-point real from an [`f64`] value.
	///
	/// The value `value` is transformed into the destination type on a best-case basis:
	///
	/// * Non-numbers are preserved (except their payloads);
	/// * Values less than [`MIN`](Self::MIN) or greater than [`MAX`](Self::MAX) become negative infinity or positive infinity, respectively;
	/// * Finite values in range are truncated to fit.
	///
	/// Note that when [`f128`] stabilises (see [`#116909`](https://github.com/rust-lang/rust/issues/116909/)), users of this function should prepare for its removal.
	#[must_use]
	fn from_f64_lossy(value: f64) -> Self;

	/// Lossily constructs a new, floating-point real from an [`f128`] value.
	///
	/// See also [`from_f64_lossy`](Self::from_f64_lossy).
	#[cfg(feature = "f128")]
	#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "f128")))]
	#[must_use]
	fn from_f128_lossy(value: f128) -> Self;

	/// Losslessly constructs a new, floating-point real from a [`u8`] value.
	#[must_use]
	fn from_u8(value: u8) -> Self;

	/// Losslessly constructs a new, floating-point real from an [`i8`] value.
	#[must_use]
	fn from_i8(value: i8) -> Self;

	#[doc(hidden)]
	#[must_use]
	unsafe fn from_f32_unchecked(value: f32) -> Self;

	#[doc(hidden)]
	#[must_use]
	unsafe fn from_f64_unchecked(value: f64) -> Self;

	#[cfg(feature = "f128")]
	#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "f128")))]
	#[doc(hidden)]
	#[must_use]
	unsafe fn from_f128_unchecked(value: f128) -> Self;

	#[doc(hidden)]
	#[must_use]
	unsafe fn from_u16_unchecked(value: u16) -> Self;

	#[doc(hidden)]
	#[must_use]
	unsafe fn from_u32_unchecked(value: u32) -> Self;

	#[doc(hidden)]
	#[must_use]
	unsafe fn from_u64_unchecked(value: u64) -> Self;

	#[doc(hidden)]
	#[must_use]
	unsafe fn from_i16_unchecked(value: i16) -> Self;

	#[doc(hidden)]
	#[must_use]
	unsafe fn from_i32_unchecked(value: i32) -> Self;

	#[doc(hidden)]
	#[must_use]
	unsafe fn from_i64_unchecked(value: i64) -> Self;

	/// See [`f64::from_ne_bytes`].
	#[must_use]
	fn from_ne_bytes(bytes: Self::Bytes) -> Self;

	/// See [`f64::from_le_bytes`].
	#[must_use]
	fn from_le_bytes(bytes: Self::Bytes) -> Self;

	/// See [`f64::from_be_bytes`].
	#[must_use]
	fn from_be_bytes(bytes: Self::Bytes) -> Self;

	/// See [`f64::from_bits`].
	#[must_use]
	fn from_bits(bits: Self::Bits) -> Self;

	/// See [`f64::abs`].
	#[must_use]
	fn abs(self) -> Self;

	/// See [`f64::recip`].
	#[must_use]
	fn recip(self) -> Self;

	/// See [`f64::midpoint`].
	#[must_use]
	fn midpoint(self, other: Self) -> Self;

	/// See [`f64::to_radians`].
	#[must_use]
	fn to_radians(self) -> Self;

	/// See [`f64::to_degrees`].
	#[must_use]
	fn to_degrees(self) -> Self;

	/// See [`f64::next_down`].
	#[must_use]
	fn next_down(self) -> Self;

	/// See [`f64::next_up`].
	#[must_use]
	fn next_up(self) -> Self;

	/// See [`f64::signum`].
	#[must_use]
	fn signum(self) -> Self;

	/// See [`f64::copysign`].
	#[must_use]
	fn copysign(self, sign: Self) -> Self;

	/// See [`f64::classify`].
	#[must_use]
	fn classify(self) -> FpCategory;

	/// See [`f64::is_nan`].
	#[must_use]
	fn is_nan(self) -> bool;

	/// See [`f64::is_infinite`].
	#[must_use]
	fn is_infinite(self) -> bool;

	/// See [`f64::is_finite`].
	#[must_use]
	fn is_finite(self) -> bool;

	/// See [`f64::is_normal`].
	#[must_use]
	fn is_normal(self) -> bool;

	/// See [`f64::is_subnormal`].
	#[must_use]
	fn is_subnormal(self) -> bool;

	/// See [`f64::is_sign_negative`].
	#[must_use]
	fn is_sign_negative(self) -> bool;

	/// See [`f64::is_sign_positive`].
	#[must_use]
	fn is_sign_positive(self) -> bool;

	/// See [`f64::clamp`].
	#[must_use]
	fn clamp(self, min: Self, max: Self) -> Self;

	/// See [`f64::total_cmp`].
	#[must_use]
	fn total_cmp(&self, rhs: &Self) -> Ordering;

	/// See [`f64::to_int_unchecked`].
	#[expect(clippy::missing_safety_doc)]
	#[must_use]
	unsafe fn to_int_unchecked<T>(self) -> T
	where
		Self: FloatToInt<T>;

	/// See [`f64::to_bits`].
	#[must_use]
	fn to_bits(self) -> Self::Bits;

	/// See [`f64::to_ne_bytes`].
	#[must_use]
	fn to_ne_bytes(self) -> Self::Bytes;

	/// See [`f64::to_le_bytes`].
	#[must_use]
	fn to_le_bytes(self) -> Self::Bytes;

	/// See [`f64::to_be_bytes`].
	#[must_use]
	fn to_be_bytes(self) -> Self::Bytes;
}

macro_rules! impl_float {
	{
		$($Ty:ty {
		Bytes = $Bytes:ty,
		Bits  = $Bits:ty,

		consts = $consts:ident$(,)?
		} $(= $feature:literal)?),*$(,)?
	} => {$(
		$(
			#[cfg(feature = $feature)]
		)*
		impl ::multitype::SealedFloat for $Ty { }

		$(
			#[cfg(feature = $feature)]
			#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = $feature)))]
		)?
		unsafe impl ::multitype::Float for $Ty {
			type Bytes = $Bytes;

			type Bits = $Bits;

			const RADIX:           ::core::primitive::u32 = Self::RADIX;

			const MANTISSA_DIGITS: ::core::primitive::u32 = Self::MANTISSA_DIGITS;
			const DIGITS:          ::core::primitive::u32 = Self::DIGITS;

			const EPSILON:         Self = Self::EPSILON;

			const MIN:             Self = Self::MIN;
			const MIN_POSITIVE:    Self = Self::MIN_POSITIVE;
			const MAX:             Self = Self::MAX;

			const INFINITY:        Self = Self::INFINITY;
			const NEG_INFINITY:    Self = Self::NEG_INFINITY;

			const NAN:             Self = Self::NAN;

			const E:               Self = ::core::$consts::consts::E;
			const PI:              Self = ::core::$consts::consts::PI;
			const TAU:             Self = ::core::$consts::consts::TAU;

			const LOG2_10:         Self = ::core::$consts::consts::LOG2_10;
			const LOG2_E:          Self = ::core::$consts::consts::LOG2_E;
			const LN_2:            Self = ::core::$consts::consts::LN_2;
			const LN_10:           Self = ::core::$consts::consts::LN_10;
			const LOG10_2:         Self = ::core::$consts::consts::LOG10_2;
			const LOG10_E:         Self = ::core::$consts::consts::LOG10_E;

			const SQRT_2:          Self = ::core::$consts::consts::SQRT_2;

			const FRAC_1_PI:       Self = ::core::$consts::consts::FRAC_1_PI;
			const FRAC_1_SQRT_2:   Self = ::core::$consts::consts::FRAC_1_SQRT_2;
			const FRAC_2_PI:       Self = ::core::$consts::consts::FRAC_2_PI;
			const FRAC_2_SQRT_PI:  Self = ::core::$consts::consts::FRAC_2_SQRT_PI;
			const FRAC_PI_2:       Self = ::core::$consts::consts::FRAC_PI_2;
			const FRAC_PI_3:       Self = ::core::$consts::consts::FRAC_PI_3;
			const FRAC_PI_4:       Self = ::core::$consts::consts::FRAC_PI_4;
			const FRAC_PI_6:       Self = ::core::$consts::consts::FRAC_PI_6;
			const FRAC_PI_8:       Self = ::core::$consts::consts::FRAC_PI_8;

			#[cfg(feature = "f16")]
			#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "f16")))]
			#[inline(always)]
			fn from_f16(value: f16) -> Self {
				value as Self
			}

			#[inline(always)]
			fn from_f64_lossy(value: f64) -> Self {
				value as Self
			}

			#[cfg(feature = "f128")]
			#[cfg_attr(feature = "unstable-docs", doc(cfg(feature = "f128")))]
			#[inline(always)]
			fn from_f128_lossy(value: f128) -> Self {
				value as Self
			}

			#[allow(clippy::cast_lossless)]
			#[inline(always)]
			fn from_u8(value: u8) -> Self {
				value as Self
			}

			#[allow(clippy::cast_lossless)]
			#[inline(always)]
			fn from_i8(value: i8) -> Self {
				value as Self
			}

			#[allow(clippy::cast_lossless)]
			#[inline(always)]
			#[track_caller]
			unsafe fn from_f32_unchecked(value: f32) -> Self {
				value as Self
			}

			#[allow(clippy::cast_lossless)]
			#[inline(always)]
			#[track_caller]
			unsafe fn from_f64_unchecked(value: f64) -> Self {
				value as Self
			}

			#[cfg(feature = "f128")]
			#[allow(clippy::cast_lossless)]
			#[inline(always)]
			#[track_caller]
			unsafe fn from_f128_unchecked(value: f128) -> Self {
				value as Self
			}

			#[allow(clippy::cast_lossless)]
			#[inline(always)]
			#[track_caller]
			unsafe fn from_u16_unchecked(value: u16) -> Self {
				value as Self
			}

			#[allow(clippy::cast_lossless)]
			#[inline(always)]
			#[track_caller]
			unsafe fn from_u32_unchecked(value: u32) -> Self {
				value as Self
			}

			#[allow(clippy::cast_lossless)]
			#[inline(always)]
			#[track_caller]
			unsafe fn from_u64_unchecked(value: u64) -> Self {
				value as Self
			}

			#[allow(clippy::cast_lossless)]
			#[inline(always)]
			#[track_caller]
			unsafe fn from_i16_unchecked(value: i16) -> Self {
				value as Self
			}

			#[allow(clippy::cast_lossless)]
			#[inline(always)]
			#[track_caller]
			unsafe fn from_i32_unchecked(value: i32) -> Self {
				value as Self
			}

			#[allow(clippy::cast_lossless)]
			#[inline(always)]
			#[track_caller]
			unsafe fn from_i64_unchecked(value: i64) -> Self {
				value as Self
			}

			#[inline(always)]
			#[track_caller]
			fn from_ne_bytes(bytes: Self::Bytes) -> Self {
				Self::from_ne_bytes(bytes)
			}

			#[inline(always)]
			#[track_caller]
			fn from_le_bytes(bytes: Self::Bytes) -> Self {
				Self::from_le_bytes(bytes)
			}

			#[inline(always)]
			#[track_caller]
			fn from_be_bytes(bytes: Self::Bytes) -> Self {
				Self::from_be_bytes(bytes)
			}

			#[inline(always)]
			#[track_caller]
			fn from_bits(bits: Self::Bits) -> Self {
				Self::from_bits(bits)
			}

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

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

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

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

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

			// FIXME(backport)
			#[inline]
			fn next_down(self) -> Self {
				if self.is_nan() || (self.is_infinite() && self.is_sign_negative()) {
				    return self;
				}

				if self.abs().to_bits() == 0x0 {
				return Self::from_bits(0x1).copysign(-1.0);
				}

				let bits = if self.is_sign_positive() {
				self.to_bits() - 0x1
				} else {
				self.to_bits() + 0x1
				};

				Self::from_bits(bits)
			}

			// FIXME(backport)
			#[inline]
			fn next_up(self) -> Self {
				if self.is_nan() || (self.is_infinite() && self.is_sign_positive()) {
				    return self;
				}

				if self.abs().to_bits() == 0x0 {
				return Self::from_bits(0x1).copysign(-1.0);
				}

				let bits = if self.is_sign_positive() {
				self.to_bits() + 0x1
				} else {
				self.to_bits() - 0x1
				};

				Self::from_bits(bits)
			}

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

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

			#[inline(always)]
			#[track_caller]
			fn classify(self) -> ::core::num::FpCategory {
				self.classify()
			}

			#[inline(always)]
			#[track_caller]
			fn is_nan(self) -> bool {
				self.is_nan()
			}

			#[inline(always)]
			#[track_caller]
			fn is_infinite(self) -> bool {
				self.is_infinite()
			}

			#[inline(always)]
			#[track_caller]
			fn is_finite(self) -> bool {
				self.is_finite()
			}

			#[inline(always)]
			#[track_caller]
			fn is_normal(self) -> bool {
				self.is_normal()
			}

			#[inline(always)]
			#[track_caller]
			fn is_subnormal(self) -> bool {
				self.is_subnormal()
			}

			#[inline(always)]
			#[track_caller]
			fn is_sign_negative(self) -> bool {
				self.is_sign_negative()
			}

			#[inline(always)]
			#[track_caller]
			fn is_sign_positive(self) -> bool {
				self.is_sign_positive()
			}

			#[inline(always)]
			#[track_caller]
			fn clamp(self, min: Self, max: Self) -> Self {
				self.clamp(min, max)
			}

			#[inline(always)]
			#[track_caller]
			fn total_cmp(&self, rhs: &Self) -> ::core::cmp::Ordering {
				self.total_cmp(rhs)
			}

			#[inline(always)]
			#[track_caller]
			unsafe fn to_int_unchecked<T>(self) -> T
			where
				Self: ::multitype::FloatToInt<T>,
			{
				unsafe { <Self as ::multitype::SealedFloatToInt<T>>::to_int_unchecked(self) }
			}

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

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

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

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

impl_float! {
	f16  { Bytes = [u8; 0x02], Bits = u16,  consts = f16  } = "f16",
	f32  { Bytes = [u8; 0x04], Bits = u32,  consts = f32  },
	f64  { Bytes = [u8; 0x08], Bits = u64,  consts = f64  },
	f128 { Bytes = [u8; 0x10], Bits = u128, consts = f128 } = "f128",
}
