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//! - **Implementations**: Validation logic for [`StrictFinitePolicy`] and implementations of
23//! [`FpChecks`] are provided for `f64`, `num::Complex<f64>`. When the "rug" feature is enabled,
24//! 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
25//! `RealRugStrictFinite` and `ComplexRugStrictFinite` (which utilize these underlying `rug` types).
26//!
27//! The goal of this module is to ensure numerical stability and correctness by allowing
28//! functions to easily validate their inputs and outputs according to defined policies,
29//! and to provide a common interface for basic floating-point characteristic checks.
30//!
31//! ## Example: Using [`StrictFinitePolicy`]
32//!
33//! ```rust
34//! use num_valid::validation::{StrictFinitePolicy, ErrorsValidationRawReal};
35//! use try_create::ValidationPolicy; // For the validate method
36//!
37//! let finite_val = 10.0f64;
38//! let nan_val = f64::NAN;
39//! let subnormal_val = f64::from_bits(1); // Smallest positive subnormal f64
40//!
41//! assert!(StrictFinitePolicy::<f64, 53>::validate(finite_val).is_ok());
42//!
43//! let nan_validation_result = StrictFinitePolicy::<f64, 53>::validate(nan_val);
44//! assert!(nan_validation_result.is_err());
45//! if let Err(ErrorsValidationRawReal::IsNaN { value, .. }) = nan_validation_result {
46//! assert!(value.is_nan());
47//! } else {
48//! panic!("Expected NaN error for f64::NAN");
49//! }
50//!
51//! let subnormal_validation_result = StrictFinitePolicy::<f64, 53>::validate(subnormal_val);
52//! assert!(subnormal_validation_result.is_err());
53//! if let Err(ErrorsValidationRawReal::IsSubnormal { value, .. }) = subnormal_validation_result {
54//! assert_eq!(value, subnormal_val);
55//! } else {
56//! panic!("Expected Subnormal error for subnormal f64");
57//! }
58//! ```
59//!
60//! ## Example: Using [`FpChecks`]
61//!
62//! ```rust
63//! use num_valid::validation::FpChecks;
64//! use num::Complex;
65//!
66//! let val_f64 = 1.0f64;
67//! let nan_f64 = f64::NAN;
68//! let inf_f64 = f64::INFINITY;
69//!
70//! assert!(val_f64.is_finite());
71//! assert!(val_f64.is_normal());
72//! assert!(nan_f64.is_nan());
73//! assert!(!nan_f64.is_finite());
74//! assert!(inf_f64.is_infinite());
75//!
76//! let val_complex = Complex::new(1.0, 2.0);
77//! let nan_complex_real = Complex::new(f64::NAN, 2.0);
78//! let inf_complex_imag = Complex::new(1.0, f64::NEG_INFINITY);
79//!
80//! assert!(val_complex.is_finite());
81//! assert!(val_complex.is_normal());
82//! assert!(nan_complex_real.is_nan());
83//! assert!(!nan_complex_real.is_finite());
84//! assert!(inf_complex_imag.is_infinite());
85//! assert!(!inf_complex_imag.is_finite());
86//! assert!(!inf_complex_imag.is_nan());
87//! ```
88
89use crate::kernels::{RawComplexTrait, RawRealTrait, RawScalarTrait};
90use duplicate::duplicate_item;
91use num::Complex;
92use std::{backtrace::Backtrace, fmt::Display, marker::PhantomData, num::FpCategory};
93use thiserror::Error;
94use try_create::ValidationPolicy;
95
96/// A marker for policies that apply to real types.
97///
98/// This trait refines the generic [`ValidationPolicy`] for types that implement
99/// [`RawRealTrait`]. It adds two key constraints:
100///
101/// 1. It associates a `const PRECISION` with the policy, essential for backends
102/// like `rug`.
103/// 2. It enforces that the policy's `Error` type must be the same as the canonical
104/// `ValidationErrors` type defined on the raw scalar itself. This architectural
105/// choice ensures consistent error handling throughout the library.
106pub trait ValidationPolicyReal:
107 ValidationPolicy<
108 Value: RawRealTrait,
109 Error = <<Self as ValidationPolicy>::Value as RawScalarTrait>::ValidationErrors,
110 >
111{
112 const PRECISION: u32;
113}
114
115/// A marker for policies that apply to complex types.
116///
117/// This trait refines the generic [`ValidationPolicy`] for types that implement
118/// [`RawComplexTrait`]. It adds two key constraints:
119///
120/// 1. It associates a `const PRECISION` with the policy, essential for backends
121/// like `rug`.
122/// 2. It enforces that the policy's `Error` type must be the same as the canonical
123/// `ValidationErrors` type defined on the raw scalar itself. This architectural
124/// choice ensures consistent error handling throughout the library.
125pub trait ValidationPolicyComplex:
126 ValidationPolicy<
127 Value: RawComplexTrait,
128 Error = <<Self as ValidationPolicy>::Value as RawScalarTrait>::ValidationErrors,
129 >
130{
131 const PRECISION: u32;
132}
133
134//--------------------------------------------------------------------------------------------------
135// Error types
136//--------------------------------------------------------------------------------------------------
137
138//--------------------------------------------
139// Errors for raw real number validation
140//--------------------------------------------
141/// Errors that can occur during the validation of a raw real number.
142///
143/// This enum is generic over `RawReal`, which is typically `f64` or [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html).
144/// It covers common validation failures like non-finite values, subnormality, infinity, and precision mismatch.
145#[derive(Debug, Error)]
146pub enum ErrorsValidationRawReal<RawReal: std::fmt::Debug + Display + Clone> {
147 /// The value is subnormal.
148 #[error("Value is subnormal: {value}")]
149 IsSubnormal {
150 /// The subnormal value that failed validation.
151 value: RawReal,
152
153 /// A captured backtrace for debugging purposes.
154 backtrace: Backtrace,
155 },
156
157 /// The value is NaN (Not a Number).
158 #[error("Value is NaN: {value}")]
159 IsNaN {
160 /// The NaN value that failed validation.
161 value: RawReal,
162
163 /// A captured backtrace for debugging purposes.
164 backtrace: Backtrace,
165 },
166
167 /// The value is positive infinity.
168 #[error("Value is positive infinity")]
169 IsPosInfinity {
170 /// The positive infinity value that failed validation.
171 backtrace: Backtrace,
172 },
173
174 /// The value is negative infinity.
175 #[error("Value is negative infinity")]
176 IsNegInfinity {
177 /// The negative infinity value that failed validation.
178 backtrace: Backtrace,
179 },
180
181 /// The precision of the input does not match the requested precision.
182 #[error(
183 "precision mismatch: input real has precision {actual_precision}, \
184 but the requested precision is {requested_precision}"
185 )]
186 PrecisionMismatch {
187 /// The input value.
188 input_value: RawReal,
189
190 /// The precision of the input value.
191 actual_precision: u32,
192
193 /// The requested precision.
194 requested_precision: u32,
195
196 /// A captured backtrace for debugging purposes.
197 backtrace: Backtrace,
198 },
199 /*
200 // The following variants are placeholders for potential future validation policies
201 // and are not currently used by StrictFinitePolicy.
202
203 #[error("Value is not strictly positive: {value}")]
204 NotPositive {
205 value: RawReal,
206 backtrace: Backtrace,
207 },
208
209 #[error("Value is negative: {value}")]
210 Negative {
211 value: RawReal,
212 backtrace: Backtrace,
213 },
214
215 #[error("Value is zero: {value}")]
216 IsZero {
217 value: RawReal,
218 backtrace: Backtrace,
219 },
220
221 #[error("Value {value} is outside the allowed range [{min}, {max}]")]
222 OutOfRange {
223 value: RawReal,
224 min: RawReal,
225 max: RawReal,
226 backtrace: Backtrace,
227 },
228 */
229}
230
231//--------------------------------------------
232// Errors for complex number validation
233//--------------------------------------------
234/// Errors that can occur during the validation of a complex number.
235///
236/// This enum is generic over `ErrorValidationReal`, which is the error type returned
237/// by validating the real and imaginary parts of the complex number (e.g.,
238/// [`ErrorsValidationRawReal<f64>`](ErrorsValidationRawReal)).
239///
240/// It indicates whether the real part, the imaginary part, or both parts failed validation.
241#[derive(Debug, Error)]
242pub enum ErrorsValidationRawComplex<ErrorValidationReal: std::error::Error> {
243 /// The real part of the complex number failed validation.
244 #[error("Real part validation error: {source}")]
245 InvalidRealPart {
246 /// The underlying validation error for the real part.
247 #[source]
248 #[backtrace]
249 source: ErrorValidationReal,
250 },
251
252 /// The imaginary part of the complex number failed validation.
253 #[error("Imaginary part validation error: {source}")]
254 InvalidImaginaryPart {
255 /// The underlying validation error for the imaginary part.
256 #[source]
257 #[backtrace]
258 source: ErrorValidationReal,
259 },
260
261 /// Both the real and imaginary parts of the complex number failed validation.
262 #[error("Both real and imaginary parts are invalid: real={real_error}, imag={imag_error}")]
263 InvalidBothParts {
264 /// The validation error for the real part.
265 real_error: ErrorValidationReal,
266
267 /// The validation error for the imaginary part.
268 imag_error: ErrorValidationReal,
269 },
270}
271
272//-------------------------------------------------------------
273/// Errors that can occur when trying to convert an `f64` to another real-type scalar of type `RawReal`.
274///
275/// This enum distinguishes between:
276/// 1. Failures due to the `f64` not being exactly representable at the target `PRECISION`
277/// of the `RawReal` type.
278/// 2. General validation failures of the output `RawReal` (e.g., NaN, Infinity, subnormal).
279#[derive(Debug, Error)]
280pub enum ErrorsTryFromf64<RawReal: RawRealTrait> {
281 /// The input `f64` value is not representable exactly at the target `precision` using the output `RawReal` type.
282 #[error(
283 "the input f64 value ({value_in:?}) cannot be exactly represented with the specific precision ({precision:?}). Try to increase the precision."
284 )]
285 NonRepresentableExactly {
286 /// The `f64` value that could not be exactly represented.
287 value_in: f64,
288
289 /// The input `value_in` after the conversion to the type `RawReal` with the requested `precision`.
290 value_converted_from_f64: RawReal,
291
292 /// The precision that was requested.
293 precision: u32,
294
295 /// A captured backtrace for debugging purposes.
296 backtrace: Backtrace,
297 },
298
299 /// The output value failed validation.
300 #[error("the output value is invalid!")]
301 Output {
302 /// The underlying validation error for the `output` value.
303 #[from]
304 source: ErrorsValidationRawReal<RawReal>,
305 },
306}
307//-------------------------------------------------------------
308
309/// A validation policy that checks for strict finiteness.
310///
311/// 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:
312/// - Not NaN (Not a Number).
313/// - Not positive or negative Infinity.
314/// - Not subnormal (for `f64`).
315/// While [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html) maintains its specified precision rather than having distinct subnormal
316/// 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
317/// [`FpCategory::Subnormal`] (e.g., results of underflow that are tiny but not exactly zero).
318///
319/// For complex types (`ScalarType = `[`Complex<f64>`], `ScalarType = `[`rug::Complex`](https://docs.rs/rug/latest/rug/struct.Complex.html)), this policy applies
320/// the strict finiteness check to both the real and imaginary parts.
321///
322/// This struct is a Zero-Sized Type (ZST) and uses [`PhantomData`] to associate
323/// with the `ScalarType` it validates.
324pub struct StrictFinitePolicy<ScalarType: Sized, const PRECISION: u32>(PhantomData<ScalarType>);
325
326/// A type alias for a validation policy that enforces strict finiteness for the raw [`f64`] type.
327///
328/// This is a convenient alias for [`StrictFinitePolicy<f64, 53>`], configured specifically
329/// for Rust's native 64-bit floating-point number. It is used to validate that a given [`f64`]
330/// value is suitable for use within the library's validated types.
331///
332/// This policy ensures that a `f64` value is:
333/// - Not `NaN` (Not a Number).
334/// - Not positive or negative `Infinity`.
335/// - Not a subnormal number.
336///
337/// It is a fundamental building block for the native `f64` kernel, providing the validation
338/// logic for [`RealNative64StrictFinite`](crate::kernels::native64_validated::RealNative64StrictFinite).
339///
340/// # Example
341///
342/// ```rust
343/// use num_valid::validation::{Native64RawRealStrictFinitePolicy, ErrorsValidationRawReal};
344/// use try_create::ValidationPolicy;
345///
346/// // A valid finite number passes validation.
347/// assert!(Native64RawRealStrictFinitePolicy::validate(1.0).is_ok());
348///
349/// // NaN fails validation.
350/// let result_nan = Native64RawRealStrictFinitePolicy::validate(f64::NAN);
351/// assert!(matches!(result_nan, Err(ErrorsValidationRawReal::IsNaN { .. })));
352///
353/// // Infinity fails validation.
354/// let result_inf = Native64RawRealStrictFinitePolicy::validate(f64::INFINITY);
355/// assert!(matches!(result_inf, Err(ErrorsValidationRawReal::IsPosInfinity { .. })));
356/// ```
357pub type Native64RawRealStrictFinitePolicy = StrictFinitePolicy<f64, 53>;
358
359/// A type alias for a validation policy that enforces strict finiteness for the raw [`num::Complex<f64>`] type.
360///
361/// This is a convenient alias for [`StrictFinitePolicy<Complex<f64>, 53>`], configured for
362/// Rust's native 64-bit complex numbers. It is used to validate that a given [`num::Complex<f64>`]
363/// value is suitable for use within the library's validated types.
364///
365/// This policy applies the strict finiteness check (as defined by [`Native64RawRealStrictFinitePolicy`])
366/// to both the real and imaginary parts of the complex number. A [`num::Complex<f64>`] value is considered
367/// valid if and only if both of its components are finite (not `NaN`, `Infinity`, or subnormal).
368///
369/// It is a fundamental building block for the native `f64` kernel, providing the validation
370/// logic for [`ComplexNative64StrictFinite`](crate::kernels::native64_validated::ComplexNative64StrictFinite).
371///
372/// # Example
373///
374/// ```rust
375/// use num_valid::validation::{Native64RawComplexStrictFinitePolicy, ErrorsValidationRawComplex, ErrorsValidationRawReal};
376/// use try_create::ValidationPolicy;
377/// use num::Complex;
378///
379/// // A complex number with valid finite parts passes validation.
380/// let valid_complex = Complex::new(1.0, -2.0);
381/// assert!(Native64RawComplexStrictFinitePolicy::validate(valid_complex).is_ok());
382///
383/// // A complex number with NaN in the real part fails validation.
384/// let nan_complex = Complex::new(f64::NAN, 2.0);
385/// let result_nan = Native64RawComplexStrictFinitePolicy::validate(nan_complex);
386/// assert!(matches!(result_nan, Err(ErrorsValidationRawComplex::InvalidRealPart { .. })));
387///
388/// // A complex number with Infinity in the imaginary part fails validation.
389/// let inf_complex = Complex::new(1.0, f64::INFINITY);
390/// let result_inf = Native64RawComplexStrictFinitePolicy::validate(inf_complex);
391/// assert!(matches!(result_inf, Err(ErrorsValidationRawComplex::InvalidImaginaryPart { .. })));
392///
393/// // A complex number with invalid parts in both also fails.
394/// let both_invalid = Complex::new(f64::NAN, f64::INFINITY);
395/// let result_both = Native64RawComplexStrictFinitePolicy::validate(both_invalid);
396/// assert!(matches!(result_both, Err(ErrorsValidationRawComplex::InvalidBothParts { .. })));
397/// ```
398pub type Native64RawComplexStrictFinitePolicy = StrictFinitePolicy<Complex<f64>, 53>;
399
400/// Ensures the `f64` value is strictly finite.
401///
402/// This policy checks if the `f64` value is:
403/// - Not NaN (Not a Number).
404/// - Not positive or negative Infinity.
405/// - Not subnormal.
406///
407/// # Errors
408///
409/// Returns [`ErrorsValidationRawReal<f64>`](ErrorsValidationRawReal) if the value
410/// fails any of these checks.
411impl ValidationPolicy for Native64RawRealStrictFinitePolicy {
412 type Value = f64;
413 type Error = ErrorsValidationRawReal<f64>;
414
415 fn validate_ref(value: &f64) -> Result<(), Self::Error> {
416 match value.classify() {
417 FpCategory::Nan => Err(ErrorsValidationRawReal::IsNaN {
418 value: *value,
419 backtrace: Backtrace::force_capture(),
420 }),
421 FpCategory::Infinite => {
422 if value.is_sign_positive() {
423 Err(ErrorsValidationRawReal::IsPosInfinity {
424 backtrace: Backtrace::force_capture(),
425 })
426 } else {
427 Err(ErrorsValidationRawReal::IsNegInfinity {
428 backtrace: Backtrace::force_capture(),
429 })
430 }
431 }
432 FpCategory::Subnormal => Err(ErrorsValidationRawReal::IsSubnormal {
433 value: *value,
434 backtrace: Backtrace::force_capture(),
435 }),
436 FpCategory::Normal | FpCategory::Zero => Ok(()),
437 }
438 }
439}
440
441/// Validates a complex number by checking both its real and imaginary parts.
442///
443/// This function uses the provided real validation policy `P` to validate both parts.
444/// If both parts are valid, it returns `Ok(())`. If either part is invalid,
445/// it returns an appropriate error variant from the `ErrorsValidationRawComplex` enum.
446///
447/// # Errors
448/// Returns [`ErrorsValidationRawComplex<P::Error>`](ErrorsValidationRawComplex)
449/// if either the real part, the imaginary part, or both parts fail validation
450/// using the policy `P`.
451pub(crate) fn validate_complex<P: ValidationPolicyReal>(
452 real_part: &P::Value,
453 imag_part: &P::Value,
454) -> Result<(), ErrorsValidationRawComplex<P::Error>> {
455 let real_validation = P::validate_ref(real_part);
456 let imag_validation = P::validate_ref(imag_part);
457 match (real_validation, imag_validation) {
458 (Ok(()), Ok(())) => Ok(()),
459 (Ok(()), Err(imag_err)) => {
460 Err(ErrorsValidationRawComplex::InvalidImaginaryPart { source: imag_err })
461 }
462 (Err(real_err), Ok(())) => {
463 Err(ErrorsValidationRawComplex::InvalidRealPart { source: real_err })
464 }
465 (Err(real_err), Err(imag_err)) => Err(ErrorsValidationRawComplex::InvalidBothParts {
466 real_error: real_err,
467 imag_error: imag_err,
468 }),
469 }
470}
471
472/// Ensures both the real and imaginary parts of a `Complex<f64>` value are strictly finite.
473///
474/// This policy applies the [`StrictFinitePolicy<f64>`](StrictFinitePolicy) to both
475/// the real and imaginary components of the complex number.
476///
477/// # Errors
478///
479/// Returns [`ErrorsValidationRawComplex<ErrorsValidationRawReal<f64>>`](ErrorsValidationRawComplex)
480/// if either the real part, the imaginary part, or both parts fail the
481/// `StrictFinitePolicy<f64>` checks.
482impl ValidationPolicy for Native64RawComplexStrictFinitePolicy {
483 type Value = Complex<f64>;
484 type Error = ErrorsValidationRawComplex<ErrorsValidationRawReal<f64>>;
485
486 fn validate_ref(value: &Complex<f64>) -> Result<(), Self::Error> {
487 validate_complex::<Native64RawRealStrictFinitePolicy>(&value.re, &value.im)
488 }
489}
490
491impl<RawReal: RawRealTrait, const PRECISION: u32> ValidationPolicyReal
492 for StrictFinitePolicy<RawReal, PRECISION>
493where
494 StrictFinitePolicy<RawReal, PRECISION>:
495 ValidationPolicy<Value = RawReal, Error = <RawReal as RawScalarTrait>::ValidationErrors>,
496{
497 const PRECISION: u32 = PRECISION;
498}
499
500impl<RawComplex: RawComplexTrait, const PRECISION: u32> ValidationPolicyComplex
501 for StrictFinitePolicy<RawComplex, PRECISION>
502where
503 StrictFinitePolicy<RawComplex, PRECISION>: ValidationPolicy<
504 Value = RawComplex,
505 Error = <RawComplex as RawScalarTrait>::ValidationErrors,
506 >,
507{
508 const PRECISION: u32 = PRECISION;
509}
510
511//--------------------------------------------------------------------------------------------------
512// Rug Implementations (conditional compilation)
513//--------------------------------------------------------------------------------------------------
514
515#[cfg(feature = "rug")]
516pub(crate) mod rug_impls {
517 use super::*; // Imports items from the parent module (validation)
518
519 /// Ensures the [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html) value is strictly finite.
520 ///
521 /// This policy checks if the underlying [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html) value:
522 /// - Is not NaN (Not a Number).
523 /// - Is not positive nor negative Infinity.
524 /// - Has the specified precision (equal to `PRECISION`).
525 ///
526 /// Note: [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html) does not have a direct concept of "subnormal" in the same
527 /// way IEEE 754 floats do; it maintains its specified precision.
528 ///
529 /// # Errors
530 ///
531 /// Returns [`ErrorsValidationRawReal<rug::Float>`](ErrorsValidationRawReal)
532 /// if the value fails any of these checks.
533 impl<const PRECISION: u32> ValidationPolicy for StrictFinitePolicy<rug::Float, PRECISION> {
534 type Value = rug::Float;
535 type Error = ErrorsValidationRawReal<rug::Float>;
536
537 fn validate_ref(value: &rug::Float) -> Result<(), Self::Error> {
538 let actual_precision = value.prec();
539 if actual_precision == PRECISION {
540 // rug::Float uses std::num::FpCategory via its own classify method
541 match value.classify() {
542 FpCategory::Infinite => {
543 if value.is_sign_positive() {
544 Err(ErrorsValidationRawReal::IsPosInfinity {
545 backtrace: Backtrace::force_capture(),
546 })
547 } else {
548 Err(ErrorsValidationRawReal::IsNegInfinity {
549 backtrace: Backtrace::force_capture(),
550 })
551 }
552 }
553 FpCategory::Nan => Err(ErrorsValidationRawReal::IsNaN {
554 value: value.clone(),
555 backtrace: Backtrace::force_capture(),
556 }),
557 FpCategory::Subnormal => Err(ErrorsValidationRawReal::IsSubnormal {
558 value: value.clone(),
559 backtrace: Backtrace::force_capture(),
560 }),
561 FpCategory::Zero | FpCategory::Normal => Ok(()),
562 }
563 } else {
564 Err(ErrorsValidationRawReal::PrecisionMismatch {
565 input_value: value.clone(),
566 actual_precision,
567 requested_precision: PRECISION,
568 backtrace: Backtrace::force_capture(),
569 })
570 }
571 }
572 }
573
574 /// Ensures both the real and imaginary parts of a [`rug::Complex`](https://docs.rs/rug/latest/rug/struct.Complex.html) value are strictly finite.
575 ///
576 /// This policy applies the [`StrictFinitePolicy<rug::Float>`](StrictFinitePolicy)
577 /// (which checks the underlying [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html)) to both the real and imaginary
578 /// components of the [`rug::Complex`](https://docs.rs/rug/latest/rug/struct.Complex.html).
579 ///
580 /// # Errors
581 ///
582 /// Returns [`ErrorsValidationRawComplex<ErrorsValidationRawReal<rug::Float>>`](ErrorsValidationRawComplex)
583 /// if either the real part, the imaginary part, or both parts fail the
584 /// `StrictFinitePolicy<RealRugStrictFinite<Precision>>` checks.
585 impl<const PRECISION: u32> ValidationPolicy for StrictFinitePolicy<rug::Complex, PRECISION> {
586 type Value = rug::Complex;
587 type Error = ErrorsValidationRawComplex<ErrorsValidationRawReal<rug::Float>>;
588
589 fn validate_ref(value: &rug::Complex) -> Result<(), Self::Error> {
590 validate_complex::<StrictFinitePolicy<rug::Float, PRECISION>>(
591 value.real(),
592 value.imag(),
593 )
594 }
595 }
596}
597//-------------------------------------------------------------
598
599//------------------------------------------------------------------------------------------------
600/// Provides a set of fundamental floating-point classification checks.
601///
602/// This trait defines common methods to determine the characteristics of a numerical value,
603/// such as whether it is finite, infinite, NaN (Not a Number), or normal.
604/// It is implemented for standard floating-point types (`f64`), complex numbers
605/// (`num::Complex<f64>`), and their arbitrary-precision counterparts from the `rug`
606/// library (via `RealRugStrictFinite<P>` and `ComplexRugStrictFinite<P>` which internally use [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html)
607/// and [`rug::Complex`](https://docs.rs/rug/latest/rug/struct.Complex.html)) when the "rug" feature is enabled.
608///
609/// `FpChecks` is often used as a supertrait for more general numerical traits like
610/// [`FpScalar`](crate::FpScalar), ensuring that types representing floating-point
611/// scalars can be queried for these basic properties. This is essential for
612/// validation policies and writing robust numerical algorithms.
613///
614/// Note that this trait focuses on these specific classifications. For more detailed
615/// categorization (like distinguishing subnormals or zero explicitly through this trait),
616/// types may provide other methods (e.g., `classify()` for `f64` and [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html),
617/// or `is_zero()` from [`FpScalar`](crate::FpScalar)).
618pub trait FpChecks {
619 /// Returns [`true`] if `self` is finite (i.e., not infinite and not NaN).
620 ///
621 /// For complex numbers, this typically means both the real and imaginary parts are finite.
622 fn is_finite(&self) -> bool;
623
624 /// Returns [`true`] if `self` is positive infinity or negative infinity.
625 ///
626 /// For complex numbers, this typically means at least one part is infinite,
627 /// and no part is NaN.
628 fn is_infinite(&self) -> bool;
629
630 /// Returns [`true`] if `self` is NaN (Not a Number).
631 ///
632 /// For complex numbers, this typically means at least one part is NaN.
633 fn is_nan(&self) -> bool;
634
635 /// Returns [`true`] if `self` is a *normal* number.
636 ///
637 /// A normal number is a finite, non-zero number that is not subnormal.
638 /// For complex numbers, this typically means both real and imaginary parts are normal
639 /// numbers (and thus individually finite, non-zero, and not subnormal).
640 fn is_normal(&self) -> bool;
641}
642
643#[duplicate_item(
644 T;
645 [f64];
646 [Complex::<f64>];
647)]
648impl FpChecks for T {
649 #[inline(always)]
650 fn is_finite(&self) -> bool {
651 T::is_finite(*self)
652 }
653
654 #[inline(always)]
655 fn is_infinite(&self) -> bool {
656 T::is_infinite(*self)
657 }
658
659 #[inline(always)]
660 fn is_nan(&self) -> bool {
661 T::is_nan(*self)
662 }
663
664 #[inline(always)]
665 fn is_normal(&self) -> bool {
666 T::is_normal(*self)
667 }
668}
669//------------------------------------------------------------------------------------------------
670
671//------------------------------------------------------------------------------------------------
672/// A validation policy that is active only in debug builds.
673///
674/// This struct acts as a wrapper around another [`ValidationPolicy`].
675///
676/// - In **debug builds** (`#[cfg(debug_assertions)]`), it delegates validation
677/// directly to the inner policy `P`.
678/// - In **release builds** (`#[cfg(not(debug_assertions))]`), it becomes a "no-op"
679/// (no operation), meaning its `validate` and `validate_ref` methods do nothing
680/// and always return `Ok`.
681///
682/// This is useful for enforcing strict, potentially expensive validations during
683/// development and testing, while eliminating their performance overhead in production code.
684///
685/// # Generic Parameters
686///
687/// - `P`: The inner [`ValidationPolicy`] to use during debug builds.
688///
689/// # Example
690///
691/// ```rust,ignore
692/// use num_valid::validation::{DebugValidationPolicy, StrictFinitePolicy};
693/// use try_create::ValidationPolicy;
694///
695/// // Define a policy that uses StrictFinitePolicy only in debug builds.
696/// type MyPolicy = DebugValidationPolicy<Native64RawRealStrictFinitePolicy>;
697///
698/// let nan_value = f64::NAN;
699/// let result = MyPolicy::validate(nan_value);
700///
701/// #[cfg(debug_assertions)]
702/// {
703/// // In debug mode, the validation fails because StrictFinitePolicy catches NaN.
704/// assert!(result.is_err());
705/// }
706///
707/// #[cfg(not(debug_assertions))]
708/// {
709/// // In release mode, the validation is skipped and always succeeds.
710/// assert!(result.is_ok());
711/// }
712/// ```
713pub struct DebugValidationPolicy<P: ValidationPolicy>(PhantomData<P>);
714
715impl<P: ValidationPolicy> ValidationPolicy for DebugValidationPolicy<P> {
716 type Value = P::Value;
717 type Error = P::Error;
718
719 #[inline(always)]
720 fn validate_ref(value: &Self::Value) -> Result<(), Self::Error> {
721 // In debug builds, this implementation delegates to the inner policy `P`.
722 #[cfg(debug_assertions)]
723 {
724 // Delegate to the inner policy's validation logic.
725 P::validate_ref(value)
726 }
727 #[cfg(not(debug_assertions))]
728 {
729 let _ = value; // Avoid unused variable warning in release mode.
730 // In release mode, this is a no-op.
731 // The validation is skipped, and we always return Ok.
732 // This allows the compiler to optimize away the validation logic entirely.
733 // This is useful for performance-critical code where validation is not needed in production.
734 // Note: The `value` is not used here, but we keep it to match the signature of `validate_ref`.
735 // This way, we can still call this method without needing to change the function signature.
736 // This is particularly useful when this policy is used in generic contexts
737 // where the `validate_ref` method is expected to take a reference to the value.
738 // The `PhantomData` ensures that the type system knows about `P` even
739 // though we don't use it directly in the release build.
740 // This is a common pattern in Rust to create zero-sized types that carry type information.
741 Ok(())
742 }
743 }
744}
745
746impl<P> ValidationPolicyReal for DebugValidationPolicy<P>
747where
748 P: ValidationPolicyReal,
749{
750 const PRECISION: u32 = P::PRECISION;
751}
752
753impl<P> ValidationPolicyComplex for DebugValidationPolicy<P>
754where
755 P: ValidationPolicyComplex,
756{
757 const PRECISION: u32 = P::PRECISION;
758}
759//------------------------------------------------------------------------------------------------
760
761/// Validates a value using the given policy, but only in debug builds.
762/// In release builds, this function is a no-op and should be optimized away.
763///
764/// # Panics
765///
766/// In debug builds, this function will panic if `P::validate_ref(value)` returns an `Err`.
767#[inline(always)]
768pub fn debug_validate<P: ValidationPolicy>(value: &P::Value, msg: &str) {
769 #[cfg(debug_assertions)]
770 {
771 if let Err(e) = P::validate_ref(value) {
772 panic!("Debug validation of {msg} value failed: {e}");
773 }
774 }
775 #[cfg(not(debug_assertions))]
776 {
777 // These are no-op in release builds
778 let _ = value;
779 let _ = msg;
780 }
781}
782
783//-------------------------------------------------------------
784#[cfg(test)]
785mod tests {
786 use super::*;
787
788 #[cfg(feature = "rug")]
789 use crate::{ComplexRugStrictFinite, RealRugStrictFinite};
790
791 mod fp_checks {
792 use super::*;
793 use num::Complex;
794
795 mod native64 {
796 use super::*;
797
798 mod real {
799 use super::*;
800
801 #[test]
802 fn test_is_finite() {
803 assert!(<f64 as FpChecks>::is_finite(&1.0));
804 assert!(!<f64 as FpChecks>::is_finite(&f64::INFINITY));
805 assert!(!<f64 as FpChecks>::is_finite(&f64::NEG_INFINITY));
806 assert!(!<f64 as FpChecks>::is_finite(&f64::NAN));
807 }
808
809 #[test]
810 fn test_is_infinite() {
811 assert!(!<f64 as FpChecks>::is_infinite(&1.0));
812 assert!(<f64 as FpChecks>::is_infinite(&f64::INFINITY));
813 assert!(<f64 as FpChecks>::is_infinite(&f64::NEG_INFINITY));
814 assert!(!<f64 as FpChecks>::is_infinite(&f64::NAN));
815 }
816
817 #[test]
818 fn test_is_nan() {
819 assert!(!<f64 as FpChecks>::is_nan(&1.0));
820 assert!(!<f64 as FpChecks>::is_nan(&f64::INFINITY));
821 assert!(!<f64 as FpChecks>::is_nan(&f64::NEG_INFINITY));
822 assert!(<f64 as FpChecks>::is_nan(&f64::NAN));
823 }
824
825 #[test]
826 fn test_is_normal() {
827 assert!(<f64 as FpChecks>::is_normal(&1.0));
828 assert!(!<f64 as FpChecks>::is_normal(&0.0));
829 assert!(!<f64 as FpChecks>::is_normal(&f64::INFINITY));
830 assert!(!<f64 as FpChecks>::is_normal(&f64::NEG_INFINITY));
831 assert!(!<f64 as FpChecks>::is_normal(&f64::NAN));
832 assert!(<f64 as FpChecks>::is_normal(&f64::MIN_POSITIVE));
833 }
834 }
835
836 mod complex {
837 use super::*;
838
839 #[test]
840 fn test_is_finite() {
841 assert!(<Complex<f64> as FpChecks>::is_finite(&Complex::new(
842 1.0, 1.0
843 )));
844 assert!(!<Complex<f64> as FpChecks>::is_finite(&Complex::new(
845 f64::INFINITY,
846 1.0
847 )));
848 assert!(!<Complex<f64> as FpChecks>::is_finite(&Complex::new(
849 1.0,
850 f64::NAN
851 )));
852 }
853
854 #[test]
855 fn test_is_infinite() {
856 assert!(!<Complex<f64> as FpChecks>::is_infinite(&Complex::new(
857 1.0, 1.0
858 )));
859 assert!(<Complex<f64> as FpChecks>::is_infinite(&Complex::new(
860 f64::INFINITY,
861 1.0
862 )));
863 assert!(!<Complex<f64> as FpChecks>::is_infinite(&Complex::new(
864 1.0,
865 f64::NAN
866 )));
867 }
868
869 #[test]
870 fn test_is_nan() {
871 assert!(!<Complex<f64> as FpChecks>::is_nan(&Complex::new(1.0, 1.0)));
872 assert!(<Complex<f64> as FpChecks>::is_nan(&Complex::new(
873 f64::NAN,
874 1.0
875 )));
876 assert!(<Complex<f64> as FpChecks>::is_nan(&Complex::new(
877 1.0,
878 f64::NAN
879 )));
880 }
881
882 #[test]
883 fn test_is_normal() {
884 assert!(<Complex<f64> as FpChecks>::is_normal(&Complex::new(
885 1.0, 1.0
886 )));
887 assert!(!<Complex<f64> as FpChecks>::is_normal(&Complex::new(
888 0.0, 0.0
889 )));
890 assert!(!<Complex<f64> as FpChecks>::is_normal(&Complex::new(
891 f64::INFINITY,
892 1.0
893 )));
894 assert!(!<Complex<f64> as FpChecks>::is_normal(&Complex::new(
895 1.0,
896 f64::NAN
897 )));
898 }
899 }
900 }
901 }
902
903 mod strict_finite_policy {
904 use super::*;
905
906 mod raw_types {
907 use super::*;
908
909 mod native64 {
910 use super::*;
911
912 mod real {
913 use super::*;
914
915 #[test]
916 fn f64_infinity() {
917 let result = StrictFinitePolicy::<f64, 53>::validate(f64::INFINITY);
918 assert!(matches!(
919 result,
920 Err(ErrorsValidationRawReal::IsPosInfinity { .. })
921 ));
922
923 let result = StrictFinitePolicy::<f64, 53>::validate_ref(&f64::INFINITY);
924 assert!(matches!(
925 result,
926 Err(ErrorsValidationRawReal::IsPosInfinity { .. })
927 ));
928
929 let result = StrictFinitePolicy::<f64, 53>::validate(f64::NEG_INFINITY);
930 assert!(matches!(
931 result,
932 Err(ErrorsValidationRawReal::IsNegInfinity { .. })
933 ));
934
935 let result =
936 StrictFinitePolicy::<f64, 53>::validate_ref(&f64::NEG_INFINITY);
937 assert!(matches!(
938 result,
939 Err(ErrorsValidationRawReal::IsNegInfinity { .. })
940 ));
941 }
942
943 #[test]
944 fn f64_nan() {
945 let result = StrictFinitePolicy::<f64, 53>::validate(f64::NAN);
946 assert!(matches!(result, Err(ErrorsValidationRawReal::IsNaN { .. })));
947
948 let result = StrictFinitePolicy::<f64, 53>::validate_ref(&f64::NAN);
949 assert!(matches!(result, Err(ErrorsValidationRawReal::IsNaN { .. })));
950 }
951
952 #[test]
953 fn f64_subnormal() {
954 // let subnormal = f64::MIN_POSITIVE / 2.0;
955 let subnormal = f64::from_bits(1); // the smallest positive subnormal
956
957 let result = StrictFinitePolicy::<f64, 53>::validate(subnormal);
958 assert!(matches!(
959 result,
960 Err(ErrorsValidationRawReal::<f64>::IsSubnormal { .. })
961 ));
962
963 let result = StrictFinitePolicy::<f64, 53>::validate_ref(&subnormal);
964 assert!(matches!(
965 result,
966 Err(ErrorsValidationRawReal::<f64>::IsSubnormal { .. })
967 ));
968 }
969
970 #[test]
971 fn f64_zero() {
972 let value = 0.0;
973 let result = StrictFinitePolicy::<f64, 53>::validate(value);
974 assert!(matches!(result, Ok(0.0)));
975 }
976
977 #[test]
978 fn f64_normal() {
979 let value = 1.0;
980 let result = StrictFinitePolicy::<f64, 53>::validate(value);
981 assert!(matches!(result, Ok(1.0)));
982
983 let result = StrictFinitePolicy::<f64, 53>::validate_ref(&value);
984 assert!(matches!(result, Ok(())));
985 }
986 }
987
988 mod complex {
989 use super::*;
990 use num::Complex;
991
992 #[test]
993 fn test_invalid_both_parts() {
994 let z = Complex::new(f64::NAN, f64::NEG_INFINITY);
995 let err =
996 StrictFinitePolicy::<Complex<f64>, 53>::validate_ref(&z).unwrap_err();
997 matches!(err, ErrorsValidationRawComplex::InvalidBothParts { .. });
998 }
999
1000 #[test]
1001 fn complex_f64_invalid_real_part() {
1002 let value = Complex::new(f64::INFINITY, 1.0);
1003 let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
1004 assert!(matches!(
1005 result,
1006 Err(ErrorsValidationRawComplex::InvalidRealPart {
1007 source: ErrorsValidationRawReal::IsPosInfinity { .. },
1008 ..
1009 })
1010 ));
1011
1012 let value = Complex::new(f64::NAN, 1.0);
1013 let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
1014 assert!(matches!(
1015 result,
1016 Err(ErrorsValidationRawComplex::InvalidRealPart {
1017 source: ErrorsValidationRawReal::IsNaN { .. },
1018 ..
1019 })
1020 ));
1021
1022 let value = Complex::new(f64::MIN_POSITIVE / 2.0, 1.0);
1023 let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
1024 assert!(matches!(
1025 result,
1026 Err(ErrorsValidationRawComplex::InvalidRealPart {
1027 source: ErrorsValidationRawReal::IsSubnormal { .. },
1028 ..
1029 })
1030 ));
1031 }
1032
1033 #[test]
1034 fn complex_f64_invalid_imaginary_part() {
1035 let value = Complex::new(1.0, f64::INFINITY);
1036 let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
1037 assert!(matches!(
1038 result,
1039 Err(ErrorsValidationRawComplex::InvalidImaginaryPart {
1040 source: ErrorsValidationRawReal::IsPosInfinity { .. },
1041 ..
1042 })
1043 ));
1044
1045 let value = Complex::new(1.0, f64::NAN);
1046 let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
1047 assert!(matches!(
1048 result,
1049 Err(ErrorsValidationRawComplex::InvalidImaginaryPart {
1050 source: ErrorsValidationRawReal::IsNaN { .. },
1051 ..
1052 })
1053 ));
1054
1055 let value = Complex::new(1.0, f64::MIN_POSITIVE / 2.0);
1056 let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
1057 assert!(matches!(
1058 result,
1059 Err(ErrorsValidationRawComplex::InvalidImaginaryPart {
1060 source: ErrorsValidationRawReal::IsSubnormal { .. },
1061 ..
1062 })
1063 ));
1064 }
1065
1066 #[test]
1067 fn complex_f64_zero() {
1068 let value = Complex::new(0.0, 0.0);
1069 let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
1070 assert!(result.is_ok());
1071 }
1072
1073 #[test]
1074 fn complex_f64_normal_value() {
1075 let value = Complex::new(1.0, 1.0);
1076 let result = StrictFinitePolicy::<Complex<f64>, 53>::validate(value);
1077 assert!(result.is_ok());
1078 }
1079 }
1080 }
1081
1082 #[cfg(feature = "rug")]
1083 mod rug53 {
1084 use super::*;
1085
1086 const PRECISION: u32 = 53; // Default precision for rug tests
1087
1088 mod float {
1089 use super::*;
1090 use rug::Float;
1091
1092 #[test]
1093 fn rug_float_valid() {
1094 let x = Float::with_val(PRECISION, 1.0);
1095 assert!(StrictFinitePolicy::<Float, PRECISION>::validate_ref(&x).is_ok());
1096 assert_eq!(
1097 StrictFinitePolicy::<Float, PRECISION>::validate(x.clone()).unwrap(),
1098 x
1099 );
1100 }
1101
1102 #[test]
1103 fn rug_float_nan() {
1104 let x = Float::with_val(PRECISION, Float::parse("nan").unwrap());
1105 let err =
1106 StrictFinitePolicy::<Float, PRECISION>::validate_ref(&x).unwrap_err();
1107 assert!(matches!(err, ErrorsValidationRawReal::IsNaN { .. }));
1108 let err = StrictFinitePolicy::<Float, PRECISION>::validate(x).unwrap_err();
1109 assert!(matches!(err, ErrorsValidationRawReal::IsNaN { .. }));
1110 }
1111
1112 #[test]
1113 fn rug_float_infinity() {
1114 use rug::Float;
1115
1116 let x = Float::with_val(PRECISION, Float::parse("inf").unwrap());
1117 let err =
1118 StrictFinitePolicy::<Float, PRECISION>::validate_ref(&x).unwrap_err();
1119 assert!(matches!(err, ErrorsValidationRawReal::IsPosInfinity { .. }));
1120 let err = StrictFinitePolicy::<Float, PRECISION>::validate(x).unwrap_err();
1121 assert!(matches!(err, ErrorsValidationRawReal::IsPosInfinity { .. }));
1122
1123 let x = Float::with_val(PRECISION, Float::parse("-inf").unwrap());
1124 let err =
1125 StrictFinitePolicy::<Float, PRECISION>::validate_ref(&x).unwrap_err();
1126 assert!(matches!(err, ErrorsValidationRawReal::IsNegInfinity { .. }));
1127 let err = StrictFinitePolicy::<Float, PRECISION>::validate(x).unwrap_err();
1128 assert!(matches!(err, ErrorsValidationRawReal::IsNegInfinity { .. }));
1129 }
1130
1131 #[test]
1132 fn rug_float_zero() {
1133 let value = Float::with_val(PRECISION, 0.0);
1134 let result = StrictFinitePolicy::<Float, PRECISION>::validate_ref(&value);
1135 assert!(result.is_ok());
1136 let result =
1137 StrictFinitePolicy::<Float, PRECISION>::validate(value.clone())
1138 .unwrap();
1139 assert_eq!(result, value);
1140 }
1141 }
1142
1143 mod complex {
1144 use super::*;
1145 use rug::{Complex, Float};
1146
1147 #[test]
1148 fn rug_complex_valid() {
1149 let z = Complex::with_val(PRECISION, (1.0, -2.0));
1150 assert!(StrictFinitePolicy::<Complex, PRECISION>::validate_ref(&z).is_ok());
1151 assert_eq!(
1152 StrictFinitePolicy::<Complex, PRECISION>::validate(z.clone()).unwrap(),
1153 z
1154 );
1155 }
1156
1157 #[test]
1158 fn rug_complex_real_invalid() {
1159 let nan = Float::with_val(PRECISION, Float::parse("nan").unwrap());
1160 let z = Complex::with_val(PRECISION, (&nan, 1.0));
1161 let err =
1162 StrictFinitePolicy::<Complex, PRECISION>::validate_ref(&z).unwrap_err();
1163 assert!(matches!(
1164 err,
1165 ErrorsValidationRawComplex::InvalidRealPart {
1166 source: ErrorsValidationRawReal::IsNaN { .. }
1167 }
1168 ));
1169 }
1170
1171 #[test]
1172 fn rug_complex_imag_invalid() {
1173 let inf = Float::with_val(PRECISION, Float::parse("inf").unwrap());
1174 let z = Complex::with_val(PRECISION, (1.0, &inf));
1175 let err =
1176 StrictFinitePolicy::<Complex, PRECISION>::validate_ref(&z).unwrap_err();
1177 assert!(matches!(
1178 err,
1179 ErrorsValidationRawComplex::InvalidImaginaryPart {
1180 source: ErrorsValidationRawReal::IsPosInfinity { .. }
1181 }
1182 ));
1183 }
1184
1185 #[test]
1186 fn rug_complex_both_invalid() {
1187 let nan = Float::with_val(PRECISION, Float::parse("nan").unwrap());
1188 let inf = Float::with_val(PRECISION, Float::parse("inf").unwrap());
1189 let z = Complex::with_val(PRECISION, (&nan, &inf));
1190 let err =
1191 StrictFinitePolicy::<Complex, PRECISION>::validate_ref(&z).unwrap_err();
1192 assert!(matches!(
1193 err,
1194 ErrorsValidationRawComplex::InvalidBothParts {
1195 real_error: ErrorsValidationRawReal::IsNaN { .. },
1196 imag_error: ErrorsValidationRawReal::IsPosInfinity { .. },
1197 ..
1198 }
1199 ));
1200 }
1201
1202 #[test]
1203 fn rug_complex_zero() {
1204 let zero = Complex::with_val(
1205 PRECISION,
1206 (
1207 Float::with_val(PRECISION, 0.0),
1208 Float::with_val(PRECISION, 0.0),
1209 ),
1210 );
1211 let result = StrictFinitePolicy::<Complex, PRECISION>::validate_ref(&zero);
1212 assert!(result.is_ok());
1213 assert_eq!(
1214 StrictFinitePolicy::<Complex, PRECISION>::validate(zero.clone())
1215 .unwrap(),
1216 zero
1217 );
1218 }
1219 }
1220 }
1221 }
1222
1223 // Module for testing the wrapper types that implement TryNew
1224 #[cfg(feature = "rug")]
1225 mod wrapper_types {
1226 use super::*;
1227
1228 mod rug_wrappers {
1229 use super::*;
1230 use crate::RealScalar;
1231 use num::One;
1232 use rug::{Complex as RugComplex, Float};
1233 use try_create::{IntoInner, TryNew};
1234
1235 const PRECISION: u32 = 53; // Default precision for rug tests
1236
1237 mod real {
1238 use super::*;
1239
1240 #[test]
1241 fn try_new_real_rug_correct_precision() {
1242 // Test RealRugStrictFinite with valid rug::Float and correct precision
1243 let val = Float::with_val(PRECISION, 1.0);
1244 let res = RealRugStrictFinite::<PRECISION>::try_new(val.clone());
1245 assert!(res.is_ok(),);
1246 assert_eq!(res.unwrap().into_inner(), val);
1247 }
1248
1249 #[test]
1250 fn try_new_real_rug_incorrect_precision() {
1251 // Test RealRugStrictFinite with valid rug::Float but incorrect precision
1252 let val_wrong_prec = Float::with_val(PRECISION + 10, 1.0); // Different precision
1253 let res = RealRugStrictFinite::<PRECISION>::try_new(val_wrong_prec);
1254 assert!(res.is_err());
1255 matches!(res, Err(ErrorsValidationRawReal::PrecisionMismatch { actual_precision, .. })
1256 if actual_precision == PRECISION + 10);
1257 }
1258
1259 #[test]
1260 fn try_new_real_rug_nan() {
1261 // Test RealRugStrictFinite with rug::Float NaN
1262 let val_nan = Float::with_val(PRECISION, Float::parse("nan").unwrap());
1263 let res = RealRugStrictFinite::<PRECISION>::try_new(val_nan);
1264 assert!(res.is_err());
1265 assert!(matches!(
1266 res,
1267 Err(ErrorsValidationRawReal::<Float>::IsNaN { .. })
1268 ));
1269 }
1270
1271 #[test]
1272 fn try_new_real_rug_infinity() {
1273 // Test RealRugStrictFinite with rug::Float positive infinity
1274 let val_inf = Float::with_val(PRECISION, Float::parse("inf").unwrap());
1275 let res_pos_inf = RealRugStrictFinite::<PRECISION>::try_new(val_inf);
1276 assert!(res_pos_inf.is_err());
1277 assert!(matches!(
1278 res_pos_inf,
1279 Err(ErrorsValidationRawReal::<Float>::IsPosInfinity { .. })
1280 ));
1281
1282 // Test RealRugStrictFinite with rug::Float negative infinity
1283 let val_neg_inf = Float::with_val(PRECISION, Float::parse("-inf").unwrap());
1284 let res_neg_inf = RealRugStrictFinite::<PRECISION>::try_new(val_neg_inf);
1285 assert!(res_neg_inf.is_err());
1286 assert!(matches!(
1287 res_neg_inf,
1288 Err(ErrorsValidationRawReal::<Float>::IsNegInfinity { .. })
1289 ));
1290 }
1291 }
1292
1293 mod complex {
1294 use super::*;
1295 use crate::ComplexScalarGetParts;
1296
1297 #[test]
1298 fn try_new_complex_rug_correct_precision() {
1299 // Test ComplexRugStrictFinite with valid rug::Complex and correct precision for both parts
1300 let val = RugComplex::with_val(PRECISION, (1.0, -2.0));
1301 let res = ComplexRugStrictFinite::<PRECISION>::try_new(val.clone());
1302 assert!(res.is_ok());
1303 assert_eq!(res.unwrap().into_inner(), val);
1304 }
1305
1306 /*
1307 #[test]
1308 #[ignore = "because the precision used to construct a rug::Complex override the precision of the real and imaginary parts"]
1309 fn try_new_complex_rug_precision_mismatch_real() {
1310 // Test ComplexRugStrictFinite where real part has incorrect precision
1311 let real_part_wrong_prec = Float::with_val(PRECISION - 1, 1.0);
1312 let imag_part_correct_prec = Float::with_val(PRECISION, -2.0);
1313 let val = RugComplex::with_val(
1314 PRECISION,
1315 (real_part_wrong_prec, imag_part_correct_prec),
1316 ); // Construct with Float parts
1317
1318 let res = ComplexRugStrictFinite::<PRECISION>::try_new(val);
1319 assert!(res.is_err());
1320 assert!(matches!(
1321 res,
1322 Err(ErrorsValidationRawComplex::InvalidRealPart {
1323 source: ErrorsValidationRawReal::PrecisionMismatch { .. }
1324 })
1325 ));
1326 }
1327
1328 #[test]
1329 #[ignore = "because the precision used to construct a rug::Complex override the precision of the real and imaginary parts"]
1330 fn try_new_complex_rug_precision_mismatch_imag() {
1331 // Test ComplexRugStrictFinite where imaginary part has incorrect precision
1332 let real_part_correct_prec = Float::with_val(PRECISION, 1.0);
1333 let imag_part_wrong_prec = Float::with_val(PRECISION - 1, -2.0);
1334 let val = RugComplex::with_val(
1335 PRECISION,
1336 (real_part_correct_prec, imag_part_wrong_prec),
1337 ); // Construct with Float parts
1338
1339 let res = ComplexRugStrictFinite::<PRECISION>::try_new(val);
1340 assert!(res.is_err());
1341 matches!(res, Err(ErrorsValidationRawComplex::InvalidImaginaryPart { source })
1342 if matches!(source, ErrorsValidationRawReal::PrecisionMismatch { .. })
1343 );
1344 }
1345 */
1346
1347 #[test]
1348 fn try_new_complex_rug_precision_mismatch_both() {
1349 // Test ComplexRugStrictFinite where both parts have incorrect precision
1350 let real_part_wrong_prec = Float::with_val(PRECISION - 1, 1.0);
1351 let imag_part_wrong_prec = Float::with_val(PRECISION - 1, -2.0);
1352 let val = RugComplex::with_val(
1353 PRECISION - 1,
1354 (real_part_wrong_prec, imag_part_wrong_prec),
1355 ); // Construct with Float parts
1356
1357 let res = ComplexRugStrictFinite::<PRECISION>::try_new(val);
1358 assert!(res.is_err());
1359 assert!(matches!(
1360 res,
1361 Err(ErrorsValidationRawComplex::InvalidBothParts {
1362 real_error: ErrorsValidationRawReal::PrecisionMismatch { .. },
1363 imag_error: ErrorsValidationRawReal::PrecisionMismatch { .. },
1364 ..
1365 })
1366 ));
1367 }
1368
1369 #[test]
1370 fn try_new_complex_rug_invalid_real_nan() {
1371 // Test ComplexRugStrictFinite where real part is NaN (value validation failure)
1372 let nan_real = Float::with_val(PRECISION, Float::parse("nan").unwrap());
1373 let imag_part = Float::with_val(PRECISION, 1.0);
1374 let val = RugComplex::with_val(PRECISION, (nan_real, imag_part));
1375
1376 let res = ComplexRugStrictFinite::<PRECISION>::try_new(val);
1377 assert!(res.is_err());
1378 assert!(matches!(
1379 res,
1380 Err(ErrorsValidationRawComplex::InvalidRealPart {
1381 source: ErrorsValidationRawReal::<Float>::IsNaN { .. },
1382 })
1383 ));
1384 }
1385
1386 #[test]
1387 fn try_new_complex_rug_invalid_imag_inf() {
1388 // Test ComplexRugStrictFinite where imaginary part is Infinity (value validation failure)
1389 let real_part = Float::with_val(PRECISION, 1.0);
1390 let inf_imag = Float::with_val(PRECISION, Float::parse("inf").unwrap());
1391 let val = RugComplex::with_val(PRECISION, (real_part, inf_imag));
1392
1393 let res = ComplexRugStrictFinite::<PRECISION>::try_new(val);
1394 assert!(res.is_err());
1395 assert!(matches!(
1396 res,
1397 Err(ErrorsValidationRawComplex::InvalidImaginaryPart {
1398 source: ErrorsValidationRawReal::<Float>::IsPosInfinity { .. }
1399 })
1400 ));
1401 }
1402
1403 #[test]
1404 fn try_new_complex_rug_invalid_both() {
1405 // Test ComplexRugStrictFinite where both parts are invalid (value validation failure)
1406 let nan_real = Float::with_val(PRECISION, Float::parse("nan").unwrap());
1407 let neg_inf_imag =
1408 Float::with_val(PRECISION, Float::parse("-inf").unwrap());
1409 let val = RugComplex::with_val(PRECISION, (nan_real, neg_inf_imag));
1410
1411 let res = ComplexRugStrictFinite::<PRECISION>::try_new(val);
1412 assert!(res.is_err());
1413 assert!(matches!(
1414 res,
1415 Err(ErrorsValidationRawComplex::InvalidBothParts {
1416 real_error: ErrorsValidationRawReal::<Float>::IsNaN { .. },
1417 imag_error: ErrorsValidationRawReal::<Float>::IsNegInfinity { .. },
1418 ..
1419 })
1420 ));
1421 }
1422
1423 #[test]
1424 fn try_from_complex_f64_to_complex_rug() {
1425 // Test valid conversion
1426 let z = num::Complex::new(1., -2.);
1427 let z_rug = ComplexRugStrictFinite::<PRECISION>::try_from(z).unwrap();
1428 let expected_real = RealRugStrictFinite::<PRECISION>::one();
1429 let expected_imaginary =
1430 RealRugStrictFinite::<PRECISION>::try_from_f64(-2.).unwrap();
1431 assert_eq!(z_rug.real_part(), expected_real);
1432 assert_eq!(z_rug.imag_part(), expected_imaginary);
1433
1434 // Test invalid real part
1435 let z = num::Complex::new(f64::INFINITY, -2.0);
1436 let result = ComplexRugStrictFinite::<PRECISION>::try_from(z);
1437 assert!(result.is_err());
1438 assert!(matches!(
1439 result,
1440 Err(ErrorsValidationRawComplex::InvalidRealPart {
1441 source: ErrorsTryFromf64::Output {
1442 source: ErrorsValidationRawReal::IsPosInfinity { .. }
1443 },
1444 })
1445 ));
1446
1447 // Test invalid imaginary part
1448 let z = num::Complex::new(1.0, f64::NAN);
1449 let result = ComplexRugStrictFinite::<PRECISION>::try_from(z).unwrap_err();
1450 //println!("result = {result}");
1451 assert!(matches!(
1452 result,
1453 ErrorsValidationRawComplex::InvalidImaginaryPart {
1454 source: ErrorsTryFromf64::Output {
1455 source: ErrorsValidationRawReal::IsNaN { .. }
1456 },
1457 }
1458 ));
1459 }
1460 }
1461 }
1462 }
1463 }
1464
1465 mod conditional_validation_policy {
1466 use core::f64;
1467
1468 use super::*;
1469
1470 type F64StrictFinitePolicy = Native64RawRealStrictFinitePolicy;
1471
1472 // Define a policy that is strict in debug, and no-op in release.
1473 type MyConditionalPolicy = DebugValidationPolicy<F64StrictFinitePolicy>;
1474
1475 #[test]
1476 #[should_panic(expected = "Debug validation of input value failed: Value is NaN: NaN")]
1477 #[cfg(debug_assertions)]
1478 fn debug_validate_function() {
1479 // This should always succeed, regardless of build mode.
1480 let nan_value = f64::NAN;
1481 debug_validate::<F64StrictFinitePolicy>(&nan_value, "input");
1482 }
1483
1484 #[test]
1485 fn test_conditional_validation_on_valid_value() {
1486 let valid_value = 42.0;
1487 // This should always succeed, regardless of build mode.
1488 assert!(MyConditionalPolicy::validate(valid_value).is_ok());
1489 }
1490
1491 #[test]
1492 fn test_conditional_validation_on_invalid_value() {
1493 let invalid_value = f64::NAN;
1494 let result = MyConditionalPolicy::validate(invalid_value);
1495
1496 if cfg!(debug_assertions) {
1497 // In debug mode, we expect an error from StrictFinitePolicy.
1498 assert!(result.is_err(), "Expected validation to fail in debug mode");
1499 assert!(matches!(result, Err(ErrorsValidationRawReal::IsNaN { .. })));
1500 } else {
1501 // In release mode, we expect it to succeed (no-op).
1502 assert!(
1503 result.is_ok(),
1504 "Expected validation to succeed in release mode"
1505 );
1506 }
1507 }
1508 }
1509}