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