# **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

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

- **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, exponentials, hyperbolic functions, and more
- **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 like:
- `StrictFinitePolicy` - Always validates (debug + release)
- `DebugValidationPolicy<P>` - Validates only in debug mode

### 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.

### 2. 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>>(())
```

### 4. 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>>(())
```

### 5. 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>>(())
```

### 6. 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();
```

### 7. 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());
```

---

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

---

## Contributing

Contributions are welcome! Please read the [Architecture Guide](docs/ARCHITECTURE.md) to understand the design philosophy before submitting PRs.

**Key resources for contributors:**
- **[docs/ARCHITECTURE.md](docs/ARCHITECTURE.md)** - Essential reading for understanding the codebase
- **[.github/copilot-instructions.md](.github/copilot-instructions.md)** - Detailed development guidelines

---

## License