# algea
[](https://crates.io/crates/algea)
[](https://docs.rs/algea)
[](https://github.com/atynagano/algea/actions/workflows/ci.yml)
[](https://github.com/atynagano/algea/blob/main/LICENSE-MIT)
[](https://github.com/atynagano/algea/blob/main/LICENSE-APACHE)
Portable, SIMD-accelerated vectors and matrices.
`algea` provides small fixed-size algebra types whose storage and operations
are selected for the target. It supports `f32`, `f64`, `i32`, `i64`, `u32`, and
`u64` elements, with vector dimensions and matrix row and column counts from one
through four. Both [`row_major`] and [`column_major`] matrix storage are
supported.
Implementations pursue target-specific performance, so floating-point results
are not guaranteed to be bit-for-bit identical; see
[Floating-point reproducibility](#floating-point-reproducibility).
## Stability
Documented public APIs follow Semantic Versioning.
The `__internal` module is public solely to support implementation details that
must remain reachable across crate boundaries, including bounds on public APIs.
It is not a supported public API and may be changed, moved, or removed in any
release. Downstream crates must not refer to it directly.
## Examples
Vectors provide lane-wise arithmetic and common geometric operations:
```rust
use algea::vector;
let a = vector![1.0_f32, 2.0, 3.0];
let b = vector![4.0_f32, 5.0, 6.0];
assert_eq!(a + b, vector![5.0, 7.0, 9.0]);
assert_eq!(a.dot(b), 32.0);
```
Row-major matrices are constructed from rows:
```rust
use algea::row_major::Matrix;
let a = Matrix::<f32, 2, 2>::from_rows([[1.0, 2.0], [3.0, 4.0]]);
let b = Matrix::<f32, 2, 2>::from_rows([[5.0, 6.0], [7.0, 8.0]]);
assert_eq!((a * b).to_rows(), [[19.0, 22.0], [43.0, 50.0]]);
```
Column-major matrices represent the same operations but are constructed from
columns:
```rust
use algea::column_major::Matrix;
let a = Matrix::<f32, 2, 2>::from_columns([[1.0, 3.0], [2.0, 4.0]]);
let b = Matrix::<f32, 2, 2>::from_columns([[5.0, 7.0], [6.0, 8.0]]);
assert_eq!((a * b).to_columns(), [[19.0, 43.0], [22.0, 50.0]]);
```
Elements convert between types with `cast`:
```rust
use algea::vector;
let v = vector![1.5_f64, -2.5, 3.5];
assert_eq!(v.cast::<i32>(), vector![1_i32, -2, 3]);
```
## Generic code
The [`Element`] family of traits expresses which element types and dimensions
are available. Generic code can state that requirement in either of the
following equivalent forms:
```rust
use algea::{Element, Vector};
fn by_parameter<T: Element<D>, const D: usize>(value: Vector<T, D>) {
let _ = value;
}
fn by_where_clause<const D: usize>(value: Vector<f32, D>)
where
f32: Element<D>,
{
let _ = value;
}
```
## Arithmetic semantics
Operations follow `std::simd::Simd`, not the scalar operators, wherever the two
differ. Integer addition, subtraction, multiplication, and negation therefore
wrap in both debug and release builds; integer division by zero panics; and
dividing the most negative value by `-1` wraps back to that value. SIMD padding
lanes do not participate in public operations or panic checks.
Casting an element follows Rust's `as` conversion, as `std::simd::Simd::cast`
does, for every pair of supported types. That includes saturation at the
destination's limits and `NaN` becoming zero when a floating-point value becomes
an integer, and the sign of zero when a floating-point value changes width. The
bit pattern of a `NaN` result is not specified.
## Floating-point reproducibility
A floating-point result is not guaranteed to be the same everywhere. What varies,
and why:
- **Between targets.** Each backend picks the order of additions and whether to
use fused multiply-add from what the target offers, so the same expression can
round differently on x86-64, aarch64 and WebAssembly.
- **Between target-feature sets of one target.** Enabling FMA on x86-64, or
`relaxed-simd` on WebAssembly, changes how many roundings an expression takes.
- **Between runtimes, on WebAssembly with `relaxed-simd`.** Those instructions
leave fusing to the runtime, so one binary can round differently under two
engines. `wasmtime -W relaxed-simd-deterministic=y` pins it to the unfused
behaviour.
- **Between versions of this crate.** A release may change the shape of a kernel,
and with it the order of additions.
Integer results, comparisons, and casts are exact, so none of this applies to
them.
## Layout
> **Note:** The layouts documented below describe the current implementation
> only. This crate does not guarantee that memory layout, size, alignment,
> padding, or element placement remains unchanged across crate versions,
> platforms, or selected backends. These details may change; do not rely on them
> for serialization, persistent data, FFI, or reinterpreting memory.
When the SIMD backend is selected, vectors have the following layouts. Sizes and
alignments are in bytes. In both tables, a 32-bit `T` is `f32`, `i32`, or `u32`,
and a 64-bit `T` is `f64`, `i64`, or `u64`.
| `Vector<T, 1>` | 4 | 4 | 8 | 8 |
| `Vector<T, 2>` | 8 | 8 | 16 | 16 |
| `Vector<T, 3>` | 16 | 16 | 32 | 32 |
| `Vector<T, 4>` | 16 | 16 | 32 | 32 |
SIMD-backed matrices have the following layouts. Each column-major matrix and
the transposed row-major shape shown beside it use the same storage layout.
| `column_major::Matrix<T, 1, 1>` / `row_major::Matrix<T, 1, 1>` | 4 | 4 | 8 | 8 |
| `column_major::Matrix<T, 2, 1>` / `row_major::Matrix<T, 1, 2>` | 8 | 8 | 16 | 16 |
| `column_major::Matrix<T, 3, 1>` / `row_major::Matrix<T, 1, 3>` | 16 | 16 | 32 | 32 |
| `column_major::Matrix<T, 4, 1>` / `row_major::Matrix<T, 1, 4>` | 16 | 16 | 32 | 32 |
| `column_major::Matrix<T, 1, 2>` / `row_major::Matrix<T, 2, 1>` | 8 | 8 | 16 | 16 |
| `column_major::Matrix<T, 2, 2>` / `row_major::Matrix<T, 2, 2>` | 16 | 16 | 32 | 32 |
| `column_major::Matrix<T, 3, 2>` / `row_major::Matrix<T, 2, 3>` | 32 | 16 | 64 | 32 |
| `column_major::Matrix<T, 4, 2>` / `row_major::Matrix<T, 2, 4>` | 32 | 16 | 64 | 32 |
| `column_major::Matrix<T, 1, 3>` / `row_major::Matrix<T, 3, 1>` | 16 | 16 | 32 | 32 |
| `column_major::Matrix<T, 2, 3>` / `row_major::Matrix<T, 3, 2>` | 32 | 16 | 48 or 64 | 16 or 32 |
| `column_major::Matrix<T, 3, 3>` / `row_major::Matrix<T, 3, 3>` | 48 | 16 | 96 | 32 |
| `column_major::Matrix<T, 4, 3>` / `row_major::Matrix<T, 3, 4>` | 48 | 16 | 96 | 32 |
| `column_major::Matrix<T, 1, 4>` / `row_major::Matrix<T, 4, 1>` | 16 | 16 | 32 | 32 |
| `column_major::Matrix<T, 2, 4>` / `row_major::Matrix<T, 4, 2>` | 32 | 16 | 64 | 32 |
| `column_major::Matrix<T, 3, 4>` / `row_major::Matrix<T, 4, 3>` | 64 | 16 | 128 | 32 |
| `column_major::Matrix<T, 4, 4>` / `row_major::Matrix<T, 4, 4>` | 64 | 16 | 128 | 32 |
The 2x3 shape is the one whose 64-bit storage depends on the target: three
two-lane units, 48 bytes with alignment 16, unless a four-lane 64-bit value
occupies one register, where it is two four-lane units, 64 bytes with
alignment 32.
When the non-SIMD backend is selected, storage uses scalar arrays instead. A
`Vector<T, D>` then has the size of `D` elements and the alignment of one, and a
matrix has the size of `R * C` elements with the same alignment. The SIMD tables
above therefore do not describe non-SIMD layouts.
## Comparison and alternatives
`algea` focuses on portable, target-selected SIMD implementations of small
fixed-size vectors and matrices, with explicit row-major and column-major
storage. Depending on the application, another crate may be a better fit.
✅ supported, ⚠️ conditional or partial, see the footnote, ❌ not supported.
| `algea` | ✅ | ✅ | ✅ | ❌ | ✅ | ⚠️ | ✅ | ✅ | ✅ | ✅ | ✅ | ❌ | `Vector<f32, 3>` | `Matrix<f32, 4, 4>` |
| [`glam` 0.33](https://docs.rs/glam) | ✅ | ✅ | ✅ | ✅ | ❌ | ❌ | ✅ | ✅ | ❌ | ✅ | ❌ | ✅ | `Vec3`, `Vec3A` | `Mat4` |
| [`nalgebra` 0.35](https://www.nalgebra.rs) | ⚠️ | ⚠️ | ⚠️ | ✅ | ✅ | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ✅ | `Matrix<f32, U3, U1, ArrayStorage<f32, 3, 1>>` | `Matrix<f32, U4, U4, ArrayStorage<f32, 4, 4>>` |
| [`cgmath` 0.18](https://docs.rs/cgmath) | ❌ | ❌ | ❌ | ❌ | ✅ | ❌ | ✅ | ❌ | ❌ | ✅ | ❌ | ✅ | `Vector3<f32>` | `Matrix4<f32>` |
| [`euclid` 0.22](https://docs.rs/euclid) | ❌ | ❌ | ❌ | ✅ | ✅ | ❌ | ✅ | ❌ | ❌ | ✅ | ❌ | ✅ | `Vector3D<f32, U>` | `Transform3D<f32, S, D>` |
| [`ultraviolet` 0.9](https://docs.rs/ultraviolet) | ⚠️ | ⚠️ | ⚠️ | ❌ | ❌ | ❌ | ⚠️ | ✅ | ❌ | ✅ | ❌ | ⚠️ | `Vec3` | `Mat4` |
| [`vek` 0.17](https://docs.rs/vek) | ⚠️ | ⚠️ | ⚠️ | ✅ | ✅ | ❌ | ✅ | ✅ | ❌ | ✅ | ✅ | ✅ | `Vec3<f32>` | `Mat4<f32>` |
| [`pathfinder_geometry` 0.5](https://docs.rs/pathfinder_geometry) | ✅ | ⚠️ | ❌ | ❌ | ❌ | ❌ | ⚠️ | ✅ | ❌ | ✅ | ❌ | ❌ | `Vector3F` | `Transform4F` |
## License
Licensed under either of Apache License, Version 2.0 or the MIT license, at your
option.