use std::fmt::Debug;
use std::ops::{Index, IndexMut};
use crate::scalar::Scalar;
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct SmallVec<T, const N: usize> {
data: [T; N],
len: usize,
}
impl<T: Scalar, const N: usize> SmallVec<T, N> {
#[inline]
pub fn zeros(len: usize) -> Self {
assert!(len <= N, "SmallVec: length {len} exceeds capacity {N}");
Self {
data: [T::zero(); N],
len,
}
}
#[inline]
pub fn from_slice(values: &[T]) -> Self {
let mut out = Self::zeros(values.len());
out.data[..values.len()].copy_from_slice(values);
out
}
#[inline]
pub fn len(&self) -> usize {
self.len
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len == 0
}
#[inline]
pub fn as_slice(&self) -> &[T] {
&self.data[..self.len]
}
#[inline]
pub fn as_mut_slice(&mut self) -> &mut [T] {
&mut self.data[..self.len]
}
}
impl<T: Scalar, const N: usize> Index<usize> for SmallVec<T, N> {
type Output = T;
#[inline]
fn index(&self, i: usize) -> &T {
assert!(i < self.len, "SmallVec: index {i} past length {}", self.len);
&self.data[i]
}
}
impl<T: Scalar, const N: usize> IndexMut<usize> for SmallVec<T, N> {
#[inline]
fn index_mut(&mut self, i: usize) -> &mut T {
assert!(i < self.len, "SmallVec: index {i} past length {}", self.len);
&mut self.data[i]
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct SmallMat<T, const N: usize> {
data: [T; N],
nrows: usize,
ncols: usize,
}
impl<T: Scalar, const N: usize> SmallMat<T, N> {
#[inline]
pub fn zeros(nrows: usize, ncols: usize) -> Self {
assert!(
nrows * ncols <= N,
"SmallMat: {nrows}x{ncols} exceeds capacity {N}"
);
Self {
data: [T::zero(); N],
nrows,
ncols,
}
}
#[inline]
pub fn from_slice(nrows: usize, ncols: usize, values: &[T]) -> Self {
assert_eq!(
values.len(),
nrows * ncols,
"SmallMat: expected {nrows} * {ncols} values"
);
let mut out = Self::zeros(nrows, ncols);
out.data[..values.len()].copy_from_slice(values);
out
}
#[inline]
pub fn nrows(&self) -> usize {
self.nrows
}
#[inline]
pub fn ncols(&self) -> usize {
self.ncols
}
#[inline]
pub fn as_slice(&self) -> &[T] {
&self.data[..self.nrows * self.ncols]
}
#[inline]
pub fn as_mut_slice(&mut self) -> &mut [T] {
&mut self.data[..self.nrows * self.ncols]
}
#[inline]
pub fn as_col_slice(&self, j: usize) -> &[T] {
assert!(j < self.ncols, "SmallMat: column {j} out of bounds");
&self.data[j * self.nrows..][..self.nrows]
}
#[inline]
pub fn as_col_slice_mut(&mut self, j: usize) -> &mut [T] {
assert!(j < self.ncols, "SmallMat: column {j} out of bounds");
&mut self.data[j * self.nrows..][..self.nrows]
}
#[inline]
pub fn transposed(&self) -> Self {
let mut out = Self::zeros(self.ncols, self.nrows);
for j in 0..self.ncols {
for i in 0..self.nrows {
out[(j, i)] = self[(i, j)];
}
}
out
}
#[inline]
pub fn mat_mul(&self, rhs: &Self) -> Self {
assert_eq!(
self.ncols, rhs.nrows,
"SmallMat: cannot multiply {}x{} by {}x{}",
self.nrows, self.ncols, rhs.nrows, rhs.ncols
);
let (m, k, n) = (self.nrows, self.ncols, rhs.ncols);
let mut out = Self::zeros(m, n);
let a = &self.data[..m * k];
let b = &rhs.data[..k * n];
let c = &mut out.data[..m * n];
for j in 0..n {
for i in 0..m {
let mut acc = T::zero();
for l in 0..k {
acc += a[l * m + i] * b[j * k + l];
}
c[j * m + i] = acc;
}
}
out
}
}
impl<T: Scalar, const N: usize> Index<(usize, usize)> for SmallMat<T, N> {
type Output = T;
#[inline]
fn index(&self, (i, j): (usize, usize)) -> &T {
assert!(
i < self.nrows && j < self.ncols,
"SmallMat: ({i}, {j}) out of bounds"
);
&self.data[j * self.nrows + i]
}
}
impl<T: Scalar, const N: usize> IndexMut<(usize, usize)> for SmallMat<T, N> {
#[inline]
fn index_mut(&mut self, (i, j): (usize, usize)) -> &mut T {
assert!(
i < self.nrows && j < self.ncols,
"SmallMat: ({i}, {j}) out of bounds"
);
&mut self.data[j * self.nrows + i]
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn small_vec_slices_only_the_live_entries() {
let v = SmallVec::<f64, 8>::from_slice(&[1.0, 2.0, 3.0]);
assert_eq!(v.len(), 3);
assert_eq!(v.as_slice(), &[1.0, 2.0, 3.0]);
assert_eq!(v[2], 3.0);
let mut z = SmallVec::<f64, 8>::zeros(2);
z[1] = 5.0;
assert_eq!(z.as_slice(), &[0.0, 5.0]);
}
#[test]
#[should_panic(expected = "exceeds capacity")]
fn small_vec_rejects_over_capacity() {
SmallVec::<f64, 2>::from_slice(&[1.0, 2.0, 3.0]);
}
#[test]
fn small_mat_columns_are_contiguous_when_not_square() {
let m = SmallMat::<f64, 32>::from_slice(3, 2, &[1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
assert_eq!(m.nrows(), 3);
assert_eq!(m.ncols(), 2);
assert_eq!(m.as_col_slice(0), &[1.0, 2.0, 3.0]);
assert_eq!(m.as_col_slice(1), &[4.0, 5.0, 6.0]);
assert_eq!(m[(2, 1)], 6.0);
assert_eq!(m.as_slice().len(), 6);
}
#[test]
fn small_mat_packs_to_the_current_shape() {
let wide = SmallMat::<f64, 36>::from_slice(2, 3, &[1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
assert_eq!(wide.as_col_slice(1), &[3.0, 4.0]);
let tall = SmallMat::<f64, 36>::from_slice(3, 2, &[1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
assert_eq!(tall.as_col_slice(1), &[4.0, 5.0, 6.0]);
}
#[test]
fn small_mat_transposed_makes_rows_contiguous() {
let m = SmallMat::<f64, 32>::from_slice(3, 2, &[1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
let t = m.transposed();
assert_eq!(t.nrows(), 2);
assert_eq!(t.ncols(), 3);
assert_eq!(t.as_col_slice(0), &[1.0, 4.0]);
assert_eq!(t.as_col_slice(2), &[3.0, 6.0]);
assert_eq!(t.transposed(), m);
}
#[test]
fn small_mat_mat_mul() {
let a = SmallMat::<f64, 36>::from_slice(2, 2, &[1.0, 2.0, 3.0, 4.0]);
let b = SmallMat::<f64, 36>::from_slice(2, 2, &[5.0, 6.0, 7.0, 8.0]);
let c = a.mat_mul(&b);
assert_eq!(c[(0, 0)], 23.0);
assert_eq!(c[(1, 0)], 34.0);
assert_eq!(c[(0, 1)], 31.0);
assert_eq!(c[(1, 1)], 46.0);
let eye = SmallMat::<f64, 36>::from_slice(2, 2, &[1.0, 0.0, 0.0, 1.0]);
assert_eq!(a.mat_mul(&eye), a);
assert_eq!(eye.mat_mul(&a), a);
}
#[test]
fn small_mat_mat_mul_non_square() {
let a = SmallMat::<f64, 36>::from_slice(2, 3, &[1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
let b = SmallMat::<f64, 36>::from_slice(3, 2, &[1.0, 0.0, 0.0, 0.0, 1.0, 0.0]);
let c = a.mat_mul(&b);
assert_eq!(c.nrows(), 2);
assert_eq!(c.ncols(), 2);
assert_eq!(c.as_col_slice(0), a.as_col_slice(0));
assert_eq!(c.as_col_slice(1), a.as_col_slice(1));
}
#[test]
#[should_panic(expected = "cannot multiply")]
fn small_mat_mat_mul_rejects_mismatched_shapes() {
let a = SmallMat::<f64, 36>::from_slice(2, 3, &[1.0; 6]);
let b = SmallMat::<f64, 36>::from_slice(2, 2, &[1.0; 4]);
let _ = a.mat_mul(&b);
}
#[test]
#[should_panic(expected = "exceeds capacity")]
fn small_mat_rejects_over_capacity() {
SmallMat::<f64, 4>::zeros(3, 3);
}
}