num_valid/core/traits/validation.rs
1#![deny(rustdoc::broken_intra_doc_links)]
2
3//! Validation traits and marker traits for scalar types.
4//!
5//! This module provides traits for floating-point classification checks and
6//! marker traits that guarantee certain properties of validated types.
7
8use crate::{
9 core::traits::raw::RawScalarValidate,
10 kernels::{RawComplexTrait, RawRealTrait},
11};
12use num::Complex;
13use num_traits::Float;
14use try_create::ValidationPolicy;
15
16pub(crate) mod sealed {
17 use crate::RawRealTrait;
18 use duplicate::duplicate_item;
19 use num_traits::{Float, FloatConst, MulAdd, MulAddAssign};
20 use std::{hash::Hash, ops::RemAssign};
21 //use try_create::IntoInner;
22
23 pub trait NativeReal:
24 RawRealTrait + Float + FloatConst + MulAdd<Output = Self> + MulAddAssign + RemAssign
25 // + IntoInner<InnerType = Self>
26 {
27 const MANTISSA_DIGITS: u32;
28 type Bits: Hash;
29
30 fn round_ties_even_native_real(&self) -> Self;
31
32 fn to_bits_native_real(&self) -> Self::Bits;
33 }
34
35 #[duplicate_item(
36 T mantissa_digits bits;
37 [f32] [24] [u32];
38 [f64] [53] [u64];
39 )]
40 impl NativeReal for T {
41 const MANTISSA_DIGITS: u32 = mantissa_digits;
42 type Bits = bits;
43
44 #[inline(always)]
45 fn round_ties_even_native_real(&self) -> Self {
46 T::round_ties_even(*self)
47 }
48
49 #[inline(always)]
50 fn to_bits_native_real(&self) -> Self::Bits {
51 T::to_bits(*self)
52 }
53 }
54}
55
56/// Provides a set of fundamental floating-point classification checks.
57///
58/// This trait defines common methods to determine the characteristics of a numerical value,
59/// such as whether it is finite, infinite, NaN (Not a Number), or normal.
60/// It is implemented for standard floating-point types (`f64`), complex numbers
61/// (`num::Complex<f64>`), and their arbitrary-precision counterparts from the `rug`
62/// library (via `RealRugStrictFinite<P>` and `ComplexRugStrictFinite<P>` which internally use [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html)
63/// and [`rug::Complex`](https://docs.rs/rug/latest/rug/struct.Complex.html)) when the "rug" feature is enabled.
64///
65/// `FpChecks` is often used as a supertrait for more general numerical traits like
66/// [`FpScalar`](crate::FpScalar), ensuring that types representing floating-point
67/// scalars can be queried for these basic properties. This is essential for
68/// validation policies and writing robust numerical algorithms.
69///
70/// Note that this trait focuses on these specific classifications. For more detailed
71/// categorization (like distinguishing subnormals or zero explicitly through this trait),
72/// types may provide other methods (e.g., `classify()` for `f64` and [`rug::Float`](https://docs.rs/rug/latest/rug/struct.Float.html),
73/// or `is_zero()` from [`FpScalar`](crate::FpScalar)).
74pub trait FpChecks {
75 /// Returns [`true`] if `self` is finite (i.e., not infinite and not NaN).
76 ///
77 /// For complex numbers, this typically means both the real and imaginary parts are finite.
78 fn is_finite(&self) -> bool;
79
80 /// Returns [`true`] if `self` is positive infinity or negative infinity.
81 ///
82 /// For complex numbers, this typically means at least one part is infinite,
83 /// and no part is NaN.
84 fn is_infinite(&self) -> bool;
85
86 /// Returns [`true`] if `self` is NaN (Not a Number).
87 ///
88 /// For complex numbers, this typically means at least one part is NaN.
89 fn is_nan(&self) -> bool;
90
91 /// Returns [`true`] if `self` is a *normal* number.
92 ///
93 /// A normal number is a finite, non-zero number that is not subnormal.
94 /// For complex numbers, this typically means both real and imaginary parts are normal
95 /// numbers (and thus individually finite, non-zero, and not subnormal).
96 fn is_normal(&self) -> bool;
97}
98
99impl<T: sealed::NativeReal> FpChecks for T {
100 #[inline(always)]
101 fn is_finite(&self) -> bool {
102 <T as Float>::is_finite(*self)
103 }
104
105 #[inline(always)]
106 fn is_infinite(&self) -> bool {
107 <T as Float>::is_infinite(*self)
108 }
109
110 #[inline(always)]
111 fn is_nan(&self) -> bool {
112 <T as Float>::is_nan(*self)
113 }
114
115 #[inline(always)]
116 fn is_normal(&self) -> bool {
117 <T as Float>::is_normal(*self)
118 }
119}
120
121impl<T: sealed::NativeReal> FpChecks for Complex<T> {
122 #[inline(always)]
123 fn is_finite(&self) -> bool {
124 self.re.is_finite() && self.im.is_finite()
125 }
126
127 #[inline(always)]
128 fn is_infinite(&self) -> bool {
129 self.re.is_infinite() || self.im.is_infinite()
130 }
131
132 #[inline(always)]
133 fn is_nan(&self) -> bool {
134 self.re.is_nan() || self.im.is_nan()
135 }
136
137 #[inline(always)]
138 fn is_normal(&self) -> bool {
139 self.re.is_normal() && self.im.is_normal()
140 }
141}
142
143//------------------------------------------------------------------------------------------------
144
145//------------------------------------------------------------------------------------------------
146/// A marker for policies that apply to real types.
147///
148/// This trait refines the generic [`ValidationPolicy`] for types that implement
149/// [`RawRealTrait`]. It adds two key constraints:
150///
151/// 1. It associates a `const PRECISION` with the policy, essential for backends
152/// like `rug`.
153/// 2. It enforces that the policy's `Error` type must be the same as the canonical
154/// `ValidationError` type defined on the raw scalar itself. This architectural
155/// choice ensures consistent error handling throughout the library.
156pub trait ValidationPolicyReal:
157 ValidationPolicy<
158 Value: RawRealTrait,
159 Error = <<Self as ValidationPolicy>::Value as RawScalarValidate>::ValidationError,
160 >
161{
162 /// The precision (in bits) of the validated real type.
163 ///
164 /// For IEEE 754 double-precision (`f64`), this is 53 bits.
165 /// For arbitrary-precision types like `rug::Float`, this can be any positive integer.
166 const PRECISION: u32;
167}
168
169/// A marker for policies that apply to complex types.
170///
171/// This trait refines the generic [`ValidationPolicy`] for types that implement
172/// [`RawComplexTrait`]. It adds two key constraints:
173///
174/// 1. It associates a `const PRECISION` with the policy, essential for backends
175/// like `rug`.
176/// 2. It enforces that the policy's `Error` type must be the same as the canonical
177/// `ValidationError` type defined on the raw scalar itself. This architectural
178/// choice ensures consistent error handling throughout the library.
179pub trait ValidationPolicyComplex:
180 ValidationPolicy<
181 Value: RawComplexTrait,
182 Error = <<Self as ValidationPolicy>::Value as RawScalarValidate>::ValidationError,
183 >
184{
185 /// The precision (in bits) of the validated complex type's components.
186 ///
187 /// For IEEE 754 double-precision (`Complex<f64>`), this is 53 bits.
188 /// For arbitrary-precision types like `rug::Complex`, this can be any positive integer.
189 const PRECISION: u32;
190}
191
192/// A marker trait for validation policies that guarantee finite real values.
193///
194/// This trait serves as a compile-time marker to identify policies that ensure
195/// all validated real values are always finite (never NaN or infinite). It enables
196/// the implementation of full equality ([`Eq`]) and hashing ([`Hash`]) for
197/// validated types, allowing them to be used as keys in hash-based collections
198/// like [`HashMap`](std::collections::HashMap) and [`HashSet`](std::collections::HashSet).
199///
200/// ## Enabled Features
201///
202/// When a validation policy implements this trait, the corresponding validated types
203/// (like [`RealValidated<K>`](crate::RealValidated)) automatically gain:
204///
205/// - **Full Equality ([`Eq`])**: Equality becomes reflexive, symmetric, and transitive
206/// since NaN values (which violate these properties) are excluded.
207/// - **Total Ordering ([`Ord`])**: Enables total ordering for validated types, allowing
208/// use in sorted collections like [`BTreeMap`](std::collections::BTreeMap) and [`BTreeSet`](std::collections::BTreeSet),
209/// and enabling standard library sorting operations.
210/// - **Hashing ([`Hash`])**: Combined with [`Eq`], this allows validated types to be
211/// used as keys in hash-based collections like [`HashMap`](std::collections::HashMap) and [`HashSet`](std::collections::HashSet).
212///
213/// ## Hashing Implementation Details
214///
215/// The [`Hash`] implementation for validated types:
216/// - Delegates to the underlying raw type's [`RawScalarTrait::compute_hash_raw_scalar()`](crate::core::traits::raw::RawScalarTrait::compute_hash_raw_scalar) method
217/// - Handles IEEE 754 floating-point edge cases correctly (e.g., signed zeros)
218/// - Maintains the contract that `a == b` implies `hash(a) == hash(b)`
219/// - Works with both native `f64`/`Complex<f64>` and arbitrary-precision `rug` types
220///
221/// ## Comparison and Ordering
222///
223/// Validated types with finite value guarantees support multiple comparison mechanisms:
224/// 1. **Partial Ordering ([`PartialOrd`])**: Always available, efficient for comparisons
225/// 2. **Total Ordering ([`Ord`])**: Available when this trait is implemented, enables sorted collections
226/// 3. **Reference-based comparison**: The library's [`Max`](crate::functions::Max)/[`Min`](crate::functions::Min)
227/// traits provide `max_by_ref()`/`min_by_ref()` methods that avoid unnecessary cloning
228///
229/// ## Use Cases
230///
231/// This marker trait enables validated numerical types to be used in contexts that
232/// require [`Eq`], [`Hash`], and [`Ord`]:
233///
234/// ```rust
235/// use num_valid::{RealNative64StrictFinite, functions::Max};
236/// use std::collections::{HashMap, HashSet, BTreeMap, BTreeSet};
237/// use try_create::TryNew;
238///
239/// // Use as HashMap keys (requires Eq + Hash)
240/// let mut scores = HashMap::new();
241/// let player1 = RealNative64StrictFinite::try_new(1.5).unwrap();
242/// scores.insert(player1, 100);
243///
244/// // Use in HashSet (requires Eq + Hash)
245/// let mut unique_values = HashSet::new();
246/// unique_values.insert(RealNative64StrictFinite::try_new(3.14).unwrap());
247///
248/// // Use in BTreeMap (requires Ord)
249/// let mut sorted_scores = BTreeMap::new();
250/// sorted_scores.insert(RealNative64StrictFinite::try_new(1.5).unwrap(), 100);
251///
252/// // Comparison works with both PartialOrd and Ord
253/// let a = RealNative64StrictFinite::try_new(1.0).unwrap();
254/// let b = RealNative64StrictFinite::try_new(2.0).unwrap();
255/// assert!(a < b); // Uses PartialOrd
256/// assert_eq!(a.clone().max(b.clone()), b); // Uses Ord::max from std (consumes values)
257/// assert_eq!(a.max_by_ref(&b), &b); // Uses library's Max trait (avoids cloning)
258/// ```
259///
260/// ## Hash Collision Considerations
261///
262/// The hashing implementation ensures:
263/// - Consistent hashes for mathematically equal values
264/// - Proper handling of floating-point edge cases
265/// - Compatibility with both native and arbitrary-precision backends
266/// - No hash collisions due to NaN values (which are excluded by design)
267///
268/// ## Currently Implemented By
269///
270/// - [`StrictFinitePolicy<T, P>`](crate::core::policies::StrictFinitePolicy): The primary policy that
271/// rejects NaN, infinity, and subnormal values.
272/// - [`DebugValidationPolicy<StrictFinitePolicy<T, P>>`](crate::core::policies::DebugValidationPolicy):
273/// A wrapper that applies strict validation only in debug builds.
274///
275/// ## Safety and Correctness
276///
277/// This trait should only be implemented by validation policies that genuinely
278/// guarantee finite values. Incorrect implementation could lead to hash collisions
279/// or violated equality contracts in the methods that rely on this guarantee.
280///
281/// The trait is designed as a marker trait (no methods) to emphasize that it's
282/// purely a compile-time contract rather than runtime functionality.
283pub trait GuaranteesFiniteRealValues: ValidationPolicyReal {}
284
285/// Marker trait indicating that a validation policy guarantees finite complex values.
286///
287/// This trait serves as a compile-time indicator that a validation policy for
288/// complex numbers ensures all validated values have finite real and imaginary components
289/// (i.e., neither component is NaN or infinity). This guarantee enables the
290/// implementation of [`Eq`] and [`Hash`] for [`ComplexValidated`](crate::core::types::ComplexValidated) types, allowing
291/// them to be used as keys in hash-based collections like [`HashMap`](std::collections::HashMap)
292/// and [`HashSet`](std::collections::HashSet).
293///
294/// ## Purpose
295///
296/// Complex numbers can have NaN or infinite components, making equality comparison
297/// problematic (NaN != NaN). By guaranteeing finiteness at the policy level,
298/// this trait allows:
299/// - Full equality ([`Eq`]) implementation for complex validated types
300/// - Consistent hashing ([`Hash`]) for use in hash-based collections
301/// - Compile-time verification of these properties
302///
303/// ## Design Rationale
304///
305/// This trait works in tandem with [`GuaranteesFiniteRealValues`] for real numbers:
306/// - [`GuaranteesFiniteRealValues`]: For real number policies
307/// - [`GuaranteesFiniteComplexValues`]: For complex number policies
308///
309/// This separation allows different validation strategies for real and complex
310/// components while maintaining type safety.
311///
312/// ## Currently Implemented By
313///
314/// - [`StrictFinitePolicy<T, P>`](crate::core::policies::StrictFinitePolicy): Validates both real and imaginary
315/// components for finiteness and non-subnormal values.
316/// - [`DebugValidationPolicy<StrictFinitePolicy<T, P>>`](crate::core::policies::DebugValidationPolicy):
317/// Applies strict validation only in debug builds.
318///
319/// ## Safety and Correctness
320///
321/// This trait should only be implemented by validation policies that genuinely
322/// guarantee finite values in both components. Incorrect implementation could lead
323/// to hash collisions or violated equality contracts.
324pub trait GuaranteesFiniteComplexValues: ValidationPolicyComplex {}