num_valid/core/
policies.rs

1#![deny(rustdoc::broken_intra_doc_links)]
2
3//! Validation policies for scalar types.
4//!
5//! This module provides the validation policies that determine how scalar values
6//! are checked for validity. The primary policy is `StrictFinitePolicy`, which
7//! ensures values are finite and not subnormal.
8
9use std::{marker::PhantomData, num::FpCategory};
10
11use num::Complex;
12use try_create::ValidationPolicy;
13
14use crate::core::{
15    errors::{ErrorsValidationRawComplex, ErrorsValidationRawReal, capture_backtrace},
16    traits::{
17        raw::{RawComplexTrait, RawRealTrait, RawScalarTrait},
18        validation::{
19            GuaranteesFiniteComplexValues, GuaranteesFiniteRealValues, ValidationPolicyComplex,
20            ValidationPolicyReal,
21        },
22    },
23};
24
25/// A validation policy that checks for strict finiteness.
26///
27/// 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:
28/// - Not NaN (Not a Number).
29/// - Not positive or negative Infinity.
30/// - Not subnormal (for `f64`).
31///   While [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html) maintains its specified precision rather than having distinct subnormal
32///   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
33///   [`FpCategory::Subnormal`] (e.g., results of underflow that are tiny but not exactly zero).
34///
35/// For complex types (`ScalarType = `[`Complex<f64>`], `ScalarType = `[`rug::Complex`](https://docs.rs/rug/latest/rug/struct.Complex.html)), this policy applies
36/// the strict finiteness check to both the real and imaginary parts.
37///
38/// This struct is a Zero-Sized Type (ZST) and uses [`PhantomData`] to associate
39/// with the `ScalarType` it validates.
40pub struct StrictFinitePolicy<ScalarType: Sized, const PRECISION: u32>(PhantomData<ScalarType>);
41
42/// A type alias for a validation policy that enforces strict finiteness for the raw [`f64`] type.
43///
44/// This is a convenient alias for [`StrictFinitePolicy<f64, 53>`], configured specifically
45/// for Rust's native 64-bit floating-point number. It is used to validate that a given [`f64`]
46/// value is suitable for use within the library's validated types.
47///
48/// This policy ensures that a `f64` value is:
49/// - Not `NaN` (Not a Number).
50/// - Not positive or negative `Infinity`.
51/// - Not a subnormal number.
52///
53/// It is a fundamental building block for the native `f64` kernel, providing the validation
54/// logic for [`RealNative64StrictFinite`](crate::backends::native64::validated::RealNative64StrictFinite).
55///
56/// # Example
57///
58/// ```rust
59/// use num_valid::{
60///     core::{
61///         errors::ErrorsValidationRawReal,
62///         policies::Native64RawRealStrictFinitePolicy,
63///     },
64/// };
65/// use try_create::ValidationPolicy;
66///
67/// // A valid finite number passes validation.
68/// assert!(Native64RawRealStrictFinitePolicy::validate(1.0).is_ok());
69///
70/// // NaN fails validation.
71/// let result_nan = Native64RawRealStrictFinitePolicy::validate(f64::NAN);
72/// assert!(matches!(result_nan, Err(ErrorsValidationRawReal::IsNaN { .. })));
73///
74/// // Infinity fails validation.
75/// let result_inf = Native64RawRealStrictFinitePolicy::validate(f64::INFINITY);
76/// assert!(matches!(result_inf, Err(ErrorsValidationRawReal::IsPosInfinity { .. })));
77/// ```
78pub type Native64RawRealStrictFinitePolicy = StrictFinitePolicy<f64, 53>;
79
80/// A type alias for a validation policy that enforces strict finiteness for the raw [`num::Complex<f64>`] type.
81///
82/// This is a convenient alias for [`StrictFinitePolicy<Complex<f64>, 53>`], configured for
83/// Rust's native 64-bit complex numbers. It is used to validate that a given [`num::Complex<f64>`]
84/// value is suitable for use within the library's validated types.
85///
86/// This policy applies the strict finiteness check (as defined by [`Native64RawRealStrictFinitePolicy`])
87/// to both the real and imaginary parts of the complex number. A [`num::Complex<f64>`] value is considered
88/// valid if and only if both of its components are finite (not `NaN`, `Infinity`, or subnormal).
89///
90/// It is a fundamental building block for the native `f64` kernel, providing the validation
91/// logic for [`ComplexNative64StrictFinite`](crate::backends::native64::validated::ComplexNative64StrictFinite).
92///
93/// # Example
94///
95/// ```rust
96/// use num_valid::{
97///     core::{
98///         errors::{ErrorsValidationRawComplex, ErrorsValidationRawReal},
99///         policies::Native64RawComplexStrictFinitePolicy,
100///     },
101/// };
102/// use try_create::ValidationPolicy;
103/// use num::Complex;
104///
105/// // A complex number with valid finite parts passes validation.
106/// let valid_complex = Complex::new(1.0, -2.0);
107/// assert!(Native64RawComplexStrictFinitePolicy::validate(valid_complex).is_ok());
108///
109/// // A complex number with NaN in the real part fails validation.
110/// let nan_complex = Complex::new(f64::NAN, 2.0);
111/// let result_nan = Native64RawComplexStrictFinitePolicy::validate(nan_complex);
112/// assert!(matches!(result_nan, Err(ErrorsValidationRawComplex::InvalidRealPart { .. })));
113///
114/// // A complex number with Infinity in the imaginary part fails validation.
115/// let inf_complex = Complex::new(1.0, f64::INFINITY);
116/// let result_inf = Native64RawComplexStrictFinitePolicy::validate(inf_complex);
117/// assert!(matches!(result_inf, Err(ErrorsValidationRawComplex::InvalidImaginaryPart { .. })));
118///
119/// // A complex number with invalid parts in both also fails.
120/// let both_invalid = Complex::new(f64::NAN, f64::INFINITY);
121/// let result_both = Native64RawComplexStrictFinitePolicy::validate(both_invalid);
122/// assert!(matches!(result_both, Err(ErrorsValidationRawComplex::InvalidBothParts { .. })));
123/// ```
124pub type Native64RawComplexStrictFinitePolicy = StrictFinitePolicy<Complex<f64>, 53>;
125
126/// Ensures the `f64` value is strictly finite.
127///
128/// This policy checks if the `f64` value is:
129/// - Not NaN (Not a Number).
130/// - Not positive or negative Infinity.
131/// - Not subnormal.
132///
133/// # Errors
134///
135/// Returns [`ErrorsValidationRawReal<f64>`](ErrorsValidationRawReal) if the value
136/// fails any of these checks.
137impl ValidationPolicy for Native64RawRealStrictFinitePolicy {
138    type Value = f64;
139    type Error = ErrorsValidationRawReal<f64>;
140
141    fn validate_ref(value: &f64) -> Result<(), Self::Error> {
142        match value.classify() {
143            FpCategory::Nan => Err(ErrorsValidationRawReal::IsNaN {
144                value: *value,
145                backtrace: capture_backtrace(),
146            }),
147            FpCategory::Infinite => {
148                if value.is_sign_positive() {
149                    Err(ErrorsValidationRawReal::IsPosInfinity {
150                        backtrace: capture_backtrace(),
151                    })
152                } else {
153                    Err(ErrorsValidationRawReal::IsNegInfinity {
154                        backtrace: capture_backtrace(),
155                    })
156                }
157            }
158            FpCategory::Subnormal => Err(ErrorsValidationRawReal::IsSubnormal {
159                value: *value,
160                backtrace: capture_backtrace(),
161            }),
162            FpCategory::Normal | FpCategory::Zero => Ok(()),
163        }
164    }
165}
166
167//------------------------------------------------------------------------------------------------
168/// A validation policy that is active only in debug builds.
169///
170/// This struct acts as a wrapper around another [`ValidationPolicy`].
171///
172/// - In **debug builds** (`#[cfg(debug_assertions)]`), it delegates validation
173///   directly to the inner policy `P`.
174/// - In **release builds** (`#[cfg(not(debug_assertions))]`), it becomes a "no-op"
175///   (no operation), meaning its `validate` and `validate_ref` methods do nothing
176///   and always return `Ok`.
177///
178/// This is useful for enforcing strict, potentially expensive validations during
179/// development and testing, while eliminating their performance overhead in production code.
180///
181/// # Generic Parameters
182///
183/// - `P`: The inner [`ValidationPolicy`] to use during debug builds.
184///
185/// # Example
186///
187/// ```rust,ignore
188/// use num_valid::{validation::{DebugValidationPolicy, StrictFinitePolicy, Native64RawRealStrictFinitePolicy}};
189/// use try_create::ValidationPolicy;
190///
191/// // Define a policy that uses Native64RawRealStrictFinitePolicy
192/// // (i.e. StrictFinitePolicy<f64, 53>) only in debug builds.
193/// type MyPolicy = DebugValidationPolicy<Native64RawRealStrictFinitePolicy>;
194///
195/// let nan_value = f64::NAN;
196/// let result = MyPolicy::validate(nan_value);
197///
198/// #[cfg(debug_assertions)]
199/// {
200///     // In debug mode, the validation fails because StrictFinitePolicy catches NaN.
201///     assert!(result.is_err());
202/// }
203///
204/// #[cfg(not(debug_assertions))]
205/// {
206///     // In release mode, the validation is skipped and always succeeds.
207///     assert!(result.is_ok());
208/// }
209/// ```
210pub struct DebugValidationPolicy<P: ValidationPolicy>(PhantomData<P>);
211
212impl<P: ValidationPolicy> ValidationPolicy for DebugValidationPolicy<P> {
213    type Value = P::Value;
214    type Error = P::Error;
215
216    #[inline(always)]
217    fn validate_ref(value: &Self::Value) -> Result<(), Self::Error> {
218        // In debug builds, this implementation delegates to the inner policy `P`.
219        #[cfg(debug_assertions)]
220        {
221            // Delegate to the inner policy's validation logic.
222            P::validate_ref(value)
223        }
224        #[cfg(not(debug_assertions))]
225        {
226            let _ = value; // Avoid unused variable warning in release mode.
227            // In release mode, this is a no-op.
228            // The validation is skipped, and we always return Ok.
229            // This allows the compiler to optimize away the validation logic entirely.
230            // This is useful for performance-critical code where validation is not needed in production.
231            // Note: The `value` is not used here, but we keep it to match the signature of `validate_ref`.
232            // This way, we can still call this method without needing to change the function signature.
233            // This is particularly useful when this policy is used in generic contexts
234            // where the `validate_ref` method is expected to take a reference to the value.
235            // The `PhantomData` ensures that the type system knows about `P` even
236            // though we don't use it directly in the release build.
237            // This is a common pattern in Rust to create zero-sized types that carry type information.
238            Ok(())
239        }
240    }
241}
242
243impl<P> ValidationPolicyReal for DebugValidationPolicy<P>
244where
245    P: ValidationPolicyReal,
246{
247    const PRECISION: u32 = P::PRECISION;
248}
249
250impl<P> ValidationPolicyComplex for DebugValidationPolicy<P>
251where
252    P: ValidationPolicyComplex,
253{
254    const PRECISION: u32 = P::PRECISION;
255}
256//------------------------------------------------------------------------------------------------
257
258//------------------------------------------------------------------------------------------------
259/// Validates a complex number by checking both its real and imaginary parts.
260///
261/// This function uses the provided real validation policy `P` to validate both parts.
262/// If both parts are valid, it returns `Ok(())`. If either part is invalid,
263/// it returns an appropriate error variant from the `ErrorsValidationRawComplex` enum.
264///
265/// # Errors
266/// Returns [`ErrorsValidationRawComplex<P::Error>`](ErrorsValidationRawComplex)
267/// if either the real part, the imaginary part, or both parts fail validation
268/// using the policy `P`.
269pub(crate) fn validate_complex<P: ValidationPolicyReal>(
270    real_part: &P::Value,
271    imag_part: &P::Value,
272) -> Result<(), ErrorsValidationRawComplex<P::Error>> {
273    let real_validation = P::validate_ref(real_part);
274    let imag_validation = P::validate_ref(imag_part);
275    match (real_validation, imag_validation) {
276        (Ok(()), Ok(())) => Ok(()),
277        (Ok(()), Err(imag_err)) => {
278            Err(ErrorsValidationRawComplex::InvalidImaginaryPart { source: imag_err })
279        }
280        (Err(real_err), Ok(())) => {
281            Err(ErrorsValidationRawComplex::InvalidRealPart { source: real_err })
282        }
283        (Err(real_err), Err(imag_err)) => Err(ErrorsValidationRawComplex::InvalidBothParts {
284            real_error: real_err,
285            imag_error: imag_err,
286        }),
287    }
288}
289//------------------------------------------------------------------------------------------------
290
291//-------------------------------------------------------------
292/// Validates a value using the given policy, but only in debug builds.
293/// In release builds, this function is a no-op and should be optimized away.
294///
295/// # Panics
296///
297/// In debug builds, this function will panic if `P::validate_ref(value)` returns an `Err`.
298#[inline(always)]
299pub fn validate_in_debug<P: ValidationPolicy>(value: &P::Value, msg: &str) {
300    #[cfg(debug_assertions)]
301    {
302        if let Err(e) = P::validate_ref(value) {
303            panic!("Debug validation of {msg} value failed: {e}");
304        }
305    }
306    #[cfg(not(debug_assertions))]
307    {
308        // These are no-op in release builds
309        let _ = value;
310        let _ = msg;
311    }
312}
313//--------------------------------------------------------------------------------------------------
314
315//--------------------------------------------------------------------------------------------------
316/// Ensures both the real and imaginary parts of a `Complex<f64>` value are strictly finite.
317///
318/// This policy applies the [`StrictFinitePolicy<f64>`](StrictFinitePolicy) to both
319/// the real and imaginary components of the complex number.
320///
321/// # Errors
322///
323/// Returns [`ErrorsValidationRawComplex<ErrorsValidationRawReal<f64>>`](ErrorsValidationRawComplex)
324/// if either the real part, the imaginary part, or both parts fail the
325/// `StrictFinitePolicy<f64>` checks.
326impl ValidationPolicy for Native64RawComplexStrictFinitePolicy {
327    type Value = Complex<f64>;
328    type Error = ErrorsValidationRawComplex<ErrorsValidationRawReal<f64>>;
329
330    fn validate_ref(value: &Complex<f64>) -> Result<(), Self::Error> {
331        validate_complex::<Native64RawRealStrictFinitePolicy>(&value.re, &value.im)
332    }
333}
334
335impl<RawReal: RawRealTrait, const PRECISION: u32> ValidationPolicyReal
336    for StrictFinitePolicy<RawReal, PRECISION>
337where
338    StrictFinitePolicy<RawReal, PRECISION>:
339        ValidationPolicy<Value = RawReal, Error = <RawReal as RawScalarTrait>::ValidationErrors>,
340{
341    const PRECISION: u32 = PRECISION;
342}
343
344impl<RawComplex: RawComplexTrait, const PRECISION: u32> ValidationPolicyComplex
345    for StrictFinitePolicy<RawComplex, PRECISION>
346where
347    StrictFinitePolicy<RawComplex, PRECISION>: ValidationPolicy<
348            Value = RawComplex,
349            Error = <RawComplex as RawScalarTrait>::ValidationErrors,
350        >,
351{
352    const PRECISION: u32 = PRECISION;
353}
354
355//--------------------------------------------------------------------------------------------------
356// Rug Implementations (conditional compilation)
357//--------------------------------------------------------------------------------------------------
358
359#[cfg(feature = "rug")]
360pub(crate) mod rug_impls {
361    use super::*; // Imports items from the parent module (validation)
362    use crate::core::errors::capture_backtrace;
363    use std::num::FpCategory;
364
365    /// Ensures the [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html) value is strictly finite.
366    ///
367    /// This policy checks if the underlying [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html) value:
368    /// - Is not NaN (Not a Number).
369    /// - Is not positive nor negative Infinity.
370    /// - Has the specified precision (equal to `PRECISION`).
371    ///
372    /// Note: [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html) does not have a direct concept of "subnormal" in the same
373    /// way IEEE 754 floats do; it maintains its specified precision.
374    ///
375    /// # Errors
376    ///
377    /// Returns [`ErrorsValidationRawReal<rug::Float>`](ErrorsValidationRawReal)
378    /// if the value fails any of these checks.
379    impl<const PRECISION: u32> ValidationPolicy for StrictFinitePolicy<rug::Float, PRECISION> {
380        type Value = rug::Float;
381        type Error = ErrorsValidationRawReal<rug::Float>;
382
383        fn validate_ref(value: &rug::Float) -> Result<(), Self::Error> {
384            let actual_precision = value.prec();
385            if actual_precision == PRECISION {
386                // rug::Float uses std::num::FpCategory via its own classify method
387                match value.classify() {
388                    FpCategory::Infinite => {
389                        if value.is_sign_positive() {
390                            Err(ErrorsValidationRawReal::IsPosInfinity {
391                                backtrace: capture_backtrace(),
392                            })
393                        } else {
394                            Err(ErrorsValidationRawReal::IsNegInfinity {
395                                backtrace: capture_backtrace(),
396                            })
397                        }
398                    }
399                    FpCategory::Nan => Err(ErrorsValidationRawReal::IsNaN {
400                        value: value.clone(),
401                        backtrace: capture_backtrace(),
402                    }),
403                    FpCategory::Subnormal => Err(ErrorsValidationRawReal::IsSubnormal {
404                        value: value.clone(),
405                        backtrace: capture_backtrace(),
406                    }),
407                    FpCategory::Zero | FpCategory::Normal => Ok(()),
408                }
409            } else {
410                Err(ErrorsValidationRawReal::PrecisionMismatch {
411                    input_value: value.clone(),
412                    actual_precision,
413                    requested_precision: PRECISION,
414                    backtrace: capture_backtrace(),
415                })
416            }
417        }
418    }
419
420    /// Ensures both the real and imaginary parts of a [`rug::Complex`](https://docs.rs/rug/latest/rug/struct.Complex.html) value are strictly finite.
421    ///
422    /// This policy applies the [`StrictFinitePolicy<rug::Float>`](StrictFinitePolicy)
423    /// (which checks the underlying [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html)) to both the real and imaginary
424    /// components of the [`rug::Complex`](https://docs.rs/rug/latest/rug/struct.Complex.html).
425    ///
426    /// # Errors
427    ///
428    /// Returns [`ErrorsValidationRawComplex<ErrorsValidationRawReal<rug::Float>>`](ErrorsValidationRawComplex)
429    /// if either the real part, the imaginary part, or both parts fail the
430    /// `StrictFinitePolicy<RealRugStrictFinite<Precision>>` checks.
431    impl<const PRECISION: u32> ValidationPolicy for StrictFinitePolicy<rug::Complex, PRECISION> {
432        type Value = rug::Complex;
433        type Error = ErrorsValidationRawComplex<ErrorsValidationRawReal<rug::Float>>;
434
435        fn validate_ref(value: &rug::Complex) -> Result<(), Self::Error> {
436            validate_complex::<StrictFinitePolicy<rug::Float, PRECISION>>(
437                value.real(),
438                value.imag(),
439            )
440        }
441    }
442}
443//-------------------------------------------------------------
444
445// Implement the marker for `StrictFinitePolicy` for any real scalar type.
446impl<RawReal: RawRealTrait, const PRECISION: u32> GuaranteesFiniteRealValues
447    for StrictFinitePolicy<RawReal, PRECISION>
448where
449    // This bound is necessary to satisfy the supertrait requirements of GuaranteesFiniteRealValues
450    StrictFinitePolicy<RawReal, PRECISION>: ValidationPolicyReal,
451{
452}
453
454// Implement the marker for `DebugValidationPolicy<StrictFinitePolicy>` for any real scalar type.
455impl<RawReal: RawRealTrait, const PRECISION: u32> GuaranteesFiniteRealValues
456    for DebugValidationPolicy<StrictFinitePolicy<RawReal, PRECISION>>
457where
458    // This bound is necessary to satisfy the supertrait requirements of GuaranteesFiniteRealValues
459    StrictFinitePolicy<RawReal, PRECISION>: ValidationPolicyReal,
460{
461}
462
463// Implement the marker for `StrictFinitePolicy` for any complex scalar type.
464impl<RawComplex: RawComplexTrait, const PRECISION: u32> GuaranteesFiniteComplexValues
465    for StrictFinitePolicy<RawComplex, PRECISION>
466where
467    // This bound is necessary to satisfy the supertrait requirements of GuaranteesFiniteComplexValues
468    StrictFinitePolicy<RawComplex, PRECISION>: ValidationPolicyComplex,
469{
470}
471
472// Implement the marker for `DebugValidationPolicy<StrictFinitePolicy>` for any complex scalar type.
473impl<RawComplex: RawComplexTrait, const PRECISION: u32> GuaranteesFiniteComplexValues
474    for DebugValidationPolicy<StrictFinitePolicy<RawComplex, PRECISION>>
475where
476    // This bound is necessary to satisfy the supertrait requirements of GuaranteesFiniteComplexValues
477    StrictFinitePolicy<RawComplex, PRECISION>: ValidationPolicyComplex,
478{
479}
480//-------------------------------------------------------------