rustebra 0.2.1

A hybrid no_std/alloc linear algebra crate for Rust, scaling from embedded targets to dynamic Krylov subspace solvers.
Documentation
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/).

## [Unreleased]

## [0.2.1] - 2026-06-25

### Added

- `examples/algorithm/vector.rs`, touring `algorithm::vector`'s functions (`add`, `sub`, `scale`, `dot`, `norm`) directly via `Storage`, the same style `examples/algorithm/matrix/*.rs` already use; wired into `examples/algorithm/main.rs` alongside `matrix`.
- `examples/storage/` (`main.rs`, `static.rs`, `dynamic.rs`), the same `main.rs`-dispatches-`mod`s structure as `examples/matrix/`/`examples/vector/`: `static.rs` constructs a `StaticStorage` and inspects it through `Storage`; `dynamic.rs`, gated behind `#[cfg(feature = "alloc")]`, does the same for `DynamicStorage`.
- `examples/scalar/sqrt.rs`, one file per elementary `Scalar` function, calling it on both `f32` and `f64`; `examples/scalar/f32.rs`/`f64.rs` are trimmed to just the basic arithmetic identities (`zero`/`one`/`add`/`sub`/`mul`/`div`) to avoid demonstrating `sqrt` twice.
- A GitHub Actions workflow that publishes to crates.io on `v*` tag push, after checking the tag matches `Cargo.toml`'s version.

### Changed

- `Scalar::sin`/`Scalar::cos`'s near-identical bodies in `f32.rs` and `f64.rs` (computing `-x^2`, `2`, `3` and calling `taylor_series` inline) are now a single pair of generic functions in `trigonometry.rs` (`sin`/`cos`, taking `zero`/`one` as parameters rather than depending on `Scalar`, the same reason `sqrt.rs`'s `newton_raphson` already takes `zero`/`two` explicitly); `f32.rs`/`f64.rs` each reduce to a one-line call, matching how `sqrt` already delegates to `newton_raphson`.
- Added scope of the version 0.2.1
- Reorganized `examples/` to mirror `tests/`'s `algorithm`/`matrix`/`scalar`/`vector` grouping: each top-level group is one example (`cargo run --example algorithm`, etc.) with a `main.rs`/`mod.rs` holding only `mod` declarations and dispatch, the same role `tests/algorithm/main.rs`/`tests/matrix/main.rs` play for the test binaries; the actual demonstration code lives one file per `src` item, named after it (e.g. `examples/algorithm/matrix/cholesky.rs` for `algorithm::matrix::cholesky`, `examples/scalar/f32.rs` for `Scalar`'s `f32` impl), rather than one flat file per type (`examples/static_matrix.rs`, `examples/algorithm_matrix.rs`). `examples/algorithm/matrix/` now has one file per `algorithm::matrix` source file (`arithmetic`, `determinant`, `rank`, `lu`, `qr`, `cholesky`, `svd`, `condition`), splitting what was previously a single `algorithm_matrix.rs`; `examples/scalar/` gained an `f32.rs` alongside the existing `f64.rs` coverage, matching `tests/scalar/`'s per-type split. `dynamic_matrix`/`dynamic_vector` are no longer separate examples gated by `required-features` in `Cargo.toml` — they're `#[cfg(feature = "alloc")]` submodules of `matrix`/`vector` invoked conditionally from `main.rs`, the same pattern `tests/matrix/main.rs`/`tests/vector/main.rs` already use, so `Cargo.toml` no longer needs explicit `[[example]]` entries at all (auto-discovery picks up every `examples/*/main.rs`).
- Migrated the docs site from Jekyll to mdbook, with a custom theme.

## [0.2.0] - 2026-06-23

### Added
- `algorithm::matrix::determinant`, computed via recursive cofactor expansion along the first row; only defined for square matrices, returning `Result<T, DimensionMismatch>` instead of panicking on non-square input, per ADR 0004. Each minor is a zero-copy `Storage` view (a private `Minor` type) rather than a materialized submatrix, since submatrix sizes are only known at runtime in this `no_std`-first crate; `Minor` holds its source as `&dyn Storage<Item = T>` rather than a generic parameter, since a generic parameter would make every recursion level its own type (`Minor<Minor<Minor<...>>>`) with no compile-time bound on recursion depth — the one exception in this module to preferring generics over `dyn Trait`.
- Unit tests for `algorithm::matrix::determinant` (known 2x2 and 3x3 values, a singular matrix with a zero row, and non-square input as an error case).
- `StaticMatrix<T, N, N>::determinant`, wiring `algorithm::matrix::determinant` to an infallible method (`R == C` is guaranteed by the type, so the dimension mismatch is unreachable), and `DynamicMatrix<T>::determinant`, returning `Result<T, DimensionMismatch>` for real since a `DynamicMatrix`'s shape isn't known at compile time.
- Unit tests for both, including the non-square error case for `DynamicMatrix`.
- Extended `tests/matrix.rs` and the `examples/static_matrix.rs`/`examples/dynamic_matrix.rs` end-to-end demonstrations to cover `determinant`.
- `algorithm::matrix::rank`, computed by reducing the `rows x cols` matrix `a` to row echelon form via Gaussian elimination with partial pivoting, then counting the rows that aren't entirely zero; unlike `determinant`, works for non-square matrices. Since `Storage` is read-only, elimination runs in a caller-provided `scratch` buffer rather than mutating `a` in place; returns `Result<usize, DimensionMismatch>` if `a` or `scratch` doesn't have exactly `rows * cols` elements, rather than panicking.
- Unit tests for `algorithm::matrix::rank` (a full-rank case, a rank-deficient case with one row a multiple of another, a non-square case, and a mismatched-scratch-length error case).
- `StaticMatrix<T, R, C>::rank` and `DynamicMatrix<T>::rank`, wiring `algorithm::matrix::rank` to an infallible method (the internal scratch buffer always matches `self`'s shape, so the dimension mismatch is unreachable) that works for non-square shapes too, unlike `determinant`.
- Unit tests for both.
- Extended `tests/matrix.rs` and the `examples/static_matrix.rs`/`examples/dynamic_matrix.rs` end-to-end demonstrations to cover `rank`.
- `Scalar::sin` and `Scalar::cos`, public elementary functions on `Scalar`, computed via fixed-iteration Taylor series expansion around zero (`sin(x) = x - x^3/3! + x^5/5! - ...`, `cos(x) = 1 - x^2/2! + x^4/4! - ...`), each via a recurrence that avoids recomputing factorials and powers from scratch per term. No range reduction is performed, so precision degrades for inputs far from zero, the same trade-off `Scalar::sqrt` already documents for its own fixed-iteration approach.
- Unit tests for `Scalar::sin`/`Scalar::cos` on `f32` and `f64` (known angles: `0`, `pi/2`, `pi`).
- `tests/scalar.rs`, a black-box integration test exercising `Scalar`'s public API (`zero`/`one`/`add`/`sub`/`mul`/`div`/`sqrt`/`sin`/`cos`) on `f32` and `f64` using only `pub` items.
- `examples/scalar.rs`, a runnable end-to-end demonstration of every `Scalar` operation on `f64`.
- `tests/no_alloc.rs`, a `harness = false` integration test that registers a `#[global_allocator]` panicking on any `alloc`/`dealloc` call, then runs `StaticVector`/`StaticMatrix` `add`, `dot`, and `norm` against known values; a standard libtest harness allocates before running any test body, so it would trip the panicking allocator regardless of whether the code under test allocates, hence the custom `main`. Gated to run under `--no-default-features` only, matching the `alloc` feature's absence by default.
- `algorithm::matrix::lu_partial_pivot`, LU decomposition via Gaussian elimination with partial pivoting: at each step, the first row at or below the pivot with a nonzero entry in the pivot column is swapped into place (the same pivoting strategy `algorithm::matrix::rank` already uses), letting decomposition succeed on inputs plain Gaussian elimination would fail on (a zero already on the diagonal). Returns the row swap count rather than materializing a permutation matrix, since `l * u == p * a` only needs `p`'s sign (via swap parity) for the determinant and solve functions expected to consume this later. A column with no nonzero entry at or below the pivot (singular along that column) is left with a zero pivot in `u` rather than erroring, since that's a property of the input, not a malformed call. Only defined for square matrices, returning `Result<usize, DimensionMismatch>` instead of panicking on non-square input or mismatched output-buffer lengths, per ADR 0004.
- `algorithm::matrix::lu`, the high-level entry point that delegates to `lu_partial_pivot`.
- Unit tests for `algorithm::matrix::lu`/`lu_partial_pivot` (a known case requiring no pivoting, a case with a zero already on the diagonal that only succeeds because of pivoting, a singular matrix with an entire zero column, and the non-square/mismatched-buffer-length error cases), each verifying `l * u == p * a`.
- `algorithm::matrix::qr_householder`, QR decomposition via Householder reflections: for each column, builds a reflector `h = i - 2 * v * vᵗ / (vᵗ * v)` (`v = x + sign(x₁) * ‖x‖ * e₁`, sign chosen to match `x₁` to avoid catastrophic cancellation) that zeroes out the entries below the diagonal, applying it directly to `r`'s columns and `q`'s rows via the reflection formula rather than materializing each `h` as its own matrix. Only defined for `rows >= cols` (`q` is always square, so `r` couldn't otherwise stay upper triangular), returning `Result<(), DimensionMismatch>` instead of panicking on that or on mismatched buffer/scratch lengths.
- `algorithm::matrix::qr_gram_schmidt`, QR decomposition via modified Gram-Schmidt orthogonalization: each column is projected against every previously orthogonalized column in turn (subtracting from the running vector rather than the original column, which is what keeps rounding error from compounding as badly as classical Gram-Schmidt) and normalized into `q`. Produces a `rows x cols` `q` rather than `qr_householder`'s `rows x rows`, since Gram-Schmidt only ever produces as many orthonormal columns as `a` has. A column linearly dependent on the earlier ones leaves a zero column in `q` instead of dividing by a zero norm, the same choice `lu_partial_pivot` makes for a zero pivot.
- `algorithm::matrix::qr`, the high-level entry point that delegates to `qr_householder`, since Householder reflections are more numerically stable than Gram-Schmidt for general use.
- Unit tests for `algorithm::matrix::qr_householder`/`qr_gram_schmidt` (orthogonality of `q` and reconstruction of `a` from `q * r`, each for a square and a non-square case, plus the `rows < cols` and mismatched-scratch-length error cases), and for `algorithm::matrix::qr` confirming it matches `qr_householder`'s output.
- `algorithm::matrix::cholesky_decompose`, Cholesky decomposition of a symmetric positive-definite matrix into `l * lᵗ`: for each column `j`, `l[j][j] = sqrt(a[j][j] - sum_{k<j} l[j][k]^2)` and `l[i][j] = (a[i][j] - sum_{k<j} l[i][k] * l[j][k]) / l[j][j]` for `i > j`. Introduces `CholeskyError`, since this algorithm has a failure mode beyond a dimension mismatch: a negative value under the square root means `a` isn't positive-definite, reported as `Err(CholeskyError::NotPositiveDefinite)` rather than computing a nonsensical result. A value of exactly zero under the square root (positive *semi*-definite) leaves that column of `l` at zero instead of dividing by it, the same "leave a zero instead of erroring" choice `lu_partial_pivot` makes for a zero pivot.
- `algorithm::matrix::cholesky`, the high-level entry point that delegates to `cholesky_decompose`, the only Cholesky algorithm currently implemented.
- Unit tests for `algorithm::matrix::cholesky`/`cholesky_decompose` (reconstruction of `a` from `l * lᵗ` for a 2x2 and a 3x3 positive-definite matrix, a non-positive-definite case returning `NotPositiveDefinite`, the non-square/mismatched-buffer-length cases returning `DimensionMismatch`, and a case confirming `cholesky` matches `cholesky_decompose`'s output).
- Reorganized `algorithm::matrix` from a single file into a folder split by algorithm (`arithmetic`, `determinant`, `rank`, `lu`, `qr`, `cholesky`), each with its own colocated unit tests; `mod.rs` re-exports every item so existing call sites are unaffected.
- `algorithm::matrix::svd_qr_iteration`, singular value decomposition of any `rows x cols` matrix `a` by eigendecomposing the symmetric positive-semi-definite `aᵗ * a` via unshifted QR iteration (fixed at 100 iterations rather than convergence-checked, the same predictable-work trade-off `Scalar::sqrt`/`sin`/`cos` make for their own fixed-iteration approaches), accumulating the eigenvector matrix into `v` and reading the eigenvalues off the resulting diagonal. Singular values are `sqrt` of the eigenvalues, sorted descending via a selection sort performed directly on the diagonal and `v`'s columns (no separate index buffer, since only `T`-typed scratch is available). Each left singular vector is `a * v[:, i] / sigma_i`, left at zero instead of dividing by a zero singular value, the same choice `lu_partial_pivot` makes for a zero pivot. Takes a single caller-provided `scratch` buffer sized `5 * cols * cols + cols + rows`, partitioned internally into the working matrices the iteration needs, rather than growing the signature by one parameter per matrix.
- `algorithm::matrix::svd`, the high-level entry point that delegates to `svd_qr_iteration`, the only SVD algorithm currently implemented.
- Unit tests for `algorithm::matrix::svd`/`svd_qr_iteration` (reconstruction of `a` from `u * diag(sigma) * vᵗ` for a square and a non-square case, descending-sorted non-negative singular values for a diagonal matrix with out-of-order entries, singular-value count matching the known rank of a rank-deficient matrix, the mismatched-buffer-length error cases, and a case confirming `svd` matches `svd_qr_iteration`'s output).
- `algorithm::matrix::condition_number_svd`, the condition number `kappa(a) = sigma_max / sigma_min` of a square matrix, computed by reusing `algorithm::matrix::svd` and reading off the first and last entries of its already-descending-sorted `sigma` output. Introduces `ConditionNumberError`, since this has a failure mode beyond a dimension mismatch: a singular matrix (`sigma_min == 0`) has no well-defined condition number, reported as `Err(ConditionNumberError::Singular)` rather than dividing by that zero. Takes a single caller-provided `scratch` buffer sized `7 * rows * rows + 3 * rows`, partitioned internally into `svd`'s `u`/`sigma`/`v` outputs plus `svd`'s own scratch requirement, the same single-buffer approach `svd_qr_iteration` uses for its own working matrices.
- `tests/decompositions.rs`, black-box tests for `lu`, `qr`, `cholesky`, `svd`, and `condition_number` exercising only `pub` items on inputs distinct from the ones already covered by their colocated unit tests. Each test checks a mathematical property the result is required to satisfy (triangularity, orthogonality, reconstruction) or cross-validates against an independently-computed quantity from elsewhere in the public API (e.g. `determinant`'s cofactor expansion against an LU- or QR-based determinant, `svd`'s nonzero singular value count against `rank`, condition number under transpose), rather than asserting specific algorithm output.
- `examples/algorithm_matrix.rs`, a runnable end-to-end tour of every `algorithm::matrix` function operating directly on `Storage` and caller-provided buffers, since LU, QR, Cholesky, SVD, and condition number aren't wired to `StaticMatrix`/`DynamicMatrix` methods yet. Each operation with both a high-level entry point and one or more explicit algorithms runs both, naming the explicit function alongside the dispatcher.
- `algorithm::matrix::condition_number`, the high-level entry point that delegates to `condition_number_svd`, the only way this crate currently computes a condition number.
- Unit tests for `algorithm::matrix::condition_number`/`condition_number_svd` (the identity matrix giving `kappa = 1`, a diagonal matrix with a known ratio giving an exact `kappa`, a singular matrix returning `Singular`, the non-square/mismatched-scratch-length cases returning `DimensionMismatch`, and a case confirming `condition_number` matches `condition_number_svd`'s output).
- `algorithm::matrix::determinant_cofactor`, the existing recursive cofactor expansion, split out of `determinant` under its own name now that `determinant` dispatches between two algorithms.
- `algorithm::matrix::determinant_lu`, computing the determinant from an LU decomposition instead: `det(a) = (-1)^s * u[0][0] * ... * u[n-1][n-1]`, where `s` is the row swap count `lu_partial_pivot` performed. `O(n^3)` rather than cofactor expansion's `O(n!)`, at the cost of a caller-provided `scratch` buffer sized `2 * rows * cols` to hold the `l`/`u` factors.
- `.github/workflows/ci.yml`: a `test` job running `cargo build`/`cargo test` under both `--no-default-features` and `--features alloc`, plus `cargo clippy -- -D warnings` and `cargo fmt --check`; and a `docs` job (gated to pushes on `main`, depending on `test`) that runs `cargo doc --no-deps --features alloc` and deploys the generated API docs alongside a Jekyll-built site to GitHub Pages.
- A `Build (no_std, bare-metal cross-compile)` step in the `test` job: `cargo build --no-default-features --target thumbv6m-none-eabi`, cross-compiling the crate without the `alloc` feature for a Cortex-M0/M0+ target that has no operating system, no `std`, and no heap. Proves `no_std` compatibility at the compiler level — catches accidental dependence on `std`-only items that would otherwise compile silently on a hosted (`x86_64`) target — rather than relying on a runtime check. Requires the `thumbv6m-none-eabi` target component, installed via `dtolnay/rust-toolchain`'s `targets` input.
- A GitHub Pages site under `docs/`, built with Jekyll via the `pages-themes/cayman` remote theme (`docs/config.yml`): an ADR index page (`docs/adr/index.md`), a project banner (`docs/assets/banner.png`), and a customized `README.md`; published by the `docs` job above.
- `.github/PULL_REQUEST_TEMPLATE.md` and `.all-contributorsrc`, standard GitHub project scaffolding.

### Changed
- `Cargo.toml`: bumped `version` to `0.2.0`; added `keywords` (`linear-algebra`, `matrix`, `vector`, `no-std`, `numerical`) and `categories` (`algorithms`, `mathematics`, `no-std`).
- `algorithm::matrix::determinant` now dispatches by size instead of always using cofactor expansion: `determinant_cofactor` for `rows <= 4`, where cofactor expansion's overhead is negligible, and `determinant_lu` for larger matrices, where `O(n!)` becomes prohibitive. Widens `determinant`'s bound from `T: Scalar` to `T: Scalar + PartialEq`, since the LU path needs it; `StaticMatrix::determinant`/`DynamicMatrix::determinant` pick up the same bound as a per-method `where` clause rather than on their enclosing `impl` blocks, the same way `rank` already does. The LU path is only reachable behind the `alloc` feature, since `determinant`'s signature has no parameter for the `O(n^2)` scratch buffer it needs; without `alloc`, `determinant` always falls back to `determinant_cofactor`.
- Added a unit test confirming `determinant`'s output for a 5x5 matrix matches `determinant_lu` called directly.
- Reorganized `tests/` to mirror `src/`'s folder structure, one file per item under test, named after it (e.g. `tests/algorithm/matrix/cholesky.rs` for `algorithm::matrix::cholesky`, `tests/matrix/static_matrix.rs` for `StaticMatrix`), rather than one flat file per top-level area (`tests/decompositions.rs`, `tests/matrix.rs`, `tests/scalar.rs`, `tests/vector.rs`). Each directory's entry point (`main.rs`, or `mod.rs` one level down) now holds only `mod` declarations, the same role `src/algorithm/mod.rs` plays for the library itself — test logic always lives in a file named after what it tests, never in the entry point. `tests/algorithm/matrix/mod.rs` keeps the assertion helpers (`assert_all_close`, `assert_upper_triangular`, etc.) the split-out `lu`/`qr`/`cholesky`/`svd`/`condition` files all share. The `decompositions` test binary is renamed `algorithm` as a result (it now lives at `tests/algorithm/main.rs`); the others keep their names.

### Removed
- `tests/no_alloc.rs` and its `[[test]]` entry in `Cargo.toml`. Its approach — a `#[global_allocator]` that panics on any `alloc`/`dealloc` call, then running `StaticVector`/`StaticMatrix` operations against it — turned out to be an unreliable correctness signal: any panic in that binary, genuine allocation or plain value mismatch alike, needs to heap-allocate to box its own unwind payload, which re-enters the panicking allocator and aborts the process before the real failure ever prints. The `thumbv6m-none-eabi` cross-compile step above already proves no `alloc`-feature code is reachable without the feature enabled, the property this test existed to check, without that failure mode.

## [0.1.0] - 2026-06-21

### Added
- `Scalar` trait (zero, one, add, sub, mul, div) defining the scalar layer.
- `Scalar` implementation for `f32`.
- `Scalar` implementation for `f64`.
- Unit tests for the `f32` and `f64` `Scalar` implementations.
- `Storage` trait (element access, length) defining the storage layer.
- `StaticStorage<T, const N: usize>`, a stack-allocated `Storage` backed by a const-generic array.
- `alloc` feature flag (per ADR 0001), and `DynamicStorage<T>`, a heap-allocated `Storage` backed by `Vec<T>`, gated behind it.
- Unit tests for the `StaticStorage` and `DynamicStorage` implementations.
- Verified the crate builds, lints, and tests clean with `--no-default-features` (no `alloc`).
- `Scalar::sqrt`, computed via fixed-iteration Newton-Raphson (Babylonian) iteration, since `no_std` has no `f32`/`f64` `sqrt`; returns `0` for `self <= 0` (no real square root, and no `NaN` sentinel since `Scalar` must stay infallible for future non-float implementors).
- Unit tests for `Scalar::sqrt` on `f32` and `f64` (exact perfect squares, tolerance-bounded irrational results, negative-input behavior).
- `algorithm::vector::add`, the first algorithm-layer function: element-wise vector addition generic over `Storage` + `Scalar`, writing into a caller-provided output slice (since `Storage` has no way to construct a new instance) and returning `Result<(), LengthMismatch>` instead of panicking on mismatched lengths, per ADR 0004.
- Unit tests for `algorithm::vector::add` (matching-length addition, mismatched operand lengths, mismatched output length).
- `algorithm::vector::sub`, element-wise vector subtraction, following the same `Storage` + `Scalar` generic, output-slice, `Result<(), LengthMismatch>` pattern as `add`.
- Unit tests for `algorithm::vector::sub` (matching-length subtraction, mismatched operand lengths, mismatched output length).
- `algorithm::vector::scale`, element-wise scalar multiplication; still returns `Result<(), LengthMismatch>` since the output buffer's length can disagree with the input's even with only one `Storage` operand.
- Unit tests for `algorithm::vector::scale` (known factor, scaling by zero, mismatched output length).
- `algorithm::vector::dot`, the inner product, returning `Result<T, LengthMismatch>` instead of panicking on mismatched lengths; documented as the building block for the upcoming `norm` (`‖v‖ = sqrt(dot(v, v))`).
- Unit tests for `algorithm::vector::dot` (known vectors, mismatched lengths).
- `algorithm::vector::norm`, the Euclidean (L2) norm, implemented as `dot(a, a).sqrt()`; infallible (returns `T` directly), since comparing `a` against itself can never produce a length mismatch.
- Unit tests for `algorithm::vector::norm` (exact known case, tolerance-bounded irrational case).
- `StaticVector<T, const N: usize>`, the first public API layer type: wires `StaticStorage` together with `algorithm::vector`'s `add`/`sub`/`scale`/`dot`/`norm` into ergonomic methods (`v1.add(&v2)`, etc.), constructed from a fixed-size array via `StaticVector::new`.
- `PartialEq` and `Debug` implementations for `StaticVector`, to support equality assertions and printing in tests without exposing raw element access.
- Unit tests for `StaticVector` construction and each operation (`add`, `sub`, `scale`, `dot`, `norm`), confirming the wiring to the algorithm layer.
- `DynamicVector<T>`, the heap-allocated counterpart to `StaticVector`, gated behind the `alloc` feature: wires `DynamicStorage` together with `algorithm::vector`'s functions the same way, but since two `DynamicVector`s aren't guaranteed by the type system to share a length, `add`/`sub`/`dot` return `Result<_, LengthMismatch>` for real (not just defensively) rather than panicking, per ADR 0004.
- `PartialEq` and `Debug` implementations for `DynamicVector`, accounting for operands of different lengths.
- Unit tests for `DynamicVector` construction and each operation, including mismatched-length cases for `add`, `sub`, and `dot`.
- `examples/static_vector.rs` and `examples/dynamic_vector.rs`, runnable end-to-end demonstrations of `StaticVector` and `DynamicVector` (the latter gated via `required-features = ["alloc"]` in `Cargo.toml`, so `cargo build`/`run --examples` skip it cleanly without the feature).
- `tests/vector.rs`, a black-box integration test exercising the public `StaticVector`/`DynamicVector` API end-to-end (construction plus `add`/`sub`/`scale`/`dot`/`norm`) using only `pub` items, with the `DynamicVector` case gated behind `alloc`.
- `algorithm::matrix::add`, the first matrix-layer function: element-wise matrix addition generic over `Storage` + `Scalar`. Matrices are stored row-major in a flat `Storage` (which has no concept of rows/columns itself, per ADR 0003), so `rows`/`cols` are passed explicitly alongside each operand; returns `Result<(), DimensionMismatch>` instead of panicking on a row, column, or flat-length disagreement, per ADR 0004.
- Unit tests for `algorithm::matrix::add` (matching dimensions, mismatched rows, mismatched columns, mismatched output length).
- `algorithm::matrix::sub`, element-wise matrix subtraction, following the same row-major, explicit-shape, `Result<(), DimensionMismatch>` pattern as `add`.
- `algorithm::matrix::mul_scalar`, element-wise scalar multiplication of a matrix.
- `algorithm::matrix::mul_vector`, the matrix-vector product: each output element is `dot(row_i, v)`, reusing `algorithm::vector::dot` via a private zero-copy `Row` `Storage` view instead of re-deriving the summation.
- `algorithm::matrix::mul_matrix`, the matrix-matrix product: each output element is `dot(row_i of a, col_j of b)`, reusing `algorithm::vector::dot` via the `Row` view and a new private `Column` `Storage` view; validates `a`'s column count against `b`'s row count (the multiplication's inner dimension).
- `algorithm::matrix::transpose`, pure reindexing with no `Scalar` arithmetic, so it only requires `T: Copy` rather than `T: Scalar`.
- Unit tests for `sub`, `mul_scalar`, `mul_vector`, `mul_matrix`, and `transpose` (known-value cases plus dimension-mismatch error cases).
- `Storage` implementations for `StaticVector` and `DynamicVector` (delegating to their internal storage), so they can be passed directly to generic `Storage`-based functions — needed for `StaticMatrix`/`DynamicMatrix` to multiply against them.
- `StaticMatrix<T, const R: usize, const C: usize>`, the first public-API matrix type: stored as `[[T; C]; R]` (since `R * C` can't be a single const-generic array length on stable Rust, unlike `StaticVector`, it implements `Storage` directly over that field rather than wrapping `StaticStorage`). Wires `add`/`sub`/`mul_scalar`/`mul_vector`/`mul_matrix`/`transpose` to `algorithm::matrix`; `mul_matrix<const C2: usize>` changes shape (`R x C` times `C x C2` gives `R x C2`), with the inner-dimension check statically guaranteed (and thus unreachable) by the type system, same as the other same-shape operations.
- Unit tests for `StaticMatrix` construction and each operation, confirming the wiring to the algorithm layer.
- `DynamicMatrix<T>`, the heap-allocated counterpart to `StaticMatrix`, gated behind the `alloc` feature: shape (`rows`/`cols`) lives in its fields rather than its type, so `add`/`sub`/`mul_vector`/`mul_matrix` return `Result<_, DimensionMismatch>` for real (not just defensively) rather than panicking, per ADR 0004; `mul_scalar`/`transpose` stay infallible, sized from `self` alone. Constructed via `DynamicMatrix::new(rows, cols, Vec<T>)`, itself fallible since the flat data's length can disagree with the claimed shape.
- `PartialEq` and `Debug` implementations for `DynamicMatrix`, accounting for operands of different shapes.
- Unit tests for `DynamicMatrix` construction and each operation, including mismatched-shape/inner-dimension error cases for `add`, `sub`, `mul_vector`, and `mul_matrix`.
- `examples/static_matrix.rs` and `examples/dynamic_matrix.rs`, runnable end-to-end demonstrations of `StaticMatrix` and `DynamicMatrix` (the latter gated via `required-features = ["alloc"]` in `Cargo.toml`, matching the vector examples' pattern).
- `tests/matrix.rs`, a black-box integration test exercising the public `StaticMatrix`/`DynamicMatrix` API end-to-end (construction plus `add`/`sub`/`mul_scalar`/`mul_vector`/`mul_matrix`/`transpose`) using only `pub` items, with the `DynamicMatrix` case gated behind `alloc`.