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