num-valid 0.2.1

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

`num-valid` is a Rust library designed for robust, generic, and high-performance numerical computation. It provides a safe and extensible framework for working with both real and complex numbers, addressing the challenges of floating-point arithmetic by ensuring correctness and preventing common errors like `NaN` propagation.

## Key Features & Architecture

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

## Compiler Requirement: Rust Nightly

This library currently requires the **nightly** toolchain because it uses some unstable Rust features which, at the time of writing (September 2025), are not yet available in stable or beta releases.

If these features are stabilized in a future Rust release, the library will be updated to support the stable compiler.

To use the nightly toolchain, please run:

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

This will set your environment to use the nightly compiler, enabling compatibility with the current version of the library.

## Getting Started

This guide will walk you through the basics of using `num-valid`.

### 1. Add `num-valid` to your Project

Add the following to your `Cargo.toml` (change the versions to the latest ones):

```toml
[dependencies]
num-valid = "0.2.0"  # Change to the latest version
try_create = "0.1.2" # Needed for the TryNew trait
```

To enable the arbitrary-precision backend, use the `rug` feature:

```toml
[dependencies]
num-valid = { version = "0.2.0", features = ["rug"] } # Change to the latest version
try_create = "0.1.2" # Needed for the TryNew trait
```

### 2. Core Concept: Validated Types

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

The most common type you'll use is `RealNative64StrictFinite`, which wraps an `f64` and ensures it's always finite, both in Debug and Release mode. For a similar type wrapping an `f64` that ensures it's always finite, but with the validity checks executed only in Debug mode (providing a performance equal to the raw `f64` type), you can use `RealNative64StrictFiniteInDebug`.

### 3. Your First Calculation

Let's perform a square root calculation. You'll need to bring the necessary traits into scope.

```rust
// Import traits for the constructor, the sqrt function and the sqrt errors.
use num_valid::{
    RealNative64StrictFinite,
    functions::{Sqrt, SqrtRealInputErrors, SqrtRealErrors},
};
use try_create::TryNew;

// 1. Create a validated number. try_new() returns a Result.
let x = RealNative64StrictFinite::try_new(4.0).unwrap();

// 2. Use the direct method for operations.
// This will panic if the operation is invalid (e.g., sqrt of a negative).
let sqrt_x = x.sqrt();
assert_eq!(sqrt_x, 2.0);

// 3. Use the `try_*` methods for error handling.
// This is the safe way to handle inputs that might be out of the function's domain.
let neg_x = RealNative64StrictFinite::try_new(-4.0).unwrap();
let sqrt_neg_x_result = neg_x.try_sqrt();

// The operation fails gracefully, returning an Err.
assert!(sqrt_neg_x_result.is_err());

// The error gives information about the problem that occurred
assert!(matches!(sqrt_neg_x_result.unwrap_err(),
    SqrtRealErrors::Input{ source: SqrtRealInputErrors::NegativeValue{ value: -4., .. }}));
```

### 4. Writing Generic Functions

The real power of `num-valid` comes from writing generic functions that work with any supported numerical type. You can do this by using the `FpScalar` and `RealScalar` traits as bounds.

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

// This function works for any type that implements RealScalar,
// including f64, RealNative64StrictFinite, and RealRugStrictFinite.
fn verify_trig_identity<T: RealScalar>(angle: T) -> T {
    // We can use .sin(), .cos(), and arithmetic ops because they are
    // required by the RealScalar trait.
    let sin_x = angle.clone().sin();
    let cos_x = angle.cos();
    (sin_x.clone() * sin_x) + (cos_x.clone() * cos_x)
}

// Define a type alias for convenience
type MyReal = RealNative64StrictFinite;

// Call it with a validated f64 type.
let angle = MyReal::try_from_f64(0.5).unwrap();
let identity = verify_trig_identity(angle);

// The result should be very close to 1.0.
let one = MyReal::one();
assert!((identity - one).abs() < 1e-15);
```

If the `rug` feature is enabled, you could call the exact same function with a high-precision number changing only the definition of the alias type `MyReal`. For example, for real numbers with precision of 100 bits:

```rust
// we need to add the proper module
use num_valid::RealRugStrictFinite;
// ... same modules as above

// ... same verify_trig_identity() function as above

// Define a type alias for convenience
type MyReal = RealRugStrictFinite<100>; // real number with precision of 100 bits

// Initialize it with an f64 value.
let angle = MyReal::try_from_f64(0.5).unwrap();
let identity = verify_trig_identity(angle);

// The result should be very close to 1.0.
let one = MyReal::one();
assert!((identity - one).abs() < 1e-15);
```

## License

Copyright 2023-2025, C.N.R. - Consiglio Nazionale delle Ricerche

Licensed under either of

- [Apache License, Version 2.0]http://www.apache.org/licenses/LICENSE-2.0
- [MIT license]http://opensource.org/licenses/MIT

at your option.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in this project by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

### License Notice for Optional Feature Dependencies (LGPL-3.0 Compliance)

If you enable the `rug` feature, this project will depend on the [`rug`](https://crates.io/crates/rug) library, which is licensed under the [LGPL-3.0 license](https://www.gnu.org/licenses/lgpl-3.0.html). Activating this feature may introduce LGPL-3.0 requirements to your project. Please review the terms of the [LGPL-3.0 license](https://www.gnu.org/licenses/lgpl-3.0.html) to ensure compliance.