# Copilot Instructions for num-valid
## Project Overview
`num-valid` is a Rust library for robust numerical computation using validated types to prevent floating-point errors like NaN propagation. It provides a generic, layered architecture supporting both native f64 and arbitrary-precision arithmetic (via the `rug` crate).
**Key Concept:** Use validated wrapper types (like `RealNative64StrictFinite`) instead of raw primitives to enforce correctness at the type level.
## Critical Requirements
### Rust Nightly Toolchain
- **Required:** This project uses unstable Rust features (`trait_alias`, `error_generic_member_access`)
- The toolchain is configured in `rust-toolchain.toml` with `channel = "nightly"`
- Never suggest stable-only solutions - assume nightly features are available
### Feature Flags
- **Default:** Native f64 backend with strict validation
- **`rug`:** Enables arbitrary-precision arithmetic (LGPL-3.0 licensed dependency)
- **`backtrace`:** Enables backtrace capture in errors (disabled by default for performance)
## Architecture: 4-Layer Design
Understanding this layered architecture is essential for making changes.
**📚 For comprehensive details, see [`docs/ARCHITECTURE.md`](../docs/ARCHITECTURE.md)** - a complete 25+ page guide covering:
- Detailed explanation of each layer with examples and rationale
- Step-by-step guides for adding backends and mathematical functions
- Error handling architecture and performance considerations
- Design patterns reference and macro system documentation
**Quick Reference:**
### Layer 1: Raw Trait Contracts
**Files:** `src/core/traits/raw.rs`, `src/backends/native/raw.rs`, `src/backends/rug/raw.rs`
- **Traits:** `RawScalarTrait`, `RawRealTrait`, `RawComplexTrait`
- **Purpose:** Low-level, unchecked operations (e.g., `unchecked_sqrt`, `unchecked_reciprocal`)
- **Contract:** Caller guarantees input validity - no validation performed
- **Key Pattern:** All methods prefixed with `unchecked_*` assume valid inputs
- **Implementations:** `f64`, `Complex<f64>`, `rug::Float`, `rug::Complex`; native backend also uses sealed native-float generic impls (`f32`, `f64`, `Complex<T>`) for shared raw behavior
### Layer 2: Validation Policies
**Files:** `src/core/policies.rs`, `src/core/traits/validation.rs`, `src/core/traits.rs`
- **Core traits:** `RawKernel` and `NumKernel` (in `src/core/traits.rs`) - bundle raw types with validation policies
- **Kernel structs:** `NumKernelStrictFinite`, `NumKernelStrictFiniteInDebug` (in `src/kernels.rs`)
- **Primary policy:** `StrictFinitePolicy` - ensures values are finite and not subnormal
- **Alternative:** `DebugValidationPolicy<P>` - validates only in debug builds
- **Type-level guarantees:** `GuaranteesFiniteRealValues` marker enables `Eq` + `Hash` for HashMap keys
- **Native validation internals:** `sealed::ValidateNativeReal` centralizes strict-finite checks for native reals (`f32`, `f64`), `sealed::ValidateNativeComplex` does the same for `Complex<T>`, and both flow through shared native validation helpers in `src/core/policies.rs`
- **Migration status note:** native genericization (`f32` + `f64` paths) is being introduced incrementally; when touching `src/backends/native/raw.rs` or `src/core/types.rs`, expect partial-work states where `cargo test --features=rug` can fail until all `f64`-specific assumptions are migrated
- **Comparison migration note:** when touching `src/core/types.rs`, `src/core/traits/raw.rs`, or `src/lib.rs`, avoid reintroducing generic assumptions like `PartialEq<f64>` / `PartialOrd<f64>` for raw-real abstractions; prefer raw typed constants or validated values
### Layer 3: Validated Wrappers
**Files:** `src/core/types.rs`
- **Types:** `RealValidated<K>`, `ComplexValidated<K>` (generic over `NumKernel`)
- **Concrete types:** `RealNative64StrictFinite`, `ComplexNative64StrictFinite`, etc.
- **Pattern:** Validate input → call `unchecked_*` method → validate output
- **Copy semantics:** Automatically `Copy` when underlying raw type is `Copy`
- **Defined via:** Modular macros (`define_validated_struct_type!`, `impl_validated_*!`)
### Layer 4: High-Level Traits
**Files:** `src/lib.rs`
- **Core trait:** `FpScalar` - universal interface for all scalar types
- **Specialized:** `RealScalar` (for real numbers), `ComplexScalar` (for complex)
- **Mutual exclusion:** Associated type `Kind` (sealed) prevents a type being both real and complex
- **Usage:** Write generic code using `T: RealScalar` bounds
## Project Structure
```
src/
├── lib.rs # Main entry point, FpScalar/RealScalar/ComplexScalar traits
├── kernels.rs # NumKernelStrictFinite, NumKernelStrictFiniteInDebug structs
├── macros.rs # Modular macro system (real!, complex!, validated struct macros)
├── scalars.rs # Constrained types: AbsoluteTolerance, PositiveRealScalar, NonNegativeRealScalar, etc.
├── prelude.rs # Common re-exports
├── core/
│ ├── traits.rs # RawKernel, NumKernel traits
│ ├── traits/
│ │ ├── raw.rs # RawScalarTrait, RawRealTrait, RawComplexTrait
│ │ └── validation.rs # ValidationPolicyReal/Complex, GuaranteesFinite* markers
│ ├── policies.rs # StrictFinitePolicy, DebugValidationPolicy
│ ├── types.rs # RealValidated<K>, ComplexValidated<K> (2800+ lines)
│ └── errors.rs # Error types, capture_backtrace() function
├── backends/
│ ├── native/
│ │ ├── raw.rs # Native raw trait implementations (concrete f64/Complex<f64> plus shared sealed-native generic impls for real and complex helper traits)
│ │ └── validated.rs # Native64StrictFinite, concrete type aliases
│ └── rug/ # (feature-gated)
│ ├── raw.rs # rug::Float/Complex raw trait implementations
│ └── validated.rs # RugStrictFinite, concrete type aliases
├── functions/ # Mathematical function traits
│ ├── sqrt.rs, trigonometric.rs, exponential.rs, logarithm.rs, ...
│ └── function_docs.rs # Shared documentation
├── algorithms/
│ ├── accumulators.rs # Accumulator trait hierarchy + NaiveSum/NeumaierSum/MaxAbsValueAccumulator
│ └── vector_norms.rs # L1/L2/L∞ vector norm APIs and trait-based norm dispatch
└── functions.rs # Re-exports all function traits
```
## Common Development Patterns
### Adding Mathematical Functions
1. Define trait in `src/functions/<category>.rs` (e.g., `trigonometric.rs`)
2. Implement for raw types (`f64`, `Complex<f64>`, rug types if needed)
3. Implement for validated wrappers in `src/core/types.rs` using `#[duplicate_item]` macro
4. Provide both panicking (`fn sqrt()`) and fallible (`fn try_sqrt()`) variants
5. Define specific error types for the function's domain (e.g., `SqrtRealInputErrors`)
### Macro Usage Pattern
The codebase heavily uses the `duplicate` crate for code generation:
```rust
#[duplicate_item(
kernel_type;
[Native64StrictFinite];
[Native64StrictFiniteInDebug];
)]
impl MyTrait for RealValidated<kernel_type> {
// implementation
}
```
This reduces duplication across validation policies and backends.
### Error Handling Convention
- **Input errors:** Issues with function arguments (e.g., negative value for sqrt)
- **Output errors:** Results that violate validation policy (e.g., operation produces NaN)
- All error types derive from `thiserror::Error`
- Backtraces captured only when `backtrace` feature enabled
- Use `capture_backtrace()` from `src/core/errors.rs`
### Error Handling Patterns
```rust
use num_valid::{RealNative64StrictFinite, RealScalar, functions::Sqrt};
// 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
use num_valid::functions::{SqrtRealErrors, SqrtRealInputErrors};
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}"),
}
}
```
### Testing Strategy
- Tests organized in module-level `mod tests` blocks
- Feature-gated tests for `rug` backend: `#[cfg(feature = "rug")]`
- Generic test functions parameterized by scalar type (e.g., `test_add::<T: FpScalar>()`)
- Run tests with: `cargo test --features=rug`
- For panicking constructors, use `#[should_panic(expected = "...")]` attribute
### API Design Patterns
- **Fallible + Panicking Pairs**: Provide both `try_*()` and panicking variants
- Example: `try_from_f64()` (returns `Result`) + `from_f64()` (panics on error)
- Panicking versions useful for constants and tests where validity is guaranteed
- Document panic conditions clearly in docstrings
### Naming Conventions
- **`kernel_` Prefix Policy**: Reserved for primitive type operations that manipulate underlying type properties and could conflict with inherent methods:
| Category | WITH `kernel_` prefix | WITHOUT prefix |
|----------|----------------------|----------------|
| Rounding | `kernel_ceil`, `kernel_floor`, `kernel_round`, `kernel_trunc`, `kernel_fract` | - |
| Sign | `kernel_copysign`, `kernel_signum`, `kernel_is_sign_positive`, `kernel_is_sign_negative` | - |
| Math | - | `sin`, `cos`, `sqrt`, `exp`, `ln`, `hypot`, `clamp`, `classify`, `exp_m1`, `ln_1p` |
- **Rationale**: The prefix prevents method resolution ambiguity when calling trait methods on types that have inherent methods with the same name.
- **UFCS in Doctests**: When trait methods have same names as inherent methods (e.g., `clamp`, `classify`), use fully qualified syntax:
```rust
Clamp::clamp(value, &min, &max) ```
### Performance Optimization Pattern (rug backend)
- **Use MPFR precomputed constants** when available instead of calculations
- Example: `rug::float::Constant::Pi` (~10x faster than `acos(-1)`)
- Example: `rug::float::Constant::Log2` (~10x faster than `ln(2)`)
- Available: `Pi`, `Log2`, `Euler`, `Catalan`
- Not available: `E` (must use `exp(1)`)
- **Cannot use LazyLock caching** inside `impl<const PRECISION>` (Rust limitation)
- Raw functions (`raw_pi`, `raw_ln_2`) should use MPFR constants directly
## Build & Test Commands
```bash
# Format code (always run before committing)
cargo fmt
# Run tests for native backend
cargo test
# Run tests with rug (arbitrary-precision)
cargo test --features=rug
# Run benchmarks
cargo bench
# Generate documentation with rug feature
cargo doc --features=rug --open
# Check coverage (requires tarpaulin)
cargo tarpaulin --config tarpaulin.toml
```
## Type System Conventions
### Naming Patterns
- `Real*` - Real number types
- `Complex*` - Complex number types
- `*Native64*` - Uses f64 backend
- `*Rug*` - Uses arbitrary-precision backend
- `*StrictFinite` - Always validates (debug + release)
- `*StrictFiniteInDebug` - Validates only in debug mode
- `Raw*Trait` - Layer 1 unchecked operations
- `*Validated` - Layer 3 validated wrappers
### Associated Types Pattern
- `RealType` - The real component type (for complex scalars)
- `RawReal` / `RawComplex` - Underlying raw type
- `Kind` - Sealed type for real/complex distinction
### Constrained Scalar Types (`src/scalars.rs`)
The `scalars.rs` module provides type-safe wrappers for mathematical constraints:
| `AbsoluteTolerance<T>` | `≥ 0` | ✅ Yes | `Zero`, `One`, `Pow<T>`, `Add`, `Mul` | Fixed error bounds for comparisons |
| `RelativeTolerance<T>` | `≥ 0` | ✅ Yes | `Zero`, `One`, `Pow<T>`, `Add`, `Mul` | Proportional error bounds |
| `PositiveRealScalar<T>` | `> 0` | ❌ No | `One`, `Pow<T>`, `Add`, `Mul` | Lengths, positive quantities |
| `NonNegativeRealScalar<T>` | `≥ 0` | ✅ Yes | `Zero`, `One`, `Pow<T>`, `Add`, `Mul` | Distances, magnitudes, absolute values |
**Architecture:**
- **Tolerance Types** (`AbsoluteTolerance<T>`, `RelativeTolerance<T>`): Lightweight `#[repr(transparent)]`
wrappers around `NonNegativeRealScalar<T>` providing semantic clarity for tolerance-specific use cases.
Both inherit all validation, traits, and error handling from the underlying `NonNegativeRealScalar`.
- **Constrained Scalars** (`PositiveRealScalar<T>`, `NonNegativeRealScalar<T>`): Full-featured types with
rich algebraic operations and immutable access patterns forming the foundation for mathematical computations.
- **Comparative Utility**: The `approximately_equal()` function provides precise, tolerance-aware value
comparisons returning `true` if `|a - b| ≤ tolerance`.
**Key Design Principles:**
- **Validation at construction**: All types use `TryNew` for fallible validation
- **Algebraic closure**: Operations preserve constraints (e.g., `NonNeg + NonNeg = NonNeg`, `Pos + NonNeg = Pos`)
- **Generic over `RealScalar`**: Work with `f64`, `RealNative64StrictFinite`, `rug::Float`, etc.
- **Reference-based arithmetic**: Support both `T op T` and `T op &T` for efficiency
- **Error semantics**: `AbsoluteTolerance` and `RelativeTolerance` use `ErrorsNonNegativeRealScalar` (no separate tolerance error type)
**`AbsoluteTolerance<T>` and `RelativeTolerance<T>` special features:**
- Wraps a `NonNegativeRealScalar<T>` with semantic specialization (tolerance vs. general non-negative)
- `AbsoluteTolerance::new(NonNegativeRealScalar)` for explicit wrapping from validated input
- `RelativeTolerance::new(NonNegativeRealScalar)` for explicit wrapping from validated input
- Both inherit all traits from `NonNegativeRealScalar`: `Add`, `Mul`, `AddAssign`, `MulAssign`, `Pow<T>`, `Max`, `Min`
- `RelativeTolerance::absolute_tolerance(reference)` converts to absolute tolerance based on reference value
- Use with `approximately_equal(a, b, tolerance)` for precise floating-point comparisons
**`PositiveRealScalar<T>` special features:**
- Implements `Add`, `Mul`, `AddAssign`, `MulAssign` (both by-value and by-reference)
- Implements `Pow<exponent_type>` for integer exponents (u8 through u128, signed variants)
- Has `.sqrt()` method for square root (always valid and remains positive for positive input)
- Has `.new(value)` constructor (validates in debug, skips in release for performance)
- Implements `One` trait for multiplicative identity
- Implements `Max`/`Min` traits for ordering and selection
- Can be converted to `NonNegativeRealScalar<T>` via `From` trait (mathematically valid since `ℝ⁺ ⊆ ℝ₀⁺`)
**`NonNegativeRealScalar<T>` special features:**
- Implements `Add`, `Mul`, `AddAssign`, `MulAssign` (both by-value and by-reference)
- Implements `Pow<exponent_type>` for integer exponents (u8 through u128, signed variants)
- Has `.sqrt()` method for square root (always valid for non-negative input)
- Has `.new(value)` constructor (validates in debug, skips in release for performance)
- Implements `Zero` and `One` traits for additive and multiplicative identities
- Implements `Max`/`Min` traits for ordering and selection
- Accepts `PositiveRealScalar<T>` via `From` trait (mathematical conversion: `ℝ⁺ ⊆ ℝ₀⁺`)
- Used internally by vector norm accumulators for validated summation
- Supports `NeumaierAddable` trait for compensated summation
## Special Considerations
### Precision in rug Backend
- Rug types use const generic precision: `RealRugStrictFinite<100>` (100-bit precision)
- Always validate f64→rug conversion for exact representability
- Non-exact f64 representations trigger `RawRealConversionError::NonRepresentableExactly`
### Raw Conversion Precision Contract (`TryConvertFromRawReal`)
- Raw real conversions now require explicit requested output precision (`precision_out`)
- Fixed-precision outputs must match native mantissa widths exactly:
- `f32` output requires `precision_out = 24`
- `f64` output requires `precision_out = 53`
- Precision mismatches return `RawRealConversionError::InvalidOutputPrecision`
- Exact representability is still enforced (roundtrip check), returning `RawRealConversionError::NonRepresentableExactly` when needed
- `rug::Float -> rug::Float` conversions are supported with precision changes, but remain exactness-checked
- For `RealValidated::try_convert_and_new_raw`, keep Option A semantics:
return `RawRealConversionError<RawRealIn, RawRealOut>` and map policy validation failures
on the converted output to `RawRealConversionError::OutputValidation`
- For native `f64` (`impl RealScalar for f64`), keep explicit finite-input validation in
`try_from_f64` and map invalid source values (`NaN`, `±∞`) to
`RawRealConversionError::InputValidation`
- When writing precision-boundary tests for f64->rug conversion, prefer values that actually
require all 53 mantissa bits (for example `1.0 + f64::EPSILON`), not values like `16.25`
that are exactly representable at much lower precision
### Complex Domain Guards
- In domain checks composed with multiple boolean clauses, parenthesize explicitly to avoid
precedence bugs (notably for complex `atanh` guards):
`imag_is_zero && (real <= -1 || real >= 1)`
### Bytemuck Integration
- `RealNative64StrictFinite` implements `CheckedBitPattern` + `NoUninit`
- Enables zero-copy conversions from byte arrays
- Invalid bit patterns (NaN, infinity, subnormals) rejected during conversion
### Reference vs. Value Semantics
- Prefer reference-based operations to avoid cloning arbitrary-precision types
- All arithmetic ops support: `T op T`, `T op &T`, `&T op T`, `&T op &T`
- Max/Min use reference-based API (not `Ord` trait) for efficiency
### Compensated Summation
- Default `Sum` implementation uses the compensated Neumaier strategy for accuracy
- The public `algorithms::accumulators` module exposes both `NaiveSum` and `NeumaierSum`, plus
`Accumulator::new_sequential()` and `Accumulator::combine()` for chunked reductions
- Example: `data.iter().sum::<RealNative64StrictFinite>()` uses compensated sum
- The public norm APIs live in `src/algorithms/vector_norms.rs` and use accumulator strategies
for naive/Neumaier reductions and max-abs scans
- See `src/algorithms/accumulators.rs` and `src/algorithms/vector_norms.rs`
### Public Accumulator Types (`algorithms::accumulators`)
**`ScaledSumSqAccumulator<ScalarType, SumSqAcc>`** — Computes numerically stable sum-of-squares
equivalent to LAPACK `dnrm2` (L2 norm) using dynamic scaling to prevent overflow/underflow:
| `new()` | Create empty accumulator (scale=0, sumsq=0) |
| `push(NonNegativeRealScalar)` | Add one non-negative value to accumulator |
| `combine(other)` | Merge two partial accumulators; handles scale reconciliation |
| `result()` | Return scale² × sumsq (squared magnitude with scaling) |
| `result_sqrt()` | Return scale × √sumsq (Euclidean L2 norm) |
| `new_sequential(iter)` | Build from iterator of pre-validated `NonNegativeRealScalar<T>` |
| `new_sequential_from_scalars(iter)` | Build from iterator of raw `ScalarType` with automatic `.abs()` |
| `new_parallel_from_scalars(par_iter)` | Parallel build using rayon `fold` + `reduce` + `.abs()` |
**Key Contract:**
- **Input type is enforced to be `NonNegativeRealScalar<T>`** — non-negativity is guaranteed at compile time
- Internal state tracks `(scale, sumsq)` where:
- `scale = max|vᵢ|` (maximum absolute value encountered, 0 if empty)
- `sumsq = Σ(|vᵢ|/scale)²` (normalized sum-of-squares)
- This representation prevents overflow for large vectors and underflow for tiny vectors
- `combine()` method performs direct `(scale, sumsq)` merge without computing intermediate norm
- Works with any summation strategy: `NaiveSum<T>` or `NeumaierSum<T>`
**Usage Pattern:**
```rust
use num_valid::algorithms::accumulators::*;
use num_valid::RealNative64StrictFinite;
// Direct accumulation of validated non-negative values
let mut acc = ScaledSumSqAccumulator::<NaiveSum<f64>>::new();
acc.push(NonNegativeRealScalar::new(3.0));
acc.push(NonNegativeRealScalar::new(4.0));
let norm = acc.result_sqrt(); // √(9 + 16) = 5.0
// Convenient construction from raw scalars (applies abs automatically)
let values = [3.0_f64, -4.0, 5.0];
let norm = ScaledSumSqAccumulator::<NeumaierSum<f64>>::new_sequential_from_scalars(values)
.result_sqrt(); // √(9 + 16 + 25) = √50
// Parallel construction for large datasets
let large_vec: Vec<f64> = (0..1_000_000).map(|i| (i as f64).sin()).collect();
let norm = ScaledSumSqAccumulator::<NeumaierSum<f64>>::new_parallel_from_scalars(
large_vec.into_par_iter()
).result_sqrt();
```
## Documentation Standards
- All public items require doc comments with examples
- Use intra-doc links: `[`RealScalar`]` instead of plain text
- Include error scenarios in function docs
- Document validation behavior (when checks occur)
- Mark unstable features with clear warnings
## Precision Requirements (rug Backend)
### Choosing Precision Values
- **Precision is bits in significand**, not decimal digits
- Common values: `53` (f64 equivalent), `100`, `200`, `500`, `1000`
- Precision conversion: `decimal_digits ≈ precision * log10(2) ≈ precision * 0.301`
- Example: 100-bit precision ≈ 30 decimal digits
### Type Declaration Pattern
```rust
// Define precision as const generic
type MyReal = RealRugStrictFinite<100>; // 100-bit precision
type MyComplex = ComplexRugStrictFinite<100>;
// Precision must match across compatible types
let a = MyReal::try_from_f64(1.5)?; // OK: validates exact representation
let b = MyReal::try_from_f64(0.1)?; // Error: 0.1 not exactly representable
```
### Precision Validation Rules
- `try_from_f64()` requires **exact representation** at target precision
- Non-exact conversions trigger `RawRealConversionError::NonRepresentableExactly` (e.g., `0.1` cannot be exactly represented)
- Precision mismatches between `rug::Float` values and expected kernel precision trigger `ErrorsValidationRawReal::PrecisionMismatch`
- Precision mismatches between types trigger compile-time errors
- Operations preserve precision: `RealRugStrictFinite<100> + RealRugStrictFinite<100> = RealRugStrictFinite<100>`
### Performance vs. Precision Trade-offs
- **53 bits:** Native f64 precision, minimal overhead
- **100-200 bits:** Good balance for most scientific computing
- **500+ bits:** High precision but noticeable performance impact
- **1000+ bits:** Use only when mathematically required
## CI/CD and GitLab Pipeline
### Pipeline Structure
The project uses GitLab CI with comprehensive test coverage:
- **4 build configurations:** Debug/Release × with/without rug
- **Test matrix:** All feature combinations tested separately
- **Coverage reporting:** `tarpaulin` with 4 configurations (see `tarpaulin.toml`)
- **Security:** `cargo-audit` runs on schedule
- **Formatting/Linting:** `cargo fmt` and `clippy` checks
### Running Coverage Locally
```bash
# Install tarpaulin
cargo install cargo-tarpaulin
# Run specific configuration from tarpaulin.toml
cargo tarpaulin --config debug_no_rug
cargo tarpaulin --config release_rug
# Generate HTML report
cargo tarpaulin --out Html --output-dir coverage/
```
### CI Requirements
- **Nightly toolchain:** Pipeline uses `rustlang/rust:nightly` Docker image
- **System dependencies:** libgmp-dev, libmpfr-dev, libmpc-dev (for rug)
- **Coverage threshold:** Aim for ≥90% (green badge), minimum 75% (yellow)
### Badge Configuration
Update README badges with your GitLab username:
```markdown
[]
[]
```
Enable coverage parsing in GitLab Settings → CI/CD with regex: `Coverage: (\d+\.?\d*)%`
## Benchmarking and Performance
### Running Benchmarks
```bash
# Run all benchmarks
cargo bench
# Run specific benchmark
cargo bench --bench performance_benchmark
# Generate detailed reports (HTML output in target/criterion/)
cargo bench -- --verbose
```
### Benchmark Organization
- Located in `benches/performance_benchmark.rs`
- Uses `criterion` crate for statistical analysis
- Test vector size: 10,000 elements (adjust `VECTOR_SIZE` constant for different scales)
### Performance Expectations
#### Native64 Backend (f64)
- **Validated types overhead:** ~5-15% compared to raw f64 (Release mode)
- **StrictFinite vs StrictFiniteInDebug:** Identical performance in Release builds
- **Compensated summation:** ~2-3x slower than naive sum, but numerically superior
#### Rug Backend (arbitrary-precision)
- **Precision impact:** Linear scaling with bit width for most operations
- **100-bit precision:** ~10-50x slower than f64 depending on operation
- **Memory usage:** Increases with precision (approximately `precision/8` bytes per value)
### Optimization Checklist
- Use `StrictFiniteInDebug` for performance-critical Release builds
- Prefer `&T` (references) over `T` (values) for rug types to avoid clones
- Use `mul_add_ref()` instead of separate multiply and add
- Consider `fold()` instead of `sum()` when accuracy is less critical than speed
## Contributing and Release Workflow
### Version Management
- **Versioning:** Follows [Semantic Versioning](https://semver.org/) (MAJOR.MINOR.PATCH)
- **Release tool:** Uses `cargo-release` configured in `Release.toml`
- **Protected branch:** Releases only from `master` branch
- **Changelog:** Maintained manually in `CHANGELOG.md` following [Keep a Changelog](https://keepachangelog.com/)
### Release Process
```bash
# 1. Update CHANGELOG.md with new version section
# Move items from [Unreleased] to [X.Y.Z] - YYYY-MM-DD
# 2. Run cargo-release (dry-run first)
cargo release patch --dry-run # For bug fixes (0.2.4 -> 0.2.5)
cargo release minor --dry-run # For new features (0.2.4 -> 0.3.0)
cargo release major --dry-run # For breaking changes (0.2.4 -> 1.0.0)
# 3. Execute release (signs commits and tags)
cargo release patch --execute
# 4. Verify GitLab pipeline passes
# 5. Manually publish to crates.io (publish = false in Release.toml)
cargo publish
```
### Commit and Change Conventions
#### Changelog Categories
- **Added:** New features, traits, functions, or types
- **Changed:** Modifications to existing functionality
- **Fixed:** Bug fixes
- **Removed:** Deprecated/removed features
#### Feature Flag Changes
When adding/modifying features:
1. Update `Cargo.toml` feature flags
2. Add feature-gated tests: `#[cfg(feature = "new_feature")]`
3. Update documentation in both README.md and lib.rs
4. Add examples demonstrating the feature
5. Update CHANGELOG.md under "Added" or "Changed"
#### Breaking Changes
- Increment MAJOR version
- Clearly document migration path in CHANGELOG
- Consider deprecation period for public APIs
- Update all examples and tests
### Pre-Commit Checklist
```bash
# Format code
cargo fmt
# Check compilation for all feature combinations
cargo check
cargo check --features=rug
cargo check --features=backtrace
cargo check --all-features
# Run tests
cargo test
cargo test --features=rug
cargo test --all-features
# Check for common mistakes
cargo clippy -- -D warnings
# Verify documentation builds
cargo doc --features=rug --no-deps
```
### Documentation Updates
- **API changes:** Update doc comments with examples
- **Architecture changes:** Update `.github/copilot-instructions.md` and `docs/ARCHITECTURE.md`
- **User-facing changes:** Update README.md
- **Internal changes:** Document in code comments
## When Working With This Codebase
**📖 Essential Reading:** Before making significant changes, read [`docs/ARCHITECTURE.md`](../docs/ARCHITECTURE.md) to understand the design philosophy and implementation patterns.
1. **Before adding raw operations:** Ensure they follow the `unchecked_*` naming convention
2. **When adding traits:** Use `#[duplicate_item]` macro to reduce boilerplate
3. **For new validation policies:** Implement `GuaranteesFiniteRealValues` if guaranteeing finite values
4. **Adding rug support:** Always feature-gate with `#[cfg(feature = "rug")]`
5. **Error types:** Use `thiserror` and follow input/output error categorization
6. **Generic bounds:** Use `FpScalar`, `RealScalar`, or `ComplexScalar` for maximum flexibility
7. **Before committing:** Run `cargo fmt ; cargo test --features=rug`
8. **Performance-critical code:** Benchmark before and after with `cargo bench`
9. **Breaking changes:** Update CHANGELOG and consider MAJOR version bump
10. **Documentation changes:** Update README.md, lib.rs docs, and ARCHITECTURE.md as needed