num-valid 0.3.0

A robust numerical library providing validated types for real and complex numbers to prevent common floating-point errors like NaN propagation. Features a generic, layered architecture with support for native f64 and optional arbitrary-precision arithmetic.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
# **num-valid**

[![Crates.io](https://img.shields.io/crates/v/num-valid.svg)](https://crates.io/crates/num-valid)
[![Docs.rs](https://docs.rs/num-valid/badge.svg)](https://docs.rs/num-valid)
[![License: MIT OR Apache-2.0](https://img.shields.io/badge/license-MIT%20OR%20Apache--2.0-blue.svg)](./LICENSE.md)
[![Pipeline Status](https://gitlab.com/max.martinelli/num-valid/badges/master/pipeline.svg)](https://gitlab.com/max.martinelli/num-valid/-/pipelines)
[![Coverage](https://gitlab.com/max.martinelli/num-valid/badges/master/coverage.svg)](https://gitlab.com/max.martinelli/num-valid/-/jobs)
[![GitLab last commit](https://img.shields.io/gitlab/last-commit/max.martinelli/num-valid)](https://gitlab.com/max.martinelli/num-valid/-/commits/master)

Robust numerical computation in Rust with type-level safety guarantees.

## Why num-valid?

Floating-point arithmetic is tricky. `NaN` propagates silently, overflow produces `Infinity`, and precision loss is common. **num-valid** solves these problems by enforcing correctness at the type level.

### Quick Example

```rust
use num_valid::RealNative64StrictFinite;
use try_create::TryNew;

// ✅ Valid value - compiles and runs safely
let x = RealNative64StrictFinite::try_new(4.0)?;
let sqrt_x = x.sqrt(); // Always safe - no NaN surprises!

// ❌ Invalid value - caught immediately
let bad = RealNative64StrictFinite::try_new(f64::NAN); // Returns Err
# Ok::<(), Box<dyn std::error::Error>>(())
```

### Core Benefits

✅ **Type Safety** - No more NaN surprises  
✅ **Generic Precision** - Switch between f64 and arbitrary-precision seamlessly  
✅ **Zero Cost** - Validation overhead only where you choose  
✅ **Complete** - Trigonometry, logarithms, complex numbers, and more  
✅ **Production Ready** - 96%+ test coverage, comprehensive error handling  

## Installation

Add to your `Cargo.toml`:

```toml
[dependencies]
num-valid = "0.2"
try_create = "0.1"
```

### Feature Flags

| Feature | Description |
|---------|-------------|
| `rug` | Enables arbitrary-precision arithmetic using [rug]https://crates.io/crates/rug. See LGPL-3.0 notice below. |
| `backtrace` | Enables backtrace capture in error types for debugging. Disabled by default for performance. |

Example with features:

```toml
[dependencies]
num-valid = { version = "0.2", features = ["rug", "backtrace"] }
```

## Quick Start

### Using Literal Macros (Simplest Approach)

The easiest way to create validated numbers is using the `real!()` and `complex!()` macros:

```rust
use num_valid::{real, complex};
use std::f64::consts::PI;

// Create validated real numbers from literals
let x = real!(3.14159);
let angle = real!(PI / 4.0);
let radius = real!(5.0);

// Create validated complex numbers (real, imaginary)
let z1 = complex!(1.0, 2.0);   // 1 + 2i
let z2 = complex!(-3.0, 4.0);  // -3 + 4i

// Use them in calculations - NaN/Inf propagation impossible!
let area = real!(PI) * radius * radius;  // Type-safe area calculation
let product = z1 * z2;                   // Safe complex arithmetic
```

**Why use macros?**

- **Ergonomic**: Concise syntax similar to Rust's built-in `vec![]`, `format!()`, etc.
- **Safe**: Validates at construction time, preventing NaN/Inf from entering your calculations
- **Clear panics**: Invalid literals (like `real!(f64::NAN)`) panic immediately with descriptive messages
- **Compile-time friendly**: Works with constants and expressions evaluated at compile time

### Manual Construction (More Control)

For runtime values or when you need error handling:

```rust
use num_valid::{RealNative64StrictFinite, RealScalar};

// Fallible construction - returns Result
let x = RealNative64StrictFinite::try_from_f64(user_input)?;

// Panicking construction - for known-valid values
let pi = RealNative64StrictFinite::from_f64(std::f64::consts::PI);
```

### Core Design Philosophy

- **Safety by Construction with Validated Types:** Instead of using raw primitives like `f64` or `num::Complex<f64>` directly, `num-valid` encourages the use of validated wrappers like `RealValidated` and `ComplexValidated`. These types guarantee that the value they hold is always valid (e.g., finite) according to a specific policy, eliminating entire classes of numerical bugs.

- **Support for Real and Complex Numbers:** The library supports both real and complex numbers, with specific validation policies for each type.

- **Layered and Extensible Design:** The library has a well-defined, layered, and highly generic architecture. It abstracts the concept of a "numerical kernel" (the underlying number representation and its operations) from the high-level mathematical traits.

  The architecture can be understood in four main layers:
  - **Layer 1: Raw Trait Contracts** (in the `kernels` module):
    - The `RawScalarTrait`, `RawRealTrait`, and `RawComplexTrait` define the low-level, "unchecked" contract for any number type.
    - These traits are the foundation, providing a standard set of `unchecked_*` methods.
    - The contract is that the caller must guarantee the validity of inputs. This is a strong design choice, separating the raw, potentially unsafe operations from the validated, safe API.
    - **Why?** This design separates the pure, high-performance computational logic from the safety and validation logic. It creates a clear, minimal contract for backend implementors and allows the validated wrappers in Layer 3 to be built on a foundation of trusted, high-speed operations.
  - **Layer 2: Validation Policies**:
    - The `NumKernel` trait is the bridge between the raw types and the validated wrappers.
    - It bundles together the raw real/complex types and their corresponding validation policies (e.g., `StrictFinitePolicy`, `DebugValidationPolicy`, etc.). This allows the entire behavior of the validated types to be configured with a single generic parameter.
    - **Why?** It acts as the central policy configuration point. By choosing a `NumKernel`, a user selects both a numerical backend (e.g., `f64` vs. `rug`) and a set of safety rules (e.g., `StrictFinitePolicy` vs. `DebugValidationPolicy<StrictFinitePolicy>`) with a single generic parameter. This dramatically simplifies writing generic code that can be configured for different safety and performance trade-offs.
  - **Layer 3: Validated Wrappers**:
    - `RealValidated<K>` and `ComplexValidated<K>` are the primary user-facing types.
    - These are newtype wrappers that are guaranteed to hold a value that conforms to the `NumKernel` `K` (and to the validation policies therein).
    - They use extensive macros to implement high-level traits. The logic is clean: perform a check (if necessary) on the input value, then call the corresponding `unchecked_*` method from the raw trait, and then perform a check on the output value before returning it. This ensures safety and correctness.
    - **Why?** These wrappers use the newtype pattern to enforce correctness at the type level. By construction, an instance of `RealValidated` is guaranteed to contain a value that has passed the validation policy, eliminating entire classes of errors (like `NaN` propagation) in user code.
  - **Layer 4: High-Level Abstraction Traits**:
    - The `FpScalar` trait is the central abstraction, defining a common interface for all scalar types. It uses an associated type sealed type (`Kind`), to enforce that a scalar is either real or complex, but not both.
    - `RealScalar` and `ComplexScalar` are specialized sub-traits of `FpScalar` that serve as markers for real and complex numbers, respectively.
    - Generic code in a consumer crate is written against these high-level traits.
    - The `RealValidated` and `ComplexValidated` structs from Layer 3 are the concrete implementors of these traits.
    - **Why?** These traits provide the final, safe, and generic public API. Library consumers write their algorithms against these traits, making their code independent of the specific numerical kernel being used.

  This layered approach is powerful, providing both high performance (by using unchecked methods internally) and safety (through the validated wrappers). The use of generics and traits makes it extensible to new numerical backends (as demonstrated by the rug implementation).

- **Multiple Numerical Backends**. At the time of writing, 2 numerical backends can be used:
  - the standard (high-performance) numerical backend is the one in which the raw floating point and complex numbers are described by the Rust's native `f64` and `num::Complex<f64>` types, as described by the ANSI/IEEE Std 754-1985;
  - an optional (high-precision) numerical backend is available if the library is compiled with the optional flag `--features=rug`, and uses the arbitrary precision raw types `rug::Float` and `rug::Complex` from the Rust library [`rug`]https://crates.io/crates/rug.
- **Comprehensive Mathematical Library**. It includes a wide range of mathematical functions for trigonometry, logarithms, exponentials, and more, all implemented as traits (e.g., Sin, Cos, Sqrt) and available on the validated types.
- **Numerically Robust Implementations**. The library commits to numerical accuracy. As an example, by using `NeumaierSum` for its default `std::iter::Sum` implementation to minimize precision loss.
- **Robust Error Handling:** The library defines detailed error types for various numerical operations, ensuring that invalid inputs and outputs are properly handled and reported. Errors are categorized into input and output errors, with specific variants for different types of numerical issues such as division by zero, invalid values, and subnormal numbers.
- **Conditional Backtrace Capture:** Backtrace capture in error types is disabled by default for maximum performance. Enable the `backtrace` feature flag for debugging to get full stack traces in error types.
- **Conditional `Copy` Implementation:** Validated types (`RealValidated<K>`, `ComplexValidated<K>`) automatically implement `Copy` when their underlying raw types support it. This enables zero-cost pass-by-value semantics for native 64-bit types while correctly requiring `Clone` for non-`Copy` backends like `rug`.
- **Comprehensive Documentation:** The library includes detailed documentation for each struct, trait, method, and error type, making it easy for users to understand and utilize the provided functionality. Examples are provided for key functions to demonstrate their usage and expected behavior.

## Key Features

- **Ergonomic Literal Macros**: `real!()` and `complex!()` for concise, validated numeric literals
- **Type-Level Safety**: Validated wrappers prevent NaN/Infinity propagation at compile time
- **Dual Backends**: Native `f64` for speed, arbitrary-precision `rug` for accuracy
- **Complete Math Library**: Trigonometry, logarithms (`ln`, `log2`, `log10`, `ln_1p`), exponentials (`exp`, `exp_m1`), hyperbolic functions, and more
- **Constrained Scalar Types**: `AbsoluteTolerance`, `RelativeTolerance`, `PositiveRealScalar`, `NonNegativeRealScalar` for domain-specific validation
- **Generic API**: Write once, run with any precision level
- **Robust Error Handling**: Detailed error types with optional backtrace support
- **Numerically Accurate**: Uses Neumaier compensated summation to minimize precision loss
- **Zero-Copy Conversions**: Efficient bytemuck integration for binary data
- **Conditional Copy**: Automatic `Copy` implementation when underlying types support it
- **Production Ready**: 96%+ test coverage, comprehensive documentation

## Architecture (For Library Developers)

The library uses a sophisticated 4-layer design:

### Layer 1: Raw Trait Contracts

Low-level `unchecked_*` operations on primitives (`f64`, `Complex<f64>`, `rug::Float`, etc.). These assume caller-validated inputs for maximum performance.

### Layer 2: Validation Policies

The `NumKernel` trait bundles raw types with validation policies. Choose the right policy for your use case:

#### Policy Comparison

| Type Alias | Policy | Debug Build | Release Build | Use Case |
|------------|--------|-------------|---------------|----------|
| `RealNative64StrictFinite` | `StrictFinitePolicy` | ✅ Validates | ✅ Validates | Safety-critical code, HashMap/BTreeMap keys |
| `RealNative64StrictFiniteInDebug` | `DebugValidationPolicy` | ✅ Validates | ❌ No validation | Performance-critical hot paths |
| `RealRugStrictFinite<P>` | `StrictFinitePolicy` | ✅ Validates | ✅ Validates | High-precision calculations |

**Key Differences:**

- **StrictFinite**: Always validates, implements `Eq` + `Hash` + `Ord` (safe for HashMap/BTreeMap keys, sorted collections)
- **StrictFiniteInDebug**: Zero overhead in release, catches bugs during development

### Layer 3: Validated Wrappers

`RealValidated<K>` and `ComplexValidated<K>` enforce correctness:

- Validate input → call `unchecked_*` → validate output
- Type aliases: `RealNative64StrictFinite`, `ComplexRugStrictFinite<100>`, etc.

### Layer 4: High-Level Traits

Generic interface for all scalars:

- `FpScalar` - Universal scalar trait
- `RealScalar` - Real number specialization
- `ComplexScalar` - Complex number specialization

This design separates performance-critical operations from safety guarantees, enabling both high speed and correctness.

<details>
<summary><b>Detailed Architecture Explanation</b></summary>

#### Why This Design?

**Layer 1** separates pure computational logic from validation, creating a minimal contract for backend implementors and enabling high-performance operations.

**Layer 2** acts as the central policy configuration point. By choosing a `NumKernel`, you select both a numerical backend and safety rules with a single generic parameter.

**Layer 3** uses the newtype pattern to enforce correctness at the type level. By construction, instances are guaranteed valid, eliminating entire error classes.

**Layer 4** provides the final safe, generic public API. Library consumers write algorithms against these traits, making code independent of the specific numerical kernel.

This layered approach provides both high performance (unchecked methods internally) and safety (validated wrappers). Generics and traits make it extensible to new numerical backends.

</details>

## Compiler Requirement

This library requires **Rust nightly** due to use of unstable features:

- `trait_alias`
- `associated_const_equality`
- `error_generic_member_access`

Set up nightly:

```bash
rustup install nightly
rustup override set nightly
```

The library will support stable Rust once these features are stabilized.

### 1. Basic Usage

The central idea in `num-valid` is to use **validated types** instead of raw primitives like `f64`. These wrappers guarantee their inner value is always valid (e.g., not `NaN` or `Infinity`).

**Most common type**: `RealNative64StrictFinite` - wraps `f64` with strict finite validation.

```rust
use num_valid::{RealNative64StrictFinite, functions::Sqrt};
use try_create::TryNew;

// Create a validated number
let x = RealNative64StrictFinite::try_new(4.0)?;

// Panicking version - for known-valid inputs
let sqrt_x = x.sqrt();
assert_eq!(*sqrt_x.as_ref(), 2.0);

// Fallible version - for runtime validation
let neg = RealNative64StrictFinite::try_new(-4.0)?;
match neg.try_sqrt() {
    Ok(result) => println!("sqrt = {}", result),
    Err(e) => println!("Error: {}", e), // "sqrt of negative number"
}
# Ok::<(), Box<dyn std::error::Error>>(())
```

### 2. Generic Functions

Write once, run with any precision:

```rust
use num_valid::{RealScalar, RealNative64StrictFinite, functions::{Sin, Cos, Abs}};
use num::One;

// Generic function works with any RealScalar type
fn pythagorean_identity<T: RealScalar>(angle: T) -> T {
    let sin_x = angle.clone().sin();
    let cos_x = angle.cos();
    (sin_x.clone() * sin_x) + (cos_x.clone() * cos_x)
}

// Use with native f64 precision
let angle = RealNative64StrictFinite::try_from_f64(0.5)?;
let result = pythagorean_identity(angle);
let one = RealNative64StrictFinite::one();

// The result should be very close to 1.0 (sin²x + cos²x = 1)
let diff = (result - one).abs();
assert!(*diff.as_ref() < 1e-15);
# Ok::<(), Box<dyn std::error::Error>>(())
```

### 3. Arbitrary Precision (Optional)

Enable the `rug` feature for arbitrary-precision arithmetic:

```toml
[dependencies]
num-valid = { version = "0.2", features = ["rug"] }
```

Then use the same generic function with high precision:

```rust
# #[cfg(feature = "rug")] {
use num_valid::RealRugStrictFinite;
use num::One;

// Same generic function from example 4
# use num_valid::RealScalar;
# fn pythagorean_identity<T: RealScalar>(angle: T) -> T {
#     let sin_x = angle.clone().sin();
#     let cos_x = angle.cos();
#     (sin_x.clone() * sin_x) + (cos_x.clone() * cos_x)
# }

// Use with 200-bit precision (≈60 decimal digits)
type HighPrecision = RealRugStrictFinite<200>;

let angle = HighPrecision::try_from_f64(0.5)?;
let result = pythagorean_identity(angle);
let one = HighPrecision::one();

// More accurate than f64!
let diff = (result - one).abs();
let threshold = HighPrecision::try_from_f64(1e-50)?;
assert!(*diff.as_ref() < *threshold.as_ref());
# }
# Ok::<(), Box<dyn std::error::Error>>(())
```

### 4. Complex Numbers

Full support for complex arithmetic:

```rust
use num_valid::{ComplexNative64StrictFinite, RealNative64StrictFinite};
use num_valid::functions::{Abs, Reciprocal, Exp, Ln, Sqrt, ComplexScalarConstructors, Conjugate};

// Create complex number: 3 + 4i
let re = RealNative64StrictFinite::from_f64(3.0);
let im = RealNative64StrictFinite::from_f64(4.0);
let z = ComplexNative64StrictFinite::new_complex(re, im);

// Complex operations
let magnitude = z.abs(); // |3 + 4i| = 5.0
assert_eq!(*magnitude.as_ref(), 5.0);

let conjugate = z.conjugate(); // 3 - 4i
let reciprocal = z.reciprocal(); // 1/z

// Complex math functions
let exp_z = z.exp();
let log_z = z.ln();
let sqrt_z = z.sqrt();
```

### 5. Zero-Copy Conversions

Safe, zero-copy conversions with [`bytemuck`](https://crates.io/crates/bytemuck):

```rust
use num_valid::RealNative64StrictFinite;
use bytemuck::checked::try_from_bytes;

// From bytes to validated type
let bytes = 42.0_f64.to_ne_bytes();
let validated: &RealNative64StrictFinite = try_from_bytes(&bytes).unwrap();
assert_eq!(*validated.as_ref(), 42.0);

// Invalid values automatically rejected
let nan_bytes = f64::NAN.to_ne_bytes();
assert!(try_from_bytes::<RealNative64StrictFinite>(&nan_bytes).is_err());
```

### 6. Error Handling

Comprehensive error handling with specific error types:

```rust
use num_valid::{RealNative64StrictFinite, RealScalar, functions::Sqrt};
use num_valid::functions::{SqrtRealErrors, SqrtRealInputErrors};

// Pattern 1: Propagate errors with ?
fn compute_radius(area: f64) -> Result<RealNative64StrictFinite, Box<dyn std::error::Error>> {
    let validated_area = RealNative64StrictFinite::try_from_f64(area)?;
    let pi = RealNative64StrictFinite::try_from_f64(std::f64::consts::PI)?;
    let radius_squared = validated_area / pi;
    Ok(radius_squared.try_sqrt()?)
}

// Pattern 2: Match on specific error variants
fn sqrt_with_fallback(x: RealNative64StrictFinite) -> RealNative64StrictFinite {
    match x.try_sqrt() {
        Ok(result) => result,
        Err(SqrtRealErrors::Input { source: SqrtRealInputErrors::NegativeValue { .. } }) => {
            // Handle negative input: return sqrt(|x|)
            (-x).sqrt()
        }
        Err(e) => panic!("Unexpected error: {e}"),
    }
}
# Ok::<(), Box<dyn std::error::Error>>(())
```

### 7. Validated Tolerances and Constrained Scalars

The `scalars` module provides validated wrapper types for common constrained numeric patterns:

```rust
use num_valid::{RealNative64StrictFinite, RealScalar};
use num_valid::scalars::{
    AbsoluteTolerance, RelativeTolerance, 
    PositiveRealScalar, NonNegativeRealScalar
};
use try_create::TryNew;

// AbsoluteTolerance: Non-negative tolerance (≥ 0)
let abs_tol = AbsoluteTolerance::try_new(
    RealNative64StrictFinite::from_f64(1e-10)
)?;

// RelativeTolerance: Non-negative with conversion to absolute
let rel_tol = RelativeTolerance::try_new(
    RealNative64StrictFinite::from_f64(1e-6)
)?;
let reference = RealNative64StrictFinite::from_f64(1000.0);
let abs_from_rel = rel_tol.absolute_tolerance(reference); // = 1e-3

// PositiveRealScalar: Strictly positive (> 0), rejects zero
let step_size = PositiveRealScalar::try_new(
    RealNative64StrictFinite::from_f64(0.001)
)?;
// PositiveRealScalar::try_new(RealNative64StrictFinite::from_f64(0.0)); // Error!

// NonNegativeRealScalar: Non-negative (≥ 0), accepts zero
let threshold = NonNegativeRealScalar::try_new(
    RealNative64StrictFinite::from_f64(0.0)
)?;  // OK!
# Ok::<(), Box<dyn std::error::Error>>(())
```

**Key differences:**

- `PositiveRealScalar`: Rejects zero (useful for divisors, step sizes)
- `NonNegativeRealScalar`: Accepts zero (useful for thresholds, counts)
- All types work with any `RealScalar` backend (`RealNative64StrictFinite`, `RealRugStrictFinite<P>`, etc.)

---

## Common Pitfalls

Here are common mistakes to avoid when using `num-valid`:

### 1. Forgetting `Clone` for Reused Values

Validated types consume `self` in mathematical operations. Clone before reuse:

```rust
# use num_valid::{real, functions::{Sin, Cos}};
let x = real!(1.0);

// ❌ Wrong - x is moved
// let sin_x = x.sin();
// let cos_x = x.cos(); // Error: x already moved!

// ✅ Correct - clone before move
let sin_x = x.clone().sin();
let cos_x = x.cos();
```

### 2. Using `from_f64` with Untrusted Input

`from_f64` panics on invalid input. Use `try_from_f64` for user data:

```rust
# use num_valid::{RealNative64StrictFinite, RealScalar};
fn process_user_input(user_input: f64) -> Result<(), Box<dyn std::error::Error>> {
    // ❌ Dangerous - panics on NaN/Inf
    // let x = RealNative64StrictFinite::from_f64(user_input);

    // ✅ Safe - handles errors gracefully
    let x = RealNative64StrictFinite::try_from_f64(user_input)?;
    println!("Validated: {}", x);
    Ok(())
}
# process_user_input(42.0).unwrap();
```

### 3. Ignoring Precision with rug Backend

`0.1` cannot be exactly represented in binary floating-point:

```rust,ignore
# #[cfg(feature = "rug")] {
use num_valid::RealRugStrictFinite;

// ❌ May fail - 0.1 not exactly representable at 53-bit precision
let x = RealRugStrictFinite::<53>::try_from_f64(0.1); // Error!

// ✅ Use higher precision or handle the error appropriately
let x = RealRugStrictFinite::<100>::try_from_f64(0.5)?; // 0.5 is exact
# }
```

### 4. Validation Policy Mismatch

Choose the right policy for your use case:

| Policy | Debug Build | Release Build | Use Case |
|--------|-------------|---------------|----------|
| `StrictFinite` | ✅ Validates | ✅ Validates | Safety-critical code |
| `StrictFiniteInDebug` | ✅ Validates | ❌ No validation | Performance-critical code |

```rust
use num_valid::{RealNative64StrictFinite, RealNative64StrictFiniteInDebug};

// For safety-critical code (always validated)
type SafeReal = RealNative64StrictFinite;

// For performance-critical code (validated only in debug builds)
type FastReal = RealNative64StrictFiniteInDebug;
```

### 5. Ignoring `#[must_use]` Warnings

All `try_*` methods return `Result`. Don't ignore them:

```rust
# use num_valid::{RealNative64StrictFinite, functions::Sqrt};
# use try_create::TryNew;
# let x = RealNative64StrictFinite::try_new(4.0).unwrap();
// ❌ Compiler warning: unused Result
// x.try_sqrt();

// ✅ Handle the result
let sqrt_x = x.try_sqrt()?;
# Ok::<(), Box<dyn std::error::Error>>(())
```

---

## Documentation

📚 **[API Documentation](https://docs.rs/num-valid)** - Complete API reference with examples

🏗️ **[Architecture Guide](docs/ARCHITECTURE.md)** - Deep dive into the 4-layer design, implementation patterns, and how to contribute

📖 **[Cookbook](docs/COOKBOOK.md)** - Practical patterns and examples for common use cases

🔄 **[Migration Guide](docs/MIGRATION.md)** - Transitioning from raw primitives (`f64`, `Complex<f64>`) to validated types

---

## Contributing

Contributions are welcome! Before submitting PRs, please familiarize yourself with the project's architecture and conventions.

### Essential Resources for Contributors

📚 **[Architecture Guide](docs/ARCHITECTURE.md)** - **Required reading** for all contributors

- Complete explanation of the 4-layer design
- Step-by-step guides for adding backends and mathematical functions  
- Error handling patterns and performance considerations
- Design patterns and macro system documentation

📖 **Additional Documentation**

- **[COOKBOOK.md]docs/COOKBOOK.md** - Practical usage patterns and examples
- **[MIGRATION.md]docs/MIGRATION.md** - Guide for migrating from raw primitives
- **[TECHNICAL_REVIEW.md]docs/TECHNICAL_REVIEW.md** - Expert analysis and quality metrics
- **[copilot-instructions.md].github/copilot-instructions.md** - Development guidelines and conventions

### Quick Start for Contributors

1. **Read** [ARCHITECTURE.md]docs/ARCHITECTURE.md - Understand the layered design
2. **Check** existing patterns in `src/functions/` for mathematical function examples
3. **Follow** naming conventions: `unchecked_*` for Layer 1, trait methods for Layer 4
4. **Test** thoroughly: `cargo test --all-features` and `cargo doc --no-deps`
5. **Format** before committing: `cargo fmt`

### Common Contribution Patterns

- **Adding a mathematical function?** → See [Adding Mathematical Functions]docs/ARCHITECTURE.md#adding-mathematical-functions
- **Adding a new backend?** → See [Adding a New Backend]docs/ARCHITECTURE.md#adding-a-new-backend
- **Working with macros?** → See [Macro System]docs/ARCHITECTURE.md#macro-system
- **Optimizing performance?** → See [Performance Considerations]docs/ARCHITECTURE.md#performance-considerations

---

## License