num_valid/validation.rs
1#![deny(rustdoc::broken_intra_doc_links)]
2
3//! # Numerical Value Validation Module
4//!
5//! This module provides a framework for validating numerical scalar values,
6//! particularly floating-point and complex numbers, including those with arbitrary precision
7//! using the [`rug`](https://crates.io/crates/rug) library (when the "rug" feature is enabled).
8//!
9//! It defines:
10//! - **Error Types**: Such as [`ErrorsValidationRawReal`], [`ErrorsValidationRawComplex`],
11//! and [`ErrorsTryFromf64`].
12//! These errors detail various validation failures like non-finite values,
13//! subnormal numbers (for `f64` and [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html)), and precision mismatches for `rug` types.
14//! - **Validation Policies**: Implemented via the [`ValidationPolicy`] trait from the
15//! [`try_create`](https://crates.io/crates/try_create) crate.
16//! The primary policy provided is [`StrictFinitePolicy`], which ensures that
17//! numbers are finite (not NaN or Infinity) and, for `f64` and [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html),
18//! not subnormal.
19//! - **Floating-Point Checks Trait**: The [`FpChecks`] trait, defined within this module,
20//! provides common classification methods like `is_finite`, `is_nan`, `is_infinite`,
21//! and `is_normal`. This trait is implemented for standard and `rug`-based scalar types.
22//! - **Enhanced Type Safety for HashMap Keys**: The [`GuaranteesFiniteValues`] marker trait enables
23//! full equality ([`Eq`]) and hashing ([`Hash`]) for validated types when their
24//! validation policy guarantees finite values. This allows validated types to be used
25//! as keys in [`HashMap`](std::collections::HashMap) and other hash-based collections.
26//! - **Implementations**: Validation logic for [`StrictFinitePolicy`] and implementations of
27//! [`FpChecks`] are provided for [`f64`] and [`num::Complex<f64>`]. When the "rug" feature is enabled,
28//! support extends to [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html), [`rug::Complex`](https://docs.rs/rug/latest/rug/struct.Complex.html), and indirectly to the wrapper types
29//! `RealRugStrictFinite` and `ComplexRugStrictFinite` (which utilize these underlying `rug` types).
30//!
31//! The goal of this module is to ensure numerical stability and correctness by allowing
32//! functions to easily validate their inputs and outputs according to defined policies,
33//! and to provide a common interface for basic floating-point characteristic checks.
34//!
35//! ## Enhanced Type Safety with [`GuaranteesFiniteValues`]
36//!
37//! One of the key innovations of this validation framework is the [`GuaranteesFiniteValues`]
38//! marker trait. This trait enables compile-time verification that a validation policy
39//! ensures all validated values are finite, which unlocks additional type system guarantees:
40//!
41//! - **Full Equality**: These types implement [`Eq`], making equality comparisons well-defined
42//! and symmetric.
43//! - **Hashing Support**: With both [`Eq`] and [`Hash`] implemented, these types can be used
44//! as keys in hash-based collections like [`HashMap`](std::collections::HashMap) and [`HashSet`](std::collections::HashSet).
45//!
46//! **Note on Ordering**: The library intentionally does NOT implement [`Ord`]
47//! even for finite-guaranteed types, as our custom [`Max`](crate::functions::Max)
48//! and [`Min`](crate::functions::Min) traits provide more efficient reference-based
49//! operations compared to [`Ord`]'s value-based interface.
50//!
51//! Currently, [`StrictFinitePolicy`] and [`DebugValidationPolicy<StrictFinitePolicy>`]
52//! implement this marker trait.
53//!
54//!
55//! ## **Usage Examples**
56//!
57//! ### Basic Validation
58//!
59//! ```rust
60//! use num_valid::validation::{StrictFinitePolicy, ErrorsValidationRawReal};
61//! use try_create::ValidationPolicy; // For the validate() method
62//!
63//! // Valid finite number
64//! assert!(StrictFinitePolicy::<f64, 53>::validate(1.0).is_ok());
65//!
66//! // Invalid: NaN
67//! let result = StrictFinitePolicy::<f64, 53>::validate(f64::NAN);
68//! assert!(matches!(result, Err(ErrorsValidationRawReal::IsNaN { .. })));
69//!
70//! // Invalid: Infinity
71//! let result = StrictFinitePolicy::<f64, 53>::validate(f64::INFINITY);
72//! assert!(matches!(result, Err(ErrorsValidationRawReal::IsPosInfinity { .. })));
73//!
74//! // Invalid: Subnormal
75//! let subnormal_val = f64::from_bits(1); // Smallest positive subnormal f64
76//! let result = StrictFinitePolicy::<f64, 53>::validate(subnormal_val);
77//! assert!(matches!(result, Err(ErrorsValidationRawReal::IsSubnormal { .. })));
78//! ```
79//!
80//! ### Using [`FpChecks`]
81//!
82//! ```rust
83//! use num_valid::validation::FpChecks;
84//! use num::Complex;
85//!
86//! let val_f64 = 1.0f64;
87//! let nan_f64 = f64::NAN;
88//! let inf_f64 = f64::INFINITY;
89//!
90//! assert!(val_f64.is_finite());
91//! assert!(val_f64.is_normal());
92//! assert!(nan_f64.is_nan());
93//! assert!(!nan_f64.is_finite());
94//! assert!(inf_f64.is_infinite());
95//!
96//! let val_complex = Complex::new(1.0, 2.0);
97//! let nan_complex_real = Complex::new(f64::NAN, 2.0);
98//! let inf_complex_imag = Complex::new(1.0, f64::NEG_INFINITY);
99//!
100//! assert!(val_complex.is_finite());
101//! assert!(val_complex.is_normal());
102//! assert!(nan_complex_real.is_nan());
103//! assert!(!nan_complex_real.is_finite());
104//! assert!(inf_complex_imag.is_infinite());
105//! assert!(!inf_complex_imag.is_finite());
106//! assert!(!inf_complex_imag.is_nan());
107//! ```
108//!
109//! ### HashMap Usage with Finite Guarantees
110//! ```rust
111//! use num_valid::kernels::native64_validated::RealNative64StrictFinite;
112//! use std::collections::HashMap;
113//! use try_create::TryNew;
114//!
115//! let mut scores = HashMap::new();
116//! let player1 = RealNative64StrictFinite::try_new(1.5).unwrap();
117//! let player2 = RealNative64StrictFinite::try_new(2.0).unwrap();
118//!
119//! scores.insert(player1, 100); // Works because Eq + Hash are implemented
120//! scores.insert(player2, 95);
121//!
122//! assert_eq!(scores.len(), 2);
123//! ```
124//!
125//! ### Conditional Debug Validation
126//! ```rust
127//! use num_valid::validation::{DebugValidationPolicy, Native64RawRealStrictFinitePolicy};
128//! use try_create::ValidationPolicy;
129//!
130//! type ConditionalPolicy = DebugValidationPolicy<Native64RawRealStrictFinitePolicy>;
131//!
132//! let value = 42.0;
133//! // Validates only in debug builds, no-op in release
134//! assert!(ConditionalPolicy::validate(value).is_ok());
135//! ```
136
137use crate::{
138 ComplexValidated, NumKernel, RealValidated,
139 kernels::{RawComplexTrait, RawRealTrait, RawScalarTrait},
140};
141use duplicate::duplicate_item;
142use num::{Complex, Integer};
143use std::{
144 backtrace::Backtrace,
145 fmt::Display,
146 hash::{Hash, Hasher},
147 marker::PhantomData,
148 num::FpCategory,
149};
150use thiserror::Error;
151use try_create::ValidationPolicy;
152
153//--------------------------------------------------------------------------------------------------
154// Conditional Backtrace Support
155//--------------------------------------------------------------------------------------------------
156
157/// Captures a backtrace if the `backtrace` feature is enabled.
158///
159/// When the `backtrace` feature is enabled, this function captures a full backtrace
160/// using [`capture_backtrace()`]. When disabled (default), it returns a
161/// disabled backtrace with zero overhead.
162///
163/// # Performance
164///
165/// Backtrace capture is expensive (~1-10μs per capture). For performance-critical
166/// code, leave the `backtrace` feature disabled. Enable it during development or
167/// debugging to get detailed error traces.
168///
169/// # Example
170///
171/// ```toml
172/// # In Cargo.toml, enable backtraces for debugging:
173/// [dependencies]
174/// num-valid = { version = "0.2", features = ["backtrace"] }
175/// ```
176#[inline(always)]
177pub fn capture_backtrace() -> Backtrace {
178 #[cfg(feature = "backtrace")]
179 {
180 Backtrace::force_capture()
181 }
182 #[cfg(not(feature = "backtrace"))]
183 {
184 Backtrace::disabled()
185 }
186}
187
188/// Returns `true` if the `backtrace` feature is enabled.
189///
190/// This can be used to conditionally check whether backtraces are being captured.
191///
192/// # Example
193///
194/// ```rust
195/// use num_valid::validation::is_backtrace_enabled;
196///
197/// if is_backtrace_enabled() {
198/// println!("Backtraces are enabled - errors will include stack traces");
199/// } else {
200/// println!("Backtraces are disabled for performance");
201/// }
202/// ```
203#[inline(always)]
204pub const fn is_backtrace_enabled() -> bool {
205 #[cfg(feature = "backtrace")]
206 {
207 true
208 }
209 #[cfg(not(feature = "backtrace"))]
210 {
211 false
212 }
213}
214
215//--------------------------------------------------------------------------------------------------
216
217/// A marker for policies that apply to real types.
218///
219/// This trait refines the generic [`ValidationPolicy`] for types that implement
220/// [`RawRealTrait`]. It adds two key constraints:
221///
222/// 1. It associates a `const PRECISION` with the policy, essential for backends
223/// like `rug`.
224/// 2. It enforces that the policy's `Error` type must be the same as the canonical
225/// `ValidationErrors` type defined on the raw scalar itself. This architectural
226/// choice ensures consistent error handling throughout the library.
227pub trait ValidationPolicyReal:
228 ValidationPolicy<
229 Value: RawRealTrait,
230 Error = <<Self as ValidationPolicy>::Value as RawScalarTrait>::ValidationErrors,
231 >
232{
233 const PRECISION: u32;
234}
235
236/// A marker for policies that apply to complex types.
237///
238/// This trait refines the generic [`ValidationPolicy`] for types that implement
239/// [`RawComplexTrait`]. It adds two key constraints:
240///
241/// 1. It associates a `const PRECISION` with the policy, essential for backends
242/// like `rug`.
243/// 2. It enforces that the policy's `Error` type must be the same as the canonical
244/// `ValidationErrors` type defined on the raw scalar itself. This architectural
245/// choice ensures consistent error handling throughout the library.
246pub trait ValidationPolicyComplex:
247 ValidationPolicy<
248 Value: RawComplexTrait,
249 Error = <<Self as ValidationPolicy>::Value as RawScalarTrait>::ValidationErrors,
250 >
251{
252 const PRECISION: u32;
253}
254
255//--------------------------------------------------------------------------------------------------
256// Error types
257//--------------------------------------------------------------------------------------------------
258
259//--------------------------------------------
260// Errors for raw real number validation
261//--------------------------------------------
262/// Errors that can occur during the validation of a raw real number.
263///
264/// This enum is generic over `RawReal`, which is typically `f64` or [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html).
265/// It covers common validation failures like non-finite values, subnormality, infinity, and precision mismatch.
266#[derive(Debug, Error)]
267pub enum ErrorsValidationRawReal<RawReal: std::fmt::Debug + Display + Clone> {
268 /// The value is subnormal.
269 #[error("Value is subnormal: {value}")]
270 IsSubnormal {
271 /// The subnormal value that failed validation.
272 value: RawReal,
273
274 /// A captured backtrace for debugging purposes.
275 backtrace: Backtrace,
276 },
277
278 /// The value is NaN (Not a Number).
279 #[error("Value is NaN: {value}")]
280 IsNaN {
281 /// The NaN value that failed validation.
282 value: RawReal,
283
284 /// A captured backtrace for debugging purposes.
285 backtrace: Backtrace,
286 },
287
288 /// The value is positive infinity.
289 #[error("Value is positive infinity")]
290 IsPosInfinity {
291 /// The positive infinity value that failed validation.
292 backtrace: Backtrace,
293 },
294
295 /// The value is negative infinity.
296 #[error("Value is negative infinity")]
297 IsNegInfinity {
298 /// The negative infinity value that failed validation.
299 backtrace: Backtrace,
300 },
301
302 /// The precision of the input does not match the requested precision.
303 #[error(
304 "precision mismatch: input real has precision {actual_precision}, \
305 but the requested precision is {requested_precision}"
306 )]
307 PrecisionMismatch {
308 /// The input value.
309 input_value: RawReal,
310
311 /// The precision of the input value.
312 actual_precision: u32,
313
314 /// The requested precision.
315 requested_precision: u32,
316
317 /// A captured backtrace for debugging purposes.
318 backtrace: Backtrace,
319 },
320 /*
321 // The following variants are placeholders for potential future validation policies
322 // and are not currently used by StrictFinitePolicy.
323
324 #[error("Value is not strictly positive: {value}")]
325 NotPositive {
326 value: RawReal,
327 backtrace: Backtrace,
328 },
329
330 #[error("Value is negative: {value}")]
331 Negative {
332 value: RawReal,
333 backtrace: Backtrace,
334 },
335
336 #[error("Value is zero: {value}")]
337 IsZero {
338 value: RawReal,
339 backtrace: Backtrace,
340 },
341
342 #[error("Value {value} is outside the allowed range [{min}, {max}]")]
343 OutOfRange {
344 value: RawReal,
345 min: RawReal,
346 max: RawReal,
347 backtrace: Backtrace,
348 },
349 */
350}
351
352//--------------------------------------------
353// Errors for complex number validation
354//--------------------------------------------
355/// Errors that can occur during the validation of a complex number.
356///
357/// This enum is generic over `ErrorValidationReal`, which is the error type returned
358/// by validating the real and imaginary parts of the complex number (e.g.,
359/// [`ErrorsValidationRawReal<f64>`](ErrorsValidationRawReal)).
360///
361/// It indicates whether the real part, the imaginary part, or both parts failed validation.
362#[derive(Debug, Error)]
363pub enum ErrorsValidationRawComplex<ErrorValidationReal: std::error::Error> {
364 /// The real part of the complex number failed validation.
365 #[error("Real part validation error: {source}")]
366 InvalidRealPart {
367 /// The underlying validation error for the real part.
368 #[source]
369 #[backtrace]
370 source: ErrorValidationReal,
371 },
372
373 /// The imaginary part of the complex number failed validation.
374 #[error("Imaginary part validation error: {source}")]
375 InvalidImaginaryPart {
376 /// The underlying validation error for the imaginary part.
377 #[source]
378 #[backtrace]
379 source: ErrorValidationReal,
380 },
381
382 /// Both the real and imaginary parts of the complex number failed validation.
383 #[error("Both real and imaginary parts are invalid: real={real_error}, imag={imag_error}")]
384 InvalidBothParts {
385 /// The validation error for the real part.
386 real_error: ErrorValidationReal,
387
388 /// The validation error for the imaginary part.
389 imag_error: ErrorValidationReal,
390 },
391}
392
393//-------------------------------------------------------------
394/// Errors that can occur when trying to convert an `f64` to another real-type scalar of type `RawReal`.
395///
396/// This enum distinguishes between:
397/// 1. Failures due to the `f64` not being exactly representable at the target `PRECISION`
398/// of the `RawReal` type.
399/// 2. General validation failures of the output `RawReal` (e.g., NaN, Infinity, subnormal).
400#[derive(Debug, Error)]
401pub enum ErrorsTryFromf64<RawReal: RawRealTrait> {
402 /// The input `f64` value is not representable exactly at the target `precision` using the output `RawReal` type.
403 #[error(
404 "the input f64 value ({value_in:?}) cannot be exactly represented with the specific precision ({precision:?}). Try to increase the precision."
405 )]
406 NonRepresentableExactly {
407 /// The `f64` value that could not be exactly represented.
408 value_in: f64,
409
410 /// The input `value_in` after the conversion to the type `RawReal` with the requested `precision`.
411 value_converted_from_f64: RawReal,
412
413 /// The precision that was requested.
414 precision: u32,
415
416 /// A captured backtrace for debugging purposes.
417 backtrace: Backtrace,
418 },
419
420 /// The output value failed validation.
421 #[error("the output value is invalid!")]
422 Output {
423 /// The underlying validation error for the `output` value.
424 #[from]
425 source: ErrorsValidationRawReal<RawReal>,
426 },
427}
428//-------------------------------------------------------------
429
430//-------------------------------------------------------------
431#[derive(Debug, Error)]
432pub enum ErrorsRawRealToInteger<RawReal: RawRealTrait, IntType: Integer> {
433 /// The `RawReal` value is not finite (i.e., it is NaN or Infinity).
434 #[error("Value is not finite: {value}")]
435 NotFinite {
436 /// The non-finite `RawReal` value that caused the error.
437 value: RawReal,
438
439 /// A captured backtrace for debugging purposes.
440 backtrace: Backtrace,
441 },
442
443 /// The `RawReal` value is not an integer (i.e., it has a fractional part).
444 #[error("Value is not an integer: {value}")]
445 NotAnInteger {
446 /// The `RawReal` value that is not an integer.
447 value: RawReal,
448
449 /// A captured backtrace for debugging purposes.
450 backtrace: Backtrace,
451 },
452
453 /// The `RawReal` value is outside the range representable by the target integer type `IntType`.
454 #[error("Value {value} is out of range [{min}, {max}] for target integer type")]
455 OutOfRange {
456 /// The `RawReal` value that is out of range.
457 value: RawReal,
458
459 /// The minimum representable value of the target integer type `IntType`.
460 min: IntType,
461
462 /// The maximum representable value of the target integer type `IntType`.
463 max: IntType,
464
465 /// A captured backtrace for debugging purposes.
466 backtrace: Backtrace,
467 },
468}
469//-------------------------------------------------------------
470
471/// A validation policy that checks for strict finiteness.
472///
473/// For floating-point types (`ScalarType = `[`f64`], `ScalarType = `[`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html)), this policy ensures that the value is:
474/// - Not NaN (Not a Number).
475/// - Not positive or negative Infinity.
476/// - Not subnormal (for `f64`).
477/// While [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html) maintains its specified precision rather than having distinct subnormal
478/// representation in the IEEE 754 sense, this policy will also reject [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html) values that are classified as
479/// [`FpCategory::Subnormal`] (e.g., results of underflow that are tiny but not exactly zero).
480///
481/// For complex types (`ScalarType = `[`Complex<f64>`], `ScalarType = `[`rug::Complex`](https://docs.rs/rug/latest/rug/struct.Complex.html)), this policy applies
482/// the strict finiteness check to both the real and imaginary parts.
483///
484/// This struct is a Zero-Sized Type (ZST) and uses [`PhantomData`] to associate
485/// with the `ScalarType` it validates.
486pub struct StrictFinitePolicy<ScalarType: Sized, const PRECISION: u32>(PhantomData<ScalarType>);
487
488/// A type alias for a validation policy that enforces strict finiteness for the raw [`f64`] type.
489///
490/// This is a convenient alias for [`StrictFinitePolicy<f64, 53>`], configured specifically
491/// for Rust's native 64-bit floating-point number. It is used to validate that a given [`f64`]
492/// value is suitable for use within the library's validated types.
493///
494/// This policy ensures that a `f64` value is:
495/// - Not `NaN` (Not a Number).
496/// - Not positive or negative `Infinity`.
497/// - Not a subnormal number.
498///
499/// It is a fundamental building block for the native `f64` kernel, providing the validation
500/// logic for [`RealNative64StrictFinite`](crate::kernels::native64_validated::RealNative64StrictFinite).
501///
502/// # Example
503///
504/// ```rust
505/// use num_valid::validation::{Native64RawRealStrictFinitePolicy, ErrorsValidationRawReal};
506/// use try_create::ValidationPolicy;
507///
508/// // A valid finite number passes validation.
509/// assert!(Native64RawRealStrictFinitePolicy::validate(1.0).is_ok());
510///
511/// // NaN fails validation.
512/// let result_nan = Native64RawRealStrictFinitePolicy::validate(f64::NAN);
513/// assert!(matches!(result_nan, Err(ErrorsValidationRawReal::IsNaN { .. })));
514///
515/// // Infinity fails validation.
516/// let result_inf = Native64RawRealStrictFinitePolicy::validate(f64::INFINITY);
517/// assert!(matches!(result_inf, Err(ErrorsValidationRawReal::IsPosInfinity { .. })));
518/// ```
519pub type Native64RawRealStrictFinitePolicy = StrictFinitePolicy<f64, 53>;
520
521/// A type alias for a validation policy that enforces strict finiteness for the raw [`num::Complex<f64>`] type.
522///
523/// This is a convenient alias for [`StrictFinitePolicy<Complex<f64>, 53>`], configured for
524/// Rust's native 64-bit complex numbers. It is used to validate that a given [`num::Complex<f64>`]
525/// value is suitable for use within the library's validated types.
526///
527/// This policy applies the strict finiteness check (as defined by [`Native64RawRealStrictFinitePolicy`])
528/// to both the real and imaginary parts of the complex number. A [`num::Complex<f64>`] value is considered
529/// valid if and only if both of its components are finite (not `NaN`, `Infinity`, or subnormal).
530///
531/// It is a fundamental building block for the native `f64` kernel, providing the validation
532/// logic for [`ComplexNative64StrictFinite`](crate::kernels::native64_validated::ComplexNative64StrictFinite).
533///
534/// # Example
535///
536/// ```rust
537/// use num_valid::validation::{Native64RawComplexStrictFinitePolicy, ErrorsValidationRawComplex, ErrorsValidationRawReal};
538/// use try_create::ValidationPolicy;
539/// use num::Complex;
540///
541/// // A complex number with valid finite parts passes validation.
542/// let valid_complex = Complex::new(1.0, -2.0);
543/// assert!(Native64RawComplexStrictFinitePolicy::validate(valid_complex).is_ok());
544///
545/// // A complex number with NaN in the real part fails validation.
546/// let nan_complex = Complex::new(f64::NAN, 2.0);
547/// let result_nan = Native64RawComplexStrictFinitePolicy::validate(nan_complex);
548/// assert!(matches!(result_nan, Err(ErrorsValidationRawComplex::InvalidRealPart { .. })));
549///
550/// // A complex number with Infinity in the imaginary part fails validation.
551/// let inf_complex = Complex::new(1.0, f64::INFINITY);
552/// let result_inf = Native64RawComplexStrictFinitePolicy::validate(inf_complex);
553/// assert!(matches!(result_inf, Err(ErrorsValidationRawComplex::InvalidImaginaryPart { .. })));
554///
555/// // A complex number with invalid parts in both also fails.
556/// let both_invalid = Complex::new(f64::NAN, f64::INFINITY);
557/// let result_both = Native64RawComplexStrictFinitePolicy::validate(both_invalid);
558/// assert!(matches!(result_both, Err(ErrorsValidationRawComplex::InvalidBothParts { .. })));
559/// ```
560pub type Native64RawComplexStrictFinitePolicy = StrictFinitePolicy<Complex<f64>, 53>;
561
562/// Ensures the `f64` value is strictly finite.
563///
564/// This policy checks if the `f64` value is:
565/// - Not NaN (Not a Number).
566/// - Not positive or negative Infinity.
567/// - Not subnormal.
568///
569/// # Errors
570///
571/// Returns [`ErrorsValidationRawReal<f64>`](ErrorsValidationRawReal) if the value
572/// fails any of these checks.
573impl ValidationPolicy for Native64RawRealStrictFinitePolicy {
574 type Value = f64;
575 type Error = ErrorsValidationRawReal<f64>;
576
577 fn validate_ref(value: &f64) -> Result<(), Self::Error> {
578 match value.classify() {
579 FpCategory::Nan => Err(ErrorsValidationRawReal::IsNaN {
580 value: *value,
581 backtrace: capture_backtrace(),
582 }),
583 FpCategory::Infinite => {
584 if value.is_sign_positive() {
585 Err(ErrorsValidationRawReal::IsPosInfinity {
586 backtrace: capture_backtrace(),
587 })
588 } else {
589 Err(ErrorsValidationRawReal::IsNegInfinity {
590 backtrace: capture_backtrace(),
591 })
592 }
593 }
594 FpCategory::Subnormal => Err(ErrorsValidationRawReal::IsSubnormal {
595 value: *value,
596 backtrace: capture_backtrace(),
597 }),
598 FpCategory::Normal | FpCategory::Zero => Ok(()),
599 }
600 }
601}
602
603/// Validates a complex number by checking both its real and imaginary parts.
604///
605/// This function uses the provided real validation policy `P` to validate both parts.
606/// If both parts are valid, it returns `Ok(())`. If either part is invalid,
607/// it returns an appropriate error variant from the `ErrorsValidationRawComplex` enum.
608///
609/// # Errors
610/// Returns [`ErrorsValidationRawComplex<P::Error>`](ErrorsValidationRawComplex)
611/// if either the real part, the imaginary part, or both parts fail validation
612/// using the policy `P`.
613pub(crate) fn validate_complex<P: ValidationPolicyReal>(
614 real_part: &P::Value,
615 imag_part: &P::Value,
616) -> Result<(), ErrorsValidationRawComplex<P::Error>> {
617 let real_validation = P::validate_ref(real_part);
618 let imag_validation = P::validate_ref(imag_part);
619 match (real_validation, imag_validation) {
620 (Ok(()), Ok(())) => Ok(()),
621 (Ok(()), Err(imag_err)) => {
622 Err(ErrorsValidationRawComplex::InvalidImaginaryPart { source: imag_err })
623 }
624 (Err(real_err), Ok(())) => {
625 Err(ErrorsValidationRawComplex::InvalidRealPart { source: real_err })
626 }
627 (Err(real_err), Err(imag_err)) => Err(ErrorsValidationRawComplex::InvalidBothParts {
628 real_error: real_err,
629 imag_error: imag_err,
630 }),
631 }
632}
633
634/// Ensures both the real and imaginary parts of a `Complex<f64>` value are strictly finite.
635///
636/// This policy applies the [`StrictFinitePolicy<f64>`](StrictFinitePolicy) to both
637/// the real and imaginary components of the complex number.
638///
639/// # Errors
640///
641/// Returns [`ErrorsValidationRawComplex<ErrorsValidationRawReal<f64>>`](ErrorsValidationRawComplex)
642/// if either the real part, the imaginary part, or both parts fail the
643/// `StrictFinitePolicy<f64>` checks.
644impl ValidationPolicy for Native64RawComplexStrictFinitePolicy {
645 type Value = Complex<f64>;
646 type Error = ErrorsValidationRawComplex<ErrorsValidationRawReal<f64>>;
647
648 fn validate_ref(value: &Complex<f64>) -> Result<(), Self::Error> {
649 validate_complex::<Native64RawRealStrictFinitePolicy>(&value.re, &value.im)
650 }
651}
652
653impl<RawReal: RawRealTrait, const PRECISION: u32> ValidationPolicyReal
654 for StrictFinitePolicy<RawReal, PRECISION>
655where
656 StrictFinitePolicy<RawReal, PRECISION>:
657 ValidationPolicy<Value = RawReal, Error = <RawReal as RawScalarTrait>::ValidationErrors>,
658{
659 const PRECISION: u32 = PRECISION;
660}
661
662impl<RawComplex: RawComplexTrait, const PRECISION: u32> ValidationPolicyComplex
663 for StrictFinitePolicy<RawComplex, PRECISION>
664where
665 StrictFinitePolicy<RawComplex, PRECISION>: ValidationPolicy<
666 Value = RawComplex,
667 Error = <RawComplex as RawScalarTrait>::ValidationErrors,
668 >,
669{
670 const PRECISION: u32 = PRECISION;
671}
672
673//--------------------------------------------------------------------------------------------------
674// Rug Implementations (conditional compilation)
675//--------------------------------------------------------------------------------------------------
676
677#[cfg(feature = "rug")]
678pub(crate) mod rug_impls {
679 use super::*; // Imports items from the parent module (validation)
680
681 /// Ensures the [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html) value is strictly finite.
682 ///
683 /// This policy checks if the underlying [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html) value:
684 /// - Is not NaN (Not a Number).
685 /// - Is not positive nor negative Infinity.
686 /// - Has the specified precision (equal to `PRECISION`).
687 ///
688 /// Note: [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html) does not have a direct concept of "subnormal" in the same
689 /// way IEEE 754 floats do; it maintains its specified precision.
690 ///
691 /// # Errors
692 ///
693 /// Returns [`ErrorsValidationRawReal<rug::Float>`](ErrorsValidationRawReal)
694 /// if the value fails any of these checks.
695 impl<const PRECISION: u32> ValidationPolicy for StrictFinitePolicy<rug::Float, PRECISION> {
696 type Value = rug::Float;
697 type Error = ErrorsValidationRawReal<rug::Float>;
698
699 fn validate_ref(value: &rug::Float) -> Result<(), Self::Error> {
700 let actual_precision = value.prec();
701 if actual_precision == PRECISION {
702 // rug::Float uses std::num::FpCategory via its own classify method
703 match value.classify() {
704 FpCategory::Infinite => {
705 if value.is_sign_positive() {
706 Err(ErrorsValidationRawReal::IsPosInfinity {
707 backtrace: capture_backtrace(),
708 })
709 } else {
710 Err(ErrorsValidationRawReal::IsNegInfinity {
711 backtrace: capture_backtrace(),
712 })
713 }
714 }
715 FpCategory::Nan => Err(ErrorsValidationRawReal::IsNaN {
716 value: value.clone(),
717 backtrace: capture_backtrace(),
718 }),
719 FpCategory::Subnormal => Err(ErrorsValidationRawReal::IsSubnormal {
720 value: value.clone(),
721 backtrace: capture_backtrace(),
722 }),
723 FpCategory::Zero | FpCategory::Normal => Ok(()),
724 }
725 } else {
726 Err(ErrorsValidationRawReal::PrecisionMismatch {
727 input_value: value.clone(),
728 actual_precision,
729 requested_precision: PRECISION,
730 backtrace: capture_backtrace(),
731 })
732 }
733 }
734 }
735
736 /// Ensures both the real and imaginary parts of a [`rug::Complex`](https://docs.rs/rug/latest/rug/struct.Complex.html) value are strictly finite.
737 ///
738 /// This policy applies the [`StrictFinitePolicy<rug::Float>`](StrictFinitePolicy)
739 /// (which checks the underlying [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html)) to both the real and imaginary
740 /// components of the [`rug::Complex`](https://docs.rs/rug/latest/rug/struct.Complex.html).
741 ///
742 /// # Errors
743 ///
744 /// Returns [`ErrorsValidationRawComplex<ErrorsValidationRawReal<rug::Float>>`](ErrorsValidationRawComplex)
745 /// if either the real part, the imaginary part, or both parts fail the
746 /// `StrictFinitePolicy<RealRugStrictFinite<Precision>>` checks.
747 impl<const PRECISION: u32> ValidationPolicy for StrictFinitePolicy<rug::Complex, PRECISION> {
748 type Value = rug::Complex;
749 type Error = ErrorsValidationRawComplex<ErrorsValidationRawReal<rug::Float>>;
750
751 fn validate_ref(value: &rug::Complex) -> Result<(), Self::Error> {
752 validate_complex::<StrictFinitePolicy<rug::Float, PRECISION>>(
753 value.real(),
754 value.imag(),
755 )
756 }
757 }
758}
759//-------------------------------------------------------------
760
761//------------------------------------------------------------------------------------------------
762/// Provides a set of fundamental floating-point classification checks.
763///
764/// This trait defines common methods to determine the characteristics of a numerical value,
765/// such as whether it is finite, infinite, NaN (Not a Number), or normal.
766/// It is implemented for standard floating-point types (`f64`), complex numbers
767/// (`num::Complex<f64>`), and their arbitrary-precision counterparts from the `rug`
768/// library (via `RealRugStrictFinite<P>` and `ComplexRugStrictFinite<P>` which internally use [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html)
769/// and [`rug::Complex`](https://docs.rs/rug/latest/rug/struct.Complex.html)) when the "rug" feature is enabled.
770///
771/// `FpChecks` is often used as a supertrait for more general numerical traits like
772/// [`FpScalar`](crate::FpScalar), ensuring that types representing floating-point
773/// scalars can be queried for these basic properties. This is essential for
774/// validation policies and writing robust numerical algorithms.
775///
776/// Note that this trait focuses on these specific classifications. For more detailed
777/// categorization (like distinguishing subnormals or zero explicitly through this trait),
778/// types may provide other methods (e.g., `classify()` for `f64` and [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html),
779/// or `is_zero()` from [`FpScalar`](crate::FpScalar)).
780pub trait FpChecks {
781 /// Returns [`true`] if `self` is finite (i.e., not infinite and not NaN).
782 ///
783 /// For complex numbers, this typically means both the real and imaginary parts are finite.
784 fn is_finite(&self) -> bool;
785
786 /// Returns [`true`] if `self` is positive infinity or negative infinity.
787 ///
788 /// For complex numbers, this typically means at least one part is infinite,
789 /// and no part is NaN.
790 fn is_infinite(&self) -> bool;
791
792 /// Returns [`true`] if `self` is NaN (Not a Number).
793 ///
794 /// For complex numbers, this typically means at least one part is NaN.
795 fn is_nan(&self) -> bool;
796
797 /// Returns [`true`] if `self` is a *normal* number.
798 ///
799 /// A normal number is a finite, non-zero number that is not subnormal.
800 /// For complex numbers, this typically means both real and imaginary parts are normal
801 /// numbers (and thus individually finite, non-zero, and not subnormal).
802 fn is_normal(&self) -> bool;
803}
804
805#[duplicate_item(
806 T;
807 [f64];
808 [Complex::<f64>];
809)]
810impl FpChecks for T {
811 #[inline(always)]
812 fn is_finite(&self) -> bool {
813 T::is_finite(*self)
814 }
815
816 #[inline(always)]
817 fn is_infinite(&self) -> bool {
818 T::is_infinite(*self)
819 }
820
821 #[inline(always)]
822 fn is_nan(&self) -> bool {
823 T::is_nan(*self)
824 }
825
826 #[inline(always)]
827 fn is_normal(&self) -> bool {
828 T::is_normal(*self)
829 }
830}
831//------------------------------------------------------------------------------------------------
832
833//------------------------------------------------------------------------------------------------
834/// A validation policy that is active only in debug builds.
835///
836/// This struct acts as a wrapper around another [`ValidationPolicy`].
837///
838/// - In **debug builds** (`#[cfg(debug_assertions)]`), it delegates validation
839/// directly to the inner policy `P`.
840/// - In **release builds** (`#[cfg(not(debug_assertions))]`), it becomes a "no-op"
841/// (no operation), meaning its `validate` and `validate_ref` methods do nothing
842/// and always return `Ok`.
843///
844/// This is useful for enforcing strict, potentially expensive validations during
845/// development and testing, while eliminating their performance overhead in production code.
846///
847/// # Generic Parameters
848///
849/// - `P`: The inner [`ValidationPolicy`] to use during debug builds.
850///
851/// # Example
852///
853/// ```rust,ignore
854/// use num_valid::{validation::{DebugValidationPolicy, StrictFinitePolicy, Native64RawRealStrictFinitePolicy}};
855/// use try_create::ValidationPolicy;
856///
857/// // Define a policy that uses Native64RawRealStrictFinitePolicy
858/// // (i.e. StrictFinitePolicy<f64, 53>) only in debug builds.
859/// type MyPolicy = DebugValidationPolicy<Native64RawRealStrictFinitePolicy>;
860///
861/// let nan_value = f64::NAN;
862/// let result = MyPolicy::validate(nan_value);
863///
864/// #[cfg(debug_assertions)]
865/// {
866/// // In debug mode, the validation fails because StrictFinitePolicy catches NaN.
867/// assert!(result.is_err());
868/// }
869///
870/// #[cfg(not(debug_assertions))]
871/// {
872/// // In release mode, the validation is skipped and always succeeds.
873/// assert!(result.is_ok());
874/// }
875/// ```
876pub struct DebugValidationPolicy<P: ValidationPolicy>(PhantomData<P>);
877
878impl<P: ValidationPolicy> ValidationPolicy for DebugValidationPolicy<P> {
879 type Value = P::Value;
880 type Error = P::Error;
881
882 #[inline(always)]
883 fn validate_ref(value: &Self::Value) -> Result<(), Self::Error> {
884 // In debug builds, this implementation delegates to the inner policy `P`.
885 #[cfg(debug_assertions)]
886 {
887 // Delegate to the inner policy's validation logic.
888 P::validate_ref(value)
889 }
890 #[cfg(not(debug_assertions))]
891 {
892 let _ = value; // Avoid unused variable warning in release mode.
893 // In release mode, this is a no-op.
894 // The validation is skipped, and we always return Ok.
895 // This allows the compiler to optimize away the validation logic entirely.
896 // This is useful for performance-critical code where validation is not needed in production.
897 // Note: The `value` is not used here, but we keep it to match the signature of `validate_ref`.
898 // This way, we can still call this method without needing to change the function signature.
899 // This is particularly useful when this policy is used in generic contexts
900 // where the `validate_ref` method is expected to take a reference to the value.
901 // The `PhantomData` ensures that the type system knows about `P` even
902 // though we don't use it directly in the release build.
903 // This is a common pattern in Rust to create zero-sized types that carry type information.
904 Ok(())
905 }
906 }
907}
908
909impl<P> ValidationPolicyReal for DebugValidationPolicy<P>
910where
911 P: ValidationPolicyReal,
912{
913 const PRECISION: u32 = P::PRECISION;
914}
915
916impl<P> ValidationPolicyComplex for DebugValidationPolicy<P>
917where
918 P: ValidationPolicyComplex,
919{
920 const PRECISION: u32 = P::PRECISION;
921}
922//------------------------------------------------------------------------------------------------
923
924/// Validates a value using the given policy, but only in debug builds.
925/// In release builds, this function is a no-op and should be optimized away.
926///
927/// # Panics
928///
929/// In debug builds, this function will panic if `P::validate_ref(value)` returns an `Err`.
930#[inline(always)]
931pub fn debug_validate<P: ValidationPolicy>(value: &P::Value, msg: &str) {
932 #[cfg(debug_assertions)]
933 {
934 if let Err(e) = P::validate_ref(value) {
935 panic!("Debug validation of {msg} value failed: {e}");
936 }
937 }
938 #[cfg(not(debug_assertions))]
939 {
940 // These are no-op in release builds
941 let _ = value;
942 let _ = msg;
943 }
944}
945//-------------------------------------------------------------
946
947//-------------------------------------------------------------
948/// A marker trait for validation policies that guarantee finite real values.
949///
950/// This trait serves as a compile-time marker to identify policies that ensure
951/// all validated real values are always finite (never NaN or infinite). It enables
952/// the implementation of full equality ([`Eq`]) and hashing ([`Hash`]) for
953/// validated types, allowing them to be used as keys in hash-based collections
954/// like [`HashMap`](std::collections::HashMap) and [`HashSet`](std::collections::HashSet).
955///
956/// ## Enabled Features
957///
958/// When a validation policy implements this trait, the corresponding validated types
959/// (like [`RealValidated<K>`](crate::RealValidated)) automatically gain:
960///
961/// - **Full Equality ([`Eq`])**: Equality becomes reflexive, symmetric, and transitive
962/// since NaN values (which violate these properties) are excluded.
963/// - **Hashing ([`Hash`])**: Combined with [`Eq`], this allows validated types to be
964/// used as keys in hash-based collections like [`HashMap`](std::collections::HashMap) and [`HashSet`](std::collections::HashSet).
965///
966/// ## Hashing Implementation Details
967///
968/// The [`Hash`] implementation for validated types:
969/// - Delegates to the underlying raw type's [`RawScalarTrait::compute_hash`] method
970/// - Handles IEEE 754 floating-point edge cases correctly (e.g., signed zeros)
971/// - Maintains the contract that `a == b` implies `hash(a) == hash(b)`
972/// - Works with both native `f64`/`Complex<f64>` and arbitrary-precision `rug` types
973///
974/// ## Design Decision: No Total Ordering
975///
976/// This trait intentionally does **not** enable total ordering ([`Ord`]) because:
977/// 1. The library's comparison functions use efficient reference-based [`PartialOrd`]
978/// 2. [`Max`](crate::functions::Max)/[`Min`](crate::functions::Min) traits provide better performance than value-based [`Ord`]
979/// 3. Mathematical operations work naturally with partial ordering semantics
980///
981/// ## Use Cases
982///
983/// This marker trait enables validated numerical types to be used in contexts that
984/// require [`Eq`] and [`Hash`]:
985///
986/// ```rust
987/// use num_valid::{RealNative64StrictFinite, functions::Max};
988/// use std::collections::{HashMap, HashSet};
989/// use try_create::TryNew;
990///
991/// // Use as HashMap keys (requires Eq + Hash)
992/// let mut scores = HashMap::new();
993/// let player1 = RealNative64StrictFinite::try_new(1.5).unwrap();
994/// scores.insert(player1, 100);
995///
996/// // Use in HashSet (requires Eq + Hash)
997/// let mut unique_values = HashSet::new();
998/// unique_values.insert(RealNative64StrictFinite::try_new(3.14).unwrap());
999///
1000/// // Comparison still works efficiently with PartialOrd
1001/// let a = RealNative64StrictFinite::try_new(1.0).unwrap();
1002/// let b = RealNative64StrictFinite::try_new(2.0).unwrap();
1003/// assert!(a < b); // Uses PartialOrd
1004/// assert_eq!(a.max(&b), &b); // Uses library's Max trait
1005/// ```
1006///
1007/// ## Hash Collision Considerations
1008///
1009/// The hashing implementation ensures:
1010/// - Consistent hashes for mathematically equal values
1011/// - Proper handling of floating-point edge cases
1012/// - Compatibility with both native and arbitrary-precision backends
1013/// - No hash collisions due to NaN values (which are excluded by design)
1014///
1015/// ## Currently Implemented By
1016///
1017/// - [`StrictFinitePolicy<T, P>`](StrictFinitePolicy): The primary policy that
1018/// rejects NaN, infinity, and subnormal values.
1019/// - [`DebugValidationPolicy<StrictFinitePolicy<T, P>>`](DebugValidationPolicy):
1020/// A wrapper that applies strict validation only in debug builds.
1021///
1022/// ## Safety and Correctness
1023///
1024/// This trait should only be implemented by validation policies that genuinely
1025/// guarantee finite values. Incorrect implementation could lead to hash collisions
1026/// or violated equality contracts in the methods that rely on this guarantee.
1027///
1028/// The trait is designed as a marker trait (no methods) to emphasize that it's
1029/// purely a compile-time contract rather than runtime functionality.
1030pub trait GuaranteesFiniteValues: ValidationPolicyReal {}
1031
1032/// Marker trait indicating that a validation policy guarantees finite complex values.
1033///
1034/// This trait serves as a compile-time indicator that a validation policy for
1035/// complex numbers ensures all validated values have finite real and imaginary components
1036/// (i.e., neither component is NaN or infinity). This guarantee enables the
1037/// implementation of [`Eq`] and [`Hash`] for [`ComplexValidated`] types, allowing
1038/// them to be used as keys in hash-based collections like [`HashMap`](std::collections::HashMap)
1039/// and [`HashSet`](std::collections::HashSet).
1040///
1041/// ## Purpose
1042///
1043/// Complex numbers can have NaN or infinite components, making equality comparison
1044/// problematic (NaN != NaN). By guaranteeing finiteness at the policy level,
1045/// this trait allows:
1046/// - Full equality ([`Eq`]) implementation for complex validated types
1047/// - Consistent hashing ([`Hash`]) for use in hash-based collections
1048/// - Compile-time verification of these properties
1049///
1050/// ## Design Rationale
1051///
1052/// This trait works in tandem with [`GuaranteesFiniteValues`] for real numbers:
1053/// - [`GuaranteesFiniteValues`]: For real number policies
1054/// - [`GuaranteesFiniteComplexValues`]: For complex number policies
1055///
1056/// This separation allows different validation strategies for real and complex
1057/// components while maintaining type safety.
1058///
1059/// ## Currently Implemented By
1060///
1061/// - [`StrictFinitePolicy<T, P>`](StrictFinitePolicy): Validates both real and imaginary
1062/// components for finiteness and non-subnormal values.
1063/// - [`DebugValidationPolicy<StrictFinitePolicy<T, P>>`](DebugValidationPolicy):
1064/// Applies strict validation only in debug builds.
1065///
1066/// ## Safety and Correctness
1067///
1068/// This trait should only be implemented by validation policies that genuinely
1069/// guarantee finite values in both components. Incorrect implementation could lead
1070/// to hash collisions or violated equality contracts.
1071pub trait GuaranteesFiniteComplexValues: ValidationPolicyComplex {}
1072
1073// Implement the marker for `StrictFinitePolicy` for any real scalar type.
1074impl<RawReal: RawRealTrait, const PRECISION: u32> GuaranteesFiniteValues
1075 for StrictFinitePolicy<RawReal, PRECISION>
1076where
1077 // This bound is necessary to satisfy the supertrait requirements of GuaranteesFiniteValues
1078 StrictFinitePolicy<RawReal, PRECISION>: ValidationPolicyReal,
1079{
1080}
1081
1082// Implement the marker for `DebugValidationPolicy<StrictFinitePolicy>` for any real scalar type.
1083impl<RawReal: RawRealTrait, const PRECISION: u32> GuaranteesFiniteValues
1084 for DebugValidationPolicy<StrictFinitePolicy<RawReal, PRECISION>>
1085where
1086 // This bound is necessary to satisfy the supertrait requirements of GuaranteesFiniteValues
1087 StrictFinitePolicy<RawReal, PRECISION>: ValidationPolicyReal,
1088{
1089}
1090
1091// Implement the marker for `StrictFinitePolicy` for any complex scalar type.
1092impl<RawComplex: RawComplexTrait, const PRECISION: u32> GuaranteesFiniteComplexValues
1093 for StrictFinitePolicy<RawComplex, PRECISION>
1094where
1095 // This bound is necessary to satisfy the supertrait requirements of GuaranteesFiniteComplexValues
1096 StrictFinitePolicy<RawComplex, PRECISION>: ValidationPolicyComplex,
1097{
1098}
1099
1100// Implement the marker for `DebugValidationPolicy<StrictFinitePolicy>` for any complex scalar type.
1101impl<RawComplex: RawComplexTrait, const PRECISION: u32> GuaranteesFiniteComplexValues
1102 for DebugValidationPolicy<StrictFinitePolicy<RawComplex, PRECISION>>
1103where
1104 // This bound is necessary to satisfy the supertrait requirements of GuaranteesFiniteComplexValues
1105 StrictFinitePolicy<RawComplex, PRECISION>: ValidationPolicyComplex,
1106{
1107}
1108//-------------------------------------------------------------
1109
1110//-------------------------------------------------------------
1111/// Implements total equality (`Eq`) for `RealValidated` types.
1112///
1113/// This implementation is only available when the kernel's `RealPolicy`
1114/// implements the `GuaranteesFiniteValues` marker trait. This ensures that
1115/// the underlying value is never `NaN`, making the equality relation reflexive,
1116/// symmetric, and transitive, as required by `Eq`.
1117impl<K: NumKernel> Eq for RealValidated<K>
1118where
1119 K::RealPolicy: GuaranteesFiniteValues,
1120{
1121 // `Eq` is a marker trait and requires no methods. Its presence simply
1122 // asserts that `PartialEq`'s `eq` method forms an equivalence relation.
1123}
1124
1125/// Implements total equality (`Eq`) for `ComplexValidated` types.
1126///
1127/// This implementation is only available when the kernel's `ComplexPolicy`
1128/// implements the `GuaranteesFiniteComplexValues` marker trait. This ensures that
1129/// the underlying complex number components are never `NaN`, making the equality
1130/// relation reflexive, symmetric, and transitive, as required by `Eq`.
1131impl<K: NumKernel> Eq for ComplexValidated<K>
1132where
1133 K::ComplexPolicy: GuaranteesFiniteComplexValues,
1134{
1135 // `Eq` is a marker trait and requires no methods. Its presence simply
1136 // asserts that `PartialEq`'s `eq` method forms an equivalence relation.
1137}
1138//-------------------------------------------------------------
1139
1140//-------------------------------------------------------------
1141/// Implements [`Hash`] for validated real number types with finite value guarantees.
1142///
1143/// This implementation is only available when the kernel's `RealPolicy`
1144/// implements the [`GuaranteesFiniteValues`] marker trait. This ensures that
1145/// the underlying value is never `NaN`, making hash values consistent and
1146/// allowing these types to be used as keys in hash-based collections.
1147///
1148/// ## Hashing Strategy
1149///
1150/// The implementation delegates to the [`RawScalarTrait::compute_hash()`] method provided by the
1151/// underlying raw scalar type (via [`RawScalarTrait`]). This method:
1152///
1153/// - For `f64` types: Uses IEEE 754 bit representation via `f64::to_bits()`
1154/// - For `rug::Float` types: Uses integer representation via `rug::Float::to_integer()`
1155/// - Handles signed zeros correctly (both `+0.0` and `-0.0` produce the same hash)
1156/// - Maintains the hash contract: `a == b` implies `hash(a) == hash(b)`
1157///
1158/// ## Safety and Correctness
1159///
1160/// This implementation is safe because:
1161/// - [`GuaranteesFiniteValues`] ensures no `NaN` values can exist
1162/// - The [`Eq`] implementation is well-defined for finite values
1163/// - Signed zeros are handled consistently in both equality and hashing
1164///
1165/// ## Usage Example
1166///
1167/// ```rust
1168/// use num_valid::RealNative64StrictFinite;
1169/// use std::collections::HashMap;
1170/// use try_create::TryNew;
1171///
1172/// let mut map = HashMap::new();
1173/// let key = RealNative64StrictFinite::try_new(3.14).unwrap();
1174/// map.insert(key, "pi approximation");
1175/// assert_eq!(map.len(), 1);
1176/// ```
1177impl<K: NumKernel> Hash for RealValidated<K>
1178where
1179 K::RealPolicy: GuaranteesFiniteValues,
1180{
1181 #[inline(always)]
1182 fn hash<H: Hasher>(&self, state: &mut H) {
1183 self.value.compute_hash(state);
1184 }
1185}
1186
1187/// Implements [`Hash`] for validated complex number types with finite value guarantees.
1188///
1189/// This implementation is only available when the kernel's `ComplexPolicy`
1190/// implements the [`GuaranteesFiniteComplexValues`] marker trait. This ensures that
1191/// both the real and imaginary parts are never `NaN`, making hash values
1192/// consistent and allowing these types to be used as keys in hash-based collections.
1193///
1194/// ## Hashing Strategy
1195///
1196/// The implementation delegates to the [`RawScalarTrait::compute_hash()`] method provided by the
1197/// underlying raw complex type (via [`RawComplexTrait`]). This method:
1198///
1199/// - For `Complex<f64>` types: Hashes real and imaginary parts sequentially using `f64::to_bits()`
1200/// - For `rug::Complex` types: Hashes real and imaginary components using their `compute_hash()` methods
1201/// - Handles signed zeros correctly in both real and imaginary parts
1202/// - Maintains the hash contract: `a == b` implies `hash(a) == hash(b)`
1203///
1204/// ## Safety and Correctness
1205///
1206/// This implementation is safe because:
1207/// - [`GuaranteesFiniteComplexValues`] ensures no `NaN` values can exist in either component
1208/// - The [`Eq`] implementation is well-defined for finite complex values
1209/// - Signed zeros are handled consistently in both equality and hashing
1210/// - Complex numbers with different component values produce different hashes
1211///
1212/// ## Usage Example
1213///
1214/// ```rust
1215/// use num_valid::ComplexNative64StrictFinite;
1216/// use num::Complex;
1217/// use std::collections::HashMap;
1218/// use try_create::TryNew;
1219///
1220/// let mut map = HashMap::new();
1221/// let key = ComplexNative64StrictFinite::try_new(Complex::new(1.0, 2.0)).unwrap();
1222/// map.insert(key, "complex value");
1223/// assert_eq!(map.len(), 1);
1224/// ```
1225impl<K: NumKernel> Hash for ComplexValidated<K>
1226where
1227 K::ComplexPolicy: GuaranteesFiniteComplexValues,
1228{
1229 #[inline(always)]
1230 fn hash<H: Hasher>(&self, state: &mut H) {
1231 self.value.compute_hash(state);
1232 }
1233}
1234//-------------------------------------------------------------
1235
1236//-------------------------------------------------------------
1237#[cfg(test)]
1238mod tests {
1239 use super::*;
1240
1241 #[cfg(feature = "rug")]
1242 use crate::{ComplexRugStrictFinite, RealRugStrictFinite};
1243
1244 mod fp_checks {
1245 use super::*;
1246 use num::Complex;
1247
1248 mod native64 {
1249 use super::*;
1250
1251 mod real {
1252 use super::*;
1253
1254 #[test]
1255 fn test_is_finite() {
1256 assert!(<f64 as FpChecks>::is_finite(&1.0));
1257 assert!(!<f64 as FpChecks>::is_finite(&f64::INFINITY));
1258 assert!(!<f64 as FpChecks>::is_finite(&f64::NEG_INFINITY));
1259 assert!(!<f64 as FpChecks>::is_finite(&f64::NAN));
1260 }
1261
1262 #[test]
1263 fn test_is_infinite() {
1264 assert!(!<f64 as FpChecks>::is_infinite(&1.0));
1265 assert!(<f64 as FpChecks>::is_infinite(&f64::INFINITY));
1266 assert!(<f64 as FpChecks>::is_infinite(&f64::NEG_INFINITY));
1267 assert!(!<f64 as FpChecks>::is_infinite(&f64::NAN));
1268 }
1269
1270 #[test]
1271 fn test_is_nan() {
1272 assert!(!<f64 as FpChecks>::is_nan(&1.0));
1273 assert!(!<f64 as FpChecks>::is_nan(&f64::INFINITY));
1274 assert!(!<f64 as FpChecks>::is_nan(&f64::NEG_INFINITY));
1275 assert!(<f64 as FpChecks>::is_nan(&f64::NAN));
1276 }
1277
1278 #[test]
1279 fn test_is_normal() {
1280 assert!(<f64 as FpChecks>::is_normal(&1.0));
1281 assert!(!<f64 as FpChecks>::is_normal(&0.0));
1282 assert!(!<f64 as FpChecks>::is_normal(&f64::INFINITY));
1283 assert!(!<f64 as FpChecks>::is_normal(&f64::NEG_INFINITY));
1284 assert!(!<f64 as FpChecks>::is_normal(&f64::NAN));
1285 assert!(<f64 as FpChecks>::is_normal(&f64::MIN_POSITIVE));
1286 }
1287 }
1288
1289 mod complex {
1290 use super::*;
1291
1292 #[test]
1293 fn test_is_finite() {
1294 assert!(<Complex<f64> as FpChecks>::is_finite(&Complex::new(
1295 1.0, 1.0
1296 )));
1297 assert!(!<Complex<f64> as FpChecks>::is_finite(&Complex::new(
1298 f64::INFINITY,
1299 1.0
1300 )));
1301 assert!(!<Complex<f64> as FpChecks>::is_finite(&Complex::new(
1302 1.0,
1303 f64::NAN
1304 )));
1305 }
1306
1307 #[test]
1308 fn test_is_infinite() {
1309 assert!(!<Complex<f64> as FpChecks>::is_infinite(&Complex::new(
1310 1.0, 1.0
1311 )));
1312 assert!(<Complex<f64> as FpChecks>::is_infinite(&Complex::new(
1313 f64::INFINITY,
1314 1.0
1315 )));
1316 assert!(!<Complex<f64> as FpChecks>::is_infinite(&Complex::new(
1317 1.0,
1318 f64::NAN
1319 )));
1320 }
1321
1322 #[test]
1323 fn test_is_nan() {
1324 assert!(!<Complex<f64> as FpChecks>::is_nan(&Complex::new(1.0, 1.0)));
1325 assert!(<Complex<f64> as FpChecks>::is_nan(&Complex::new(
1326 f64::NAN,
1327 1.0
1328 )));
1329 assert!(<Complex<f64> as FpChecks>::is_nan(&Complex::new(
1330 1.0,
1331 f64::NAN
1332 )));
1333 }
1334
1335 #[test]
1336 fn test_is_normal() {
1337 assert!(<Complex<f64> as FpChecks>::is_normal(&Complex::new(
1338 1.0, 1.0
1339 )));
1340 assert!(!<Complex<f64> as FpChecks>::is_normal(&Complex::new(
1341 0.0, 0.0
1342 )));
1343 assert!(!<Complex<f64> as FpChecks>::is_normal(&Complex::new(
1344 f64::INFINITY,
1345 1.0
1346 )));
1347 assert!(!<Complex<f64> as FpChecks>::is_normal(&Complex::new(
1348 1.0,
1349 f64::NAN
1350 )));
1351 }
1352 }
1353 }
1354 }
1355
1356 mod strict_finite_policy {
1357 use super::*;
1358
1359 mod raw_types {
1360 use super::*;
1361
1362 mod native64 {
1363 use super::*;
1364
1365 mod real {
1366 use super::*;
1367
1368 #[test]
1369 fn f64_infinity() {
1370 let result = StrictFinitePolicy::<f64, 53>::validate(f64::INFINITY);
1371 assert!(matches!(
1372 result,
1373 Err(ErrorsValidationRawReal::IsPosInfinity { .. })
1374 ));
1375
1376 let result = StrictFinitePolicy::<f64, 53>::validate_ref(&f64::INFINITY);
1377 assert!(matches!(
1378 result,
1379 Err(ErrorsValidationRawReal::IsPosInfinity { .. })
1380 ));
1381
1382 let result = StrictFinitePolicy::<f64, 53>::validate(f64::NEG_INFINITY);
1383 assert!(matches!(
1384 result,
1385 Err(ErrorsValidationRawReal::IsNegInfinity { .. })
1386 ));
1387
1388 let result =
1389 StrictFinitePolicy::<f64, 53>::validate_ref(&f64::NEG_INFINITY);
1390 assert!(matches!(
1391 result,
1392 Err(ErrorsValidationRawReal::IsNegInfinity { .. })
1393 ));
1394 }
1395
1396 #[test]
1397 fn f64_nan() {
1398 let result = StrictFinitePolicy::<f64, 53>::validate(f64::NAN);
1399 assert!(matches!(result, Err(ErrorsValidationRawReal::IsNaN { .. })));
1400
1401 let result = StrictFinitePolicy::<f64, 53>::validate_ref(&f64::NAN);
1402 assert!(matches!(result, Err(ErrorsValidationRawReal::IsNaN { .. })));
1403 }
1404
1405 #[test]
1406 fn f64_subnormal() {
1407 // let subnormal = f64::MIN_POSITIVE / 2.0;
1408 let subnormal = f64::from_bits(1); // the smallest positive subnormal
1409
1410 let result = StrictFinitePolicy::<f64, 53>::validate(subnormal);
1411 assert!(matches!(
1412 result,
1413 Err(ErrorsValidationRawReal::<f64>::IsSubnormal { .. })
1414 ));
1415
1416 let result = StrictFinitePolicy::<f64, 53>::validate_ref(&subnormal);
1417 assert!(matches!(
1418 result,
1419 Err(ErrorsValidationRawReal::<f64>::IsSubnormal { .. })
1420 ));
1421 }
1422
1423 #[test]
1424 fn f64_zero() {
1425 let value = 0.0;
1426 let result = StrictFinitePolicy::<f64, 53>::validate(value);
1427 assert!(matches!(result, Ok(0.0)));
1428 }
1429
1430 #[test]
1431 fn f64_normal() {
1432 let value = 1.0;
1433 let result = StrictFinitePolicy::<f64, 53>::validate(value);
1434 assert!(matches!(result, Ok(1.0)));
1435
1436 let result = StrictFinitePolicy::<f64, 53>::validate_ref(&value);
1437 assert!(matches!(result, Ok(())));
1438 }
1439 }
1440
1441 mod complex {
1442 use super::*;
1443 use num::Complex;
1444
1445 #[test]
1446 fn test_invalid_both_parts() {
1447 let z = Complex::new(f64::NAN, f64::NEG_INFINITY);
1448 let err =
1449 StrictFinitePolicy::<Complex<f64>, 53>::validate_ref(&z).unwrap_err();
1450 matches!(err, ErrorsValidationRawComplex::InvalidBothParts { .. });
1451 }
1452
1453 #[test]
1454 fn complex_f64_invalid_real_part() {
1455 let value = Complex::new(f64::INFINITY, 1.0);
1456 let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
1457 assert!(matches!(
1458 result,
1459 Err(ErrorsValidationRawComplex::InvalidRealPart {
1460 source: ErrorsValidationRawReal::IsPosInfinity { .. },
1461 ..
1462 })
1463 ));
1464
1465 let value = Complex::new(f64::NAN, 1.0);
1466 let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
1467 assert!(matches!(
1468 result,
1469 Err(ErrorsValidationRawComplex::InvalidRealPart {
1470 source: ErrorsValidationRawReal::IsNaN { .. },
1471 ..
1472 })
1473 ));
1474
1475 let value = Complex::new(f64::MIN_POSITIVE / 2.0, 1.0);
1476 let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
1477 assert!(matches!(
1478 result,
1479 Err(ErrorsValidationRawComplex::InvalidRealPart {
1480 source: ErrorsValidationRawReal::IsSubnormal { .. },
1481 ..
1482 })
1483 ));
1484 }
1485
1486 #[test]
1487 fn complex_f64_invalid_imaginary_part() {
1488 let value = Complex::new(1.0, f64::INFINITY);
1489 let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
1490 assert!(matches!(
1491 result,
1492 Err(ErrorsValidationRawComplex::InvalidImaginaryPart {
1493 source: ErrorsValidationRawReal::IsPosInfinity { .. },
1494 ..
1495 })
1496 ));
1497
1498 let value = Complex::new(1.0, f64::NAN);
1499 let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
1500 assert!(matches!(
1501 result,
1502 Err(ErrorsValidationRawComplex::InvalidImaginaryPart {
1503 source: ErrorsValidationRawReal::IsNaN { .. },
1504 ..
1505 })
1506 ));
1507
1508 let value = Complex::new(1.0, f64::MIN_POSITIVE / 2.0);
1509 let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
1510 assert!(matches!(
1511 result,
1512 Err(ErrorsValidationRawComplex::InvalidImaginaryPart {
1513 source: ErrorsValidationRawReal::IsSubnormal { .. },
1514 ..
1515 })
1516 ));
1517 }
1518
1519 #[test]
1520 fn complex_f64_zero() {
1521 let value = Complex::new(0.0, 0.0);
1522 let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
1523 assert!(result.is_ok());
1524 }
1525
1526 #[test]
1527 fn complex_f64_normal_value() {
1528 let value = Complex::new(1.0, 1.0);
1529 let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
1530 assert!(result.is_ok());
1531 }
1532 }
1533 }
1534
1535 #[cfg(feature = "rug")]
1536 mod rug53 {
1537 use super::*;
1538
1539 const PRECISION: u32 = 53; // Default precision for rug tests
1540
1541 mod float {
1542 use super::*;
1543 use rug::Float;
1544
1545 #[test]
1546 fn rug_float_valid() {
1547 let x = Float::with_val(PRECISION, 1.0);
1548 assert!(StrictFinitePolicy::<Float, PRECISION>::validate_ref(&x).is_ok());
1549 assert_eq!(
1550 StrictFinitePolicy::<Float, PRECISION>::validate(x.clone()).unwrap(),
1551 x
1552 );
1553 }
1554
1555 #[test]
1556 fn rug_float_nan() {
1557 let x = Float::with_val(PRECISION, Float::parse("nan").unwrap());
1558 let err =
1559 StrictFinitePolicy::<Float, PRECISION>::validate_ref(&x).unwrap_err();
1560 assert!(matches!(err, ErrorsValidationRawReal::IsNaN { .. }));
1561 let err = StrictFinitePolicy::<Float, PRECISION>::validate(x).unwrap_err();
1562 assert!(matches!(err, ErrorsValidationRawReal::IsNaN { .. }));
1563 }
1564
1565 #[test]
1566 fn rug_float_infinity() {
1567 use rug::Float;
1568
1569 let x = Float::with_val(PRECISION, Float::parse("inf").unwrap());
1570 let err =
1571 StrictFinitePolicy::<Float, PRECISION>::validate_ref(&x).unwrap_err();
1572 assert!(matches!(err, ErrorsValidationRawReal::IsPosInfinity { .. }));
1573 let err = StrictFinitePolicy::<Float, PRECISION>::validate(x).unwrap_err();
1574 assert!(matches!(err, ErrorsValidationRawReal::IsPosInfinity { .. }));
1575
1576 let x = Float::with_val(PRECISION, Float::parse("-inf").unwrap());
1577 let err =
1578 StrictFinitePolicy::<Float, PRECISION>::validate_ref(&x).unwrap_err();
1579 assert!(matches!(err, ErrorsValidationRawReal::IsNegInfinity { .. }));
1580 let err = StrictFinitePolicy::<Float, PRECISION>::validate(x).unwrap_err();
1581 assert!(matches!(err, ErrorsValidationRawReal::IsNegInfinity { .. }));
1582 }
1583
1584 #[test]
1585 fn rug_float_zero() {
1586 let value = Float::with_val(PRECISION, 0.0);
1587 let result = StrictFinitePolicy::<Float, PRECISION>::validate_ref(&value);
1588 assert!(result.is_ok());
1589 let result =
1590 StrictFinitePolicy::<Float, PRECISION>::validate(value.clone())
1591 .unwrap();
1592 assert_eq!(result, value);
1593 }
1594 }
1595
1596 mod complex {
1597 use super::*;
1598 use rug::{Complex, Float};
1599
1600 #[test]
1601 fn rug_complex_valid() {
1602 let z = Complex::with_val(PRECISION, (1.0, -2.0));
1603 assert!(StrictFinitePolicy::<Complex, PRECISION>::validate_ref(&z).is_ok());
1604 assert_eq!(
1605 StrictFinitePolicy::<Complex, PRECISION>::validate(z.clone()).unwrap(),
1606 z
1607 );
1608 }
1609
1610 #[test]
1611 fn rug_complex_real_invalid() {
1612 let nan = Float::with_val(PRECISION, Float::parse("nan").unwrap());
1613 let z = Complex::with_val(PRECISION, (&nan, 1.0));
1614 let err =
1615 StrictFinitePolicy::<Complex, PRECISION>::validate_ref(&z).unwrap_err();
1616 assert!(matches!(
1617 err,
1618 ErrorsValidationRawComplex::InvalidRealPart {
1619 source: ErrorsValidationRawReal::IsNaN { .. }
1620 }
1621 ));
1622 }
1623
1624 #[test]
1625 fn rug_complex_imag_invalid() {
1626 let inf = Float::with_val(PRECISION, Float::parse("inf").unwrap());
1627 let z = Complex::with_val(PRECISION, (1.0, &inf));
1628 let err =
1629 StrictFinitePolicy::<Complex, PRECISION>::validate_ref(&z).unwrap_err();
1630 assert!(matches!(
1631 err,
1632 ErrorsValidationRawComplex::InvalidImaginaryPart {
1633 source: ErrorsValidationRawReal::IsPosInfinity { .. }
1634 }
1635 ));
1636 }
1637
1638 #[test]
1639 fn rug_complex_both_invalid() {
1640 let nan = Float::with_val(PRECISION, Float::parse("nan").unwrap());
1641 let inf = Float::with_val(PRECISION, Float::parse("inf").unwrap());
1642 let z = Complex::with_val(PRECISION, (&nan, &inf));
1643 let err =
1644 StrictFinitePolicy::<Complex, PRECISION>::validate_ref(&z).unwrap_err();
1645 assert!(matches!(
1646 err,
1647 ErrorsValidationRawComplex::InvalidBothParts {
1648 real_error: ErrorsValidationRawReal::IsNaN { .. },
1649 imag_error: ErrorsValidationRawReal::IsPosInfinity { .. },
1650 ..
1651 }
1652 ));
1653 }
1654
1655 #[test]
1656 fn rug_complex_zero() {
1657 let zero = Complex::with_val(
1658 PRECISION,
1659 (
1660 Float::with_val(PRECISION, 0.0),
1661 Float::with_val(PRECISION, 0.0),
1662 ),
1663 );
1664 let result = StrictFinitePolicy::<Complex, PRECISION>::validate_ref(&zero);
1665 assert!(result.is_ok());
1666 assert_eq!(
1667 StrictFinitePolicy::<Complex, PRECISION>::validate(zero.clone())
1668 .unwrap(),
1669 zero
1670 );
1671 }
1672 }
1673 }
1674 }
1675
1676 // Module for testing the wrapper types that implement TryNew
1677 #[cfg(feature = "rug")]
1678 mod wrapper_types {
1679 use super::*;
1680
1681 mod rug_wrappers {
1682 use super::*;
1683 use crate::RealScalar;
1684 use num::One;
1685 use rug::{Complex as RugComplex, Float};
1686 use try_create::{IntoInner, TryNew};
1687
1688 const PRECISION: u32 = 53; // Default precision for rug tests
1689
1690 mod real {
1691 use super::*;
1692
1693 #[test]
1694 fn try_new_real_rug_correct_precision() {
1695 // Test RealRugStrictFinite with valid rug::Float and correct precision
1696 let val = Float::with_val(PRECISION, 1.0);
1697 let res = RealRugStrictFinite::<PRECISION>::try_new(val.clone());
1698 assert!(res.is_ok(),);
1699 assert_eq!(res.unwrap().into_inner(), val);
1700 }
1701
1702 #[test]
1703 fn try_new_real_rug_incorrect_precision() {
1704 // Test RealRugStrictFinite with valid rug::Float but incorrect precision
1705 let val_wrong_prec = Float::with_val(PRECISION + 10, 1.0); // Different precision
1706 let res = RealRugStrictFinite::<PRECISION>::try_new(val_wrong_prec);
1707 assert!(res.is_err());
1708 matches!(res, Err(ErrorsValidationRawReal::PrecisionMismatch { actual_precision, .. })
1709 if actual_precision == PRECISION + 10
1710 );
1711 }
1712
1713 #[test]
1714 fn try_new_real_rug_nan() {
1715 // Test RealRugStrictFinite with rug::Float NaN
1716 let val_nan = Float::with_val(PRECISION, Float::parse("nan").unwrap());
1717 let res = RealRugStrictFinite::<PRECISION>::try_new(val_nan);
1718 assert!(res.is_err());
1719 assert!(matches!(
1720 res,
1721 Err(ErrorsValidationRawReal::<Float>::IsNaN { .. })
1722 ));
1723 }
1724
1725 #[test]
1726 fn try_new_real_rug_infinity() {
1727 // Test RealRugStrictFinite with rug::Float positive infinity
1728 let val_inf = Float::with_val(PRECISION, Float::parse("inf").unwrap());
1729 let res_pos_inf = RealRugStrictFinite::<PRECISION>::try_new(val_inf);
1730 assert!(res_pos_inf.is_err());
1731 assert!(matches!(
1732 res_pos_inf,
1733 Err(ErrorsValidationRawReal::<Float>::IsPosInfinity { .. })
1734 ));
1735
1736 // Test RealRugStrictFinite with rug::Float negative infinity
1737 let val_neg_inf = Float::with_val(PRECISION, Float::parse("-inf").unwrap());
1738 let res_neg_inf = RealRugStrictFinite::<PRECISION>::try_new(val_neg_inf);
1739 assert!(res_neg_inf.is_err());
1740 assert!(matches!(
1741 res_neg_inf,
1742 Err(ErrorsValidationRawReal::<Float>::IsNegInfinity { .. })
1743 ));
1744 }
1745 }
1746
1747 mod complex {
1748 use super::*;
1749 use crate::ComplexScalarGetParts;
1750
1751 #[test]
1752 fn try_new_complex_rug_correct_precision() {
1753 // Test ComplexRugStrictFinite with valid rug::Complex and correct precision for both parts
1754 let val = RugComplex::with_val(PRECISION, (1.0, -2.0));
1755 let res = ComplexRugStrictFinite::<PRECISION>::try_new(val.clone());
1756 assert!(res.is_ok());
1757 assert_eq!(res.unwrap().into_inner(), val);
1758 }
1759
1760 /*
1761 #[test]
1762 #[ignore = "because the precision used to construct a rug::Complex override the precision of the real and imaginary parts"]
1763 fn try_new_complex_rug_precision_mismatch_real() {
1764 // Test ComplexRugStrictFinite where real part has incorrect precision
1765 let real_part_wrong_prec = Float::with_val(PRECISION - 1, 1.0);
1766 let imag_part_correct_prec = Float::with_val(PRECISION, -2.0);
1767 let val = RugComplex::with_val(
1768 PRECISION,
1769 (real_part_wrong_prec, imag_part_correct_prec),
1770 ); // Construct with Float parts
1771
1772 let res = ComplexRugStrictFinite::<PRECISION>::try_new(val);
1773 assert!(res.is_err());
1774 assert!(matches!(
1775 res,
1776 Err(ErrorsValidationRawComplex::InvalidRealPart {
1777 source: ErrorsValidationRawReal::PrecisionMismatch { .. }
1778 })
1779 ));
1780 }
1781
1782 #[test]
1783 #[ignore = "because the precision used to construct a rug::Complex override the precision of the real and imaginary parts"]
1784 fn try_new_complex_rug_precision_mismatch_imag() {
1785 // Test ComplexRugStrictFinite where imaginary part has incorrect precision
1786 let real_part_correct_prec = Float::with_val(PRECISION, 1.0);
1787 let imag_part_wrong_prec = Float::with_val(PRECISION - 1, -2.0);
1788 let val = RugComplex::with_val(
1789 PRECISION,
1790 (real_part_correct_prec, imag_part_wrong_prec),
1791 ); // Construct with Float parts
1792
1793 let res = ComplexRugStrictFinite::<PRECISION>::try_new(val);
1794 assert!(res.is_err());
1795 matches!(res, Err(ErrorsValidationRawComplex::InvalidImaginaryPart { source })
1796 if matches!(source, ErrorsValidationRawReal::PrecisionMismatch { .. })
1797 );
1798 }
1799 */
1800
1801 #[test]
1802 fn try_new_complex_rug_precision_mismatch_both() {
1803 // Test ComplexRugStrictFinite where both parts have incorrect precision
1804 let real_part_wrong_prec = Float::with_val(PRECISION - 1, 1.0);
1805 let imag_part_wrong_prec = Float::with_val(PRECISION - 1, -2.0);
1806 let val = RugComplex::with_val(
1807 PRECISION - 1,
1808 (real_part_wrong_prec, imag_part_wrong_prec),
1809 ); // Construct with Float parts
1810
1811 let res = ComplexRugStrictFinite::<PRECISION>::try_new(val);
1812 assert!(res.is_err());
1813 assert!(matches!(
1814 res,
1815 Err(ErrorsValidationRawComplex::InvalidBothParts {
1816 real_error: ErrorsValidationRawReal::PrecisionMismatch { .. },
1817 imag_error: ErrorsValidationRawReal::PrecisionMismatch { .. },
1818 ..
1819 })
1820 ));
1821 }
1822
1823 #[test]
1824 fn try_new_complex_rug_invalid_real_nan() {
1825 // Test ComplexRugStrictFinite where real part is NaN (value validation failure)
1826 let nan_real = Float::with_val(PRECISION, Float::parse("nan").unwrap());
1827 let imag_part = Float::with_val(PRECISION, 1.0);
1828 let val = RugComplex::with_val(PRECISION, (nan_real, imag_part));
1829
1830 let res = ComplexRugStrictFinite::<PRECISION>::try_new(val);
1831 assert!(res.is_err());
1832 assert!(matches!(
1833 res,
1834 Err(ErrorsValidationRawComplex::InvalidRealPart {
1835 source: ErrorsValidationRawReal::<Float>::IsNaN { .. },
1836 })
1837 ));
1838 }
1839
1840 #[test]
1841 fn try_new_complex_rug_invalid_imag_inf() {
1842 // Test ComplexRugStrictFinite where imaginary part is Infinity (value validation failure)
1843 let real_part = Float::with_val(PRECISION, 1.0);
1844 let inf_imag = Float::with_val(PRECISION, Float::parse("inf").unwrap());
1845 let val = RugComplex::with_val(PRECISION, (real_part, inf_imag));
1846
1847 let res = ComplexRugStrictFinite::<PRECISION>::try_new(val);
1848 assert!(res.is_err());
1849 assert!(matches!(
1850 res,
1851 Err(ErrorsValidationRawComplex::InvalidImaginaryPart {
1852 source: ErrorsValidationRawReal::<Float>::IsPosInfinity { .. }
1853 })
1854 ));
1855 }
1856
1857 #[test]
1858 fn try_new_complex_rug_invalid_both() {
1859 // Test ComplexRugStrictFinite where both parts are invalid (value validation failure)
1860 let nan_real = Float::with_val(PRECISION, Float::parse("nan").unwrap());
1861 let neg_inf_imag =
1862 Float::with_val(PRECISION, Float::parse("-inf").unwrap());
1863 let val = RugComplex::with_val(PRECISION, (nan_real, neg_inf_imag));
1864
1865 let res = ComplexRugStrictFinite::<PRECISION>::try_new(val);
1866 assert!(res.is_err());
1867 assert!(matches!(
1868 res,
1869 Err(ErrorsValidationRawComplex::InvalidBothParts {
1870 real_error: ErrorsValidationRawReal::<Float>::IsNaN { .. },
1871 imag_error: ErrorsValidationRawReal::<Float>::IsNegInfinity { .. },
1872 ..
1873 })
1874 ));
1875 }
1876
1877 #[test]
1878 fn try_from_complex_f64_to_complex_rug() {
1879 // Test valid conversion
1880 let z = num::Complex::new(1., -2.);
1881 let z_rug = ComplexRugStrictFinite::<PRECISION>::try_from(z).unwrap();
1882 let expected_real = RealRugStrictFinite::<PRECISION>::one();
1883 let expected_imaginary =
1884 RealRugStrictFinite::<PRECISION>::try_from_f64(-2.).unwrap();
1885 assert_eq!(z_rug.real_part(), expected_real);
1886 assert_eq!(z_rug.imag_part(), expected_imaginary);
1887
1888 // Test invalid real part
1889 let z = num::Complex::new(f64::INFINITY, -2.0);
1890 let result = ComplexRugStrictFinite::<PRECISION>::try_from(z);
1891 assert!(result.is_err());
1892 assert!(matches!(
1893 result,
1894 Err(ErrorsValidationRawComplex::InvalidRealPart {
1895 source: ErrorsTryFromf64::Output {
1896 source: ErrorsValidationRawReal::IsPosInfinity { .. }
1897 },
1898 })
1899 ));
1900
1901 // Test invalid imaginary part
1902 let z = num::Complex::new(1.0, f64::NAN);
1903 let result = ComplexRugStrictFinite::<PRECISION>::try_from(z).unwrap_err();
1904 //println!("result = {result}");
1905 assert!(matches!(
1906 result,
1907 ErrorsValidationRawComplex::InvalidImaginaryPart {
1908 source: ErrorsTryFromf64::Output {
1909 source: ErrorsValidationRawReal::IsNaN { .. }
1910 },
1911 }
1912 ));
1913 }
1914 }
1915 }
1916 }
1917 }
1918
1919 mod conditional_validation_policy {
1920 use core::f64;
1921
1922 use super::*;
1923
1924 type F64StrictFinitePolicy = Native64RawRealStrictFinitePolicy;
1925
1926 // Define a policy that is strict in debug, and no-op in release.
1927 type MyConditionalPolicy = DebugValidationPolicy<F64StrictFinitePolicy>;
1928
1929 #[test]
1930 #[should_panic(expected = "Debug validation of input value failed: Value is NaN: NaN")]
1931 #[cfg(debug_assertions)]
1932 fn debug_validate_function() {
1933 // This should always succeed, regardless of build mode.
1934 let nan_value = f64::NAN;
1935 debug_validate::<F64StrictFinitePolicy>(&nan_value, "input");
1936 }
1937
1938 #[test]
1939 fn test_conditional_validation_on_valid_value() {
1940 let valid_value = 42.0;
1941 // This should always succeed, regardless of build mode.
1942 assert!(MyConditionalPolicy::validate(valid_value).is_ok());
1943 }
1944
1945 #[test]
1946 fn test_conditional_validation_on_invalid_value() {
1947 let invalid_value = f64::NAN;
1948 let result = MyConditionalPolicy::validate(invalid_value);
1949
1950 if cfg!(debug_assertions) {
1951 // In debug mode, we expect an error from StrictFinitePolicy.
1952 assert!(result.is_err(), "Expected validation to fail in debug mode");
1953 assert!(matches!(result, Err(ErrorsValidationRawReal::IsNaN { .. })));
1954 } else {
1955 // In release mode, we expect it to succeed (no-op).
1956 assert!(
1957 result.is_ok(),
1958 "Expected validation to succeed in release mode"
1959 );
1960 }
1961 }
1962 }
1963
1964 mod conditional_backtrace_tests {
1965 use super::*;
1966 use std::backtrace::BacktraceStatus;
1967
1968 type F64StrictFinitePolicy = Native64RawRealStrictFinitePolicy;
1969
1970 /// Tests that `capture_backtrace()` returns the correct backtrace status
1971 /// based on the `backtrace` feature flag.
1972 #[test]
1973 fn test_capture_backtrace_status() {
1974 let bt = capture_backtrace();
1975
1976 if cfg!(feature = "backtrace") {
1977 // With backtrace feature enabled, should capture a real backtrace
1978 assert_ne!(
1979 bt.status(),
1980 BacktraceStatus::Disabled,
1981 "Backtrace should not be disabled when 'backtrace' feature is enabled"
1982 );
1983 } else {
1984 // Without backtrace feature, should return a disabled backtrace
1985 assert_eq!(
1986 bt.status(),
1987 BacktraceStatus::Disabled,
1988 "Backtrace should be disabled when 'backtrace' feature is not enabled"
1989 );
1990 }
1991 }
1992
1993 /// Tests that `is_backtrace_enabled()` correctly reports the feature state.
1994 #[test]
1995 fn test_is_backtrace_enabled() {
1996 let enabled = is_backtrace_enabled();
1997
1998 if cfg!(feature = "backtrace") {
1999 assert!(
2000 enabled,
2001 "is_backtrace_enabled() should return true when feature is enabled"
2002 );
2003 } else {
2004 assert!(
2005 !enabled,
2006 "is_backtrace_enabled() should return false when feature is disabled"
2007 );
2008 }
2009 }
2010
2011 /// Tests that error types contain appropriate backtraces based on feature.
2012 #[test]
2013 fn test_nan_error_backtrace_status() {
2014 // Create an error by validating an invalid value
2015 let result = F64StrictFinitePolicy::validate(f64::NAN);
2016
2017 assert!(result.is_err());
2018
2019 if let Err(ErrorsValidationRawReal::IsNaN { backtrace, .. }) = result {
2020 if cfg!(feature = "backtrace") {
2021 assert_ne!(
2022 backtrace.status(),
2023 BacktraceStatus::Disabled,
2024 "Error backtrace should be captured when feature is enabled"
2025 );
2026 } else {
2027 assert_eq!(
2028 backtrace.status(),
2029 BacktraceStatus::Disabled,
2030 "Error backtrace should be disabled when feature is not enabled"
2031 );
2032 }
2033 } else {
2034 panic!("Expected IsNaN error");
2035 }
2036 }
2037
2038 /// Tests that subnormal error backtraces are correct.
2039 #[test]
2040 fn test_subnormal_error_backtrace() {
2041 let subnormal = f64::MIN_POSITIVE / 2.0;
2042 let result = F64StrictFinitePolicy::validate(subnormal);
2043
2044 assert!(result.is_err());
2045
2046 if let Err(ErrorsValidationRawReal::IsSubnormal { backtrace, .. }) = result {
2047 if cfg!(feature = "backtrace") {
2048 assert_ne!(
2049 backtrace.status(),
2050 BacktraceStatus::Disabled,
2051 "Subnormal error backtrace should be captured when feature is enabled"
2052 );
2053 } else {
2054 assert_eq!(
2055 backtrace.status(),
2056 BacktraceStatus::Disabled,
2057 "Subnormal error backtrace should be disabled when feature is not enabled"
2058 );
2059 }
2060 } else {
2061 panic!("Expected IsSubnormal error");
2062 }
2063 }
2064
2065 /// Tests that infinity error backtraces are correct.
2066 #[test]
2067 fn test_infinity_error_backtrace() {
2068 let result = F64StrictFinitePolicy::validate(f64::INFINITY);
2069
2070 assert!(result.is_err());
2071
2072 if let Err(ErrorsValidationRawReal::IsPosInfinity { backtrace, .. }) = result {
2073 if cfg!(feature = "backtrace") {
2074 assert_ne!(
2075 backtrace.status(),
2076 BacktraceStatus::Disabled,
2077 "Infinity error backtrace should be captured when feature is enabled"
2078 );
2079 } else {
2080 assert_eq!(
2081 backtrace.status(),
2082 BacktraceStatus::Disabled,
2083 "Infinity error backtrace should be disabled when feature is not enabled"
2084 );
2085 }
2086 } else {
2087 panic!("Expected IsPosInfinity error");
2088 }
2089 }
2090
2091 /// Tests that negative infinity error backtraces are correct.
2092 #[test]
2093 fn test_neg_infinity_error_backtrace() {
2094 let result = F64StrictFinitePolicy::validate(f64::NEG_INFINITY);
2095
2096 assert!(result.is_err());
2097
2098 if let Err(ErrorsValidationRawReal::IsNegInfinity { backtrace, .. }) = result {
2099 if cfg!(feature = "backtrace") {
2100 assert_ne!(
2101 backtrace.status(),
2102 BacktraceStatus::Disabled,
2103 "NegInfinity error backtrace should be captured when feature is enabled"
2104 );
2105 } else {
2106 assert_eq!(
2107 backtrace.status(),
2108 BacktraceStatus::Disabled,
2109 "NegInfinity error backtrace should be disabled when feature is not enabled"
2110 );
2111 }
2112 } else {
2113 panic!("Expected IsNegInfinity error");
2114 }
2115 }
2116 }
2117}