num-valid 0.6.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
# Validated Wrapper Impact Matrix

This note answers a narrower question than the native validation matrix:

What happens to validated wrappers such as `RealValidated<K>` and `ComplexValidated<K>` if the native `f64` trait implementations are changed so that validation happens only in debug builds?

## Short Answer

Mostly, the wrappers remain protected, but not uniformly.

- Many wrapper methods still validate in release because they revalidate outputs with `try_new_with_policy(...)` or `Policy::validate_ref(...)`.
- Some wrapper methods are safe in practice because they only move already-validated values around.
- A small but important set of wrapper entry points and mutating methods do **not** revalidate, so they would either weaken or become the main risk surface.

## Legend

- **Always validates in release**: wrapper itself performs release-mode validation.
- **Invariant-preserving, no release validation**: wrapper does not validate, but only rearranges already-validated values or uses mathematically closed operations.
- **Risk if native layer becomes debug-only**: wrapper depends on a raw/native boundary or mutates raw state without revalidation.

## `RealValidated<K>`

| Method or group | Location | Release validation at wrapper layer | Assessment | Why |
| --- | --- | --- | --- | --- |
| `try_new`, `try_new_with_policy`, `new` | [src/macros.rs]../../src/macros.rs#L372 | **Yes** | **Always validates in release** | All construction goes through `Self::Policy::validate(...)`. |
| `new_unchecked` | [src/macros.rs]../../src/macros.rs#L426 | No | Risk by design | Debug-only validation, no release validation by contract. |
| `Sqrt::try_sqrt`, `sqrt` | [src/core/types.rs]../../src/core/types.rs#L126 | **Yes** | **Always validates in release** | Does domain check, then `Self::try_new_with_policy(...)` on output. |
| `Rounding` methods (`kernel_ceil`, `kernel_floor`, `kernel_fract`, `kernel_round`, `kernel_round_ties_even`, `kernel_trunc`) | [src/core/types.rs]../../src/core/types.rs#L180 | **Yes** | **Always validates in release** | Every method wraps output with `Self::try_new_with_policy(...)`. |
| Constant factories (`pi`, `two`, `epsilon`, etc.) | [src/core/types.rs]../../src/core/types.rs#L270 | **Yes** | **Always validates in release** | `from_raw_factory(...)` ends in `Self::try_new_with_policy(raw_value)`. |
| Most arithmetic and function wrappers based on `unchecked_*` | [src/core/types.rs]../../src/core/types.rs#L389 | **Yes** | **Always validates in release** | Pattern is raw op followed by `try_new_with_policy(...)` or `validate_ref(...)`. |
| `abs`, `abs_square`, `reciprocal`, `atan2`, `asin`, `acos`, `acosh`, `atanh`, `ln`, `log2`, `log10`, `pow`, `clamp`, `exp_m1`, `ln_1p`, `hypot` | [src/core/types.rs]../../src/core/types.rs#L560 | **Yes** | **Always validates in release** | All these methods shown in this area revalidate the produced value. |
| `kernel_copysign`, `kernel_signum` | [src/core/types.rs]../../src/core/types.rs#L160 | No | Invariant-preserving, no release validation | These build `Self { value: ... }` directly, but for finite validated inputs the results remain finite. |
| `kernel_mul_add_mul_mut`, `kernel_mul_sub_mul_mut` | [src/core/types.rs]../../src/core/types.rs#L1253 | No | **Risk if native layer becomes debug-only** | They mutate raw state through `unchecked_mul_*` and do not call `validate_ref(...)` afterward. Overflow or invalid raw output can break the wrapper invariant in release. |
| `try_from_f64` | [src/core/types.rs]../../src/core/types.rs#L1285 | **Yes** | **Always validates in release** | Delegates to `try_convert_and_new_raw`, which converts the raw input and applies `K::RealPolicy::validate(...)` before constructing the wrapper. |
| `from_f64` | [src/lib.rs]../../src/lib.rs#L1305 and [src/core/types.rs]../../src/core/types.rs#L1285 | Indirect | Same as `try_from_f64` | It delegates to `try_from_f64`. |

## `ComplexValidated<K>`

| Method or group | Location | Release validation at wrapper layer | Assessment | Why |
| --- | --- | --- | --- | --- |
| `try_new`, `try_new_with_policy`, `new` | [src/macros.rs]../../src/macros.rs#L372 | **Yes** | **Always validates in release** | Complex wrappers are also constructed through policy validation. |
| `ComplexScalarConstructors::try_new_complex` | [src/core/types.rs]../../src/core/types.rs#L835 | **Yes** | **Always validates in release** | Builds raw complex with `new_unchecked_raw_complex`, then immediately calls `Self::try_new_with_policy(complex)`. |
| `ComplexScalarGetParts::real_part`, `imag_part` | [src/core/types.rs]../../src/core/types.rs#L847 | No | Invariant-preserving, no release validation | They extract parts from an already-validated complex value. |
| `ComplexScalarSetParts::set_real_part`, `set_imaginary_part` | [src/core/types.rs]../../src/core/types.rs#L877 | No | Invariant-preserving, no release validation | Inputs are `RealValidated<K>`, so assigned components are already validated. |
| `ComplexScalarMutateParts::add_to_real_part`, `add_to_imaginary_part`, `multiply_real_part`, `multiply_imaginary_part` | [src/core/types.rs]../../src/core/types.rs#L896 | **Yes** | **Always validates in release** | These methods mutate a raw component, then immediately call `K::RealPolicy::validate_ref(...)`. |
| Mixed complex/real arithmetic assignment helpers (`+=`, `-=`, etc.) | [src/core/types.rs]../../src/core/types.rs#L432 | **Yes** | **Always validates in release** | After assignment, they call `K::ComplexPolicy::validate_ref(&self.value)`. |
| Methods returning `ComplexValidated<K>` from arithmetic/function wrappers | [src/core/types.rs]../../src/core/types.rs#L389 | **Yes** | **Always validates in release** | They follow the same raw-op then `try_new_with_policy(...)` pattern. |

## Methods That Especially Matter

These are the wrapper methods that most clearly still enforce validation in release:

1. `RealValidated<K>::try_new_with_policy` and `ComplexValidated<K>::try_new_with_policy` in [src/macros.rs]../../src/macros.rs#L372
2. `RealValidated<K>::kernel_ceil` / `kernel_floor` / `kernel_round` / `kernel_trunc` in [src/core/types.rs]../../src/core/types.rs#L180
3. `RealValidated<K>::try_sqrt` in [src/core/types.rs]../../src/core/types.rs#L126
4. `ComplexValidated<K>::try_new_complex` in [src/core/types.rs]../../src/core/types.rs#L835
5. `ComplexValidated<K>::add_to_real_part` / `add_to_imaginary_part` / `multiply_real_part` / `multiply_imaginary_part` in [src/core/types.rs]../../src/core/types.rs#L896
6. Mixed arithmetic assignment helpers that call `K::ComplexPolicy::validate_ref(...)` in [src/core/types.rs]../../src/core/types.rs#L432

These are the wrapper methods that remain problematic or at least sensitive:

2. `RealValidated<K>::kernel_mul_add_mul_mut` in [src/core/types.rs]../../src/core/types.rs#L1253
3. `RealValidated<K>::kernel_mul_sub_mul_mut` in [src/core/types.rs]../../src/core/types.rs#L1261

## Consequence If Native `f64` Trait Implementations Become Debug-Only

If native `f64` trait impls are changed so they validate only in debug:

- Wrapper methods that already call `try_new_with_policy(...)` or `validate_ref(...)` will continue to enforce invariants in release.
- Wrapper methods that only delegate to raw mutation or trust a raw constructor bridge will become the critical points.
- The biggest post-construction mutation risk is `kernel_mul_add_mul_mut` / `kernel_mul_sub_mul_mut` on `RealValidated<K>`.

## Proposed Design For `f64` Trait Implementations

The clean design is to make the validation policy explicit by layer, not by accident.

### 1. Native `f64` trait impls should be performance-oriented

For `f64` and `Complex<f64>` trait impls in the native backend:

- non-fallible math delegates should do no release validation
- optional debug checks may use `debug_assert!`
- they should be treated as fast backend adapters, not invariant guardians

This is already the de facto direction for many methods.

### 2. All fallible entry points should always validate

The following must remain release-validating boundaries:

- `ValidateNativeReal::validate_native_scalar` and `validate_native_scalar_ref`
- `ValidateNativeComplex::validate_native_complex` and `validate_native_complex_ref`
- `RawRealTrait::try_new_raw_real_from_f64`
- `ComplexScalarConstructors::try_new_complex`
- fallible ops such as `try_pow`

Reason: these are the boundaries where untrusted raw values enter the validated world.

### 3. Panicking convenience methods may be debug-checked and release-fast

For native raw types only, a consistent rule can be:

- `try_*` methods always validate
- panicking `*` methods call `try_*` in debug and direct std/num operation in release

This is already how `Pow::pow` is documented and implemented for native `f64` in [src/functions/pow.rs](../../src/functions/pow.rs#L356).

### 4. Validated wrappers must own invariant preservation

Any method on `RealValidated<K>` or `ComplexValidated<K>` that can produce a new value or mutate stored raw state should do one of two things:

- either prove closure structurally because inputs are already validated and the operation cannot leave the policy domain
- or explicitly revalidate the output or mutated state in both debug and release

In practice, this means:

- keep `try_new_with_policy(...)` / `validate_ref(...)` after unchecked ops
- do not rely on native `f64` impls to preserve invariants for wrapper methods

### 5. Tighten the known weak spots

If you adopt debug-only validation for native `f64` impls, the wrapper layer should tighten these methods:

- `RealValidated<K>::kernel_mul_add_mul_mut` should validate `self.value` after mutation
- `RealValidated<K>::kernel_mul_sub_mul_mut` should validate `self.value` after mutation

Without that, the wrapper invariant would depend on assumptions outside the wrapper layer.

## Recommended Rule Set

The most coherent model is:

1. Raw native trait impls: fast, debug-assert at most.
2. Fallible constructors and fallible operations: always validate in release too.
3. Validated wrappers: always preserve invariants themselves, never by trusting native raw impl behavior.
4. Any wrapper mutation that can overflow or create invalid values must revalidate after mutation.

That gives a clear separation of concerns:

- native backend traits provide speed
- validation traits provide checked boundaries
- validated wrappers provide invariant preservation