strict_num_extended/
lib.rs

1//! # Finite Floating-Point Types
2//!
3//! This module provides finite floating-point types. All types guarantee finite values
4//! (excluding NaN and infinity) **automatically** - no need to manually specify `is_finite()`:
5//! - `FinF32` and `FinF64`: Finite floating-point numbers (excludes NaN and infinity)
6//! - `NonNegativeF32` and `NonNegativeF64`: Non-negative floating-point numbers (>= 0, finite)
7//! - `NonZeroF32` and `NonZeroF64`: Non-zero floating-point numbers (!= 0, excludes 0.0, -0.0, NaN, infinity)
8//! - `PositiveF32` and `PositiveF64`: Positive floating-point numbers (> 0, finite)
9//! - `NonPositiveF32` and `NonPositiveF64`: Non-positive floating-point numbers (<= 0, finite)
10//! - `NegativeF32` and `NegativeF64`: Negative floating-point numbers (< 0, finite)
11//! - `NormalizedF32` and `NormalizedF64`: Normalized floating-point numbers (0.0 <= value <= 1.0, finite)
12//! - `NegativeNormalizedF32` and `NegativeNormalizedF64`: Negative normalized floating-point numbers (-1.0 <= value <= 0.0, finite)
13//! - `SymmetricF32` and `SymmetricF64`: Symmetric floating-point numbers (-1.0 <= value <= 1.0, finite)
14//! - `PiBoundedF32` and `PiBoundedF64`: PI-bounded floating-point numbers (-PI <= value <= PI, finite)
15//!
16//! ## Feature Flags
17//!
18//! ### `serde` (optional)
19//!
20//! When the `serde` feature is enabled, all types implement `serde::Serialize` and
21//! `serde::Deserialize` traits:
22//!
23//! Example usage with serde:
24//!
25//! ```rust,ignore
26//! use strict_num_extended::FinF32;
27//! use serde_json;
28//!
29//! let value = FinF32::new(3.14).unwrap();
30//! let json = serde_json::to_string(&value).unwrap();
31//! let deserialized: FinF32 = serde_json::from_str(&json).unwrap();
32//! ```
33//!
34//! **Note**: This example requires the `std` feature (for `serde_json`).
35//! For `no_std` environments with `serde`, use alternative serialization formats.
36//!
37//! ## Type Safety
38//!
39//! This library provides type safety through both compile-time and runtime guarantees:
40//!
41//! ### Compile-Time Safety
42//!
43//! Create constants at compile time with guaranteed validity:
44//!
45//! ```
46//! use strict_num_extended::*;
47//!
48//! const MAX_VALUE: PositiveF64 = PositiveF64::new_const(100.0);
49//! const HALF: NormalizedF32 = NormalizedF32::new_const(0.5);
50//! ```
51//!
52//! The `new_const()` method validates constraints at compile time, ensuring invalid values
53//! are caught before your code even runs.
54//!
55//! ### Runtime Safety
56//!
57//! At runtime, all operations automatically:
58//! - **Validate value ranges** when creating instances
59//! - **Detect overflow** in arithmetic operations
60//! - **Return detailed errors** via `Result<T, FloatError>` for any violation
61//!
62//! ```
63//! use strict_num_extended::*;
64//!
65//! // Value creation validates ranges
66//! let value = PositiveF64::new(42.0);
67//! assert!(value.is_ok());
68//!
69//! // Invalid value returns error
70//! let invalid = PositiveF64::new(-1.0);
71//! assert!(invalid.is_err());
72//! ```
73//!
74//! This two-layer safety approach ensures your floating-point code is correct both at
75//! compile time and at runtime, catching errors early and preventing undefined behavior.
76//!
77//! ```
78//! use strict_num_extended::*;
79//!
80//! // Arithmetic detects overflow
81//! let a = PositiveF64::new(1e308).unwrap();
82//! let b = PositiveF64::new(1e308).unwrap();
83//! let result = a + b;  // Returns Result, detects overflow
84//! assert!(result.is_err());
85//! ```
86//!
87//! ### Basic Usage
88//!
89//! ```
90//! use strict_num_extended::{FinF32, NonNegativeF32, PositiveF32, SymmetricF32};
91//!
92//! // Create finite floating-point numbers (no NaN or infinity)
93//! const FINITE: FinF32 = FinF32::new_const(3.14);
94//! assert_eq!(FINITE.get(), 3.14);
95//!
96//! // Rejected: NaN and infinity are not allowed
97//! assert!(FinF32::new(f32::NAN).is_err());
98//! assert!(FinF32::new(f32::INFINITY).is_err());
99//!
100//! // Non-negative numbers (>= 0)
101//! const NON_NEGATIVE: NonNegativeF32 = NonNegativeF32::new_const(42.0);
102//! assert!(NON_NEGATIVE >= NonNegativeF32::new_const(0.0));
103//!
104//! // Positive numbers (> 0)
105//! const POSITIVE: PositiveF32 = PositiveF32::new_const(10.0);
106//! assert!(POSITIVE.get() > 0.0);
107//!
108//! // Arithmetic operations preserve constraints
109//! let result = (POSITIVE + POSITIVE).unwrap();
110//! assert_eq!(result.get(), 20.0);
111//!
112//! // Symmetric numbers in range [-1.0, 1.0]
113//! const SYMMETRIC: SymmetricF32 = SymmetricF32::new_const(0.75);
114//! assert_eq!(SYMMETRIC.get(), 0.75);
115//!
116//! // Negation is reflexive (Symmetric → Symmetric)
117//! let negated: SymmetricF32 = -SYMMETRIC;
118//! assert_eq!(negated.get(), -0.75);
119//! ```
120//!
121//! ## Composable Constraints
122//!
123//! All constraints can be freely combined. For example, `PositiveF32` combines
124//! `Positive` (> 0) constraint:
125//!
126//! ```
127//! use strict_num_extended::*;
128//!
129//! // Use predefined combined types
130//! let positive: PositiveF32 = PositiveF32::new(10.0).unwrap();
131//! ```
132//!
133//! Additionally, `Option` versions are provided for handling potentially failing operations.
134//!
135//! # Examples
136//!
137//! ## Quick Overview
138//!
139//! ```
140//! use strict_num_extended::{FinF32, NonNegativeF32, PositiveF32};
141//!
142//! // Create finite floating-point numbers (no NaN or infinity)
143//! const FINITE: FinF32 = FinF32::new_const(3.14);
144//! assert_eq!(FINITE.get(), 3.14);
145//!
146//! // Rejected: NaN and infinity are not allowed
147//! assert!(FinF32::new(f32::NAN).is_err());
148//! assert!(FinF32::new(f32::INFINITY).is_err());
149//!
150//! // Non-negative numbers (>= 0)
151//! const NON_NEGATIVE: NonNegativeF32 = NonNegativeF32::new_const(42.0);
152//! assert!(NON_NEGATIVE >= NonNegativeF32::new_const(0.0));
153//!
154//! // Positive numbers (> 0)
155//! const POSITIVE: PositiveF32 = PositiveF32::new_const(10.0);
156//! assert!(POSITIVE.get() > 0.0);
157//! ```
158//!
159//! ## Type Conversions
160//!
161//! ### From/TryFrom Traits
162//!
163//! Conversions between constraint types and primitive types are provided through
164//! the standard `From` and `TryFrom` traits:
165//!
166//! ```
167//! use strict_num_extended::*;
168//! use std::convert::TryInto;
169//!
170//! // 1. Constraint type → Primitive (always succeeds)
171//! let fin = FinF32::new(2.5).unwrap();
172//! let f32_val: f32 = fin.into();  // Using From trait
173//! assert_eq!(f32_val, 2.5);
174//!
175//! // 2. Primitive → Constraint type (validated)
176//! let fin: Result<FinF32, _> = FinF32::try_from(3.14);
177//! assert!(fin.is_ok());
178//!
179//! let invalid: Result<FinF32, _> = FinF32::try_from(f32::NAN);
180//! assert!(invalid.is_err());
181//! ```
182//!
183//! ### Subset → Superset Conversions
184//!
185//! Conversions from more constrained types to less constrained types use `From`
186//! and always succeed:
187//!
188//! ```
189//! use strict_num_extended::*;
190//!
191//! // Normalized → Fin (subset to superset)
192//! let normalized = NormalizedF32::new(0.5).unwrap();
193//! let fin: FinF32 = normalized.into();  // Always succeeds
194//! assert_eq!(fin.get(), 0.5);
195//!
196//! // NonNegative → Fin
197//! let non_negative = NonNegativeF64::new(42.0).unwrap();
198//! let fin: FinF64 = non_negative.into();
199//! ```
200//!
201//! ### Superset → Subset Conversions
202//!
203//! Conversions from less constrained types to more constrained types use `TryFrom`
204//! and may fail:
205//!
206//! ```
207//! use strict_num_extended::*;
208//! use std::convert::TryInto;
209//!
210//! // Fin → Normalized (may fail)
211//! let fin = FinF64::new(0.5).unwrap();
212//! let normalized: Result<NormalizedF64, _> = fin.try_into();
213//! assert!(normalized.is_ok());
214//!
215//! let fin_out_of_range = FinF64::new(2.0).unwrap();
216//! let normalized: Result<NormalizedF64, _> = fin_out_of_range.try_into();
217//! assert!(normalized.is_err());  // Out of range
218//! ```
219//!
220//! ### F32 ↔ F64 Conversions
221//!
222//! Conversions between F32 and F64 types are supported with precision awareness:
223//!
224//! ```
225//! use strict_num_extended::*;
226//!
227//! // F32 → F64 (always safe, lossless)
228//! let fin32 = FinF32::new(3.0).unwrap();
229//! let fin64: FinF64 = fin32.into();  // From trait
230//! assert_eq!(fin64.get(), 3.0);
231//!
232//! // F64 → F32 (may overflow or lose precision)
233//! let fin64_small = FinF64::new(3.0).unwrap();
234//! let fin32: Result<FinF32, _> = fin64_small.try_into();
235//! assert!(fin32.is_ok());
236//!
237//! let fin64_large = FinF64::new(1e40).unwrap();
238//! let fin32: Result<FinF32, _> = fin64_large.try_into();
239//! assert!(fin32.is_err());  // F32 range overflow
240//! ```
241//!
242//! ## F32/F64 Conversion Methods
243//!
244//! For explicit conversions between F32 and F64 types, specialized methods are provided:
245//!
246//! ### `try_into_f32_type()` - F64 → F32 with Constraint Validation
247//!
248//! The `try_into_f32_type()` method converts F64 types to F32 with constraint validation:
249//!
250//! ```
251//! use strict_num_extended::*;
252//!
253//! // Success: value fits in F32 and satisfies constraint
254//! let f64_val = FinF64::new(3.0).unwrap();
255//! let f32_val: Result<FinF32, _> = f64_val.try_into_f32_type();
256//! assert!(f32_val.is_ok());
257//!
258//! // Success: precision loss is allowed
259//! let f64_precise = FinF64::new(1.234_567_890_123_456_7).unwrap();
260//! let f32_result: Result<FinF32, _> = f64_precise.try_into_f32_type();
261//! assert!(f32_result.is_ok());  // Truncated to f32, but still valid
262//!
263//! // Error: F32 range overflow (becomes infinity)
264//! let f64_large = FinF64::new(1e40).unwrap();
265//! let f32_result: Result<FinF32, _> = f64_large.try_into_f32_type();
266//! assert!(f32_result.is_err());  // Infinity is rejected
267//! ```
268//!
269//! ### `as_f64_type()` - F32 → F64 Lossless Conversion
270//!
271//! The `as_f64_type()` method converts F32 types to F64 without loss of precision:
272//!
273//! ```
274//! use strict_num_extended::*;
275//!
276//! let f32_val = FinF32::new(2.5).unwrap();
277//! let f64_val: FinF64 = f32_val.as_f64_type();  // Always succeeds
278//! assert_eq!(f64_val.get(), 2.5);
279//!
280//! // Roundtrip: F32 → F64 → F32
281//! let original_f32 = FinF32::new(2.5).unwrap();
282//! let f64_val: FinF64 = original_f32.as_f64_type();
283//! let back_to_f32: Result<FinF32, _> = f64_val.try_into_f32_type();
284//! assert!(back_to_f32.is_ok());
285//! assert_eq!(back_to_f32.unwrap().get(), original_f32.get());
286//! ```
287//!
288//! These methods support const contexts for creating values:
289//!
290//! ```
291//! use strict_num_extended::*;
292//!
293//! const F64_VAL: FinF64 = FinF64::new_const(42.0);
294//! const F32_VAL: FinF32 = FinF32::new_const(3.0);
295//! ```
296//!
297//! > **Type Inference for Arithmetic Operations**: The library automatically infers the appropriate
298//! > result type for arithmetic operations based on operand properties. The library supports
299//! > standard Rust `From` and `TryFrom` traits for seamless conversions between constraint types,
300//! > along with F32 ↔ F64 conversions with precision awareness. See the detailed examples above
301//! > for conversion rules and patterns.
302//!
303//! ## Arithmetic Operations
304//!
305//! Arithmetic operations automatically validate results and return either the target type
306//! or `Result<T, FloatError>` for potentially failing operations. Safe operations (like
307//! bounded type multiplication) return direct values:
308//!
309//! ```
310//! use strict_num_extended::PositiveF32;
311//!
312//! const A: PositiveF32 = PositiveF32::new_const(10.0);
313//! const B: PositiveF32 = PositiveF32::new_const(20.0);
314//!
315//! // Addition returns Result (overflow possible)
316//! let sum = (A + B).unwrap();
317//! assert_eq!(sum.get(), 30.0);
318//!
319//! // Multiplication returns Result (overflow possible for unbounded types)
320//! let product = (A * B).unwrap();
321//! assert_eq!(product.get(), 200.0);
322//! ```
323//!
324//! ### Type Inference and Conversion Rules
325//!
326//! Arithmetic operations automatically infer the appropriate result type based on the operands'
327//! properties. The type system guarantees correctness through compile-time and runtime validation.
328//!
329//! #### Operation Safety Classification
330//!
331//! Operations are classified as either **safe** or **fallible**:
332//!
333//! - **Safe Operations**: Always produce valid results within the inferred type's constraints.
334//!   Return the result directly without `Result` wrapping.
335//! - **Fallible Operations**: May produce invalid results (overflow, division by zero, etc.).
336//!   Return `Result<T, FloatError>` for explicit error handling.
337//!
338//! #### Type Inference Rules
339//!
340//! The result type is determined by analyzing:
341//!
342//! 1. **Sign Properties** (Positive, Negative, Any)
343//! 2. **Bound Properties** (bounded/unbounded ranges)
344//! 3. **Zero Exclusion** (whether the type excludes zero)
345//!
346//! **Addition Rules**:
347//!
348//! - `Positive + Positive → Positive` (fallible, may overflow)
349//! - `Negative + Negative → Negative` (fallible, may overflow)
350//! - `Positive + Negative → Fin` (safe, result has no sign constraint)
351//! - `NonZero + NonZero (same sign) → NonZero` (fallible, may overflow)
352//! - `NonZero + NonZero (different signs) → Fin` (safe)
353//!
354//! **Subtraction Rules**:
355//!
356//! - `Positive - Positive → Fin` (safe, result may have different sign)
357//! - `Negative - Negative → Fin` (safe, result may have different sign)
358//! - `Positive - Negative → Positive` (fallible, may overflow)
359//! - `Negative - Positive → Negative` (fallible, may overflow)
360//! - `NonZero - NonZero → Fin` (fallible if same sign, safe if different signs)
361//!
362//! **Multiplication Rules**:
363//!
364//! - `Positive × Positive → Positive` (fallible)
365//! - `Negative × Negative → Positive` (fallible)
366//! - `Positive × Negative → Negative` (fallible)
367//! - `Bounded × Bounded → Bounded` (safe, result bounds computed from operand bounds)
368//! - `NonZero × NonZero → NonZero` (fallible)
369//!
370//! **Division Rules**:
371//!
372//! - `Positive ÷ Positive → Positive` (fallible, division by zero detection)
373//! - `Negative ÷ Negative → Positive` (fallible, division by zero detection)
374//! - `Positive ÷ Negative → Negative` (fallible, division by zero detection)
375//! - `Bounded (in [-1,1]) ÷ NonZero → Bounded` (safe, when dividend is within [-1,1])
376//! - `NonZero ÷ NonZero → NonZero` (fallible, division by zero detection)
377//!
378//! #### Examples
379//!
380//! ```
381//! use strict_num_extended::*;
382//!
383//! // Safe operation: returns direct value
384//! let a = PositiveF64::new(5.0).unwrap();
385//! let b = NegativeF64::new(-3.0).unwrap();
386//! let result: FinF64 = a + b;  // Returns FinF64 directly
387//! assert_eq!(result.get(), 2.0);
388//!
389//! // Fallible operation: returns Result
390//! let x = PositiveF64::new(10.0).unwrap();
391//! let y = PositiveF64::new(5.0).unwrap();
392//! let result: Result<PositiveF64, FloatError> = x + y;  // May overflow
393//! assert!(result.is_ok());
394//!
395//! // Safe bounded multiplication
396//! let norm1 = NormalizedF64::new(0.5).unwrap();
397//! let norm2 = NormalizedF64::new(0.4).unwrap();
398//! let product: NormalizedF64 = norm1 * norm2;  // Always in [0,1]
399//! assert_eq!(product.get(), 0.2);
400//!
401//! // Error propagation in Result operations
402//! let valid: Result<PositiveF64, FloatError> = Ok(PositiveF64::new(10.0).unwrap());
403//! const B: NegativeF64 = NegativeF64::new_const(-3.0);
404//! let result: Result<FinF64, FloatError> = valid + B;  // Result + Concrete
405//! assert!(result.is_ok());
406//! assert_eq!(result.unwrap().get(), 7.0);
407//!
408//! // Error propagation when Result is Err
409//! let invalid: Result<PositiveF64, FloatError> = Err(FloatError::OutOfRange);
410//! let error_result: Result<FinF64, FloatError> = invalid + B;  // Error propagates
411//! assert!(error_result.is_err());
412//! ```
413//!
414//! #### Precision and Range Validation
415//!
416//! All arithmetic operations automatically validate:
417//!
418//! - **Range Constraints**: Result values must satisfy the target type's bounds
419//! - **Overflow Detection**: Operations that may exceed representable ranges return errors
420//! - **NaN/Infinity**: Operations producing NaN or infinity return `FloatError::NaN`
421//!
422//! This ensures mathematical correctness while maintaining ergonomic API design through automatic type inference.
423//!
424//! ## Comparison Operations
425//!
426//! All types support full ordering operations:
427//!
428//! ```
429//! use strict_num_extended::PositiveF32;
430//!
431//! const A: PositiveF32 = PositiveF32::new_const(5.0);
432//! const B: PositiveF32 = PositiveF32::new_const(10.0);
433//!
434//! assert!(A < B);
435//! assert!(B > A);
436//! assert!(A <= B);
437//! assert!(B >= A);
438//! assert_ne!(A, B);
439//! ```
440//!
441//! ## Result Type Arithmetic
442//!
443//! Arithmetic operations between `Result<T, FloatError>` and concrete types are supported
444//! with automatic error propagation:
445//!
446//! ### Result op Concrete
447//!
448//! ```
449//! use strict_num_extended::*;
450//!
451//! // Result<LHS> op Concrete<RHS>
452//! let a: Result<PositiveF64, FloatError> = Ok(PositiveF64::new(5.0).unwrap());
453//! const B: NegativeF64 = NegativeF64::new_const(-3.0);
454//! let result: Result<FinF64, FloatError> = a + B;
455//! assert!(result.is_ok());
456//! assert_eq!(result.unwrap().get(), 2.0);
457//!
458//! // Error propagation
459//! let err: Result<PositiveF64, FloatError> = Err(FloatError::NaN);
460//! let result: Result<FinF64, FloatError> = err + B;
461//! assert!(result.is_err());
462//! ```
463//!
464//! ### Concrete op Result
465//!
466//! ```
467//! use strict_num_extended::*;
468//!
469//! // Concrete<LHS> op Result<RHS>
470//! const A: PositiveF64 = PositiveF64::new_const(10.0);
471//! let b: Result<NegativeF64, FloatError> = Ok(NegativeF64::new(-3.0).unwrap());
472//! let result: Result<FinF64, FloatError> = A + b;
473//! assert!(result.is_ok());
474//! assert_eq!(result.unwrap().get(), 7.0);
475//!
476//! // Error on RHS
477//! let err_b: Result<NegativeF64, FloatError> = Err(FloatError::PosInf);
478//! let result: Result<FinF64, FloatError> = A + err_b;
479//! assert!(result.is_err());
480//! ```
481//!
482//! ### Error Propagation Rules
483//!
484//! - If LHS is `Err`, the error propagates directly
485//! - If RHS is `Err`, the error propagates directly
486//! - If both are `Ok`, the operation proceeds with normal validation
487//! - Division by zero returns `Err(FloatError::NaN)`
488//!
489//! ```
490//! use strict_num_extended::*;
491//!
492//! // Division by zero detection
493//! let a: Result<NonNegativeF64, FloatError> = Ok(NonNegativeF64::new(10.0).unwrap());
494//! const ZERO: NonNegativeF64 = NonNegativeF64::new_const(0.0);
495//! let result: Result<NonNegativeF64, FloatError> = a / ZERO;
496//! assert!(result.is_err());
497//! assert_eq!(result.unwrap_err(), FloatError::NaN);
498//! ```
499//!
500//! ## Option Type Arithmetic
501//!
502//! Arithmetic operations with `Option<T>` types provide graceful handling of optional values:
503//!
504//! ### Safe Operations (Concrete op Option)
505//!
506//! Safe operations (like `Positive + Negative`) return `Option<Output>`:
507//!
508//! ```
509//! use strict_num_extended::*;
510//!
511//! const A: PositiveF64 = PositiveF64::new_const(5.0);
512//! let b: Option<NegativeF64> = Some(NegativeF64::new(-3.0).unwrap());
513//! let result: Option<FinF64> = A + b;
514//! assert!(result.is_some());
515//! assert_eq!(result.unwrap().get(), 2.0);
516//!
517//! // None propagation
518//! let none_b: Option<NegativeF64> = None;
519//! let result: Option<FinF64> = A + none_b;
520//! assert!(result.is_none());
521//! ```
522//!
523//! ### Unsafe Operations (Concrete op Option)
524//!
525//! Unsafe operations (multiplication, division) return `Result<Output, FloatError>`:
526//!
527//! ```
528//! use strict_num_extended::*;
529//!
530//! const A: PositiveF64 = PositiveF64::new_const(5.0);
531//! let b: Option<PositiveF64> = Some(PositiveF64::new(3.0).unwrap());
532//! let result: Result<PositiveF64, FloatError> = A * b;
533//! assert!(result.is_ok());
534//! assert_eq!(result.unwrap().get(), 15.0);
535//!
536//! // None operand returns error
537//! let none_b: Option<PositiveF64> = None;
538//! let result: Result<PositiveF64, FloatError> = A / none_b;
539//! assert!(result.is_err());
540//! assert_eq!(result.unwrap_err(), FloatError::NoneOperand);
541//! ```
542//!
543//! ### Chaining Option Operations
544//!
545//! Option arithmetic can be chained for complex calculations:
546//!
547//! ```
548//! use strict_num_extended::*;
549//!
550//! const A: PositiveF64 = PositiveF64::new_const(10.0);
551//! let b: Option<NegativeF64> = Some(NegativeF64::new(-2.0).unwrap());
552//! let c: Option<PositiveF64> = Some(PositiveF64::new(3.0).unwrap());
553//!
554//! // Safe operation returns Option
555//! let step1: Option<FinF64> = A + b;  // Some(8.0)
556//! assert!(step1.is_some());
557//!
558//! // Chain with unsafe operation
559//! let result: Result<FinF64, FloatError> = step1.map(|x| x * c).unwrap();
560//! assert!(result.is_ok());
561//! assert_eq!(result.unwrap().get(), 24.0);
562//! ```
563//!
564//! ## Result & Option Arithmetic Overview
565//!
566//! The library provides comprehensive support for arithmetic operations with `Result<T, FloatError>`
567//! and `Option<T>` types:
568//!
569//! ### Result Types
570//!
571//! Automatic error propagation for arithmetic operations with `Result<T, FloatError>`:
572//!
573//! - Operations between `Result<T>` and concrete types automatically propagate errors
574//! - If either operand is `Err`, the error is forwarded directly
575//! - When both operands are `Ok`, the operation proceeds with normal validation
576//! - Division by zero returns `FloatError::NaN`
577//!
578//! This eliminates verbose error handling boilerplate in calculations that may fail.
579//!
580//! ### Option Types
581//!
582//! Graceful handling of optional values in arithmetic operations:
583//!
584//! - **Safe Operations** (e.g., `Positive + Negative`): Return `Option<Output>`, propagating `None` automatically
585//! - **Unsafe Operations** (e.g., multiplication, division): Return `Result<Output, FloatError>`, with `None` operands converted to `FloatError::NoneOperand`
586//! - Supports chaining operations for complex calculations with optional values
587//!
588//! This design provides ergonomic handling of missing values without nested match expressions.
589//!
590//! > **Note**: For more detailed examples and API documentation, see the specific sections above
591//! > covering [Result Type Arithmetic](#result-type-arithmetic) and [Option Type Arithmetic](#option-type-arithmetic).
592//!
593//! # Error Handling
594//!
595//! All operations that can fail return `Result<T, FloatError>`. The `FloatError` enum
596//! provides detailed error information:
597//!
598//! ## Error Types
599//!
600//! - `FloatError::NaN` - Value is NaN (Not a Number)
601//! - `FloatError::PosInf` - Value is positive infinity
602//! - `FloatError::NegInf` - Value is negative infinity
603//! - `FloatError::OutOfRange` - Value is outside the valid range for the target type
604//! - `FloatError::NoneOperand` - Right-hand side operand is None in Option arithmetic
605//!
606//! ## Example: Error Handling
607//!
608//! ```
609//! use strict_num_extended::{FinF32, NonZeroF32, FloatError, NormalizedF32, PositiveF64};
610//!
611//! // Successful creation
612//! let valid: Result<FinF32, FloatError> = FinF32::new(3.14);
613//! assert!(valid.is_ok());
614//!
615//! // NaN error
616//! let nan: Result<FinF32, FloatError> = FinF32::new(f32::NAN);
617//! assert!(nan.is_err());
618//! assert_eq!(nan.unwrap_err(), FloatError::NaN);
619//!
620//! // Infinity error
621//! let inf: Result<FinF32, FloatError> = FinF32::new(f32::INFINITY);
622//! assert!(inf.is_err());
623//! assert_eq!(inf.unwrap_err(), FloatError::PosInf);
624//!
625//! // Out of range error
626//! let out_of_range: Result<NormalizedF32, FloatError> = NormalizedF32::new(2.0);
627//! assert!(out_of_range.is_err());
628//! assert_eq!(out_of_range.unwrap_err(), FloatError::OutOfRange);
629//!
630//! // Division by zero is prevented at creation time
631//! let a = PositiveF64::new(10.0).unwrap();
632//! let zero_result = PositiveF64::new(0.0);
633//! assert!(zero_result.is_err());  // Cannot create zero value
634//! assert_eq!(zero_result.unwrap_err(), FloatError::OutOfRange);
635//! ```
636//!
637//! ## Practical Example: Safe Division Function
638//!
639//! ```
640//! use strict_num_extended::{FinF32, NonZeroF32, FloatError};
641//!
642//! fn safe_divide(
643//!     numerator: Result<FinF32, FloatError>,
644//!     denominator: Result<NonZeroF32, FloatError>,
645//! ) -> Result<FinF32, FloatError> {
646//!     match (numerator, denominator) {
647//!         (Ok(num), Ok(denom)) => {
648//!             // Safe: denom is guaranteed non-zero
649//!             FinF32::new(num.get() / denom.get())
650//!         }
651//!         (Err(e), _) => Err(e),
652//!         (Ok(_), Err(e)) => Err(e),
653//!     }
654//! }
655//!
656//! let result = safe_divide(
657//!     FinF32::new(10.0),
658//!     NonZeroF32::new(2.0)
659//! );
660//! assert!(result.is_ok());
661//! assert_eq!(result.unwrap().get(), 5.0);
662//!
663//! // Division by zero is prevented
664//! let invalid = safe_divide(
665//!     FinF32::new(10.0),
666//!     NonZeroF32::new(0.0)  // Returns Err
667//! );
668//! assert!(invalid.is_err());
669//! ```
670//!
671//! # Compile-Time Constants
672//!
673//! ```
674//! use strict_num_extended::FinF32;
675//!
676//! const ONE: FinF32 = FinF32::new_const(1.0);
677//! assert_eq!(ONE.get(), 1.0);
678//! ```
679//!
680//! **Note**: The `new_const` method now supports compile-time validation and will panic at
681//! compile time if the value does not satisfy the constraint conditions.
682//!
683//! ### Unsafe Creation
684//!
685//! For performance-critical code where you can guarantee validity, use `new_unchecked()`:
686//!
687//! ```
688//! use strict_num_extended::*;
689//!
690//! // WARNING: Only use when you can guarantee the value is valid!
691//! // Undefined behavior if constraint is violated
692//! const FIN: FinF32 = unsafe { FinF32::new_unchecked(3.14) };
693//! assert_eq!(FIN.get(), 3.14);
694//!
695//! // Safe usage: compile-time constant
696//! const ONE: PositiveF32 = unsafe { PositiveF32::new_unchecked(1.0) };
697//!
698//! // UNSAFE: Invalid value causes undefined behavior
699//! // const NAN: FinF32 = unsafe { FinF32::new_unchecked(f32::NAN) };
700//! ```
701//!
702//! **Note**: Prefer `new_const()` for compile-time constants. It provides validation
703//! during compilation and is safer than `new_unchecked()`.
704//!
705//! # How Constraints Work
706//!
707//! All types automatically enforce constraints through runtime validation and compile-time checks:
708//!
709//! - **Compile-time**: Use `new_const()` to create validated constants
710//! - **Runtime**: Use `new()` which returns `Result<T, FloatError>` after validation
711//!
712//! Constraint violations are caught early:
713//! - At compile time for constants (if value is invalid)
714//! - At runtime for dynamic values (returns `FloatError`)
715//!
716//! ## Finite Constraint
717//!
718//! The `Fin` types exclude only NaN and infinity, accepting all other finite values:
719//!
720//! ```
721//! use strict_num_extended::FinF32;
722//!
723//! let valid = FinF32::new(3.14);         // Ok(value)
724//! let invalid = FinF32::new(f32::NAN);    // Err(FloatError::NaN)
725//! let invalid = FinF32::new(f32::INFINITY); // Err(FloatError::PosInf)
726//! ```
727//!
728//! ## `NonNegative` Constraint
729//!
730//! The `NonNegative` types require finite AND non-negative values (x ≥ 0):
731//!
732//! ```
733//! use strict_num_extended::NonNegativeF32;
734//!
735//! let valid = NonNegativeF32::new(0.0);      // Ok(value)
736//! let valid = NonNegativeF32::new(1.5);      // Ok(value)
737//! let invalid = NonNegativeF32::new(-1.0);   // Err(FloatError::OutOfRange) (negative)
738//! let invalid = NonNegativeF32::new(f32::INFINITY); // Err(FloatError::PosInf) (infinite)
739//! ```
740//!
741//! ## `NonPositive` Constraint
742//!
743//! The `NonPositive` types require finite AND non-positive values (x ≤ 0):
744//!
745//! ```
746//! use strict_num_extended::NonPositiveF32;
747//!
748//! let valid = NonPositiveF32::new(0.0);      // Ok(value)
749//! let valid = NonPositiveF32::new(-1.5);     // Ok(value)
750//! let invalid = NonPositiveF32::new(1.0);    // Err(FloatError::OutOfRange) (positive)
751//! let invalid = NonPositiveF32::new(f32::NEG_INFINITY); // Err(FloatError::NegInf) (infinite)
752//! ```
753//!
754//! ## `NonZero` Constraint
755//!
756//! The `NonZero` types require finite AND non-zero values (x ≠ 0), excluding both +0.0 and -0.0:
757//!
758//! ```
759//! use strict_num_extended::NonZeroF32;
760//!
761//! let valid = NonZeroF32::new(1.0);       // Ok(value)
762//! let valid = NonZeroF32::new(-1.0);      // Ok(value)
763//! let invalid = NonZeroF32::new(0.0);     // Err(FloatError::OutOfRange) (zero)
764//! let invalid = NonZeroF32::new(-0.0);    // Err(FloatError::OutOfRange) (negative zero)
765//! ```
766//!
767//! ## Positive Constraint
768//!
769//! The `Positive` types require finite AND positive values (x > 0):
770//!
771//! ```
772//! use strict_num_extended::PositiveF32;
773//!
774//! let valid = PositiveF32::new(1.0);       // Ok(value)
775//! let valid = PositiveF32::new(0.001);     // Ok(value)
776//! let invalid = PositiveF32::new(0.0);     // Err(FloatError::OutOfRange) (zero)
777//! let invalid = PositiveF32::new(-1.0);    // Err(FloatError::OutOfRange) (negative)
778//! let invalid = PositiveF32::new(f32::INFINITY); // Err(FloatError::PosInf) (infinite)
779//! ```
780//!
781//! ## Negative Constraint
782//!
783//! The `Negative` types require finite AND negative values (x < 0):
784//!
785//! ```
786//! use strict_num_extended::NegativeF32;
787//!
788//! let valid = NegativeF32::new(-1.0);      // Ok(value)
789//! let valid = NegativeF32::new(-0.001);    // Ok(value)
790//! let invalid = NegativeF32::new(0.0);     // Err(FloatError::OutOfRange) (zero)
791//! let invalid = NegativeF32::new(1.0);     // Err(FloatError::OutOfRange) (positive)
792//! let invalid = NegativeF32::new(f32::NEG_INFINITY); // Err(FloatError::NegInf) (infinite)
793//! ```
794//!
795//! ## Normalized Constraint
796//!
797//! The `Normalized` types require finite values in [0.0, 1.0]:
798//!
799//! ```
800//! use strict_num_extended::NormalizedF32;
801//!
802//! let valid = NormalizedF32::new(0.75);   // Ok(value)
803//! let valid = NormalizedF32::new(0.0);    // Ok(value)
804//! let valid = NormalizedF32::new(1.0);    // Ok(value)
805//! let invalid = NormalizedF32::new(1.5);  // Err(FloatError::OutOfRange) (> 1.0)
806//! let invalid = NormalizedF32::new(-0.5); // Err(FloatError::OutOfRange) (< 0.0)
807//! ```
808//!
809//! ## `NegativeNormalized` Constraint
810//!
811//! The `NegativeNormalized` types require finite values in [-1.0, 0.0]:
812//!
813//! ```
814//! use strict_num_extended::NegativeNormalizedF32;
815//!
816//! let valid = NegativeNormalizedF32::new(-0.75);  // Ok(value)
817//! let valid = NegativeNormalizedF32::new(-1.0);   // Ok(value)
818//! let valid = NegativeNormalizedF32::new(0.0);    // Ok(value)
819//! let invalid = NegativeNormalizedF32::new(1.5);  // Err(FloatError::OutOfRange) (> 0.0)
820//! let invalid = NegativeNormalizedF32::new(-1.5); // Err(FloatError::OutOfRange) (< -1.0)
821//! ```
822//!
823//! ## `Symmetric` Constraint
824//!
825//! The `Symmetric` types require finite values in [-1.0, 1.0]:
826//!
827//! ```
828//! use strict_num_extended::SymmetricF32;
829//!
830//! let valid = SymmetricF32::new(0.75);   // Ok(value)
831//! let valid = SymmetricF32::new(-0.5);   // Ok(value)
832//! let valid = SymmetricF32::new(1.0);    // Ok(value)
833//! let valid = SymmetricF32::new(-1.0);   // Ok(value)
834//! let invalid = SymmetricF32::new(1.5);  // Err(FloatError::OutOfRange) (> 1.0)
835//! let invalid = SymmetricF32::new(-1.5); // Err(FloatError::OutOfRange) (< -1.0)
836//! ```
837//!
838//! Note: The `Symmetric` type is reflexive under negation (negating a `Symmetric` returns `Symmetric`):
839//!
840//! ```
841//! use strict_num_extended::SymmetricF64;
842//!
843//! let val = SymmetricF64::new(0.75).unwrap();
844//! let neg_val: SymmetricF64 = -val;  // Still SymmetricF64
845//! assert_eq!(neg_val.get(), -0.75);
846//! ```
847//!
848//! ## `PiBounded` Constraint
849//!
850//! The `PiBounded` types require finite values in [-PI, PI]:
851//!
852//! ```
853//! use strict_num_extended::PiBoundedF64;
854//!
855//! let valid = PiBoundedF64::new(core::f64::consts::FRAC_PI_3);   // Ok(value)
856//! let valid = PiBoundedF64::new(-core::f64::consts::FRAC_PI_3);  // Ok(value)
857//! let valid = PiBoundedF64::new(core::f64::consts::PI);          // Ok(value)
858//! let valid = PiBoundedF64::new(-core::f64::consts::PI);         // Ok(value)
859//! let invalid = PiBoundedF64::new(4.0);                          // Err(FloatError::OutOfRange) (> PI)
860//! let invalid = PiBoundedF64::new(-4.0);                         // Err(FloatError::OutOfRange) (< -PI)
861//! ```
862//!
863//! ## Combined Constraints
864//!
865//! Combined types enforce multiple constraints simultaneously:
866//!
867//! ```
868//! use strict_num_extended::PositiveF32;
869//!
870//! // Positive = NonNegative AND NonZero (equivalent to x > 0)
871//! let valid = PositiveF32::new(1.0);     // Ok(value)
872//! let valid = PositiveF32::new(0.001);   // Ok(value)
873//! let invalid = PositiveF32::new(0.0);   // Err(FloatError::OutOfRange) (zero)
874//! let invalid = PositiveF32::new(-1.0);  // Err(FloatError::OutOfRange) (negative)
875//! ```
876//!
877//! ## Unary Negation
878//!
879//! The unary negation operator (`-`) is supported with automatic type inference:
880//!
881//! ```
882//! use strict_num_extended::*;
883//!
884//! // NonNegative ↔ NonPositive
885//! const NON_NEG: NonNegativeF64 = NonNegativeF64::new_const(5.0);
886//! let non_pos: NonPositiveF64 = -NON_NEG;
887//! assert_eq!(non_pos.get(), -5.0);
888//!
889//! // Positive ↔ Negative
890//! const POS: PositiveF32 = PositiveF32::new_const(10.0);
891//! let neg: NegativeF32 = -POS;
892//! assert_eq!(neg.get(), -10.0);
893//!
894//! // Normalized ↔ NegativeNormalized
895//! const NORM: NormalizedF64 = NormalizedF64::new_const(0.75);
896//! let neg_norm: NegativeNormalizedF64 = -NORM;
897//! assert_eq!(neg_norm.get(), -0.75);
898//!
899//! // Fin is reflexive (negating Fin returns Fin)
900//! const FIN: FinF32 = FinF32::new_const(2.5);
901//! let neg_fin: FinF32 = -FIN;
902//! assert_eq!(neg_fin.get(), -2.5);
903//! ```
904
905#![cfg_attr(not(feature = "std"), no_std)]
906#![cfg_attr(docsrs, feature(doc_cfg))]
907#![allow(clippy::manual_range_contains)]
908#![expect(clippy::approx_constant)]
909
910#[cfg(feature = "std")]
911extern crate std;
912
913// Generate all code using proc_macro
914strict_num_extended_macros::generate_finite_float_types!(
915    [
916        (Fin, []),
917        (NonNegative, [">= 0.0"]),
918        (NonPositive, ["<= 0.0"]),
919        (NonZero, ["!= 0.0"]),
920        (Normalized, [">= 0.0", "<= 1.0"]),
921        (NegativeNormalized, [">= -1.0", "<= 0.0"]),
922        (Positive, ["> 0.0"]),
923        (Negative, ["< 0.0"]),
924        (Symmetric, [">= -1.0", "<= 1.0"]),
925        (PiBounded, [">= -PI", "<= PI"]),
926    ],
927    [
928        (Positive, Pos),
929        (Negative, Neg),
930        (NonNegative, NonNeg),
931        (NonPositive, NonPos),
932        (Normalized, Norm),
933        (NegativeNormalized, NegNorm),
934        (Symmetric, Sym),
935    ]
936);