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//-------------------------------------------------------------