#![deny(rustdoc::broken_intra_doc_links)]
#![feature(error_generic_member_access)]
//#![feature(const_trait_impl)]
#![feature(associated_const_equality)]
#![feature(trait_alias)]
//#![feature(generic_const_exprs)]
//#![feature(associated_type_defaults)]
//#![feature(non_lifetime_binders)]
//#![feature(negative_impls)]

//! [`num-valid`](crate) is a Rust library designed for robust, generic, and high-performance numerical computation.
//! It provides a safe and extensible framework for working with both real and complex numbers, addressing the challenges
//! of floating-point arithmetic by ensuring correctness and preventing common errors like `NaN` propagation.
//!
//! # Key Features \& Architecture
//!
//! - **Safety by Construction with Validated Types:** Instead of using raw primitives like [`f64`] or [`Complex<f64>`](num::Complex)
//!   directly, [`num-valid`](crate) encourages the use of validated wrappers like [`RealValidated`]
//!   and [`ComplexValidated`]. These types guarantee that the value they hold is always valid (e.g.,
//!   finite) according to a specific policy, eliminating entire classes of numerical bugs.
//!
//! - **Enhanced Type Safety with Hashing Support:** Types validated with policies that guarantee
//!   finite values (like [`Native64StrictFinite`]) automatically
//!   implement [`Eq`] and [`Hash`]. This enables their use as keys in [`HashMap`](std::collections::HashMap) and other
//!   hash-based collections, while maintaining compatibility with the library's [`PartialOrd`]-based
//!   comparison functions. The hashing implementation properly handles IEEE 754 floating-point
//!   edge cases and ensures that mathematically equal values produce identical hash codes.
//!
//! - **Support for Real and Complex Numbers:** The library supports both real and complex numbers,
//!   with specific validation policies for each type.
//!
//! - **Layered and Extensible Design:** The library has a well-defined, layered, and highly generic architecture.
//!   It abstracts the concept of a "numerical kernel" (the underlying number representation and its operations) from the high-level mathematical traits.
//!
//!   The architecture can be understood in four main layers:
//!   - **Layer 1: Raw Trait Contracts** ([`kernels`] module):
//!     - The [`RawScalarTrait`], [`RawRealTrait`], and [`RawComplexTrait`](crate::kernels::RawComplexTrait) define the low-level, "unchecked" contract
//!       for any number type.
//!     - These traits are the foundation, providing a standard set of `unchecked_*` methods.
//!     - The contract is that the caller must guarantee the validity of inputs. This is a strong design choice, separating the raw, potentially unsafe operations from the validated, safe API.
//!
//!     This design separates the pure, high-performance computational logic from the safety and validation logic.
//!     It creates a clear, minimal contract for backend implementors and allows the validated wrappers in Layer 3
//!     to be built on a foundation of trusted, high-speed operations.
//!
//!   - **Layer 2: Validation Policies** ([`validation`] module):
//!     - The [`NumKernel`] trait is the bridge between the raw types and the validated wrappers.
//!     - It bundles together the raw real/complex types and their corresponding validation policies
//!       (e.g., [`Native64StrictFinite`], [`Native64StrictFiniteInDebug`],
//!       etc.). This allows the entire behavior of the validated types to be configured with a single generic parameter.
//!
//!     It acts as the central policy configuration point. By choosing a [`NumKernel`], a user selects both a
//!     numerical backend (e.g., [`f64`]/[`Complex<f64>`] or [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html)/[`rug::Complex`](https://docs.rs/rug/latest/rug/struct.Complex.html)) and a set of validation policies (e.g., [`StrictFinitePolicy`](crate::validation::StrictFinitePolicy)
//!     vs. [`DebugValidationPolicy`](crate::validation::DebugValidationPolicy)) for real and complex numbers,
//!     with a single generic parameter. This dramatically simplifies writing generic code that can be configured for
//!     different safety and performance trade-offs.
//!
//!   - **Layer 3: Validated Wrappers**:
//!     - [`RealValidated<K>`] and [`ComplexValidated<K>`]
//!       are the primary user-facing types.
//!     - These are [newtype](https://doc.rust-lang.org/rust-by-example/generics/new_types.html) wrappers that are guaranteed to hold a value that conforms to the [`NumKernel`] `K` (and to the validation policies therein).
//!     - They use extensive macros to implement high-level traits. The logic is clean: perform a check (if necessary) on the input value,
//!       then call the corresponding `unchecked_*` method from the raw trait, and then perform a check on the output value before returning it.
//!       This ensures safety and correctness.
//!
//!     These wrappers use the [newtype pattern](https://doc.rust-lang.org/rust-by-example/generics/new_types.html) to enforce correctness at the type level. By construction, an instance
//!     of [`RealValidated`] is guaranteed to contain a value that has passed the validation policy, eliminating entire
//!     classes of errors (like `NaN` propagation) in user code.
//!
//!   - **Layer 4: High-Level Abstraction Traits**:
//!     - The [`FpScalar`] trait is the central abstraction, defining a common interface for all scalar types. It uses an associated type sealed type (`Kind`),
//!       to enforce that a scalar is either real or complex, but not both.
//!     - [`RealScalar`] and [`ComplexScalar`] are specialized sub-traits of [`FpScalar`] that serve as markers for real and complex numbers, respectively.
//!     - Generic code in a consumer crate is written against these high-level traits.
//!     - The [`RealValidated`] and [`ComplexValidated`] structs from Layer 3 are the concrete implementors of these traits.
//!
//!     These traits provide the final, safe, and generic public API. Library consumers write their algorithms
//!     against these traits, making their code independent of the specific numerical kernel being used.
//!
//!   This layered approach is powerful, providing both high performance (by using unchecked methods internally) and safety
//!   (through the validated wrappers). The use of generics and traits makes it extensible to new numerical backends (as demonstrated by the rug implementation).
//!
//! - **Multiple Numerical Backends**. At the time of writing, 2 numerical backends can be used:
//!   - the standard (high-performance) numerical backend is the one in which the raw floating point and complex numbers are
//!     described by the Rust's native [`f64`] and [`Complex<f64>`](num::Complex) types, as described by the ANSI/IEEE Std 754-1985;
//!   - an optional (high-precision) numerical backend is available if the library is compiled with the optional flag `--features=rug`,
//!     and uses the arbitrary precision raw types `rug::Float` and `rug::Complex` from the Rust library [`rug`](https://crates.io/crates/rug).
//! - **Comprehensive Mathematical Library**. It includes a wide range of mathematical functions for
//!   trigonometry, logarithms, exponentials, and more, all implemented as traits (e.g., [`functions::Sin`], [`functions::Cos`], [`functions::Sqrt`]) and available on the validated types.
//! - **Numerically Robust Implementations**. The library commits to numerical accuracy. As an example,
//!   by using `NeumaierSum` for its default [`std::iter::Sum`] implementation to minimize precision loss.
//! - **Robust Error Handling:** The library defines detailed error types for various numerical operations,
//!   ensuring that invalid inputs and outputs are properly handled and reported.
//!   Errors are categorized into input and output errors, with specific variants for different types of numerical
//!   issues such as division by zero, invalid values, and subnormal numbers.
//! - **Conditional Backtrace Capture:** Backtrace capture in error types is disabled by default for maximum
//!   performance. Enable the `backtrace` feature flag for debugging to get full stack traces in error types.
//! - **Conditional `Copy` Implementation:** Validated types ([`RealValidated`], [`ComplexValidated`]) automatically
//!   implement [`Copy`] when their underlying raw types support it. This enables zero-cost pass-by-value semantics
//!   for native 64-bit types while correctly requiring [`Clone`] for non-[`Copy`] backends like `rug`.
//! - **Comprehensive Documentation:** The library includes detailed documentation for each struct, trait, method,
//!   and error type, making it easy for users to understand and utilize the provided functionality.
//!   Examples are provided for key functions to demonstrate their usage and expected behavior.
//! - **Zero-Copy Conversions:** The native 64-bit validated types ([`RealNative64StrictFinite`], [`RealNative64StrictFiniteInDebug`])
//!   implement [`bytemuck::CheckedBitPattern`](https://docs.rs/bytemuck/latest/bytemuck/checked/trait.CheckedBitPattern.html)
//!   and [`bytemuck::NoUninit`](https://docs.rs/bytemuck/latest/bytemuck/trait.NoUninit.html),
//!   enabling safe, zero-copy conversions between byte representations and validated types. This is particularly useful for
//!   interoperability with binary data formats, serialization, and performance-critical code. The conversion automatically
//!   validates the bit pattern, rejecting invalid values (NaN, Infinity, subnormal numbers) at compile time for type safety.
//!
//! # Architecture Deep Dive
//!
//! For a comprehensive understanding of the library's architecture, design patterns, and implementation details,
//! see the **[Architecture Guide](https://gitlab.com/max.martinelli/num-valid/-/blob/master/docs/ARCHITECTURE.md)**
//! (also available in the repository at `docs/ARCHITECTURE.md`).
//!
//! The guide covers:
//! - **Detailed explanation of the 4-layer design** with examples and rationale
//! - **How to add a new numerical backend** (step-by-step guide with checklist)
//! - **How to implement new mathematical functions** (complete template)
//! - **Error handling architecture** (two-phase error model, backtrace handling)
//! - **Performance considerations** (zero-cost abstractions, optimization patterns)
//! - **Macro system** (code generation patterns and proposals)
//! - **Design patterns reference** (newtype, marker traits, sealed traits, etc.)
//!
//! **For Contributors**: Reading the architecture guide is essential before submitting PRs, as it explains
//! the design philosophy and implementation conventions that keep the codebase consistent and maintainable.
//!
//! # Compiler Requirement: Rust Nightly
//! This library currently requires the **nightly** toolchain because it uses some unstable Rust features which,
//! at the time of writing (September 2025), are not yet available in stable or beta releases. These features are:
//!   - `trait_alias`: For ergonomic trait combinations
//!   - `associated_const_equality`: For rug precision safety
//!   - `error_generic_member_access`: For advanced error handling
//!
//! If these features are stabilized in a future Rust release, the library will be updated to support the stable compiler.
//!
//! To use the nightly toolchain, please run:
//!
//! ```bash
//! rustup install nightly
//! rustup override set nightly
//! ```
//!
//! This will set your environment to use the nightly compiler, enabling compatibility with the current version of the
//! library.
//!
//! # Getting Started
//!
//! This guide will walk you through the basics of using [`num-valid`](crate).
//!
//! ## 1. Add [`num-valid`](crate) to your Project
//!
//! Add the following to your `Cargo.toml` (change the versions to the latest ones):
//!
//! ```toml
//! [dependencies]
//! num-valid = "0.2.0"  # Change to the latest version
//! try_create = "0.1.2" # Needed for the TryNew trait
//! num = "0.4"          # For basic numeric traits
//! ```
//!
//! ### Feature Flags
//!
//! The library provides several optional feature flags:
//!
//! | Feature | Description |
//! |---------|-------------|
//! | `rug` | Enables the high-precision arbitrary arithmetic backend using [`rug`](https://crates.io/crates/rug). See LGPL-3.0 notice below. |
//! | `backtrace` | Enables backtrace capture in error types for debugging. Disabled by default for performance. |
//!
//! Example with multiple features:
//!
//! ```toml
//! [dependencies]
//! num-valid = { version = "0.2.0", features = ["rug", "backtrace"] }
//! try_create = "0.1.2"
//! num = "0.4"
//! ```
//!
//! ## 2. Core Concept: Validated Types
//!
//! The central idea in [`num-valid`](crate) is to use **validated types** instead of raw primitives like [`f64`].
//! These are wrappers that guarantee their inner value is always valid (e.g., not `NaN` or `Infinity`) according to
//! a specific policy.
//!
//! The most common type you'll use is [`RealNative64StrictFinite`], which wraps an [`f64`] and ensures it's always finite,
//! both in Debug and Release mode. For a similar type wrapping an [`f64`] that ensures it's always finite, but with the
//! validity checks executed only in Debug mode (providing performance equal to the raw [`f64`] type), you can use
//! [`RealNative64StrictFiniteInDebug`].
//!
//! ## 3. Your First Calculation
//!
//! Let's perform a square root calculation. You'll need to bring the necessary traits into scope.
//!
//! ```rust
//! // Import the validated type and necessary traits
//! use num_valid::{
//!     RealNative64StrictFinite,
//!     functions::{Sqrt, SqrtRealInputErrors, SqrtRealErrors},
//! };
//! use try_create::TryNew;
//!
//! // 1. Create a validated number. try_new() returns a Result.
//! let x = RealNative64StrictFinite::try_new(4.0).unwrap();
//!
//! // 2. Use the direct method for operations.
//! // This will panic if the operation is invalid (e.g., sqrt of a negative).
//! let sqrt_x = x.sqrt();
//! assert_eq!(*sqrt_x.as_ref(), 2.0);
//!
//! // 3. Use the `try_*` methods for error handling.
//! // This is the safe way to handle inputs that might be out of the function's domain.
//! let neg_x = RealNative64StrictFinite::try_new(-4.0).unwrap();
//! let sqrt_neg_x_result = neg_x.try_sqrt();
//!
//! // The operation fails gracefully, returning an Err.
//! assert!(sqrt_neg_x_result.is_err());
//!
//! // The error gives information about the problem that occurred
//! if let Err(SqrtRealErrors::Input { source }) = sqrt_neg_x_result {
//!     assert!(matches!(source, SqrtRealInputErrors::NegativeValue { .. }));
//! }
//! ```
//!
//! ## 4. Writing Generic Functions
//!
//! The real power of [`num-valid`](crate) comes from writing generic functions that work with any
//! supported numerical type. You can do this by using the [`FpScalar`] and [`RealScalar`] traits as bounds.
//!
//! ```rust
//! use num_valid::{
//!     RealScalar, RealNative64StrictFinite, FpScalar,
//!     functions::{Abs, Sin, Cos},
//! };
//! use num::One;
//! use try_create::TryNew;
//!
//! // This function works for any type that implements RealScalar,
//! // including f64, RealNative64StrictFinite, and RealRugStrictFinite.
//! fn verify_trig_identity<T: RealScalar>(angle: T) -> T {
//!     // We can use .sin(), .cos(), and arithmetic ops because they are
//!     // required by the RealScalar trait.
//!     let sin_x = angle.clone().sin();
//!     let cos_x = angle.cos();
//!     (sin_x.clone() * sin_x) + (cos_x.clone() * cos_x)
//! }
//!
//! // Define a type alias for convenience
//! type MyReal = RealNative64StrictFinite;
//!
//! // Call it with a validated f64 type.
//! let angle = MyReal::try_from_f64(0.5).unwrap();
//! let identity = verify_trig_identity(angle);
//!
//! // The result should be very close to 1.0.
//! let one = MyReal::one();
//! assert!((identity - one).abs() < MyReal::try_from_f64(1e-15).unwrap());
//! ```
//!
//! If the `rug` feature is enabled, you could call the exact same
//! function with a high-precision number changing only the definition
//! of the alias type `MyReal`. For example, for real numbers with precision of 100 bits:
//! ```rust
//! # #[cfg(feature = "rug")] {
//! // Import the rug-based types
//! use num_valid::{
//!     RealScalar, RealRugStrictFinite, FpScalar,
//!     functions::{Abs, Sin, Cos},
//! };
//! use num::One;
//! use try_create::TryNew;
//!
//! // This function works for any type that implements RealScalar,
//! // including f64, RealNative64StrictFinite, and RealRugStrictFinite.
//! fn verify_trig_identity<T: RealScalar>(angle: T) -> T {
//!     // We can use .sin(), .cos(), and arithmetic ops because they are
//!     // required by the RealScalar trait.
//!     let sin_x = angle.clone().sin();
//!     let cos_x = angle.cos();
//!     (sin_x.clone() * sin_x) + (cos_x.clone() * cos_x)
//! }
//!
//! // Define a type alias for convenience - real number with precision of 100 bits
//! type MyReal = RealRugStrictFinite<100>;
//!
//! // Initialize it with an f64 value.
//! let angle = MyReal::try_from_f64(0.5).unwrap();
//! let identity = verify_trig_identity(angle);
//!
//! // The result should be very close to 1.0.
//! let one = MyReal::one();
//! assert!((identity - one).abs() < MyReal::try_from_f64(1e-15).unwrap());
//! # }
//! ```
//!
//! ## 5. Working with Complex Numbers
//!
//! The library also provides validated complex number types:
//!
//! ```rust
//! use num_valid::{ComplexNative64StrictFinite, functions::Abs};
//! use num::Complex;
//! use try_create::TryNew;
//!
//! // Create a validated complex number
//! let z = ComplexNative64StrictFinite::try_new(Complex::new(3.0, 4.0)).unwrap();
//!
//! // Complex operations work the same way
//! let magnitude = z.abs();
//! assert_eq!(*magnitude.as_ref(), 5.0); // |3 + 4i| = 5
//! ```
//!
//! ## 6. Zero-Copy Conversions with Bytemuck
//!
//! For performance-critical applications working with binary data, the library provides safe, zero-copy
//! conversions through the [`bytemuck`](https://docs.rs/bytemuck) crate. The native 64-bit validated types
//! ([`RealNative64StrictFinite`], [`RealNative64StrictFiniteInDebug`]) implement
//! [`CheckedBitPattern`](https://docs.rs/bytemuck/latest/bytemuck/checked/trait.CheckedBitPattern.html)
//! and [`NoUninit`](https://docs.rs/bytemuck/latest/bytemuck/trait.NoUninit.html), enabling safe conversions
//! that automatically validate bit patterns:
//!
//! ```rust
//! use num_valid::RealNative64StrictFinite;
//! use bytemuck::checked::try_from_bytes;
//!
//! // Convert from bytes - validation happens automatically
//! let value = 42.0_f64;
//! let bytes = value.to_ne_bytes();
//! let validated: &RealNative64StrictFinite = try_from_bytes(&bytes).unwrap();
//! assert_eq!(*validated.as_ref(), 42.0);
//!
//! // Invalid values (NaN, Infinity, subnormals) are rejected
//! let nan_bytes = f64::NAN.to_ne_bytes();
//! assert!(try_from_bytes::<RealNative64StrictFinite>(&nan_bytes).is_err());
//! ```
//!
//! This is particularly useful for:
//! - Loading validated numbers from binary file formats
//! - Interoperability with serialization libraries
//! - Processing validated numerical data in performance-critical loops
//! - Working with memory-mapped files containing numerical data
//!
//! For comprehensive examples including slice operations and error handling, see the test module
//! in the [`native64_validated`](crate::kernels::native64_validated) module source code.
//!
//! # License
//! Copyright 2023-2025, C.N.R. - Consiglio Nazionale delle Ricerche
//!
//! Licensed under either of
//! - Apache License, Version 2.0
//! - MIT license
//!
//! at your option.
//!
//! Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this project by you,
//! as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
//!
//! ## License Notice for Optional Feature Dependencies (LGPL-3.0 Compliance)
//! If you enable the `rug` feature, this project will depend on the [`rug`](https://crates.io/crates/rug) library,
//! which is licensed under the LGPL-3.0 license. Activating this feature may introduce LGPL-3.0 requirements to your
//! project. Please review the terms of the LGPL-3.0 license to ensure compliance.

pub mod functions;
pub mod kernels;
pub mod neumaier_compensated_sum;
pub mod validation;

use crate::{
    functions::{
        ACos, ACosH, ASin, ASinH, ATan, ATan2, ATanH, Abs, Arg, Arithmetic, Clamp, Classify,
        ComplexScalarConstructors, ComplexScalarGetParts, ComplexScalarMutateParts,
        ComplexScalarSetParts, Conjugate, Cos, CosH, Exp, ExpM1, HyperbolicFunctions, Hypot, Ln,
        Ln1p, Log2, Log10, LogarithmFunctions, Max, Min, MulAddRef, NegAssign, Pow,
        PowComplexBaseRealExponentErrors, PowIntExponent, PowRealBaseRealExponentErrors,
        Reciprocal, Rounding, Sign, Sin, SinH, Sqrt, Tan, TanH, TotalCmp, TrigonometricFunctions,
    },
    kernels::{RawRealTrait, RawScalarTrait},
    neumaier_compensated_sum::NeumaierAddable,
    validation::{ErrorsRawRealToInteger, ErrorsTryFromf64, FpChecks},
};
use num::{Complex, One, Zero};
use rand::{Rng, distr::Distribution};
use serde::{Serialize, de::DeserializeOwned};
use std::{
    fmt::{Debug, Display},
    ops::{Mul, MulAssign, Neg},
};
use try_create::IntoInner;

pub use crate::kernels::{
    ComplexValidated, NumKernel, RawKernel, RealValidated,
    native64::Native64,
    native64_validated::{
        ComplexNative64StrictFinite, ComplexNative64StrictFiniteInDebug, Native64StrictFinite,
        Native64StrictFiniteInDebug, RealNative64StrictFinite, RealNative64StrictFiniteInDebug,
    },
};

#[cfg(feature = "rug")]
pub use crate::kernels::rug::{ComplexRugStrictFinite, RealRugStrictFinite, RugStrictFinite};

//------------------------------------------------------------------------------------------------
/// Private module to encapsulate implementation details of the scalar kind for mutual exclusion.
///
/// This module provides the sealed trait pattern to ensure that scalar types can only be
/// either real or complex, but never both. The sealed nature prevents external crates
/// from implementing the trait, maintaining the library's type safety guarantees.
pub(crate) mod scalar_kind {
    /// A sealed trait to mark the "kind" of a scalar.
    ///
    /// External users cannot implement this trait due to the sealed pattern.
    /// This ensures that only the library-defined scalar kinds (Real and Complex)
    /// can be used with the [`FpScalar`](super::FpScalar) trait.
    pub trait Sealed {}

    /// A marker type for real scalar types.
    ///
    /// This type is used as the `Kind` associated type for real number types
    /// in the [`FpScalar`](super::FpScalar) trait, ensuring type-level distinction
    /// between real and complex scalars.
    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
    pub struct Real;
    impl Sealed for Real {}

    /// A marker type for complex scalar types.
    ///
    /// This type is used as the `Kind` associated type for complex number types
    /// in the [`FpScalar`](super::FpScalar) trait, ensuring type-level distinction
    /// between real and complex scalars.
    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
    pub struct Complex;
    impl Sealed for Complex {}
}
//------------------------------------------------------------------------------------------------

//------------------------------------------------------------------------------------------------

/// Fundamental trait alias bundling core requirements for all scalar types.
///
/// [`ScalarCore`] aggregates the essential traits that every scalar type in [`num-valid`](crate)
/// must implement, regardless of whether it's real or complex, native or arbitrary-precision.
/// This trait alias provides a single, maintainable definition of the foundational capabilities
/// required by all floating-point scalar types.
///
/// ## Included Traits
///
/// - **[`Sized`]**: Types must have a known size at compile time
/// - **[`Clone`]**: Values can be duplicated (note: not necessarily [`Copy`])
/// - **[`Debug`]**: Supports formatted debugging output with `{:?}`
/// - **[`Display`]**: Supports user-facing formatted output with `{}`
/// - **[`PartialEq`]**: Supports equality comparisons (may upgrade to [`Eq`] with finite guarantees)
/// - **[`Send`]**: Safe to transfer ownership across thread boundaries
/// - **[`Sync`]**: Safe to share references across threads
/// - **[`Zero`]**: Has an additive identity element (0)
/// - **[`One`]**: Has a multiplicative identity element (1)
/// - **[`Serialize`]**: Can be serialized (via [`serde`])
/// - **[`DeserializeOwned`]**: Can be deserialized without borrowing (via [`serde`])
/// - **`'static`**: Contains no non-static references
///
/// ## Design Rationale
///
/// This trait alias serves several important purposes:
///
/// 1. **Single Source of Truth**: Defines core requirements in one place, reducing duplication
/// 2. **Maintainability**: Changes to fundamental requirements only need to be made once
/// 3. **Readability**: Simplifies trait bounds in [`FpScalar`] and other high-level traits
/// 4. **Consistency**: Ensures all scalar types share the same baseline capabilities
///
/// ## Usage in Type Bounds
///
/// [`ScalarCore`] is primarily used as a super-trait bound in [`FpScalar`]:
///
/// ```rust
/// # use num_valid::ScalarCore;
/// // Instead of listing all traits individually:
/// // pub trait FpScalar: Sized + Clone + Debug + Display + PartialEq + Send + Sync + ...
///
/// // We use the trait alias:
/// pub trait FpScalar: ScalarCore + /* other mathematical traits */ {
///     // ...
/// }
/// ```
///
/// ## Thread Safety
///
/// The inclusion of [`Send`] and [`Sync`] makes all scalar types safe for concurrent use:
/// - **[`Send`]**: Scalars can be moved between threads
/// - **[`Sync`]**: Scalars can be shared via `&T` between threads
///
/// This enables parallel numerical computations without additional synchronization overhead.
///
/// ## Serialization Support
///
/// All scalar types support serialization through [`serde`]:
///
/// ```rust,ignore
/// use num_valid::RealNative64StrictFinite;
/// use try_create::TryNew;
/// use serde_json;
///
/// let value = RealNative64StrictFinite::try_new(3.14159).unwrap();
///
/// // Serialize to JSON
/// let json = serde_json::to_string(&value).unwrap();
/// assert_eq!(json, "3.14159");
///
/// // Deserialize from JSON
/// let deserialized: RealNative64StrictFinite = serde_json::from_str(&json).unwrap();
/// assert_eq!(value, deserialized);
/// ```
///
/// ## Relationship to Other Traits
///
/// - **[`FpScalar`]**: Extends [`ScalarCore`] with mathematical operations and floating-point checks
/// - **[`RealScalar`]**: Further specializes [`FpScalar`] for real numbers
/// - **[`ComplexScalar`]**: Further specializes [`FpScalar`] for complex numbers
///
/// ## Implementation Note
///
/// This is a **trait alias**, not a regular trait. It cannot be implemented directly;
/// instead, types must implement all the constituent traits individually. The trait alias
/// simply provides a convenient shorthand for referring to this specific combination of traits.
///
/// ## See Also
///
/// - [`FpScalar`]: The main scalar trait that uses [`ScalarCore`] as a foundation
/// - [`RealScalar`]: Specialized trait for real number types
/// - [`ComplexScalar`]: Specialized trait for complex number types
pub trait ScalarCore = Sized
    + Clone
    + Debug
    + Display
    + PartialEq
    + Send
    + Sync
    + Zero
    + One
    + Serialize
    + DeserializeOwned
    + 'static;

/// # Core trait for a floating-point scalar number (real or complex)
///
/// [`FpScalar`] is a fundamental trait in [`num-valid`](crate) that provides a common interface
/// for all floating-point scalar types, whether they are real or complex, and regardless
/// of their underlying numerical kernel (e.g., native [`f64`] or arbitrary-precision [`rug`](https://docs.rs/rug/latest/rug/index.html) types).
///
/// This trait serves as a primary bound for generic functions and structures that need to
/// operate on scalar values capable of floating-point arithmetic. It aggregates a large
/// number of mathematical function traits (e.g., [`functions::Sin`], [`functions::Cos`], [`functions::Exp`], [`functions::Sqrt`]) and
/// core properties.
///
/// ## Key Responsibilities and Associated Types:
///
/// - **Scalar Kind (`FpScalar::Kind`):**
///   This associated type specifies the fundamental nature of the scalar, which can be
///   either `scalar_kind::Real` or `scalar_kind::Complex`. It is bound by the private,
///   sealed trait `scalar_kind::Sealed`. This design enforces **mutual exclusion**:
///   since a type can only implement [`FpScalar`] once, it can only have one `Kind`,
///   making it impossible for a type to be both a `RealScalar` and a `ComplexScalar` simultaneously.
///
/// - **Real Type Component ([`FpScalar::RealType`]):**
///   Specifies the real number type corresponding to this scalar via [`RealType`](FpScalar::RealType).
///   - For real scalars (e.g., [`f64`], [`RealNative64StrictFinite`], [`RealNative64StrictFiniteInDebug`],
///     `RealRugStrictFinite`, etc.), [`FpScalar::RealType`] is `Self`.
///   - For complex scalars (e.g., [`Complex<f64>`](num::Complex), [`ComplexNative64StrictFinite`], [`ComplexNative64StrictFiniteInDebug`],
///     `ComplexRugStrictFinite`, etc.), [`FpScalar::RealType`] is their underlying real component type
///     (e.g., [`f64`], [`RealNative64StrictFinite`], [`RealNative64StrictFiniteInDebug`], `RealRugStrictFinite`, etc.).
///  
///   Crucially, this [`FpScalar::RealType`] is constrained to implement [`RealScalar`], ensuring consistency.
///
/// - **Core Floating-Point Properties:**
///   [`FpScalar`] requires implementations of the trait [`FpChecks`], which provides fundamental floating-point checks:
///   - [`is_finite()`](FpChecks::is_finite): Checks if the number is finite.
///   - [`is_infinite()`](FpChecks::is_infinite): Checks if the number is positive or negative infinity.
///   - [`is_nan()`](FpChecks::is_nan): Checks if the number is "Not a Number".
///   - [`is_normal()`](FpChecks::is_normal): Checks if the number is a normal (not zero, subnormal, infinite, or NaN).
///
/// ## Trait Bounds:
///
/// [`FpScalar`] itself has several important trait bounds:
/// - `Sized + Clone + Debug + Display + PartialEq`: Standard utility traits. Note that scalar
///   types are [`Clone`] but not necessarily [`Copy`].
/// - [`Zero`] + [`One`]: From `num_traits`, ensuring the type has additive and multiplicative identities.
/// - [`Neg<Output = Self>`](Neg): Ensures the type can be negated.
/// - [`Arithmetic`]: A custom aggregate trait in [`num-valid`](crate) that bundles standard
///   arithmetic operator traits (like [`Add`](std::ops::Add), [`Sub`](std::ops::Sub), [`Mul`], [`Div`](std::ops::Div)).
/// - [`RandomSampleFromF64`]: Allows the type to be randomly generated from any distribution
///   that produces `f64`, integrating with the [`rand`] crate.
/// - `Send + Sync + 'static`: Makes the scalar types suitable for use in concurrent contexts.
/// - A comprehensive suite of mathematical function traits like [`functions::Sin`], [`functions::Cos`], [`functions::Exp`], [`functions::Ln`], [`functions::Sqrt`], etc.
///
/// ## Relationship to Other Traits:
///
/// - [`RealScalar`]: A sub-trait of [`FpScalar`] for real numbers, defined by the constraint `FpScalar<Kind = scalar_kind::Real>`.
/// - [`ComplexScalar`]: A sub-trait of [`FpScalar`] for complex numbers, defined by the constraint `FpScalar<Kind = scalar_kind::Complex>`.
/// - [`NumKernel`]: Policies like [`Native64StrictFinite`]
///   are used to validate the raw values that are wrapped inside types implementing `FpScalar`.
///
/// ## Example: Generic Function
///
/// Here is how you can write a generic function that works with any scalar type
/// implementing `FpScalar`.
///
/// ```rust
/// use num_valid::{FpScalar, functions::{MulAddRef, Sqrt}, RealNative64StrictFinite};
/// use num::Zero;
/// use try_create::TryNew;
///
/// // This function works with any FpScalar type.
/// fn norm_and_sqrt<T: FpScalar>(a: T, b: T) -> T {
///     let val = a.clone().mul_add_ref(&a, &(b.clone() * b)); // a*a + b*b
///     val.sqrt()
/// }
///
/// // Also this function works with any FpScalar type.
/// fn multiply_if_not_zero<T: FpScalar>(a: T, b: T) -> T {
///     if !a.is_zero() && !b.is_zero() {
///         a * b
///     } else {
///         T::zero()
///     }
/// }
///
/// // Example usage
/// let a = RealNative64StrictFinite::try_new(3.0).unwrap();
/// let b = RealNative64StrictFinite::try_new(4.0).unwrap();
/// let result = norm_and_sqrt(a, b);
/// assert_eq!(*result.as_ref(), 5.0);
/// ```
pub trait FpScalar:
    ScalarCore
    + IntoInner<InnerType: RawScalarTrait>
    + Arithmetic
    + Abs<Output = Self::RealType>
    + Sqrt
    + PowIntExponent<RawType = Self::InnerType>
    + TrigonometricFunctions
    + HyperbolicFunctions
    + LogarithmFunctions
    + Exp
    + FpChecks
    + Neg<Output = Self>
    + MulAddRef
    + Reciprocal
    + NeumaierAddable
    + RandomSampleFromF64
{
    /// The kind of scalar this is, e.g., `Real` or `Complex`.
    /// This is a sealed trait to prevent external implementations.
    type Kind: scalar_kind::Sealed;

    /// The real number type corresponding to this scalar.
    ///
    /// - For real scalars (e.g., `f64`), `RealType` is `Self`.
    /// - For complex scalars (e.g., `Complex<f64>`), `RealType` is their underlying
    ///   real component type (e.g., `f64`).
    ///
    /// This `RealType` is guaranteed to implement [`RealScalar`] and belong to the
    /// same numerical kernel as `Self`.
    type RealType: RealScalar<RealType = Self::RealType>;

    /// Returns a reference to the underlying raw scalar value.
    fn as_raw_ref(&self) -> &Self::InnerType;
}
//-------------------------------------------------------------------------------------------------------------

//-------------------------------------------------------------------------------------------------------------

/// Provides fundamental mathematical constants for floating-point scalar types.
///
/// This trait defines methods to obtain commonly used mathematical constants
/// in the appropriate precision and type for the implementing scalar type.
/// All constants are guaranteed to be finite and valid according to the
/// scalar's validation policy.
///
/// ## Design Principles
///
/// - **Type-Appropriate Precision**: Constants are provided at the precision
///   of the implementing type (e.g., `f64` precision for native types,
///   arbitrary precision for `rug` types).
/// - **Validation Compliance**: All returned constants are guaranteed to pass
///   the validation policy of the implementing type.
/// - **Mathematical Accuracy**: Constants use the most accurate representation
///   available for the underlying numerical type.
///
/// ## Usage Examples
///
/// ```rust
/// use num_valid::{RealNative64StrictFinite, Constants, functions::Exp};
/// use try_create::TryNew;
///
/// // Get mathematical constants
/// let pi = RealNative64StrictFinite::pi();
/// let e = RealNative64StrictFinite::e();
/// let eps = RealNative64StrictFinite::epsilon();
///
/// // Use in calculations
/// let circle_area = pi.clone() * &(pi * RealNative64StrictFinite::two());
/// let exp_1 = e.exp(); // e^e
/// ```
///
/// ## Backend-Specific Behavior
///
/// ### Native `f64` Backend
/// - Uses standard library constants like `std::f64::consts::PI`
/// - IEEE 754 double precision (53-bit significand)
/// - Hardware-optimized representations
///
/// ### Arbitrary-Precision (`rug`) Backend
/// - Constants computed at compile-time specified precision
/// - Exact representations within precision limits
/// - May use higher-precision intermediate calculations
pub trait Constants: Sized {
    /// [Machine epsilon] value for `Self`.
    ///
    /// This is the difference between `1.0` and the next larger representable number.
    /// It represents the relative precision of the floating-point format.
    ///
    /// ## Examples
    ///
    /// ```rust
    /// use num_valid::{RealNative64StrictFinite, Constants};
    ///
    /// let eps = RealNative64StrictFinite::epsilon();
    /// // For f64, this is approximately 2.220446049250313e-16
    /// ```
    ///
    /// [Machine epsilon]: https://en.wikipedia.org/wiki/Machine_epsilon
    fn epsilon() -> Self;

    /// Build and return the (floating point) value -1. represented by the proper type.
    fn negative_one() -> Self;

    /// Build and return the (floating point) value 0.5 represented by the proper type.
    fn one_div_2() -> Self;

    /// Build and return the (floating point) value `π` represented by the proper type.
    fn pi() -> Self;

    /// Build and return the (floating point) value `2 π` represented by the proper type.
    fn two_pi() -> Self;

    /// Build and return the (floating point) value `π/2` represented by the proper type.
    fn pi_div_2() -> Self;

    /// Build and return the (floating point) value 2. represented by the proper type.
    fn two() -> Self;

    /// Build and return the maximum finite value allowed by the current floating point representation.
    fn max_finite() -> Self;

    /// Build and return the minimum finite (i.e., the most negative) value allowed by the current floating point representation.
    fn min_finite() -> Self;

    /// Build and return the natural logarithm of 2, i.e. the (floating point) value `ln(2)`, represented by the proper type.
    fn ln_2() -> Self;

    /// Build and return the natural logarithm of 10, i.e. the (floating point) value `ln(10)`, represented by the proper type.
    fn ln_10() -> Self;

    /// Build and return the base-10 logarithm of 2, i.e. the (floating point) value `Log_10(2)`, represented by the proper type.
    fn log10_2() -> Self;

    /// Build and return the base-2 logarithm of 10, i.e. the (floating point) value `Log_2(10)`, represented by the proper type.
    fn log2_10() -> Self;

    /// Build and return the base-2 logarithm of `e`, i.e. the (floating point) value `Log_2(e)`, represented by the proper type.
    fn log2_e() -> Self;

    /// Build and return the base-10 logarithm of `e`, i.e. the (floating point) value `Log_10(e)`, represented by the proper type.
    fn log10_e() -> Self;

    /// Build and return the (floating point) value `e` represented by the proper type.
    fn e() -> Self;
}

/// # Trait for scalar real numbers
///
/// [`RealScalar`] extends the fundamental [`FpScalar`] trait, providing an interface
/// specifically for real (non-complex) floating-point numbers. It introduces
/// operations and properties that are unique to real numbers, such as ordering,
/// rounding, and clamping.
///
/// This trait is implemented by real scalar types within each numerical kernel,
/// for example, [`f64`] for the native kernel,
/// and `RealRugStrictFinite` for the `rug` kernel (when the `rug` feature is enabled).
///
/// ## Key Design Principles
///
/// - **Inheritance from [`FpScalar`]:** As a sub-trait of [`FpScalar`], any `RealScalar`
///   type automatically gains all the capabilities of a general floating-point number,
///   including basic arithmetic and standard mathematical functions (e.g., `sin`, `exp`, `sqrt`).
/// - **Raw Underlying Type ([`RawReal`](Self::RawReal)):**
///   This associated type specifies the most fundamental, "raw" representation of the real number,
///   which implements the [`RawRealTrait`]. This is the type used for low-level, unchecked
///   operations within the library's internal implementation.
///   - For [`f64`], [`RealNative64StrictFinite`] and [`RealNative64StrictFiniteInDebug`], `RawReal` is [`f64`].
///   - For `RealRugStrictFinite<P>`, `RawReal` is [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html).
/// - **Reference-Based Operations**: Many operations take arguments by reference (`&Self`)
///   to avoid unnecessary clones of potentially expensive arbitrary-precision numbers.
/// - **Fallible Constructors**: [`try_from_f64()`](Self::try_from_f64) validates inputs
///   and ensures exact representability for arbitrary-precision types.
///
/// ## Creating Validated Real Numbers
///
/// There are multiple ways to create validated real scalar instances, depending on your
/// source data and use case:
///
/// ### 1. From f64 Values (Most Common)
///
/// Use [`try_from_f64()`](Self::try_from_f64) for fallible conversion with error handling,
/// or [`from_f64()`](Self::from_f64) for infallible conversion that panics on invalid input:
///
/// ```rust
/// use num_valid::{RealNative64StrictFinite, RealScalar};
///
/// // Fallible conversion (recommended for runtime values)
/// let x = RealNative64StrictFinite::try_from_f64(3.14)?;
/// assert_eq!(x.as_ref(), &3.14);
///
/// // Panicking conversion (safe for known-valid constants)
/// let pi = RealNative64StrictFinite::from_f64(std::f64::consts::PI);
/// let e = RealNative64StrictFinite::from_f64(std::f64::consts::E);
///
/// // Error handling for invalid values
/// let invalid = RealNative64StrictFinite::try_from_f64(f64::NAN);
/// assert!(invalid.is_err()); // NaN is rejected
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
///
/// ### 2. From Raw Values (Advanced)
///
/// Use [`try_new()`](try_create::TryNew::try_new) when working with the raw underlying type directly:
///
/// ```rust
/// use num_valid::RealNative64StrictFinite;
/// use try_create::TryNew;
///
/// // For native f64 types
/// let x = RealNative64StrictFinite::try_new(42.0)?;
///
/// // For arbitrary-precision types (with rug feature)
/// # #[cfg(feature = "rug")] {
/// use num_valid::RealRugStrictFinite;
/// let high_precision = RealRugStrictFinite::<200>::try_new(
///     rug::Float::with_val(200, 1.5)
/// )?;
/// # }
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
///
/// ### 3. Using Constants
///
/// Leverage the [`Constants`] trait for mathematical constants:
///
/// ```rust
/// use num_valid::{RealNative64StrictFinite, Constants};
///
/// let pi = RealNative64StrictFinite::pi();
/// let e = RealNative64StrictFinite::e();
/// let two = RealNative64StrictFinite::two();
/// let epsilon = RealNative64StrictFinite::epsilon();
/// ```
///
/// ### 4. From Arithmetic Operations
///
/// Create values through validated arithmetic on existing validated numbers:
///
/// ```rust
/// use num_valid::RealNative64StrictFinite;
/// use try_create::TryNew;
///
/// let a = RealNative64StrictFinite::try_new(2.0)?;
/// let b = RealNative64StrictFinite::try_new(3.0)?;
///
/// let sum = a.clone() + b.clone(); // Automatically validated
/// let product = &a * &b;           // Also works with references
/// # Ok::<(), Box<dyn std::error::Error>>(())
/// ```
///
/// ### 5. Using Zero and One Traits
///
/// For generic code, use [`num::Zero`] and [`num::One`]:
///
/// ```rust
/// use num_valid::RealNative64StrictFinite;
/// use num::{Zero, One};
///
/// let zero = RealNative64StrictFinite::zero();
/// let one = RealNative64StrictFinite::one();
/// assert_eq!(*zero.as_ref(), 0.0);
/// assert_eq!(*one.as_ref(), 1.0);
/// ```
///
/// ### Choosing the Right Method
///
/// | Method | Use When | Panics? | Example |
/// |--------|----------|---------|---------|
/// | `from_f64()` | Value is guaranteed valid (constants) | Yes | `from_f64(PI)` |
/// | `try_from_f64()` | Value might be invalid (user input) | No | `try_from_f64(x)?` |
/// | `try_new()` | Working with raw backend types | No | `try_new(raw_val)?` |
/// | Constants trait | Need mathematical constants | No | `pi()`, `e()` |
/// | Arithmetic | Deriving from other validated values | No | `a + b` |
///
/// ## Type Safety with Validated Types
///
/// Real scalars that use validation policies implementing finite value guarantees
/// automatically gain:
/// - **Full Equality ([`Eq`])**: Well-defined, symmetric equality comparisons
/// - **Hashing ([`Hash`])**: Use as keys in [`HashMap`](std::collections::HashMap) and [`HashSet`](std::collections::HashSet)
/// - **No Total Ordering**: The library intentionally avoids [`Ord`] in favor of
///   more efficient reference-based [`functions::Max`]/[`functions::Min`] operations
///
/// ## Mathematical Operations
///
/// ### Core Arithmetic
/// All standard arithmetic operations are available through the [`Arithmetic`] trait,
/// supporting both value and reference semantics:
/// ```rust
/// use num_valid::RealNative64StrictFinite;
/// use try_create::TryNew;
///
/// let a = RealNative64StrictFinite::try_new(2.0).unwrap();
/// let b = RealNative64StrictFinite::try_new(3.0).unwrap();
///
/// // All combinations supported: T op T, T op &T, &T op T, &T op &T
/// let sum1 = a.clone() + b.clone();
/// let sum2 = &a + &b;
/// let sum3 = a.clone() + &b;
/// let sum4 = &a + b.clone();
///
/// assert_eq!(sum1, sum2);
/// assert_eq!(sum2, sum3);
/// assert_eq!(sum3, sum4);
/// ```
///
/// ### Advanced Functions
///
/// In addition to the functions from [`FpScalar`], `RealScalar` provides a suite of methods common in real number arithmetic.
/// Methods prefixed with `kernel_` provide direct access to underlying mathematical operations with minimal overhead:
///
/// - **Rounding ([`Rounding`]):**
///   [`kernel_ceil()`](Rounding::kernel_ceil), [`kernel_floor()`](Rounding::kernel_floor),
///   [`kernel_round()`](Rounding::kernel_round), [`kernel_round_ties_even()`](Rounding::kernel_round_ties_even),
///   [`kernel_trunc()`](Rounding::kernel_trunc), and [`kernel_fract()`](Rounding::kernel_fract).
///
/// - **Sign Manipulation ([`Sign`]):**
///   [`kernel_copysign()`](Sign::kernel_copysign), [`kernel_signum()`](Sign::kernel_signum),
///   [`kernel_is_sign_positive()`](Sign::kernel_is_sign_positive), and [`kernel_is_sign_negative()`](Sign::kernel_is_sign_negative).
///
/// - **Comparison and Ordering:**
///   - From [`functions::Max`]/[`functions::Min`]: [`max()`](functions::Max::max) and [`min()`](functions::Min::min).
///   - From [`TotalCmp`]: [`total_cmp()`](TotalCmp::total_cmp) for a total ordering compliant with IEEE 754.
///   - From [`Clamp`]: [`clamp()`](Clamp::clamp).
///
/// - **Specialized Functions:**
///   - From [`ATan2`]: [`atan2()`](ATan2::atan2).
///   - From [`ExpM1`]/[`Ln1p`]: [`exp_m1()`](ExpM1::exp_m1) and [`ln_1p()`](Ln1p::ln_1p).
///   - From [`Hypot`]: [`hypot()`](Hypot::hypot) for computing sqrt(a² + b²).
///   - From [`Classify`]: [`classify()`](Classify::classify).
///
/// - **Fused Multiply-Add Variants:**
///   [`kernel_mul_add_mul_mut()`](Self::kernel_mul_add_mul_mut) and
///   [`kernel_mul_sub_mul_mut()`](Self::kernel_mul_sub_mul_mut).
///
/// ### Constants and Utilities
///
/// ```rust
/// use num_valid::{RealNative64StrictFinite, Constants};
///
/// let pi = RealNative64StrictFinite::pi();
/// let e = RealNative64StrictFinite::e();
/// let eps = RealNative64StrictFinite::epsilon();
/// let max_val = RealNative64StrictFinite::max_finite();
/// ```
///
/// ## Naming Convention for `kernel_*` Methods
///
/// Methods prefixed with `kernel_` (e.g., `kernel_ceil`, `kernel_copysign`) are
/// part of the low-level kernel interface. They typically delegate directly to the
/// most efficient implementation for the underlying type (like `f64::ceil`) without
/// adding extra validation layers. They are intended to be fast primitives upon which
/// safer, higher-level abstractions can be built.
///
/// ## Critical Trait Bounds
///
/// - `Self: FpScalar<RealType = Self>`: This is the defining constraint. It ensures that the type
///   has all basic floating-point capabilities and confirms that its associated real type is itself.
/// - `Self: PartialOrd + PartialOrd<f64>`: These bounds are essential for comparison operations,
///   allowing instances to be compared both with themselves and with native `f64` constants.
///
/// ## Backend-Specific Behavior
///
/// ### Native `f64` Backend
/// - Direct delegation to standard library functions
/// - IEEE 754 compliance
/// - Maximum performance
///
/// ### Arbitrary-Precision (`rug`) Backend
/// - Configurable precision at compile-time
/// - Exact arithmetic within precision limits
/// - [`try_from_f64()`](Self::try_from_f64) validates exact representability
///
/// ## Error Handling
///
/// Operations that can fail provide both panicking and non-panicking variants:
/// ```rust
/// use num_valid::{RealNative64StrictFinite, functions::Sqrt};
/// use try_create::TryNew;
///
/// let positive = RealNative64StrictFinite::try_new(4.0).unwrap();
/// let negative = RealNative64StrictFinite::try_new(-4.0).unwrap();
///
/// // Panicking version (use when input validity is guaranteed)
/// let sqrt_pos = positive.sqrt();
/// assert_eq!(*sqrt_pos.as_ref(), 2.0);
///
/// // Non-panicking version (use for potentially invalid inputs)
/// let sqrt_neg_result = negative.try_sqrt();
/// assert!(sqrt_neg_result.is_err());
/// ```
pub trait RealScalar:
    FpScalar<RealType = Self, InnerType = Self::RawReal>
    + Sign
    + Rounding
    + Constants
    + PartialEq<f64>
    + PartialOrd
    + PartialOrd<f64>
    + Max
    + Min
    + ATan2
    + for<'a> Pow<&'a Self, Error = PowRealBaseRealExponentErrors<Self::RawReal>>
    + Clamp
    + Classify
    + ExpM1
    + Hypot
    + Ln1p
    + TotalCmp
{
    /// The most fundamental, "raw" representation of this real number.
    ///
    /// This type provides the foundation for all mathematical operations and
    /// is used to parameterize error types for this scalar.
    ///
    /// # Examples
    /// - For [`f64`]: `RawReal = f64`
    /// - For [`RealNative64StrictFinite`]: `RawReal = f64`
    /// - For `RealRugStrictFinite<P>`: `RawReal = rug::Float`
    type RawReal: RawRealTrait;

    /// Multiplies two products and adds them in one fused operation, rounding to the nearest with only one rounding error.
    /// `a.kernel_mul_add_mul_mut(&b, &c, &d)` produces a result like `&a * &b + &c * &d`, but stores the result in `a` using its precision.
    fn kernel_mul_add_mul_mut(&mut self, mul: &Self, add_mul1: &Self, add_mul2: &Self);

    /// Multiplies two products and subtracts them in one fused operation, rounding to the nearest with only one rounding error.
    /// `a.kernel_mul_sub_mul_mut(&b, &c, &d)` produces a result like `&a * &b - &c * &d`, but stores the result in `a` using its precision.
    fn kernel_mul_sub_mul_mut(&mut self, mul: &Self, sub_mul1: &Self, sub_mul2: &Self);

    /// Tries to create an instance of `Self` from a [`f64`].
    ///
    /// This conversion is fallible and validates the input `value`. For `rug`-based types,
    /// it also ensures that the `f64` can be represented exactly at the target precision.
    ///
    /// # Errors
    /// Returns [`ErrorsTryFromf64`] if the `value` is not finite or cannot be
    /// represented exactly by `Self`.
    ///
    /// # Examples
    ///
    /// ```
    /// use num_valid::{RealNative64StrictFinite, RealScalar};
    ///
    /// // Valid value
    /// let x = RealNative64StrictFinite::try_from_f64(3.14).unwrap();
    /// assert_eq!(x.as_ref(), &3.14);
    ///
    /// // Invalid value (NaN)
    /// assert!(RealNative64StrictFinite::try_from_f64(f64::NAN).is_err());
    /// ```
    fn try_from_f64(value: f64) -> Result<Self, ErrorsTryFromf64<Self::RawReal>>;

    /// Creates an instance of `Self` from a [`f64`], panicking if the value is invalid.
    ///
    /// This is a convenience method for cases where you know the value is valid (e.g., constants).
    /// For error handling without panics, use [`try_from_f64`](Self::try_from_f64).
    ///
    /// # Panics
    ///
    /// Panics if the input value fails validation (e.g., NaN, infinity, or subnormal for strict policies).
    ///
    /// # Examples
    ///
    /// ```
    /// use num_valid::{RealNative64StrictFinite, RealScalar};
    ///
    /// // Valid constants - cleaner syntax without unwrap()
    /// let pi = RealNative64StrictFinite::from_f64(std::f64::consts::PI);
    /// let e = RealNative64StrictFinite::from_f64(std::f64::consts::E);
    /// let sqrt2 = RealNative64StrictFinite::from_f64(std::f64::consts::SQRT_2);
    ///
    /// assert_eq!(pi.as_ref(), &std::f64::consts::PI);
    /// ```
    ///
    /// ```should_panic
    /// use num_valid::{RealNative64StrictFinite, RealScalar};
    ///
    /// // This will panic because NaN is invalid
    /// let invalid = RealNative64StrictFinite::from_f64(f64::NAN);
    /// ```
    fn from_f64(value: f64) -> Self {
        Self::try_from_f64(value).expect("RealScalar::from_f64() failed: invalid f64 value")
    }

    /// Safely converts the truncated value to `usize`.
    ///
    /// Truncates toward zero and validates the result is a valid `usize`.
    ///
    /// # Returns
    ///
    /// - `Ok(usize)`: If truncated value is in `0..=usize::MAX`
    /// - `Err(_)`: If value is not finite or out of range
    ///
    /// # Examples
    ///
    /// ```rust
    /// use num_valid::{RealNative64StrictFinite, RealScalar};
    /// use try_create::TryNew;
    ///
    /// let x = RealNative64StrictFinite::try_new(42.9)?;
    /// assert_eq!(x.truncate_to_usize()?, 42);
    ///
    /// let neg = RealNative64StrictFinite::try_new(-1.0)?;
    /// assert!(neg.truncate_to_usize().is_err());
    /// # Ok::<(), Box<dyn std::error::Error>>(())
    /// ```
    ///
    /// <details>
    /// <summary>Detailed Behavior and Edge Cases</summary>
    ///
    /// ## Truncation Rules
    ///
    /// The fractional part is discarded, moving toward zero:
    /// - `3.7` → `3`
    /// - `-2.9` → `-2`
    /// - `0.9` → `0`
    ///
    /// ## Error Conditions
    ///
    /// - [`NotFinite`](ErrorsRawRealToInteger::NotFinite): Value is `NaN` or `±∞`
    /// - [`OutOfRange`](ErrorsRawRealToInteger::OutOfRange): Value is negative or > `usize::MAX`
    ///
    /// ## Additional Examples
    ///
    /// ```rust
    /// use num_valid::{RealNative64StrictFinite, RealScalar, validation::ErrorsRawRealToInteger};
    /// use try_create::TryNew;
    ///
    /// // Zero
    /// let zero = RealNative64StrictFinite::try_new(0.0)?;
    /// assert_eq!(zero.truncate_to_usize()?, 0);
    ///
    /// // Large valid values
    /// let large = RealNative64StrictFinite::try_new(1_000_000.7)?;
    /// assert_eq!(large.truncate_to_usize()?, 1_000_000);
    ///
    /// // Values too large for usize
    /// let too_large = RealNative64StrictFinite::try_new(1e20)?;
    /// assert!(matches!(too_large.truncate_to_usize(), Err(ErrorsRawRealToInteger::OutOfRange { .. })));
    /// # Ok::<(), Box<dyn std::error::Error>>(())
    /// ```
    ///
    /// ## Practical Usage
    ///
    /// ```rust
    /// use num_valid::{RealNative64StrictFinite, RealScalar};
    /// use try_create::TryNew;
    ///
    /// fn create_vector_with_size<T: Default + Clone>(
    ///     size_float: RealNative64StrictFinite
    /// ) -> Result<Vec<T>, Box<dyn std::error::Error>> {
    ///     let size = size_float.truncate_to_usize()?;
    ///     Ok(vec![T::default(); size])
    /// }
    ///
    /// let size = RealNative64StrictFinite::try_new(10.7)?;
    /// let vec: Vec<i32> = create_vector_with_size(size)?;
    /// assert_eq!(vec.len(), 10); // Truncated from 10.7
    /// # Ok::<(), Box<dyn std::error::Error>>(())
    /// ```
    ///
    /// ## Comparison with Alternatives
    ///
    /// | Method | Behavior | Range Check | Fractional |
    /// |--------|----------|-------------|------------|
    /// | `truncate_to_usize()` | Towards zero | ✓ | Discarded |
    /// | `as usize` (raw) | Undefined | ✗ | Undefined |
    /// | `round().as usize` | Nearest | ✗ | Rounded |
    /// | `floor().as usize` | Towards -∞ | ✗ | Discarded |
    /// | `ceil().as usize` | Towards +∞ | ✗ | Discarded |
    ///
    /// ## Backend-Specific Notes
    ///
    /// - **Native f64**: Uses `az::CheckedAs` for safe conversion with overflow detection
    /// - **Arbitrary-precision (rug)**: Respects current precision, may adjust for very large numbers
    ///
    /// </details>
    fn truncate_to_usize(self) -> Result<usize, ErrorsRawRealToInteger<Self::RawReal, usize>> {
        let raw: Self::RawReal = self.into_inner();
        raw.truncate_to_usize()
    }
}

/// # Trait for complex scalar numbers
///
/// [`ComplexScalar`] is a specialized trait for complex number types that extends the
/// core [`FpScalar`] functionality with complex-specific operations. It provides a
/// unified interface for working with complex numbers across different validation
/// policies and underlying representations.
///
/// ## Design Philosophy
///
/// This trait bridges the gap between raw complex number operations and the validated
/// complex number types in [`num-valid`](crate). It ensures that complex-specific
/// operations like conjugation, argument calculation, and real-number scaling are
/// available in a type-safe, validated context.
///
/// ## Core Capabilities
///
/// The trait provides several key features:
///
/// - **Component manipulation**: Through [`ComplexScalarMutateParts`], allowing safe
///   modification of real and imaginary parts
/// - **Conjugation**: Via the [`functions::Conjugate`] trait for computing complex conjugates
/// - **Argument calculation**: Through [`functions::Arg`] for computing the phase angle
/// - **Real scaling**: Multiplication and assignment with real numbers
/// - **Power operations**: Raising complex numbers to real exponents
/// - **Convenience methods**: Like [`scale`](Self::scale) and [`scale_mut`](Self::scale_mut)
///   for efficient real-number scaling
///
/// ## Type Relationships
///
/// Types implementing [`ComplexScalar`] must also implement [`FpScalar`] with
/// `Kind = scalar_kind::Complex`, establishing them as complex number types within
/// the [`num-valid`](crate) type system.
///
/// ## Usage Examples
///
/// ### Basic Complex Operations
/// ```rust
/// use num_valid::{ComplexNative64StrictFinite, RealNative64StrictFinite, ComplexScalar};
/// use num::Complex;
/// use try_create::TryNew;
///
/// let z = ComplexNative64StrictFinite::try_new(Complex::new(3.0, 4.0)).unwrap();
/// let factor = RealNative64StrictFinite::try_new(2.5).unwrap();
///
/// // Scale by a real number
/// let scaled = z.scale(&factor);
/// // Result: (7.5, 10.0)
/// ```
///
/// ### In-place Operations
/// ```rust
/// use num_valid::{ComplexNative64StrictFinite, RealNative64StrictFinite, ComplexScalar};
/// use num::Complex;
/// use try_create::TryNew;
///
/// let mut z = ComplexNative64StrictFinite::try_new(Complex::new(1.0, 2.0)).unwrap();
/// let factor = RealNative64StrictFinite::try_new(3.0).unwrap();
///
/// z.scale_mut(&factor);
/// // z is now (3.0, 6.0)
/// ```
///
/// ## See Also
///
/// - [`FpScalar`]: The base trait for all floating-point scalars
/// - [`RealScalar`]: The equivalent trait for real numbers
/// - [`ComplexScalarMutateParts`]: For component-wise operations
/// - [`functions`]: Module containing mathematical functions for complex numbers
pub trait ComplexScalar:
    ComplexScalarMutateParts
    + Conjugate
    + Arg<Output = Self::RealType>
    + for<'a> Mul<&'a Self::RealType, Output = Self>
    + for<'a> MulAssign<&'a Self::RealType>
    + for<'a> Pow<&'a Self::RealType, Error = PowComplexBaseRealExponentErrors<Self::RawComplex>>
{
    /// Scale the complex number `self` by the real coefficient `c`.
    ///
    /// This is equivalent to complex multiplication by a real number,
    /// scaling both the real and imaginary parts by the same factor.
    ///
    /// ## Examples
    ///
    /// ```rust
    /// use num_valid::{ComplexNative64StrictFinite, RealNative64StrictFinite, ComplexScalar};
    /// use num::Complex;
    /// use try_create::TryNew;
    ///
    /// let z = ComplexNative64StrictFinite::try_new(Complex::new(3.0, 4.0)).unwrap();
    /// let factor = RealNative64StrictFinite::try_new(2.0).unwrap();
    ///
    /// let scaled = z.scale(&factor);
    /// // Result: (6.0, 8.0)
    /// ```
    #[inline(always)]
    fn scale(self, c: &Self::RealType) -> Self {
        self * c
    }

    /// Scale (in-place) the complex number `self` by the real coefficient `c`.
    ///
    /// This modifies the complex number in place, scaling both components
    /// by the real factor.
    ///
    /// ## Examples
    ///
    /// ```rust
    /// use num_valid::{ComplexNative64StrictFinite, RealNative64StrictFinite, ComplexScalar};
    /// use num::Complex;
    /// use try_create::TryNew;
    ///
    /// let mut z = ComplexNative64StrictFinite::try_new(Complex::new(3.0, 4.0)).unwrap();
    /// let factor = RealNative64StrictFinite::try_new(2.0).unwrap();
    ///
    /// z.scale_mut(&factor);
    /// // z is now (6.0, 8.0)
    /// ```
    #[inline(always)]
    fn scale_mut(&mut self, c: &Self::RealType) {
        *self *= c;
    }

    /// Consumes the complex number and returns its real and imaginary parts as a tuple.
    ///
    /// This method moves ownership of the complex number and extracts its two components,
    /// returning them as separate real scalar values. This is useful when you need to
    /// work with the components independently and no longer need the original complex value.
    ///
    /// # Returns
    ///
    /// A tuple `(real, imaginary)` where:
    /// - `real` is the real part of the complex number
    /// - `imaginary` is the imaginary part of the complex number
    ///
    /// # Examples
    ///
    /// ```rust
    /// use num_valid::{ComplexNative64StrictFinite, ComplexScalar};
    /// use num::Complex;
    /// use try_create::TryNew;
    ///
    /// let z = ComplexNative64StrictFinite::try_new(Complex::new(3.0, 4.0)).unwrap();
    ///
    /// // Consume z and extract its parts
    /// let (real, imag) = z.into_parts();
    ///
    /// assert_eq!(*real.as_ref(), 3.0);
    /// assert_eq!(*imag.as_ref(), 4.0);
    /// // z is no longer available here (moved)
    /// ```
    ///
    /// # See Also
    ///
    /// - [`ComplexScalarGetParts::real_part`]: Get the real part without consuming
    /// - [`ComplexScalarGetParts::imag_part`]: Get the imaginary part without consuming
    fn into_parts(self) -> (Self::RealType, Self::RealType);
}

//------------------------------------------------------------------------------------------------------------

//------------------------------------------------------------------------------------------------------------
/// Attempts to convert a vector of [`f64`] values into a vector of the specified real scalar type.
///
/// This function provides a fallible conversion that validates each input value according
/// to the target type's validation policy. If any value fails validation, the entire
/// operation fails and returns an error.
///
/// ## Parameters
///
/// * `vec`: A vector of [`f64`] values to convert
///
/// ## Return Value
///
/// * `Ok(Vec<RealType>)`: If all values can be successfully converted and validated
/// * `Err(ErrorsTryFromf64)`: If any value fails validation, containing details about the failure
///
/// ## Usage Examples
///
/// ### Successful Conversion
/// ```rust
/// use num_valid::{try_vec_f64_into_vec_real, RealNative64StrictFinite};
///
/// let input = vec![1.0, -2.5, 3.14159];
/// let result = try_vec_f64_into_vec_real::<RealNative64StrictFinite>(input);
/// assert!(result.is_ok());
///
/// let validated_vec = result.unwrap();
/// assert_eq!(validated_vec.len(), 3);
/// assert_eq!(*validated_vec[0].as_ref(), 1.0);
/// ```
///
/// ### Failed Conversion
/// ```rust
/// use num_valid::{try_vec_f64_into_vec_real, RealNative64StrictFinite};
///
/// let input = vec![1.0, f64::NAN, 3.0]; // Contains NaN
/// let result = try_vec_f64_into_vec_real::<RealNative64StrictFinite>(input);
/// assert!(result.is_err()); // Fails due to NaN value
/// ```
#[inline(always)]
pub fn try_vec_f64_into_vec_real<RealType: RealScalar>(
    vec: Vec<f64>,
) -> Result<Vec<RealType>, ErrorsTryFromf64<RealType::RawReal>> {
    vec.into_iter().map(|v| RealType::try_from_f64(v)).collect()
}

/// Converts a vector of [`f64`] values into a vector of the specified real scalar type.
///
/// This is the panicking version of [`try_vec_f64_into_vec_real`]. It converts each
/// [`f64`] value to the target real scalar type, panicking if any conversion fails.
/// Use this function only when you are certain that all input values are valid for
/// the target type.
///
/// ## Parameters
///
/// * `vec`: A vector of [`f64`] values to convert
///
/// ## Return Value
///
/// A vector of validated real scalar values of type `RealType`.
///
/// ## Panics
///
/// Panics if any value in the input vector cannot be converted to `RealType`.
/// This can happen for various reasons:
/// - Input contains `NaN` or infinite values when using strict finite validation
/// - Precision loss when converting to arbitrary-precision types
/// - Values outside the representable range of the target type
///
/// ## Usage Examples
///
/// ### Successful Conversion
/// ```rust
/// use num_valid::{vec_f64_into_vec_real, RealNative64StrictFinite};
///
/// let input = vec![0.0, 1.0, -2.5, 3.14159];
/// let validated_vec: Vec<RealNative64StrictFinite> = vec_f64_into_vec_real(input);
///
/// assert_eq!(validated_vec.len(), 4);
/// assert_eq!(*validated_vec[0].as_ref(), 0.0);
/// assert_eq!(*validated_vec[1].as_ref(), 1.0);
/// ```
///
/// ## When to Use
///
/// - **Use this function when**: You are certain all input values are valid for the target type
/// - **Use [`try_vec_f64_into_vec_real`] when**: Input validation is uncertain and you want to handle errors gracefully
#[inline(always)]
pub fn vec_f64_into_vec_real<RealType: RealScalar>(vec: Vec<f64>) -> Vec<RealType> {
    try_vec_f64_into_vec_real(vec).expect(
        "The conversion from f64 to RealType failed, which should not happen in a well-defined numerical kernel."
    )
}
//------------------------------------------------------------------------------------------------------------

//------------------------------------------------------------------------------------------------------------
/// A trait for types that can be randomly generated from a distribution over `f64`.
///
/// This trait provides a universal dispatch mechanism for generating random scalar values,
/// allowing a single generic API to work for primitive types like [`f64`] and
/// [`Complex<f64>`], as well as custom validated types like [`RealValidated`] and
/// [`ComplexValidated`].
///
/// ## Purpose
///
/// The primary goal of [`RandomSampleFromF64`] is to abstract the process of creating a random
/// scalar. Many random number distributions in the [`rand`] crate are defined to produce
/// primitive types like `f64`. This trait acts as a bridge, allowing those same
/// distributions to be used to generate more complex or validated types.
///
/// ## How It Works
///
/// The trait defines a single required method, [`RandomSampleFromF64::sample_from()`], which takes a reference
/// to any distribution that implements [`rand::distr::Distribution<f64>`] and a
/// random number generator (RNG). Each implementing type provides its own logic for
/// this method:
///
/// - For `f64`, it simply samples directly from the distribution.
/// - For `Complex<f64>`, it samples twice to create the real and imaginary parts.
/// - For [`RealValidated<K>`], it samples an `f64` and then passes it through the
///   validation and conversion logic of [`RealValidated::try_from_f64`].
/// - For [`ComplexValidated<K>`], it reuses the logic for `RealValidated<K>` to sample
///   the real and imaginary components.
///
/// ## Example
///
/// Here is how you can write a generic function that creates a vector of random numbers
/// for any type that implements [`RandomSampleFromF64`].
///
/// ```rust
/// use num_valid::{RealNative64StrictFinite, RealScalar, new_random_vec};
/// use rand::{distr::Uniform, rngs::StdRng, Rng, SeedableRng};
/// use try_create::IntoInner;
///
/// let seed = [42; 32]; // Example seed for reproducibility
/// let mut rng = StdRng::from_seed(seed);
/// let uniform = Uniform::new(-10.0, 10.0).unwrap();
///
/// // Create a vector of random f64 values.
/// let f64_vec: Vec<f64> = new_random_vec(3, &uniform, &mut rng);
/// assert_eq!(f64_vec.len(), 3);
///
/// // Create a vector of random validated real numbers using the same function.
/// // Reset RNG to get same sequence
/// let mut rng = StdRng::from_seed(seed);
/// let validated_vec: Vec<RealNative64StrictFinite> = new_random_vec(3, &uniform, &mut rng);
/// assert_eq!(validated_vec.len(), 3);
///
/// // The underlying numerical values should be identical because the RNG was seeded the same.
/// assert_eq!(&f64_vec[0], validated_vec[0].as_ref());
/// assert_eq!(&f64_vec[1], validated_vec[1].as_ref());
/// assert_eq!(&f64_vec[2], validated_vec[2].as_ref());
/// ```
pub trait RandomSampleFromF64: Sized + Clone {
    /// Samples a single value of `Self` using the given `f64` distribution.
    fn sample_from<D, R>(dist: &D, rng: &mut R) -> Self
    where
        D: Distribution<f64>,
        R: Rng + ?Sized;

    fn sample_iter_from<D, R>(dist: &D, rng: &mut R, n: usize) -> impl Iterator<Item = Self>
    where
        D: Distribution<f64>,
        R: Rng + ?Sized,
    {
        // Create an iterator that samples `n` values from the distribution.
        (0..n).map(move |_| Self::sample_from(dist, rng))
    }
}

impl RandomSampleFromF64 for f64 {
    #[inline]
    fn sample_from<D, R>(dist: &D, rng: &mut R) -> Self
    where
        D: Distribution<f64>,
        R: Rng + ?Sized,
    {
        // Straightforward implementation: sample a f64.
        dist.sample(rng)
    }
}
impl RandomSampleFromF64 for Complex<f64> {
    #[inline]
    fn sample_from<D, R>(dist: &D, rng: &mut R) -> Self
    where
        D: Distribution<f64>,
        R: Rng + ?Sized,
    {
        // Sample two f64 for the real and imaginary parts.
        let re = dist.sample(rng);
        let im = dist.sample(rng);
        Complex::new(re, im)
    }
}

impl<K> RandomSampleFromF64 for RealValidated<K>
where
    K: NumKernel,
{
    #[inline]
    fn sample_from<D, R>(dist: &D, rng: &mut R) -> Self
    where
        D: Distribution<f64>,
        R: Rng + ?Sized,
    {
        loop {
            // Sample a f64 and then convert/validate it.
            // The loop ensures that a valid value is returned
            let value_f64 = dist.sample(rng);
            let value = RealValidated::try_from_f64(value_f64);
            if let Ok(validated_value) = value {
                return validated_value;
            }
        }
    }
}

impl<K> RandomSampleFromF64 for ComplexValidated<K>
where
    K: NumKernel,
{
    #[inline]
    fn sample_from<D, R>(dist: &D, rng: &mut R) -> Self
    where
        D: Distribution<f64>,
        R: Rng + ?Sized,
    {
        // Reuse the RealValidated sampling logic for both parts.
        let re = RealValidated::<K>::sample_from(dist, rng);
        let im = RealValidated::<K>::sample_from(dist, rng);
        ComplexValidated::new_complex(re, im)
    }
}

//------------------------------------------------------------------------------------------------------------

//------------------------------------------------------------------------------------------------------------
/// Generates a `Vec<T>` of a specified length with random values.
///
/// This function leverages the [`RandomSampleFromF64`] trait to provide a universal way
/// to create a vector of random numbers for any supported scalar type, including
/// primitive types like `f64` and `Complex<f64>`, as well as validated types
/// like [`RealValidated`] and [`ComplexValidated`].
///
/// # Parameters
///
/// * `n`: The number of random values to generate in the vector.
/// * `distribution`: A reference to any distribution from the `rand` crate that
///   implements `Distribution<f64>` (e.g., `Uniform`, `StandardNormal`).
/// * `rng`: A mutable reference to a random number generator that implements `Rng`.
///
/// # Type Parameters
///
/// * `T`: The scalar type of the elements in the returned vector. Must implement [`RandomSampleFromF64`].
/// * `D`: The type of the distribution.
/// * `R`: The type of the random number generator.
///
/// # Example
///
/// # Example
///
/// ```rust
/// use num_valid::{new_random_vec, RealNative64StrictFinite};
/// use rand::{distr::Uniform, rngs::StdRng, SeedableRng};
/// use try_create::IntoInner;
///
/// let seed = [42; 32]; // Use a fixed seed for a reproducible example
///
/// // Generate a vector of random f64 values.
/// let mut rng_f64 = StdRng::from_seed(seed);
/// let uniform = Uniform::new(-10.0, 10.0).unwrap();
/// let f64_vec: Vec<f64> = new_random_vec(3, &uniform, &mut rng_f64);
///
/// // Generate a vector of random validated real numbers using the same seed.
/// let mut rng_validated = StdRng::from_seed(seed);
/// let validated_vec: Vec<RealNative64StrictFinite> = new_random_vec(3, &uniform, &mut rng_validated);
///
/// assert_eq!(f64_vec.len(), 3);
/// assert_eq!(validated_vec.len(), 3);
///
/// // The underlying numerical values should be identical because the RNG was seeded the same.
/// assert_eq!(&f64_vec[0], validated_vec[0].as_ref());
/// assert_eq!(&f64_vec[1], validated_vec[1].as_ref());
/// assert_eq!(&f64_vec[2], validated_vec[2].as_ref());
/// ```
pub fn new_random_vec<T, D, R>(n: usize, distribution: &D, rng: &mut R) -> Vec<T>
where
    T: RandomSampleFromF64,
    D: Distribution<f64>,
    R: Rng + ?Sized,
{
    T::sample_iter_from(distribution, rng, n).collect()
}
//------------------------------------------------------------------------------------------------------------

//------------------------------------------------------------------------------------------------------------
#[cfg(test)]
mod tests {
    use super::*;
    use num::Complex;
    use num_traits::MulAddAssign;
    use std::ops::{Add, Div, Sub};

    mod functions_general_type {
        use super::*;

        fn test_recip<RealType: RealScalar>() {
            let a = RealType::two();

            let a = a.try_reciprocal().unwrap();
            let expected = RealType::one_div_2();
            assert_eq!(a, expected);
        }

        fn test_zero<RealType: RealScalar>() {
            let a = RealType::zero();

            assert_eq!(a, 0.0);
        }

        fn test_one<RealType: RealScalar>() {
            let a = RealType::one();

            assert_eq!(a, 1.0);
        }

        fn test_add<ScalarType: FpScalar>(a: ScalarType, b: ScalarType, c_expected: ScalarType)
        where
            for<'a> &'a ScalarType:
                Add<ScalarType, Output = ScalarType> + Add<&'a ScalarType, Output = ScalarType>,
        {
            let c = a.clone() + &b;
            assert_eq!(c, c_expected);

            let c = &a + b.clone();
            assert_eq!(c, c_expected);

            let c = a.clone() + b.clone();
            assert_eq!(c, c_expected);

            let c = &a + &b;
            assert_eq!(c, c_expected);
        }

        fn test_sub<ScalarType: FpScalar>(a: ScalarType, b: ScalarType, c_expected: ScalarType)
        where
            for<'a> &'a ScalarType:
                Sub<ScalarType, Output = ScalarType> + Sub<&'a ScalarType, Output = ScalarType>,
        {
            let c = a.clone() - &b;
            assert_eq!(c, c_expected);

            let c = &a - b.clone();
            assert_eq!(c, c_expected);

            let c = a.clone() - b.clone();
            assert_eq!(c, c_expected);

            let c = &a - &b;
            assert_eq!(c, c_expected);
        }

        fn test_mul<ScalarType: FpScalar>(a: ScalarType, b: ScalarType, c_expected: ScalarType)
        where
            for<'a> &'a ScalarType:
                Mul<ScalarType, Output = ScalarType> + Mul<&'a ScalarType, Output = ScalarType>,
        {
            let c = a.clone() * &b;
            assert_eq!(c, c_expected);

            let c = &a * b.clone();
            assert_eq!(c, c_expected);

            let c = a.clone() * b.clone();
            assert_eq!(c, c_expected);

            let c = &a * &b;
            assert_eq!(c, c_expected);
        }

        fn test_div<ScalarType: FpScalar>(a: ScalarType, b: ScalarType, c_expected: ScalarType)
        where
            for<'a> &'a ScalarType:
                Div<ScalarType, Output = ScalarType> + Div<&'a ScalarType, Output = ScalarType>,
        {
            let c = a.clone() / &b;
            assert_eq!(c, c_expected);

            let c = &a / b.clone();
            assert_eq!(c, c_expected);

            let c = a.clone() / b.clone();
            assert_eq!(c, c_expected);

            let c = &a / &b;
            assert_eq!(c, c_expected);
        }

        fn test_mul_complex_with_real<ComplexType: ComplexScalar>(
            a: ComplexType,
            b: ComplexType::RealType,
            a_times_b_expected: ComplexType,
        ) {
            let a_times_b = a.clone().scale(&b);
            assert_eq!(a_times_b, a_times_b_expected);

            let a_times_b = a.clone() * &b;
            assert_eq!(a_times_b, a_times_b_expected);

            /*
            let b_times_a_expected = a_times_b_expected.clone();

            let b_times_a = &b * a.clone();
            assert_eq!(b_times_a, b_times_a_expected);

            let b_times_a = b.clone() * a.clone();
            assert_eq!(b_times_a, b_times_a_expected);
            */
        }

        fn test_mul_assign_complex_with_real<ComplexType: ComplexScalar>(
            a: ComplexType,
            b: ComplexType::RealType,
            a_times_b_expected: ComplexType,
        ) {
            let mut a_times_b = a.clone();
            a_times_b.scale_mut(&b);
            assert_eq!(a_times_b, a_times_b_expected);

            //        let mut a_times_b = a.clone();
            //        a_times_b *= b;
            //        assert_eq!(a_times_b, a_times_b_expected);
        }

        fn test_neg_assign_real<RealType: RealScalar>() {
            let mut a = RealType::one();
            a.neg_assign();

            let a_expected = RealType::try_from_f64(-1.).unwrap();
            assert_eq!(a, a_expected);
        }

        fn test_add_assign_real<RealType: RealScalar>() {
            let mut a = RealType::try_from_f64(1.0).unwrap();
            let b = RealType::try_from_f64(2.0).unwrap();

            a += &b;
            let a_expected = RealType::try_from_f64(3.0).unwrap();
            assert_eq!(a, a_expected);

            a += b;
            let a_expected = RealType::try_from_f64(5.0).unwrap();
            assert_eq!(a, a_expected);
        }

        fn test_sub_assign_real<RealType: RealScalar>() {
            let mut a = RealType::try_from_f64(1.0).unwrap();
            let b = RealType::try_from_f64(2.0).unwrap();

            a -= &b;
            let a_expected = RealType::try_from_f64(-1.0).unwrap();
            assert_eq!(a, a_expected);

            a -= b;
            let a_expected = RealType::try_from_f64(-3.0).unwrap();
            assert_eq!(a, a_expected);
        }

        fn test_mul_assign_real<RealType: RealScalar>() {
            let mut a = RealType::try_from_f64(1.0).unwrap();
            let b = RealType::try_from_f64(2.0).unwrap();

            a *= &b;
            let a_expected = RealType::try_from_f64(2.0).unwrap();
            assert_eq!(a, a_expected);

            a *= b;
            let a_expected = RealType::try_from_f64(4.0).unwrap();
            assert_eq!(a, a_expected);
        }

        fn test_div_assign_real<RealType: RealScalar>() {
            let mut a = RealType::try_from_f64(4.0).unwrap();
            let b = RealType::try_from_f64(2.0).unwrap();

            a /= &b;
            let a_expected = RealType::try_from_f64(2.0).unwrap();
            assert_eq!(a, a_expected);

            a /= b;
            let a_expected = RealType::try_from_f64(1.0).unwrap();
            assert_eq!(a, a_expected);
        }

        fn test_mul_add_ref_real<RealType: RealScalar>() {
            let a = RealType::try_from_f64(2.0).unwrap();
            let b = RealType::try_from_f64(3.0).unwrap();
            let c = RealType::try_from_f64(1.0).unwrap();

            let d_expected = RealType::try_from_f64(7.0).unwrap();

            let d = a.mul_add_ref(&b, &c);
            assert_eq!(d, d_expected);
        }

        fn test_sin_real<RealType: RealScalar>() {
            let a = RealType::zero();

            let a = a.sin();
            let expected = RealType::zero();
            assert_eq!(a, expected);
        }

        fn test_cos_real<RealType: RealScalar>() {
            let a = RealType::zero();

            let a = a.cos();
            let expected = RealType::one();
            assert_eq!(a, expected);
        }

        fn test_abs_real<RealType: RealScalar>() {
            let a = RealType::try_from_f64(-1.).unwrap();

            let abs: RealType = a.abs();
            let expected = RealType::one();
            assert_eq!(abs, expected);
        }

        mod native64 {
            use super::*;

            mod real {
                use super::*;

                #[test]
                fn zero() {
                    test_zero::<f64>();
                }

                #[test]
                fn one() {
                    test_one::<f64>();
                }

                #[test]
                fn recip() {
                    test_recip::<f64>();
                }

                #[test]
                fn add() {
                    let a = 1.0;
                    let b = 2.0;
                    let c_expected = 3.0;
                    test_add(a, b, c_expected);
                }

                #[test]
                fn sub() {
                    let a = 1.0;
                    let b = 2.0;
                    let c_expected = -1.0;
                    test_sub(a, b, c_expected);
                }

                #[test]
                fn mul() {
                    let a = 2.0;
                    let b = 3.0;
                    let c_expected = 6.0;
                    test_mul(a, b, c_expected);
                }

                #[test]
                fn div() {
                    let a = 6.;
                    let b = 2.;
                    let c_expected = 3.;
                    test_div(a, b, c_expected);
                }

                #[test]
                fn neg_assign() {
                    test_neg_assign_real::<f64>();
                }

                #[test]
                fn add_assign() {
                    test_add_assign_real::<f64>();
                }

                #[test]
                fn sub_assign() {
                    test_sub_assign_real::<f64>();
                }

                #[test]
                fn mul_assign() {
                    test_mul_assign_real::<f64>();
                }

                #[test]
                fn div_assign() {
                    test_div_assign_real::<f64>();
                }
                #[test]
                fn mul_add_ref() {
                    test_mul_add_ref_real::<f64>();
                }

                #[test]
                fn from_f64() {
                    let v_native64 = f64::try_from_f64(16.25).unwrap();
                    assert_eq!(v_native64, 16.25);
                }

                #[test]
                fn abs() {
                    test_abs_real::<f64>();
                }

                #[test]
                fn acos() {
                    let a = 0.;

                    let pi_over_2 = a.acos();
                    let expected = std::f64::consts::FRAC_PI_2;
                    assert_eq!(pi_over_2, expected);
                }

                #[test]
                fn asin() {
                    let a = 1.;

                    let pi_over_2 = a.asin();
                    let expected = std::f64::consts::FRAC_PI_2;
                    assert_eq!(pi_over_2, expected);
                }

                #[test]
                fn cos() {
                    test_cos_real::<f64>();
                }

                #[test]
                fn sin() {
                    test_sin_real::<f64>();
                }

                #[test]
                fn test_acos() {
                    let value: f64 = 0.5;
                    let result = value.acos();
                    assert_eq!(result, value.acos());
                }

                #[test]
                fn test_acosh() {
                    let value: f64 = 1.5;
                    let result = value.acosh();
                    assert_eq!(result, value.acosh());
                }

                #[test]
                fn test_asin() {
                    let value: f64 = 0.5;
                    let result = value.asin();
                    assert_eq!(result, value.asin());
                }

                #[test]
                fn test_asinh() {
                    let value: f64 = 0.5;
                    let result = value.asinh();
                    assert_eq!(result, value.asinh());
                }

                #[test]
                fn test_atan() {
                    let value: f64 = 0.5;
                    let result = value.atan();
                    assert_eq!(result, value.atan());
                }

                #[test]
                fn test_atanh() {
                    let value: f64 = 0.5;
                    let result = value.atanh();
                    assert_eq!(result, value.atanh());
                }

                #[test]
                fn test_cos_02() {
                    let value: f64 = 0.5;
                    let result = value.cos();
                    assert_eq!(result, value.cos());
                }

                #[test]
                fn test_cosh() {
                    let value: f64 = 0.5;
                    let result = value.cosh();
                    assert_eq!(result, value.cosh());
                }

                #[test]
                fn test_exp() {
                    let value: f64 = 0.5;
                    let result = value.exp();
                    println!("result = {result:?}");

                    assert_eq!(result, value.exp());
                }

                #[test]
                fn test_is_finite() {
                    let value: f64 = 0.5;
                    assert!(value.is_finite());

                    let value: f64 = f64::INFINITY;
                    assert!(!value.is_finite());
                }

                #[test]
                fn test_is_infinite() {
                    let value: f64 = f64::INFINITY;
                    assert!(value.is_infinite());

                    let value: f64 = 0.5;
                    assert!(!value.is_infinite());
                }

                #[test]
                fn test_ln() {
                    let value: f64 = std::f64::consts::E;
                    let result = value.ln();
                    println!("result = {result:?}");
                    assert_eq!(result, value.ln());
                }

                #[test]
                fn test_log10() {
                    let value: f64 = 10.0;
                    let result = value.log10();
                    println!("result = {result:?}");
                    assert_eq!(result, value.log10());
                }

                #[test]
                fn test_log2() {
                    let value: f64 = 8.0;
                    let result = value.log2();
                    println!("result = {result:?}");
                    assert_eq!(result, value.log2());
                }

                #[test]
                fn test_recip_02() {
                    let value: f64 = 2.0;
                    let result = value.try_reciprocal().unwrap();
                    assert_eq!(result, value.recip());
                }

                #[test]
                fn test_sin_02() {
                    let value: f64 = 0.5;
                    let result = value.sin();
                    assert_eq!(result, value.sin());
                }

                #[test]
                fn test_sinh() {
                    let value: f64 = 0.5;
                    let result = value.sinh();
                    assert_eq!(result, value.sinh());
                }

                #[test]
                fn sqrt() {
                    let value: f64 = 4.0;
                    let result = value.sqrt();
                    assert_eq!(result, value.sqrt());
                }

                #[test]
                fn try_sqrt() {
                    let value: f64 = 4.0;
                    let result = value.try_sqrt().unwrap();
                    assert_eq!(result, value.sqrt());

                    assert!((-1.0).try_sqrt().is_err());
                }

                #[test]
                fn test_tan() {
                    let value: f64 = 0.5;
                    let result = value.tan();
                    assert_eq!(result, value.tan());
                }

                #[test]
                fn test_tanh() {
                    let value: f64 = 0.5;
                    let result = value.tanh();
                    assert_eq!(result, value.tanh());
                }
            }

            mod complex {
                use super::*;

                #[test]
                fn add() {
                    let a = Complex::new(1., 2.);
                    let b = Complex::new(3., 4.);

                    let c_expected = Complex::new(4., 6.);

                    test_add(a, b, c_expected);
                }

                #[test]
                fn sub() {
                    let a = Complex::new(3., 2.);
                    let b = Complex::new(1., 4.);

                    let c_expected = Complex::new(2., -2.);

                    test_sub(a, b, c_expected);
                }

                #[test]
                fn mul() {
                    let a = Complex::new(3., 2.);
                    let b = Complex::new(1., 4.);
                    let c_expected = Complex::new(-5., 14.);
                    test_mul(a, b, c_expected);
                }

                #[test]
                fn div() {
                    let a = Complex::new(-5., 14.);
                    let b = Complex::new(1., 4.);

                    let c_expected = Complex::new(3., 2.);

                    test_div(a, b, c_expected);
                }

                #[test]
                fn add_assign() {
                    let mut a = Complex::new(1., 2.);
                    let b = Complex::new(3., 4.);

                    a += &b;
                    let a_expected = Complex::new(4., 6.);
                    assert_eq!(a, a_expected);

                    a += b;
                    let a_expected = Complex::new(7., 10.);
                    assert_eq!(a, a_expected);
                }

                #[test]
                fn sub_assign() {
                    let mut a = Complex::new(3., 2.);
                    let b = Complex::new(2., 4.);

                    a -= &b;
                    let a_expected = Complex::new(1., -2.);
                    assert_eq!(a, a_expected);

                    a -= b;
                    let a_expected = Complex::new(-1., -6.);
                    assert_eq!(a, a_expected);
                }

                #[test]
                fn mul_assign() {
                    let mut a = Complex::new(3., 2.);
                    let b = Complex::new(2., 4.);

                    a *= &b;
                    let a_expected = Complex::new(-2., 16.);
                    assert_eq!(a, a_expected);

                    a *= b;
                    let a_expected = Complex::new(-68., 24.);
                    assert_eq!(a, a_expected);
                }

                #[test]
                fn div_assign() {
                    let mut a = Complex::new(-68., 24.);
                    let b = Complex::new(2., 4.);

                    a /= &b;
                    let a_expected = Complex::new(-2., 16.);
                    assert_eq!(a, a_expected);

                    a /= b;
                    let a_expected = Complex::new(3., 2.);
                    assert_eq!(a, a_expected);
                }

                #[test]
                fn from_f64() {
                    let v = Complex::new(16.25, 2.);
                    assert_eq!(v.real_part(), 16.25);
                    assert_eq!(v.imag_part(), 2.);
                }

                #[test]
                fn conj() {
                    let v = Complex::new(16.25, 2.);

                    let v_conj = v.conjugate();
                    assert_eq!(v_conj.real_part(), 16.25);
                    assert_eq!(v_conj.imag_part(), -2.);
                }

                #[test]
                fn neg_assign() {
                    let mut a = Complex::new(1., 2.);
                    a.neg_assign();

                    let a_expected = Complex::new(-1., -2.);
                    assert_eq!(a, a_expected);
                }

                #[test]
                fn abs() {
                    let a = Complex::new(-3., 4.);

                    let abs = a.abs();
                    let expected = 5.;
                    assert_eq!(abs, expected);
                }

                #[test]
                fn mul_add_ref() {
                    let a = Complex::new(2., -3.);
                    let b = Complex::new(3., 1.);
                    let c = Complex::new(1., -4.);

                    let d_expected = Complex::new(10., -11.);

                    let d = a.mul_add_ref(&b, &c);
                    assert_eq!(d, d_expected);
                }

                #[test]
                fn mul_complex_with_real() {
                    let a = Complex::new(1., 2.);
                    let b = 3.;

                    let a_times_b_expected = Complex::new(3., 6.);

                    test_mul_complex_with_real(a, b, a_times_b_expected);
                }

                #[test]
                fn mul_assign_complex_with_real() {
                    let a = Complex::new(1., 2.);
                    let b = 3.;

                    let a_times_b_expected = Complex::new(3., 6.);

                    test_mul_assign_complex_with_real(a, b, a_times_b_expected);
                }

                #[test]
                fn test_acos() {
                    let value: Complex<f64> = Complex::new(0.5, 0.5);
                    let result = value.acos();
                    assert_eq!(result, value.acos());
                }

                #[test]
                fn test_acosh() {
                    let value: Complex<f64> = Complex::new(1.5, 0.5);
                    let result = value.acosh();
                    assert_eq!(result, value.acosh());
                }

                #[test]
                fn test_asin() {
                    let value: Complex<f64> = Complex::new(0.5, 0.5);
                    let result = value.asin();
                    assert_eq!(result, value.asin());
                }

                #[test]
                fn test_asinh() {
                    let value: Complex<f64> = Complex::new(0.5, 0.5);
                    let result = value.asinh();
                    assert_eq!(result, value.asinh());
                }

                #[test]
                fn test_atan() {
                    let value: Complex<f64> = Complex::new(0.5, 0.5);
                    let result = value.atan();
                    assert_eq!(result, value.atan());
                }

                #[test]
                fn test_atanh() {
                    let value: Complex<f64> = Complex::new(0.5, 0.5);
                    let result = value.atanh();
                    assert_eq!(result, value.atanh());
                }

                #[test]
                fn test_cos_01() {
                    let value: Complex<f64> = Complex::new(0.5, 0.5);
                    let result = value.cos();
                    assert_eq!(result, value.cos());
                }

                #[test]
                fn test_cosh() {
                    let value: Complex<f64> = Complex::new(0.5, 0.5);
                    let result = value.cosh();
                    assert_eq!(result, value.cosh());
                }

                #[test]
                fn test_exp() {
                    let value: Complex<f64> = Complex::new(0.5, 0.5);
                    let result = value.exp();
                    println!("result = {result:?}");
                    assert_eq!(result, value.exp());
                }

                /*
                #[test]
                fn test_is_finite() {
                    let value: Complex<f64> = Complex::new(0.5, 0.5);
                    assert!(value.is_finite());

                    let value: Complex<f64> = Complex::new(f64::INFINITY, 0.5);
                    assert!(!value.is_finite());
                }

                #[test]
                fn test_is_infinite() {
                    let value: Complex<f64> = Complex::new(f64::INFINITY, 0.5);
                    assert!(value.is_infinite());

                    let value: Complex<f64> = Complex::new(0.5, 0.5);
                    assert!(!value.is_infinite());
                }
                */

                #[test]
                fn test_ln() {
                    let value: Complex<f64> = Complex::new(std::f64::consts::E, 1.0);
                    let result = value.ln();
                    println!("result = {result:?}");
                    assert_eq!(result, value.ln());
                }

                #[test]
                fn test_log10() {
                    let value: Complex<f64> = Complex::new(10.0, 1.0);
                    let result = value.log10();
                    println!("result = {result:?}");
                    assert_eq!(result, value.log10());
                }

                #[test]
                fn test_log2() {
                    let value: Complex<f64> = Complex::new(8.0, 1.0);
                    let result = value.log2();
                    println!("result = {result:?}");
                    assert_eq!(result, value.log2());
                }

                #[test]
                fn test_recip() {
                    let value: Complex<f64> = Complex::new(2.0, 0.0);
                    let result = value.try_reciprocal().unwrap();
                    assert_eq!(result, value.finv());
                }

                #[test]
                fn test_sin_01() {
                    let value: Complex<f64> = Complex::new(0.5, 0.5);
                    let result = value.sin();
                    assert_eq!(result, value.sin());
                }

                #[test]
                fn test_sinh() {
                    let value: Complex<f64> = Complex::new(0.5, 0.5);
                    let result = value.sinh();
                    assert_eq!(result, value.sinh());
                }

                #[test]
                fn sqrt() {
                    let value: Complex<f64> = Complex::new(4.0, 1.0);
                    let result = value.sqrt();
                    assert_eq!(result, value.sqrt());
                }

                #[test]
                fn try_sqrt() {
                    let value: Complex<f64> = Complex::new(4.0, 1.0);
                    let result = value.try_sqrt().unwrap();
                    assert_eq!(result, value.sqrt());
                }

                #[test]
                fn test_tan() {
                    let value: Complex<f64> = Complex::new(0.5, 0.5);
                    let result = value.tan();
                    assert_eq!(result, value.tan());
                }

                #[test]
                fn test_tanh() {
                    let value: Complex<f64> = Complex::new(0.5, 0.5);
                    let result = value.tanh();
                    assert_eq!(result, value.tanh());
                }
            }
        }

        #[cfg(feature = "rug")]
        mod rug_ {
            use super::*;
            use crate::kernels::rug::{ComplexRugStrictFinite, RealRugStrictFinite};
            use rug::ops::CompleteRound;
            use try_create::{IntoInner, TryNew};

            const PRECISION: u32 = 100;

            mod real {
                use super::*;
                use rug::Float;

                #[test]
                fn zero() {
                    test_zero::<RealRugStrictFinite<64>>();
                    test_zero::<RealRugStrictFinite<PRECISION>>();
                }

                #[test]
                fn one() {
                    test_one::<RealRugStrictFinite<64>>();
                    test_one::<RealRugStrictFinite<PRECISION>>();
                }

                #[test]
                fn recip() {
                    test_recip::<RealRugStrictFinite<64>>();
                    test_recip::<RealRugStrictFinite<PRECISION>>();
                }

                #[test]
                fn add() {
                    let a = RealRugStrictFinite::<PRECISION>::try_from_f64(1.0).unwrap();
                    let b = RealRugStrictFinite::<PRECISION>::try_from_f64(2.0).unwrap();
                    let c_expected = RealRugStrictFinite::<PRECISION>::try_from_f64(3.0).unwrap();
                    test_add(a, b, c_expected);
                }

                #[test]
                fn sub() {
                    let a = RealRugStrictFinite::<PRECISION>::try_from_f64(1.0).unwrap();
                    let b = RealRugStrictFinite::<PRECISION>::try_from_f64(2.0).unwrap();
                    let c_expected = RealRugStrictFinite::<PRECISION>::try_from_f64(-1.0).unwrap();
                    test_sub(a, b, c_expected);
                }

                #[test]
                fn mul() {
                    let a = RealRugStrictFinite::<PRECISION>::try_from_f64(2.0).unwrap();
                    let b = RealRugStrictFinite::<PRECISION>::try_from_f64(3.0).unwrap();
                    let c_expected = RealRugStrictFinite::<PRECISION>::try_from_f64(6.0).unwrap();
                    test_mul(a, b, c_expected);
                }

                #[test]
                fn div() {
                    let a = RealRugStrictFinite::<PRECISION>::try_from_f64(6.).unwrap();
                    let b = RealRugStrictFinite::<PRECISION>::try_from_f64(2.).unwrap();
                    let c_expected = RealRugStrictFinite::<PRECISION>::try_from_f64(3.).unwrap();

                    test_div(a, b, c_expected);
                }

                #[test]
                fn neg_assign() {
                    test_neg_assign_real::<RealRugStrictFinite<PRECISION>>();
                }

                #[test]
                fn add_assign() {
                    test_add_assign_real::<RealRugStrictFinite<PRECISION>>();
                }

                #[test]
                fn sub_assign() {
                    test_sub_assign_real::<RealRugStrictFinite<PRECISION>>();
                }

                #[test]
                fn mul_assign() {
                    test_mul_assign_real::<RealRugStrictFinite<PRECISION>>();
                }

                #[test]
                fn div_assign() {
                    test_div_assign_real::<RealRugStrictFinite<PRECISION>>();
                }

                #[test]
                fn mul_add_ref() {
                    test_mul_add_ref_real::<RealRugStrictFinite<PRECISION>>();
                }

                #[test]
                fn abs() {
                    test_abs_real::<RealRugStrictFinite<PRECISION>>();
                }

                #[test]
                fn acos() {
                    {
                        let a = RealRugStrictFinite::<53>::zero();
                        let pi_over_2 = RealRugStrictFinite::<53>::acos(a);
                        let expected = rug::Float::with_val(53, std::f64::consts::FRAC_PI_2);
                        assert_eq!(pi_over_2.as_ref(), &expected);
                    }
                    {
                        let a = RealRugStrictFinite::<100>::zero();
                        let pi_over_2 = RealRugStrictFinite::<100>::acos(a);
                        let expected = rug::Float::with_val(
                            100,
                            rug::Float::parse("1.5707963267948966192313216916397").unwrap(),
                        );
                        assert_eq!(pi_over_2.as_ref(), &expected);
                    }
                }

                #[test]
                fn asin() {
                    {
                        let a = RealRugStrictFinite::<53>::one();
                        let pi_over_2 = RealRugStrictFinite::<53>::asin(a);
                        let expected = rug::Float::with_val(53, std::f64::consts::FRAC_PI_2);
                        assert_eq!(pi_over_2.as_ref(), &expected);
                    }
                    {
                        let a = RealRugStrictFinite::<100>::one();
                        let pi_over_2 = RealRugStrictFinite::<100>::asin(a);
                        let expected = rug::Float::with_val(
                            100,
                            rug::Float::parse("1.5707963267948966192313216916397").unwrap(),
                        );
                        assert_eq!(pi_over_2.as_ref(), &expected);
                    }
                }

                #[test]
                fn cos() {
                    test_cos_real::<RealRugStrictFinite<64>>();
                    test_cos_real::<RealRugStrictFinite<PRECISION>>();
                }

                #[test]
                fn sin() {
                    test_sin_real::<RealRugStrictFinite<64>>();
                    test_sin_real::<RealRugStrictFinite<PRECISION>>();
                }

                #[test]
                fn dot_product() {
                    let a = &[
                        RealRugStrictFinite::<100>::one(),
                        RealRugStrictFinite::<100>::try_from_f64(2.).unwrap(),
                    ];

                    let b = &[
                        RealRugStrictFinite::<100>::try_from_f64(2.).unwrap(),
                        RealRugStrictFinite::<100>::try_from_f64(-1.).unwrap(),
                    ];

                    let a: Vec<_> = a.iter().map(|a_i| a_i.as_ref()).collect();
                    let b: Vec<_> = b.iter().map(|b_i| b_i.as_ref()).collect();

                    let value = RealRugStrictFinite::<100>::try_new(
                        rug::Float::dot(a.into_iter().zip(b)).complete(100),
                    )
                    .unwrap();

                    assert_eq!(value.as_ref(), &rug::Float::with_val(100, 0.));
                }
                #[test]
                fn test_acos() {
                    let value =
                        RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 0.5))
                            .unwrap();
                    let result = value.clone().acos();
                    assert_eq!(
                        result,
                        RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(
                            PRECISION,
                            value.into_inner().acos()
                        ))
                        .unwrap()
                    );
                }

                #[test]
                fn test_acosh() {
                    let value =
                        RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 1.5))
                            .unwrap();
                    let result = value.clone().acosh();
                    assert_eq!(
                        result,
                        RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(
                            PRECISION,
                            value.into_inner().acosh()
                        ))
                        .unwrap()
                    );
                }

                #[test]
                fn test_asin() {
                    let value =
                        RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 0.5))
                            .unwrap();
                    let result = value.clone().asin();
                    assert_eq!(
                        result,
                        RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(
                            PRECISION,
                            value.into_inner().asin()
                        ))
                        .unwrap()
                    );
                }

                #[test]
                fn test_asinh() {
                    let value =
                        RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 0.5))
                            .unwrap();
                    let result = value.clone().asinh();
                    assert_eq!(
                        result,
                        RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(
                            PRECISION,
                            value.into_inner().asinh()
                        ))
                        .unwrap()
                    );
                }

                #[test]
                fn test_atan() {
                    let value =
                        RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 0.5))
                            .unwrap();
                    let result = value.clone().atan();
                    assert_eq!(
                        result,
                        RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(
                            PRECISION,
                            value.into_inner().atan()
                        ))
                        .unwrap()
                    );
                }

                #[test]
                fn test_atanh() {
                    let value =
                        RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 0.5))
                            .unwrap();
                    let result = value.clone().atanh();
                    assert_eq!(
                        result,
                        RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(
                            PRECISION,
                            value.into_inner().atanh()
                        ))
                        .unwrap()
                    );
                }

                #[test]
                fn test_cos_02() {
                    let value =
                        RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 0.5))
                            .unwrap();
                    let result = value.clone().cos();
                    assert_eq!(
                        result,
                        RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(
                            PRECISION,
                            value.into_inner().cos()
                        ))
                        .unwrap()
                    );
                }

                #[test]
                fn test_cosh() {
                    let value =
                        RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 0.5))
                            .unwrap();
                    let result = value.clone().cosh();
                    assert_eq!(
                        result,
                        RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(
                            PRECISION,
                            value.into_inner().cosh()
                        ))
                        .unwrap()
                    );
                }

                #[test]
                fn test_exp() {
                    let value =
                        RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 0.5))
                            .unwrap();
                    let result = value.clone().exp();
                    println!("result = {result:?}");
                    assert_eq!(
                        result,
                        RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(
                            PRECISION,
                            value.into_inner().exp()
                        ))
                        .unwrap()
                    );
                }

                #[test]
                fn test_is_finite() {
                    let value =
                        RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 0.5))
                            .unwrap();
                    assert!(value.is_finite());

                    let value = RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(
                        PRECISION,
                        f64::INFINITY,
                    ));
                    assert!(value.is_err());
                }

                #[test]
                fn test_is_infinite() {
                    let value = RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(
                        PRECISION,
                        f64::INFINITY,
                    ));
                    assert!(value.is_err());

                    let value =
                        RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 0.5))
                            .unwrap();
                    assert!(!value.is_infinite());
                }

                #[test]
                fn test_ln() {
                    let value = RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(
                        PRECISION,
                        std::f64::consts::E,
                    ))
                    .unwrap();
                    let result = value.clone().ln();
                    println!("result = {result:?}");
                    assert_eq!(
                        result,
                        RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(
                            PRECISION,
                            value.into_inner().ln()
                        ))
                        .unwrap()
                    );
                }

                #[test]
                fn test_log10() {
                    let value =
                        RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 10.0))
                            .unwrap();
                    let result = value.clone().log10();
                    println!("result = {result:?}");
                    assert_eq!(
                        result,
                        RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(
                            PRECISION,
                            value.into_inner().log10()
                        ))
                        .unwrap()
                    );
                }

                #[test]
                fn test_log2() {
                    let value =
                        RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 8.0))
                            .unwrap();
                    let result = value.clone().log2();
                    println!("result = {result:?}");
                    assert_eq!(
                        result,
                        RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(
                            PRECISION,
                            value.into_inner().log2()
                        ))
                        .unwrap()
                    );
                }

                #[test]
                fn test_recip_02() {
                    let value =
                        RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 2.0))
                            .unwrap();
                    let result = value.clone().try_reciprocal().unwrap();
                    assert_eq!(
                        result,
                        RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(
                            PRECISION,
                            value.into_inner().recip()
                        ))
                        .unwrap()
                    );
                }

                #[test]
                fn test_sin_02() {
                    let value =
                        RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 0.5))
                            .unwrap();
                    let result = value.clone().sin();
                    assert_eq!(
                        result,
                        RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(
                            PRECISION,
                            value.into_inner().sin()
                        ))
                        .unwrap()
                    );
                }

                #[test]
                fn test_sinh() {
                    let value =
                        RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 0.5))
                            .unwrap();
                    let result = value.clone().sinh();
                    assert_eq!(
                        result,
                        RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(
                            PRECISION,
                            value.into_inner().sinh()
                        ))
                        .unwrap()
                    );
                }

                #[test]
                fn sqrt() {
                    let value =
                        RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 4.0))
                            .unwrap();
                    let result = value.clone().sqrt();
                    assert_eq!(
                        result,
                        RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(
                            PRECISION,
                            value.into_inner().sqrt()
                        ))
                        .unwrap()
                    );
                }

                #[test]
                fn try_sqrt() {
                    let value =
                        RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 4.0))
                            .unwrap();
                    let result = value.clone().try_sqrt().unwrap();
                    assert_eq!(
                        result,
                        RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(
                            PRECISION,
                            value.into_inner().sqrt()
                        ))
                        .unwrap()
                    );

                    assert!(
                        RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, -4.0))
                            .unwrap()
                            .try_sqrt()
                            .is_err()
                    )
                }

                #[test]
                fn test_tan() {
                    let value =
                        RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 0.5))
                            .unwrap();
                    let result = value.clone().tan();
                    assert_eq!(
                        result,
                        RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(
                            PRECISION,
                            value.into_inner().tan()
                        ))
                        .unwrap()
                    );
                }

                #[test]
                fn test_tanh() {
                    let value =
                        RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 0.5))
                            .unwrap();
                    let result = value.clone().tanh();
                    assert_eq!(
                        result,
                        RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(
                            PRECISION,
                            value.into_inner().tanh()
                        ))
                        .unwrap()
                    );
                }

                #[test]
                fn test_mul_add() {
                    let a =
                        RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 2.0))
                            .unwrap();
                    let b =
                        RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 3.0))
                            .unwrap();
                    let c =
                        RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 4.0))
                            .unwrap();
                    let result = a.clone().mul_add_ref(&b, &c);
                    assert_eq!(
                        result,
                        RealRugStrictFinite::<PRECISION>::try_new(
                            a.into_inner() * b.as_ref() + c.as_ref()
                        )
                        .unwrap()
                    );
                }
            }

            mod complex {
                use super::*;
                //use rug::Complex;
                use rug::Float;

                #[test]
                fn add() {
                    let a = ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(1., 2.))
                        .unwrap();
                    let b = ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(3., 4.))
                        .unwrap();
                    let c_expected =
                        ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(4., 6.))
                            .unwrap();
                    test_add(a, b, c_expected);
                }

                #[test]
                fn sub() {
                    let a = ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(3., 2.))
                        .unwrap();
                    let b = ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(1., 4.))
                        .unwrap();
                    let c_expected =
                        ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(2., -2.))
                            .unwrap();
                    test_sub(a, b, c_expected);
                }

                #[test]
                fn mul() {
                    let a = ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(3., 2.))
                        .unwrap();
                    let b = ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(1., 4.))
                        .unwrap();
                    let c_expected =
                        ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(-5., 14.))
                            .unwrap();
                    test_mul(a, b, c_expected);
                }

                #[test]
                fn div() {
                    let a = ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(-5., 14.))
                        .unwrap();
                    let b = ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(1., 4.))
                        .unwrap();
                    let c_expected =
                        ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(3., 2.))
                            .unwrap();
                    test_div(a, b, c_expected);
                }

                #[test]
                fn add_assign() {
                    let mut a = ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(1., 2.))
                        .unwrap();
                    let b = ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(3., 4.))
                        .unwrap();

                    a += &b;
                    let a_expected =
                        ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(4., 6.))
                            .unwrap();
                    assert_eq!(a, a_expected);

                    a += b;
                    let a_expected =
                        ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(7., 10.))
                            .unwrap();
                    assert_eq!(a, a_expected);
                }

                #[test]
                fn sub_assign() {
                    let mut a = ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(3., 2.))
                        .unwrap();
                    let b = ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(2., 4.))
                        .unwrap();

                    a -= &b;
                    let a_expected =
                        ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(1., -2.))
                            .unwrap();
                    assert_eq!(a, a_expected);

                    a -= b;
                    let a_expected =
                        ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(-1., -6.))
                            .unwrap();
                    assert_eq!(a, a_expected);
                }

                #[test]
                fn mul_assign() {
                    let mut a = ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(3., 2.))
                        .unwrap();
                    let b = ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(2., 4.))
                        .unwrap();

                    a *= &b;
                    let a_expected =
                        ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(-2., 16.))
                            .unwrap();
                    assert_eq!(a, a_expected);

                    a *= b;
                    let a_expected =
                        ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(-68., 24.))
                            .unwrap();
                    assert_eq!(a, a_expected);
                }

                #[test]
                fn div_assign() {
                    let mut a =
                        ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(-68., 24.))
                            .unwrap();
                    let b = ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(2., 4.))
                        .unwrap();

                    a /= &b;
                    let a_expected =
                        ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(-2., 16.))
                            .unwrap();
                    assert_eq!(a, a_expected);

                    a /= b;
                    let a_expected =
                        ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(3., 2.))
                            .unwrap();
                    assert_eq!(a, a_expected);
                }

                #[test]
                fn from_f64() {
                    let v_100bits =
                        ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(16.25, 2.))
                            .unwrap();
                    assert_eq!(
                        ComplexRugStrictFinite::<PRECISION>::real_part(&v_100bits),
                        16.25
                    );
                    assert_eq!(
                        ComplexRugStrictFinite::<PRECISION>::imag_part(&v_100bits),
                        2.
                    );

                    let v_53bits =
                        ComplexRugStrictFinite::<53>::try_from(Complex::new(16.25, 2.)).unwrap();
                    assert_eq!(ComplexRugStrictFinite::<53>::real_part(&v_53bits), 16.25);
                    assert_eq!(ComplexRugStrictFinite::<53>::imag_part(&v_53bits), 2.);

                    // 16.25 has seven significant bits (binary 10000.01)
                    let v_7bits =
                        ComplexRugStrictFinite::<7>::try_from(Complex::new(16.25, 2.)).unwrap();
                    assert_eq!(ComplexRugStrictFinite::<7>::real_part(&v_7bits), 16.25);
                    assert_eq!(ComplexRugStrictFinite::<7>::imag_part(&v_7bits), 2.);
                }

                #[test]
                #[should_panic]
                fn from_f64_failing() {
                    // this should fail because 16.25 has seven significant bits (binary 10000.01) and we ask a float with 6 significant bits
                    let _v_6bits =
                        ComplexRugStrictFinite::<6>::try_from(Complex::new(16.25, 2.)).unwrap();
                }

                #[test]
                fn conj() {
                    let v = ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(16.25, 2.))
                        .unwrap();

                    let v_conj = ComplexRugStrictFinite::<PRECISION>::conjugate(v);
                    assert_eq!(
                        ComplexRugStrictFinite::<PRECISION>::real_part(&v_conj),
                        16.25
                    );
                    assert_eq!(ComplexRugStrictFinite::<PRECISION>::imag_part(&v_conj), -2.);
                }

                #[test]
                fn neg_assign() {
                    let mut a = ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(1., 2.))
                        .unwrap();
                    a.neg_assign();

                    let a_expected =
                        ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(-1., -2.))
                            .unwrap();
                    assert_eq!(a, a_expected);
                }

                #[test]
                fn abs() {
                    let a = ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(-3., 4.))
                        .unwrap();

                    let abs = a.abs();
                    let abs_expected = RealRugStrictFinite::<100>::try_from_f64(5.).unwrap();
                    assert_eq!(abs, abs_expected);
                }

                #[test]
                fn mul_add_ref() {
                    let a = ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(2., -3.))
                        .unwrap();
                    let b = ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(3., 1.))
                        .unwrap();
                    let c = ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(1., -4.))
                        .unwrap();

                    let d_expected =
                        ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(10., -11.))
                            .unwrap();

                    let d = a.mul_add_ref(&b, &c);
                    assert_eq!(d, d_expected);
                }

                #[test]
                fn mul_complex_with_real() {
                    let a = ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(1., 2.))
                        .unwrap();
                    let b = RealRugStrictFinite::<100>::try_from_f64(3.).unwrap();

                    let a_times_b_expected =
                        ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(3., 6.))
                            .unwrap();

                    test_mul_complex_with_real(a, b, a_times_b_expected);
                }

                #[test]
                fn mul_assign_complex_with_real() {
                    let a = ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(1., 2.))
                        .unwrap();
                    let b = RealRugStrictFinite::<100>::try_from_f64(3.).unwrap();

                    let a_times_b_expected =
                        ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(3., 6.))
                            .unwrap();

                    test_mul_assign_complex_with_real(a, b, a_times_b_expected);
                }

                #[test]
                fn dot_product() {
                    let a = &[
                        ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(1., 3.))
                            .unwrap(),
                        ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(2., 4.))
                            .unwrap(),
                    ];

                    let b = &[
                        ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(-2., -5.))
                            .unwrap(),
                        ComplexRugStrictFinite::<PRECISION>::try_from(Complex::new(-1., 6.))
                            .unwrap(),
                    ];

                    let a: Vec<_> = a.iter().map(|a_i| a_i.as_ref()).collect();
                    let b: Vec<_> = b.iter().map(|b_i| b_i.as_ref()).collect();

                    // computes a * a^T
                    let a_times_a = ComplexRugStrictFinite::<PRECISION>::try_new(
                        rug::Complex::dot(a.clone().into_iter().zip(a.clone()))
                            .complete((100, 100)),
                    )
                    .unwrap();
                    assert_eq!(
                        a_times_a.as_ref(),
                        &rug::Complex::with_val(100, (-20., 22.))
                    );

                    // computes a * b^T
                    let a_times_b = ComplexRugStrictFinite::<PRECISION>::try_new(
                        rug::Complex::dot(a.clone().into_iter().zip(b.clone()))
                            .complete((100, 100)),
                    )
                    .unwrap();
                    assert_eq!(
                        a_times_b.as_ref(),
                        &rug::Complex::with_val(100, (-13., -3.))
                    );

                    // computes b * a^T
                    let b_times_a = ComplexRugStrictFinite::<PRECISION>::try_new(
                        rug::Complex::dot(b.into_iter().zip(a)).complete((100, 100)),
                    )
                    .unwrap();
                    assert_eq!(
                        b_times_a.as_ref(),
                        &rug::Complex::with_val(100, (-13., -3.))
                    );
                }

                #[test]
                fn test_acos() {
                    let value = ComplexRugStrictFinite::<PRECISION>::try_new(
                        rug::Complex::with_val(PRECISION, (0.5, 0.5)),
                    )
                    .unwrap();
                    let result = value.clone().acos();
                    assert_eq!(
                        result,
                        ComplexRugStrictFinite::<PRECISION>::try_new(rug::Complex::with_val(
                            PRECISION,
                            value.into_inner().acos()
                        ))
                        .unwrap()
                    );
                }

                #[test]
                fn test_acosh() {
                    let value = ComplexRugStrictFinite::<PRECISION>::try_new(
                        rug::Complex::with_val(PRECISION, (1.5, 0.5)),
                    )
                    .unwrap();
                    let result = value.clone().acosh();
                    assert_eq!(
                        result,
                        ComplexRugStrictFinite::<PRECISION>::try_new(rug::Complex::with_val(
                            PRECISION,
                            value.into_inner().acosh()
                        ))
                        .unwrap()
                    );
                }

                #[test]
                fn test_asin() {
                    let value = ComplexRugStrictFinite::<PRECISION>::try_new(
                        rug::Complex::with_val(PRECISION, (0.5, 0.5)),
                    )
                    .unwrap();
                    let result = value.clone().asin();
                    assert_eq!(
                        result,
                        ComplexRugStrictFinite::<PRECISION>::try_new(rug::Complex::with_val(
                            PRECISION,
                            value.into_inner().asin()
                        ))
                        .unwrap()
                    );
                }

                #[test]
                fn test_asinh() {
                    let value = ComplexRugStrictFinite::<PRECISION>::try_new(
                        rug::Complex::with_val(PRECISION, (0.5, 0.5)),
                    )
                    .unwrap();
                    let result = value.clone().asinh();
                    assert_eq!(
                        result,
                        ComplexRugStrictFinite::<PRECISION>::try_new(rug::Complex::with_val(
                            PRECISION,
                            value.into_inner().asinh()
                        ))
                        .unwrap()
                    );
                }

                #[test]
                fn test_atan() {
                    let value = ComplexRugStrictFinite::<PRECISION>::try_new(
                        rug::Complex::with_val(PRECISION, (0.5, 0.5)),
                    )
                    .unwrap();
                    let result = value.clone().atan();
                    assert_eq!(
                        result,
                        ComplexRugStrictFinite::<PRECISION>::try_new(rug::Complex::with_val(
                            PRECISION,
                            value.into_inner().atan()
                        ))
                        .unwrap()
                    );
                }

                #[test]
                fn test_atanh() {
                    let value = ComplexRugStrictFinite::<PRECISION>::try_new(
                        rug::Complex::with_val(PRECISION, (0.5, 0.5)),
                    )
                    .unwrap();
                    let result = value.clone().atanh();
                    assert_eq!(
                        result,
                        ComplexRugStrictFinite::<PRECISION>::try_new(rug::Complex::with_val(
                            PRECISION,
                            value.into_inner().atanh()
                        ))
                        .unwrap()
                    );
                }

                #[test]
                fn test_cos_01() {
                    let value = ComplexRugStrictFinite::<PRECISION>::try_new(
                        rug::Complex::with_val(PRECISION, (0.5, 0.5)),
                    )
                    .unwrap();
                    let result = value.clone().cos();
                    assert_eq!(
                        result,
                        ComplexRugStrictFinite::<PRECISION>::try_new(rug::Complex::with_val(
                            PRECISION,
                            value.into_inner().cos()
                        ))
                        .unwrap()
                    );
                }

                #[test]
                fn test_cosh() {
                    let value = ComplexRugStrictFinite::<PRECISION>::try_new(
                        rug::Complex::with_val(PRECISION, (0.5, 0.5)),
                    )
                    .unwrap();
                    let result = value.clone().cosh();
                    assert_eq!(
                        result,
                        ComplexRugStrictFinite::<PRECISION>::try_new(rug::Complex::with_val(
                            PRECISION,
                            value.into_inner().cosh()
                        ))
                        .unwrap()
                    );
                }

                #[test]
                fn test_exp() {
                    let value = ComplexRugStrictFinite::<PRECISION>::try_new(
                        rug::Complex::with_val(PRECISION, (0.5, 0.5)),
                    )
                    .unwrap();
                    let result = value.clone().exp();
                    println!("result = {result:?}");
                    assert_eq!(
                        result,
                        ComplexRugStrictFinite::<PRECISION>::try_new(rug::Complex::with_val(
                            PRECISION,
                            value.into_inner().exp()
                        ))
                        .unwrap()
                    );
                }

                #[test]
                fn test_is_finite() {
                    let value = ComplexRugStrictFinite::<PRECISION>::try_new(
                        rug::Complex::with_val(PRECISION, (0.5, 0.5)),
                    )
                    .unwrap();
                    assert!(value.is_finite());

                    let value =
                        ComplexRugStrictFinite::<PRECISION>::try_new(rug::Complex::with_val(
                            100,
                            (Float::with_val(PRECISION, f64::INFINITY), 0.5),
                        ));
                    assert!(value.is_err());
                }

                #[test]
                fn test_is_infinite() {
                    let value =
                        ComplexRugStrictFinite::<PRECISION>::try_new(rug::Complex::with_val(
                            100,
                            (Float::with_val(PRECISION, f64::INFINITY), 0.5),
                        ));
                    assert!(value.is_err());

                    let value = ComplexRugStrictFinite::<PRECISION>::try_new(
                        rug::Complex::with_val(PRECISION, (0.5, 0.5)),
                    )
                    .unwrap();
                    assert!(!value.is_infinite());
                }

                #[test]
                fn test_ln() {
                    let value = ComplexRugStrictFinite::<PRECISION>::try_new(
                        rug::Complex::with_val(PRECISION, (std::f64::consts::E, 1.0)),
                    )
                    .unwrap();
                    let result = value.clone().ln();
                    println!("result = {result:?}");
                    assert_eq!(
                        result,
                        ComplexRugStrictFinite::<PRECISION>::try_new(rug::Complex::with_val(
                            PRECISION,
                            value.into_inner().ln()
                        ))
                        .unwrap()
                    );
                }

                #[test]
                fn test_log10() {
                    let value = ComplexRugStrictFinite::<PRECISION>::try_new(
                        rug::Complex::with_val(PRECISION, (10.0, 1.0)),
                    )
                    .unwrap();
                    let result = value.clone().log10();
                    println!("result = {result:?}");
                    assert_eq!(
                        result,
                        ComplexRugStrictFinite::<PRECISION>::try_new(rug::Complex::with_val(
                            PRECISION,
                            value.into_inner().log10()
                        ))
                        .unwrap()
                    );
                }

                #[test]
                fn test_log2() {
                    let value = ComplexRugStrictFinite::<PRECISION>::try_new(
                        rug::Complex::with_val(PRECISION, (8.0, 1.0)),
                    )
                    .unwrap();
                    let result = value.clone().log2();
                    println!("result = {result:?}");
                    assert_eq!(
                        result,
                        ComplexRugStrictFinite::<PRECISION>::try_new(rug::Complex::with_val(
                            PRECISION,
                            value.into_inner().ln() / rug::Float::with_val(PRECISION, 2.).ln()
                        ))
                        .unwrap()
                    );
                }

                #[test]
                fn test_recip() {
                    let value = ComplexRugStrictFinite::<PRECISION>::try_new(
                        rug::Complex::with_val(PRECISION, (2.0, 0.0)),
                    )
                    .unwrap();
                    let result = value.clone().try_reciprocal().unwrap();
                    assert_eq!(
                        result,
                        ComplexRugStrictFinite::<PRECISION>::try_new(rug::Complex::with_val(
                            PRECISION,
                            value.into_inner().recip()
                        ))
                        .unwrap()
                    );
                }

                #[test]
                fn test_sin_01() {
                    let value = ComplexRugStrictFinite::<PRECISION>::try_new(
                        rug::Complex::with_val(PRECISION, (0.5, 0.5)),
                    )
                    .unwrap();
                    let result = value.clone().sin();
                    assert_eq!(
                        result,
                        ComplexRugStrictFinite::<PRECISION>::try_new(rug::Complex::with_val(
                            PRECISION,
                            value.into_inner().sin()
                        ))
                        .unwrap()
                    );
                }

                #[test]
                fn test_sinh() {
                    let value = ComplexRugStrictFinite::<PRECISION>::try_new(
                        rug::Complex::with_val(PRECISION, (0.5, 0.5)),
                    )
                    .unwrap();
                    let result = value.clone().sinh();
                    assert_eq!(
                        result,
                        ComplexRugStrictFinite::<PRECISION>::try_new(rug::Complex::with_val(
                            PRECISION,
                            value.into_inner().sinh()
                        ))
                        .unwrap()
                    );
                }

                #[test]
                fn sqrt() {
                    let value = ComplexRugStrictFinite::<PRECISION>::try_new(
                        rug::Complex::with_val(PRECISION, (4.0, 1.0)),
                    )
                    .unwrap();
                    let result = value.clone().sqrt();
                    assert_eq!(
                        result,
                        ComplexRugStrictFinite::<PRECISION>::try_new(rug::Complex::with_val(
                            PRECISION,
                            value.into_inner().sqrt()
                        ))
                        .unwrap()
                    );
                }

                #[test]
                fn try_sqrt() {
                    let value = ComplexRugStrictFinite::<PRECISION>::try_new(
                        rug::Complex::with_val(PRECISION, (4.0, 1.0)),
                    )
                    .unwrap();
                    let result = value.clone().try_sqrt().unwrap();
                    assert_eq!(
                        result,
                        ComplexRugStrictFinite::<PRECISION>::try_new(rug::Complex::with_val(
                            PRECISION,
                            value.into_inner().sqrt()
                        ))
                        .unwrap()
                    );
                }

                #[test]
                fn test_tan() {
                    let value = ComplexRugStrictFinite::<PRECISION>::try_new(
                        rug::Complex::with_val(PRECISION, (0.5, 0.5)),
                    )
                    .unwrap();
                    let result = value.clone().tan();
                    assert_eq!(
                        result,
                        ComplexRugStrictFinite::<PRECISION>::try_new(rug::Complex::with_val(
                            PRECISION,
                            value.into_inner().tan()
                        ))
                        .unwrap()
                    );
                }

                #[test]
                fn test_tanh() {
                    let value = ComplexRugStrictFinite::<PRECISION>::try_new(
                        rug::Complex::with_val(PRECISION, (0.5, 0.5)),
                    )
                    .unwrap();
                    let result = value.clone().tanh();
                    assert_eq!(
                        result,
                        ComplexRugStrictFinite::<PRECISION>::try_new(rug::Complex::with_val(
                            PRECISION,
                            value.into_inner().tanh()
                        ))
                        .unwrap()
                    );
                }

                #[test]
                fn test_mul_add() {
                    let a = ComplexRugStrictFinite::<PRECISION>::try_new(rug::Complex::with_val(
                        PRECISION,
                        (2.0, 1.0),
                    ))
                    .unwrap();
                    let b = ComplexRugStrictFinite::<PRECISION>::try_new(rug::Complex::with_val(
                        PRECISION,
                        (3.0, 1.0),
                    ))
                    .unwrap();
                    let c = ComplexRugStrictFinite::<PRECISION>::try_new(rug::Complex::with_val(
                        PRECISION,
                        (4.0, 1.0),
                    ))
                    .unwrap();
                    let result = a.clone().mul_add_ref(&b, &c);
                    assert_eq!(
                        result,
                        ComplexRugStrictFinite::<PRECISION>::try_new(rug::Complex::with_val(
                            PRECISION,
                            a.as_ref() * b.as_ref() + c.as_ref()
                        ))
                        .unwrap()
                    );
                }
            }
        }
    }

    mod functions_real_type {
        use super::*;

        mod native64 {

            use super::*;

            #[test]
            fn test_atan2() {
                let a: f64 = 27.0;
                let b: f64 = 13.0;

                let result = a.atan2(b);
                assert_eq!(result, a.atan2(b));
            }

            #[test]
            fn test_ceil() {
                let value: f64 = 3.7;
                let result = value.kernel_ceil();
                assert_eq!(result, value.ceil());
            }

            #[test]
            fn test_clamp() {
                let value: f64 = 5.0;
                let min: f64 = 3.0;
                let max: f64 = 7.0;
                let result = Clamp::clamp(value, &min, &max);
                assert_eq!(result, f64::clamp(value, min, max));
            }

            #[test]
            fn test_classify() {
                let value: f64 = 3.7;
                let result = Classify::classify(&value);
                assert_eq!(result, f64::classify(value));
            }

            #[test]
            fn test_copysign() {
                let value: f64 = 3.5;
                let sign: f64 = -1.0;
                let result = value.kernel_copysign(&sign);
                assert_eq!(result, value.copysign(sign));
            }

            #[test]
            fn test_epsilon() {
                let eps = f64::epsilon();
                assert_eq!(eps, f64::EPSILON);
            }

            #[test]
            fn test_exp_m1() {
                let value: f64 = 0.5;
                let result = ExpM1::exp_m1(value);
                assert_eq!(result, f64::exp_m1(value));
            }

            #[test]
            fn test_floor() {
                let value: f64 = 3.7;
                let result = value.kernel_floor();
                assert_eq!(result, value.floor());
            }

            #[test]
            fn test_fract() {
                let value: f64 = 3.7;
                let result = value.kernel_fract();
                assert_eq!(result, value.fract());
            }

            #[test]
            fn test_hypot() {
                let a: f64 = 3.0;
                let b: f64 = 4.0;
                let result = Hypot::hypot(a, &b);
                assert_eq!(result, f64::hypot(a, b));
            }

            #[test]
            fn test_is_sign_negative() {
                let value: f64 = -1.0;
                assert!(value.kernel_is_sign_negative());

                let value: f64 = -0.0;
                assert!(value.kernel_is_sign_negative());

                let value: f64 = 0.0;
                assert!(!value.kernel_is_sign_negative());

                let value: f64 = 1.0;
                assert!(!value.kernel_is_sign_negative());
            }

            #[test]
            fn test_is_sign_positive() {
                let value: f64 = -1.0;
                assert!(!value.kernel_is_sign_positive());

                let value: f64 = -0.0;
                assert!(!value.kernel_is_sign_positive());

                let value: f64 = 0.0;
                assert!(value.kernel_is_sign_positive());

                let value: f64 = 1.0;
                assert!(value.kernel_is_sign_positive());
            }

            #[test]
            fn test_ln_1p() {
                let value: f64 = 0.5;
                let result = Ln1p::ln_1p(value);
                assert_eq!(result, f64::ln_1p(value));
            }

            #[test]
            fn test_max() {
                let a: f64 = 3.0;
                let b: f64 = 4.0;
                let result = a.max(b);
                assert_eq!(result, a.max(b));
            }

            #[test]
            fn test_min() {
                let a: f64 = 3.0;
                let b: f64 = 4.0;
                let result = a.min(b);
                assert_eq!(result, a.min(b));
            }

            #[test]
            fn max_finite() {
                let max = f64::max_finite();
                assert_eq!(max, f64::MAX);
            }

            #[test]
            fn min_finite() {
                let min = f64::min_finite();
                assert_eq!(min, f64::MIN);
            }

            #[test]
            fn test_mul_add_mul_mut() {
                let mut a: f64 = 2.0;
                let b: f64 = 3.0;
                let c: f64 = 4.0;
                let d: f64 = -1.0;
                let mut result = a;
                result.kernel_mul_add_mul_mut(&b, &c, &d);

                a.mul_add_assign(b, c * d);
                assert_eq!(result, a);
            }

            #[test]
            fn test_mul_sub_mul_mut() {
                let mut a: f64 = 2.0;
                let b: f64 = 3.0;
                let c: f64 = 4.0;
                let d: f64 = -1.0;
                let mut result = a;
                result.kernel_mul_sub_mul_mut(&b, &c, &d);

                a.mul_add_assign(b, -c * d);
                assert_eq!(result, a);
            }

            #[test]
            fn test_negative_one() {
                let value = f64::negative_one();
                assert_eq!(value, -1.0);
            }

            #[test]
            fn test_one() {
                let value = f64::one();
                assert_eq!(value, 1.0);
            }

            #[test]
            fn test_round() {
                let value: f64 = 3.5;
                let result = value.kernel_round();
                assert_eq!(result, value.round());
            }

            #[test]
            fn test_round_ties_even() {
                let value: f64 = 3.5;
                let result = value.kernel_round_ties_even();
                assert_eq!(result, value.round_ties_even());
            }

            #[test]
            fn test_signum() {
                let value: f64 = -3.5;
                let result = value.kernel_signum();
                assert_eq!(result, value.signum());
            }

            #[test]
            fn test_total_cmp() {
                let a: f64 = 3.0;
                let b: f64 = 4.0;
                let result = a.total_cmp(&b);
                assert_eq!(result, a.total_cmp(&b));
            }

            #[test]
            fn test_try_from_64() {
                let result = f64::try_from_f64(3.7);
                assert!(result.is_ok());
            }

            #[test]
            fn test_try_from_64_error_infinite() {
                let result = f64::try_from_f64(f64::INFINITY);
                assert!(result.is_err());
            }

            #[test]
            fn test_try_from_64_error_nan() {
                let result = f64::try_from_f64(f64::NAN);
                assert!(result.is_err());
            }

            #[test]
            fn test_trunc() {
                let value: f64 = 3.7;
                let result = value.kernel_trunc();
                assert_eq!(result, value.trunc());
            }

            #[test]
            fn test_two() {
                let value = f64::two();
                assert_eq!(value, 2.0);
            }
        }

        #[cfg(feature = "rug")]
        mod rug100 {
            use super::*;
            use crate::kernels::rug::RealRugStrictFinite;
            use rug::{Float, ops::CompleteRound};
            use try_create::{IntoInner, TryNew};

            const PRECISION: u32 = 100;

            #[test]
            fn from_f64() {
                let v_100bits = RealRugStrictFinite::<100>::try_from_f64(16.25).unwrap();

                assert_eq!(v_100bits, 16.25);

                let v_53bits = RealRugStrictFinite::<53>::try_from_f64(16.25).unwrap();
                assert_eq!(v_53bits, 16.25);

                // 16.25 has seven significant bits (binary 10000.01)
                let v_7bits = RealRugStrictFinite::<7>::try_from_f64(16.25).unwrap();
                assert_eq!(v_7bits, 16.25);
            }

            #[test]
            #[should_panic]
            fn from_f64_failing() {
                // this should fail because 16.25 has seven significant bits (binary 10000.01) and we ask a float with 6 significant bits
                let _v_6bits = RealRugStrictFinite::<6>::try_from_f64(16.25).unwrap();
            }

            #[test]
            fn max_finite() {
                let max = RealRugStrictFinite::<53>::max_finite();
                assert_eq!(
                    max.as_ref(),
                    &rug::Float::with_val(
                        53,
                        rug::Float::parse("1.0492893582336937e323228496").unwrap()
                    )
                );
            }

            #[test]
            fn min_finite() {
                let min = RealRugStrictFinite::<53>::min_finite();
                assert_eq!(
                    min.as_ref(),
                    &rug::Float::with_val(
                        53,
                        rug::Float::parse("-1.0492893582336937e323228496").unwrap()
                    )
                );
            }

            #[test]
            fn test_atan2() {
                let a = RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 27.0))
                    .unwrap();
                let b = RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 13.0))
                    .unwrap();
                let result = a.clone().atan2(&b);
                assert_eq!(
                    result,
                    RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(
                        PRECISION,
                        a.into_inner().atan2(b.as_ref())
                    ))
                    .unwrap()
                );
            }

            #[test]
            fn test_ceil() {
                let value =
                    RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 3.7))
                        .unwrap();
                let result = value.clone().kernel_ceil();
                assert_eq!(
                    result,
                    RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(
                        PRECISION,
                        value.into_inner().ceil()
                    ))
                    .unwrap()
                );
            }

            #[test]
            fn test_clamp() {
                let value =
                    RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 5.0))
                        .unwrap();
                let min =
                    RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 3.0))
                        .unwrap();
                let max =
                    RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 7.0))
                        .unwrap();
                let result = value.clone().clamp(&min, &max);
                assert_eq!(
                    result,
                    RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(
                        PRECISION,
                        value.into_inner().clamp(min.as_ref(), max.as_ref())
                    ))
                    .unwrap()
                );
            }

            #[test]
            fn test_classify() {
                let value =
                    RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 3.7))
                        .unwrap();
                let result = value.classify();
                assert_eq!(result, value.into_inner().classify());
            }

            #[test]
            fn test_copysign() {
                let value =
                    RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 3.5))
                        .unwrap();
                let sign =
                    RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, -1.0))
                        .unwrap();
                let result = value.clone().kernel_copysign(&sign);
                assert_eq!(
                    result,
                    RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(
                        PRECISION,
                        value.into_inner().copysign(sign.as_ref())
                    ))
                    .unwrap()
                );
            }

            #[test]
            fn test_epsilon() {
                let rug_eps = rug::Float::u_pow_u(2, PRECISION - 1)
                    .complete(PRECISION)
                    .recip();
                //println!("eps: {}", rug_eps);

                let eps = RealRugStrictFinite::<PRECISION>::epsilon();
                assert_eq!(
                    eps,
                    RealRugStrictFinite::<PRECISION>::try_new(rug_eps.clone()).unwrap()
                );

                // here we compute new_eps as the difference between 1 and the next larger floating point number
                let mut new_eps = Float::with_val(PRECISION, 1.);
                new_eps.next_up();
                new_eps -= Float::with_val(PRECISION, 1.);
                assert_eq!(new_eps, rug_eps.clone());

                //println!("new_eps: {new_eps}");

                let one = RealRugStrictFinite::<PRECISION>::one();
                let result = RealRugStrictFinite::<PRECISION>::try_new(
                    new_eps / Float::with_val(PRECISION, 2.),
                )
                .unwrap()
                    + &one;
                assert_eq!(result, one);
            }

            #[test]
            fn test_exp_m1() {
                let value =
                    RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 0.5))
                        .unwrap();
                let result = value.clone().exp_m1();
                assert_eq!(
                    result,
                    RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(
                        PRECISION,
                        value.into_inner().exp_m1()
                    ))
                    .unwrap()
                );
            }

            #[test]
            fn test_floor() {
                let value =
                    RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 3.7))
                        .unwrap();
                let result = value.clone().kernel_floor();
                assert_eq!(
                    result,
                    RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(
                        PRECISION,
                        value.into_inner().floor()
                    ))
                    .unwrap()
                );
            }

            #[test]
            fn test_fract() {
                let value =
                    RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 3.7))
                        .unwrap();
                let result = value.clone().kernel_fract();
                assert_eq!(
                    result,
                    RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(
                        PRECISION,
                        value.into_inner().fract()
                    ))
                    .unwrap()
                );
            }

            #[test]
            fn test_hypot() {
                let a = RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 3.0))
                    .unwrap();
                let b = RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 4.0))
                    .unwrap();
                let result = a.clone().hypot(&b);
                assert_eq!(
                    result,
                    RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(
                        PRECISION,
                        a.into_inner().hypot(b.as_ref())
                    ))
                    .unwrap()
                );
            }

            #[test]
            fn test_is_sign_negative() {
                let value =
                    RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, -1.0))
                        .unwrap();
                assert!(value.kernel_is_sign_negative());

                let value =
                    RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, -0.0))
                        .unwrap();
                assert!(value.kernel_is_sign_negative());

                let value =
                    RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 0.0))
                        .unwrap();
                assert!(!value.kernel_is_sign_negative());

                let value =
                    RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 1.0))
                        .unwrap();
                assert!(!value.kernel_is_sign_negative());
            }

            #[test]
            fn test_is_sign_positive() {
                let value =
                    RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, -1.0))
                        .unwrap();
                assert!(!value.kernel_is_sign_positive());

                let value =
                    RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, -0.0))
                        .unwrap();
                assert!(!value.kernel_is_sign_positive());

                let value =
                    RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 0.0))
                        .unwrap();
                assert!(value.kernel_is_sign_positive());

                let value =
                    RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 1.0))
                        .unwrap();
                assert!(value.kernel_is_sign_positive());
            }

            #[test]
            fn test_ln_1p() {
                let value =
                    RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 0.5))
                        .unwrap();
                let result = value.clone().ln_1p();
                assert_eq!(
                    result,
                    RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(
                        PRECISION,
                        value.into_inner().ln_1p()
                    ))
                    .unwrap()
                );
            }

            #[test]
            fn test_max() {
                let a = RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 3.0))
                    .unwrap();
                let b = RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 4.0))
                    .unwrap();
                let result = a.max(&b);
                assert_eq!(
                    result,
                    &RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(
                        PRECISION,
                        a.clone().into_inner().max(b.as_ref())
                    ))
                    .unwrap()
                );
            }

            #[test]
            fn test_min() {
                let a = RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 3.0))
                    .unwrap();
                let b = RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 4.0))
                    .unwrap();
                let result = a.min(&b);
                assert_eq!(
                    result,
                    &RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(
                        PRECISION,
                        a.clone().into_inner().min(b.as_ref())
                    ))
                    .unwrap()
                );
            }

            #[test]
            fn test_mul_add_mul_mut() {
                let a = RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 2.0))
                    .unwrap();
                let b = RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 3.0))
                    .unwrap();
                let c = RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 4.0))
                    .unwrap();
                let d = RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, -1.0))
                    .unwrap();
                let mut result = a.clone();
                result.kernel_mul_add_mul_mut(&b, &c, &d);
                assert_eq!(
                    result,
                    RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(
                        PRECISION,
                        a.into_inner()
                            .mul_add_ref(b.as_ref(), &(c.into_inner() * d.as_ref()))
                    ))
                    .unwrap()
                );
            }

            #[test]
            fn test_mul_sub_mul_mut() {
                let a = RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 2.0))
                    .unwrap();
                let b = RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 3.0))
                    .unwrap();
                let c = RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 4.0))
                    .unwrap();
                let d = RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, -1.0))
                    .unwrap();
                let mut result = a.clone();
                result.kernel_mul_sub_mul_mut(&b, &c, &d);
                assert_eq!(
                    result,
                    RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(
                        PRECISION,
                        a.into_inner()
                            .mul_add_ref(b.as_ref(), &(-c.into_inner() * d.as_ref()))
                    ))
                    .unwrap()
                );
            }

            #[test]
            fn test_negative_one() {
                let value = RealRugStrictFinite::<PRECISION>::negative_one();
                assert_eq!(
                    value,
                    RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, -1.0))
                        .unwrap()
                );
            }

            #[test]
            fn test_one() {
                let value = RealRugStrictFinite::<PRECISION>::one();
                assert_eq!(
                    value,
                    RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 1.0))
                        .unwrap()
                );
            }

            #[test]
            fn test_round() {
                let value =
                    RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 3.5))
                        .unwrap();
                let result = value.clone().kernel_round();
                assert_eq!(
                    result,
                    RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(
                        PRECISION,
                        value.into_inner().round()
                    ))
                    .unwrap()
                );
            }

            #[test]
            fn test_round_ties_even() {
                let value =
                    RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 3.5))
                        .unwrap();
                let result = value.clone().kernel_round_ties_even();
                assert_eq!(
                    result,
                    RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(
                        PRECISION,
                        value.into_inner().round_even()
                    ))
                    .unwrap()
                );
            }

            #[test]
            fn test_signum() {
                let value =
                    RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, -3.5))
                        .unwrap();
                let result = value.clone().kernel_signum();
                assert_eq!(
                    result,
                    RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(
                        PRECISION,
                        value.into_inner().signum()
                    ))
                    .unwrap()
                );
            }

            #[test]
            fn test_total_cmp() {
                let a = RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 3.0))
                    .unwrap();
                let b = RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 4.0))
                    .unwrap();
                let result = a.total_cmp(&b);
                assert_eq!(result, a.into_inner().total_cmp(b.as_ref()));
            }

            #[test]
            fn test_try_from_64() {
                let result = RealRugStrictFinite::<PRECISION>::try_from_f64(3.7);
                assert!(result.is_ok());
            }

            #[test]
            fn test_try_from_64_error_infinite() {
                let result = RealRugStrictFinite::<PRECISION>::try_from_f64(f64::INFINITY);
                assert!(result.is_err());
            }

            #[test]
            fn test_try_from_64_error_nan() {
                let result = RealRugStrictFinite::<PRECISION>::try_from_f64(f64::NAN);
                assert!(result.is_err());
            }

            #[test]
            fn test_trunc() {
                let value =
                    RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 3.7))
                        .unwrap();
                let result = value.clone().kernel_trunc();
                assert_eq!(
                    result,
                    RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(
                        PRECISION,
                        value.into_inner().trunc()
                    ))
                    .unwrap()
                );
            }

            #[test]
            fn test_two() {
                let value = RealRugStrictFinite::<PRECISION>::two();
                assert_eq!(
                    value,
                    RealRugStrictFinite::<PRECISION>::try_new(Float::with_val(PRECISION, 2.0))
                        .unwrap()
                );
            }
        }
    }

    mod util_funcs {
        use crate::{
            kernels::native64_validated::RealNative64StrictFinite, new_random_vec,
            try_vec_f64_into_vec_real,
        };
        use rand::{SeedableRng, distr::Uniform, rngs::StdRng};

        #[test]
        fn test_new_random_vec_deterministic() {
            let seed = [42; 32];
            let mut rng1 = StdRng::from_seed(seed);
            let mut rng2 = StdRng::from_seed(seed);
            let uniform = Uniform::new(-1.0, 1.0).unwrap();

            let vec1: Vec<f64> = new_random_vec(10, &uniform, &mut rng1);
            let vec2: Vec<RealNative64StrictFinite> = new_random_vec(10, &uniform, &mut rng2);

            assert_eq!(vec1.len(), 10);
            assert_eq!(vec2.len(), 10);
            for i in 0..10 {
                assert_eq!(&vec1[i], vec2[i].as_ref());
            }
        }

        #[test]
        fn test_try_vec_f64_into_vec_real_success() {
            let input = vec![1.0, -2.5, 1e10];
            let result = try_vec_f64_into_vec_real::<RealNative64StrictFinite>(input);
            assert!(result.is_ok());
            let output = result.unwrap();
            assert_eq!(output[0].as_ref(), &1.0);
            assert_eq!(output[1].as_ref(), &-2.5);
        }

        #[test]
        fn test_try_vec_f64_into_vec_real_fail() {
            let input = vec![1.0, f64::NAN, 3.0];
            let result = try_vec_f64_into_vec_real::<RealNative64StrictFinite>(input);
            assert!(result.is_err());
        }
    }
}
