Expand API and start adding tests

This commit is contained in:
Andrew Cassidy 2022-08-18 21:07:05 -07:00
parent 2bb625bdaf
commit e928ed6926
6 changed files with 409 additions and 191 deletions

View File

@ -6,3 +6,4 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
generic_parameterize = "0.1.0"

View File

@ -1,4 +1,6 @@
pub trait Index2D: Copy { use std::fmt::Debug;
pub trait Index2D: Copy + Debug {
fn to_1d(self, height: usize, width: usize) -> Option<usize> { fn to_1d(self, height: usize, width: usize) -> Option<usize> {
let (r, c) = self.to_2d(height, width)?; let (r, c) = self.to_2d(height, width)?;
Some(r * width + c) Some(r * width + c)

View File

@ -1,3 +1,7 @@
extern crate core;
pub mod index; pub mod index;
mod matrix;
mod macros; mod macros;
mod matrix;
pub use matrix::{Matrix, Scalar, Vector};

View File

@ -2,137 +2,141 @@
#[doc(hidden)] #[doc(hidden)]
#[macro_export] #[macro_export]
macro_rules! impl_matrix_op { macro_rules! impl_matrix_op {
(neg, $f:expr) => { (neg) => {
$crate::_impl_op_m_internal_ex!(Neg, neg, $f); $crate::_impl_op_m_internal_ex!(Neg, neg);
}; };
(!, $f:expr) => { (!) => {
$crate::_impl_op_m_internal_ex!(Not, not, $f); $crate::_impl_op_m_internal_ex!(Not, not);
}; };
(+, $f:expr) => { (+) => {
$crate::_impl_op_mm_internal_ex!(Add, add, $f); $crate::_impl_op_mm_internal_ex!(Add, add);
$crate::_impl_opassign_mm_internal_ex!(Add, AddAssign, add_assign, $f); $crate::_impl_opassign_mm_internal_ex!(AddAssign, add_assign);
}; };
(-, $f:expr) => { (-) => {
$crate::_impl_op_mm_internal_ex!(Sub, sub, $f); $crate::_impl_op_mm_internal_ex!(Sub, sub);
$crate::_impl_opassign_mm_internal_ex!(Sub, SubAssign, sub_assign, $f); $crate::_impl_opassign_mm_internal_ex!(SubAssign, sub_assign);
}; };
(*, $f:expr) => { (*) => {
$crate::_impl_op_mm_internal_ex!(Mul, mul, $f); $crate::_impl_op_mm_internal_ex!(Mul, mul);
$crate::_impl_op_ms_internal_ex!(Mul, mul, $f); $crate::_impl_op_ms_internal_ex!(Mul, mul);
$crate::_impl_opassign_mm_internal_ex!(Mul, MulAssign, mul_assign, $f); $crate::_impl_opassign_mm_internal_ex!(MulAssign, mul_assign);
$crate::_impl_opassign_ms_internal_ex!(Mul, MulAssign, mul_assign, $f); $crate::_impl_opassign_ms_internal_ex!(MulAssign, mul_assign);
}; };
(/, $f:expr) => { (/) => {
$crate::_impl_op_mm_internal_ex!(Div, div, $f); $crate::_impl_op_mm_internal_ex!(Div, div);
$crate::_impl_op_ms_internal_ex!(Div, div, $f); $crate::_impl_op_ms_internal_ex!(Div, div);
$crate::_impl_opassign_mm_internal_ex!(Div, DivAssign, div_assign, $f); $crate::_impl_opassign_mm_internal_ex!(DivAssign, div_assign);
$crate::_impl_opassign_ms_internal_ex!(Div, DivAssign, div_assign, $f); $crate::_impl_opassign_ms_internal_ex!(DivAssign, div_assign);
}; };
(%, $f:expr) => { (%) => {
$crate::_impl_op_mm_internal_ex!(Rem, rem, $f); $crate::_impl_op_mm_internal_ex!(Rem, rem);
$crate::_impl_op_ms_internal_ex!(Rem, rem, $f); $crate::_impl_op_ms_internal_ex!(Rem, rem);
$crate::_impl_opassign_mm_internal_ex!(Rem, RemAssign, rem_assign, $f); $crate::_impl_opassign_mm_internal_ex!(RemAssign, rem_assign);
$crate::_impl_opassign_ms_internal_ex!(Rem, RemAssign, rem_assign, $f); $crate::_impl_opassign_ms_internal_ex!(RemAssign, rem_assign);
}; };
(&, $f:expr) => { (&) => {
$crate::_impl_op_mm_internal_ex!(BitAnd, bitand, $f); $crate::_impl_op_mm_internal_ex!(BitAnd, bitand);
$crate::_impl_opassign_mm_internal_ex!(BitAnd, BitAndAssign, bitand_assign, $f); $crate::_impl_opassign_mm_internal_ex!(BitAndAssign, bitand_assign);
}; };
(|, $f:expr) => { (|) => {
$crate::_impl_op_mm_internal_ex!(BitOr, bitor, $f); $crate::_impl_op_mm_internal_ex!(BitOr, bitor);
$crate::_impl_opassign_mm_internal_ex!(BitOr, BitOrAssign, bitor_assign, $f); $crate::_impl_opassign_mm_internal_ex!(BitOrAssign, bitor_assign);
}; };
(^, $f:expr) => { (^) => {
$crate::_impl_op_mm_internal_ex!(BitXor, bitxor, $f); $crate::_impl_op_mm_internal_ex!(BitXor, bitxor);
$crate::_impl_opassign_mm_internal_ex!(BitXor, BitXorAssign, bitxor_assign, $f); $crate::_impl_opassign_mm_internal_ex!(BitXorAssign, bitxor_assign);
}; };
(<<, $f:expr) => { (<<) => {
$crate::_impl_op_ms_internal_ex!(Shl, shl, $f); $crate::_impl_op_ms_internal_ex!(Shl, shl);
$crate::_impl_opassign_mm_internal_ex!(Shl, ShlAssign, shl_assign, $f); $crate::_impl_opassign_ms_internal_ex!(ShlAssign, shl_assign);
}; };
(>>, $f:expr) => { (>>) => {
$crate::_impl_op_ms_internal_ex!(Shr, shr, $f); $crate::_impl_op_ms_internal_ex!(Shr, shr);
$crate::_impl_opassign_mm_internal_ex!(Shr, ShrAssign, shr_assign, $f); $crate::_impl_opassign_ms_internal_ex!(ShrAssign, shr_assign);
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! impl_op_ms {
(*, $f:expr) => {
_impl_op_ms_internal!(Mul, mul, $f);
};
(/, $f:expr) => {
_impl_op_ms_internal!(Div, div, $f);
};
(%, $f:expr) => {
_impl_op_ms_internal!(Rem, rem, $f);
};
(<<, $f:expr) => {
_impl_op_ms_internal!(Shl, shl, $f);
};
(>>, $f:expr) => {
_impl_op_ms_internal!(Shr, shr, $d);
}; };
} }
#[macro_export] #[macro_export]
macro_rules! _impl_op_m_internal_ex { macro_rules! _impl_op_m_internal_ex {
($ops_trait:ident, $ops_fn:ident, $f:expr) => { ($ops_trait:ident, $ops_fn:ident) => {
$crate::_impl_op_m_internal!($ops_trait, $ops_fn, Matrix<L,M,N>, Matrix<L,M,N>, $f); $crate::_impl_op_m_internal!($ops_trait, $ops_fn, Matrix<L,M,N>, Matrix<L,M,N>);
$crate::_impl_op_m_internal!($ops_trait, $ops_fn, &Matrix<L,M,N>, Matrix<L,M,N>, $f); $crate::_impl_op_m_internal!($ops_trait, $ops_fn, &Matrix<L,M,N>, Matrix<L,M,N>);
} }
} }
#[macro_export] #[macro_export]
macro_rules! _impl_op_mm_internal_ex { macro_rules! _impl_op_mm_internal_ex {
($ops_trait:ident, $ops_fn:ident, $f:expr) => { ($ops_trait:ident, $ops_fn:ident) => {
$crate::_impl_op_mm_internal!($ops_trait, $ops_fn, Matrix<L,M,N>, Matrix<R,M,N>, Matrix<L,M,N>, $f); $crate::_impl_op_mm_internal!($ops_trait, $ops_fn, Matrix<L,M,N>, Matrix<R,M,N>, Matrix<L,M,N>);
$crate::_impl_op_mm_internal!($ops_trait, $ops_fn, &Matrix<L,M,N>, Matrix<R,M,N>, Matrix<L,M,N>, $f); $crate::_impl_op_mm_internal!($ops_trait, $ops_fn, &Matrix<L,M,N>, Matrix<R,M,N>, Matrix<L,M,N>);
$crate::_impl_op_mm_internal!($ops_trait, $ops_fn, Matrix<L,M,N>, &Matrix<R,M,N>, Matrix<L,M,N>, $f); $crate::_impl_op_mm_internal!($ops_trait, $ops_fn, Matrix<L,M,N>, &Matrix<R,M,N>, Matrix<L,M,N>);
$crate::_impl_op_mm_internal!($ops_trait, $ops_fn, &Matrix<L,M,N>, &Matrix<R,M,N>, Matrix<L,M,N>, $f); $crate::_impl_op_mm_internal!($ops_trait, $ops_fn, &Matrix<L,M,N>, &Matrix<R,M,N>, Matrix<L,M,N>);
} }
} }
#[macro_export] #[macro_export]
macro_rules! _impl_opassign_mm_internal_ex { macro_rules! _impl_opassign_mm_internal_ex {
($ops_super:ident, $ops_trait:ident, $ops_fn:ident, $f:expr) => { ($ops_trait:ident, $ops_fn:ident) => {
$crate::_impl_opassign_mm_internal!($ops_super, $ops_trait, $ops_fn, Matrix<L,M,N>, Matrix<R,M,N>, Matrix<L,M,N>, $f); $crate::_impl_opassign_mm_internal!($ops_trait, $ops_fn, Matrix<L,M,N>, Matrix<R,M,N>, Matrix<L,M,N>);
$crate::_impl_opassign_mm_internal!($ops_super, $ops_trait, $ops_fn, Matrix<L,M,N>, &Matrix<R,M,N>, Matrix<L,M,N>, $f); $crate::_impl_opassign_mm_internal!($ops_trait, $ops_fn, Matrix<L,M,N>, &Matrix<R,M,N>, Matrix<L,M,N>);
} }
} }
#[macro_export] #[macro_export]
macro_rules! _impl_op_ms_internal_ex { macro_rules! _impl_op_ms_internal_ex {
($ops_trait:ident, $ops_fn:ident, $f:expr) => { ($ops_trait:ident, $ops_fn:ident) => {
$crate::_impl_op_ms_internal!($ops_trait, $ops_fn, Matrix<L,M,N>, R, Matrix<L,M,N>, $f); $crate::_impl_op_ms_internal!($ops_trait, $ops_fn, Matrix<L,M,N>, R, Matrix<L,M,N>);
$crate::_impl_op_ms_internal!($ops_trait, $ops_fn, &Matrix<L,M,N>, R, Matrix<L,M,N>, $f); $crate::_impl_op_ms_internal!($ops_trait, $ops_fn, &Matrix<L,M,N>, R, Matrix<L,M,N>);
} }
} }
#[macro_export] #[macro_export]
macro_rules! _impl_opassign_ms_internal_ex { macro_rules! _impl_opassign_ms_internal_ex {
($ops_super:ident, $ops_trait:ident, $ops_fn:ident, $f:expr) => { ($ops_trait:ident, $ops_fn:ident) => {
$crate::_impl_opassign_ms_internal!($ops_super, $ops_trait, $ops_fn, Matrix<L,M,N>, R, Matrix<L,M,N>, $f); $crate::_impl_opassign_ms_internal!($ops_trait, $ops_fn, Matrix<L,M,N>, R, Matrix<L,M,N>);
} }
} }
#[macro_export]
macro_rules! _impl_op_m_internal {
($ops_trait:ident, $ops_fn:ident, $lhs:ty, $out:ty) => {
impl<L, const M: usize, const N: usize> ::std::ops::$ops_trait for $lhs
where
L: ::std::ops::$ops_trait<Output = L> + Scalar,
{
type Output = $out;
#[inline(always)]
fn $ops_fn(self) -> Self::Output {
let mut result = self.clone();
for m in 0..M {
for n in 0..N {
result.data[m][n] = self.data[m][n].$ops_fn();
}
}
result
}
}
};
}
#[macro_export] #[macro_export]
macro_rules! _impl_op_mm_internal { macro_rules! _impl_op_mm_internal {
($ops_trait:ident, $ops_fn:ident, $lhs:ty, $rhs:ty, $out:ty, $f:expr) => { ($ops_trait:ident, $ops_fn:ident, $lhs:ty, $rhs:ty, $out:ty) => {
impl<L, R, const M: usize, const N: usize> ::std::ops::$ops_trait<$rhs> for $lhs impl<L, R, const M: usize, const N: usize> ::std::ops::$ops_trait<$rhs> for $lhs
where where
L: ::std::ops::$ops_trait<R, Output = L>, L: ::std::ops::$ops_trait<R, Output = L> + Scalar,
L: Scalar,
R: Scalar, R: Scalar,
{ {
type Output = $out; type Output = $out;
fn $ops_fn(self, rhs_i: $rhs) -> Self::Output { #[inline(always)]
fn $ops_fn(self, other: $rhs) -> Self::Output {
let mut result = self.clone(); let mut result = self.clone();
let op = $f; for m in 0..M {
for (l, r) in zip(result.elements_mut(), rhs_i.elements()) { for n in 0..N {
*l = op(*l, *r); result.data[m][n] = self.data[m][n].$ops_fn(other.data[m][n]);
}
} }
result result
} }
@ -142,62 +146,41 @@ macro_rules! _impl_op_mm_internal {
#[macro_export] #[macro_export]
macro_rules! _impl_opassign_mm_internal { macro_rules! _impl_opassign_mm_internal {
($ops_super:ident, $ops_trait:ident, $ops_fn:ident, $lhs:ty, $rhs:ty, $out:ty, $f:expr) => { ($ops_trait:ident, $ops_fn:ident, $lhs:ty, $rhs:ty, $out:ty) => {
impl<L, R, const M: usize, const N: usize> ::std::ops::$ops_trait<$rhs> for $lhs impl<L, R, const M: usize, const N: usize> ::std::ops::$ops_trait<$rhs> for $lhs
where where
L: ::std::ops::$ops_trait<R>, L: ::std::ops::$ops_trait<R> + Scalar,
L: ::std::ops::$ops_super<R, Output = L>,
L: Scalar,
R: Scalar, R: Scalar,
{ {
fn $ops_fn(&mut self, rhs_i: $rhs) { #[inline(always)]
let op = $f; fn $ops_fn(&mut self, other: $rhs) {
for (l, r) in zip(self.elements_mut(), rhs_i.elements()) { for m in 0..M {
*l = op(*l, *r); for n in 0..N {
self.data[m][n].$ops_fn(other.data[m][n]);
}
} }
} }
} }
}; };
} }
#[macro_export]
macro_rules! _impl_op_m_internal {
($ops_trait:ident, $ops_fn:ident, $lhs:ty, $out:ty, $f:expr) => {
impl<L, const M: usize, const N: usize> ::std::ops::$ops_trait for $lhs
where
L: ::std::ops::$ops_trait<Output = L>,
L: Scalar,
{
type Output = $out;
fn $ops_fn(self) -> Self::Output {
let mut result = self.clone();
let op = $f;
for l in result.elements_mut() {
*l = op(*l);
}
result
}
}
};
}
#[macro_export] #[macro_export]
macro_rules! _impl_op_ms_internal { macro_rules! _impl_op_ms_internal {
($ops_trait:ident, $ops_fn:ident, $lhs:ty, $rhs:ty, $out:ty, $f:expr) => { ($ops_trait:ident, $ops_fn:ident, $lhs:ty, $rhs:ty, $out:ty) => {
impl<L, R, const M: usize, const N: usize> ::std::ops::$ops_trait<$rhs> for $lhs impl<L, R, const M: usize, const N: usize> ::std::ops::$ops_trait<$rhs> for $lhs
where where
L: ::std::ops::$ops_trait<R, Output = L>, L: ::std::ops::$ops_trait<R, Output = L> + Scalar,
L: Scalar,
R: Scalar, R: Scalar,
{ {
type Output = $out; type Output = $out;
fn $ops_fn(self, r: $rhs) -> Self::Output { #[inline(always)]
fn $ops_fn(self, other: $rhs) -> Self::Output {
let mut result = self.clone(); let mut result = self.clone();
let op = $f; for m in 0..M {
for l in result.elements_mut() { for n in 0..N {
*l = op(*l, r); result.data[m][n] = self.data[m][n].$ops_fn(other);
}
} }
result result
} }
@ -207,18 +190,18 @@ macro_rules! _impl_op_ms_internal {
#[macro_export] #[macro_export]
macro_rules! _impl_opassign_ms_internal { macro_rules! _impl_opassign_ms_internal {
($ops_super:ident, $ops_trait:ident, $ops_fn:ident, $lhs:ty, $rhs:ty, $out:ty, $f:expr) => { ($ops_trait:ident, $ops_fn:ident, $lhs:ty, $rhs:ty, $out:ty) => {
impl<L, R, const M: usize, const N: usize> ::std::ops::$ops_trait<$rhs> for $lhs impl<L, R, const M: usize, const N: usize> ::std::ops::$ops_trait<$rhs> for $lhs
where where
L: ::std::ops::$ops_trait<R>, L: ::std::ops::$ops_trait<R> + Scalar,
L: ::std::ops::$ops_super<R, Output = L>,
L: Scalar,
R: Scalar, R: Scalar,
{ {
#[inline(always)]
fn $ops_fn(&mut self, r: $rhs) { fn $ops_fn(&mut self, r: $rhs) {
let op = $f; for m in 0..M {
for l in self.elements_mut() { for n in 0..N {
*l = op(*l, r); self.data[m][n].$ops_fn(r);
}
} }
} }
} }

View File

@ -1,92 +1,197 @@
use crate::impl_matrix_op; use crate::impl_matrix_op;
use crate::index::Index2D; use crate::index::Index2D;
use std::iter::{zip, Enumerate, Flatten}; use std::fmt::Debug;
use std::ops::{Add, Deref, DerefMut, Index, IndexMut, Range}; use std::iter::{zip, Flatten, Product, Sum};
use std::option::IntoIter; use std::ops::{AddAssign, Deref, DerefMut, Index, IndexMut, MulAssign};
pub trait Get2D { /// A Scalar that a [Matrix] can be made up of.
type Scalar: Sized + Copy; ///
const HEIGHT: usize; /// This trait has no associated functions and can be implemented on any type that is [Default] and
const WIDTH: usize; /// [Copy] and has a static lifetime.
pub trait Scalar: Default + Copy + 'static {}
fn get<I: Index2D>(&self, i: I) -> Option<&Self::Scalar>;
}
pub trait Get2DMut: Get2D {
fn get_mut<I: Index2D>(&mut self, i: I) -> Option<&mut Self::Scalar>;
}
trait Scalar: Copy + 'static {}
macro_rules! multi_impl { ($name:ident for $($t:ty),*) => ($( impl $name for $t {} )*) } macro_rules! multi_impl { ($name:ident for $($t:ty),*) => ($( impl $name for $t {} )*) }
multi_impl!(Scalar for i8, i16, i32, i64, isize, u8, u16, u32, u64, usize, f32, f64); multi_impl!(Scalar for i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize, f32, f64);
impl<T> Scalar for &'static T where T: Scalar {} impl<T> Scalar for &'static T
where
T: Scalar,
&'static T: Default,
{
}
#[derive(Debug, Copy, Clone)] /// A 2D array of values which can be operated upon.
struct Matrix<T, const M: usize, const N: usize> ///
/// Matrices have a fixed size known at compile time, and must be made up of types that implement
/// the [Scalar] trait.
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Matrix<T, const M: usize, const N: usize>
where where
T: Scalar, T: Scalar,
{ {
data: [[T; N]; M], data: [[T; N]; M],
} }
type Vector<T, const N: usize> = Matrix<T, N, 1>; /// An alias for a [Matrix] with a single column
pub type Vector<T, const N: usize> = Matrix<T, N, 1>;
impl<T: Scalar, const M: usize, const N: usize> Matrix<T, M, N> { impl<T: Scalar, const M: usize, const N: usize> Matrix<T, M, N> {
fn new(data: [[T; N]; M]) -> Self { /// Generate a new matrix from a 2D Array
return Matrix::<T, M, N> { data }; ///
/// # Arguments
///
/// * `data`: A 2D array of elements to copy into the new matrix
///
/// returns: Matrix<T, M, N>
///
/// # Examples
///
/// ```
/// # use vector_victor::Matrix;
/// let a = Matrix::new([[1,2,3,4];4]);
/// ```
#[must_use]
pub fn new(data: [[T; N]; M]) -> Self {
Matrix::<T, M, N> { data }
} }
fn from_rows<I>(iter: &I) -> Self /// Generate a new matrix from a single scalar
///
/// # Arguments
///
/// * `scalar`: Scalar value to copy into the new matrix.
///
/// returns: Matrix<T, M, N>
///
/// # Examples
///
/// ```
/// # use vector_victor::Matrix;
/// let my_matrix = Matrix::<i32,4,4>::fill(5);
/// // is equivalent to
/// assert_eq!(my_matrix, Matrix::new([[5;4];4]))
/// ```
#[must_use]
pub fn fill(scalar: T) -> Matrix<T, M, N> {
Matrix::<T, M, N> {
data: [[scalar; N]; M],
}
}
/// Create a matrix from an iterator of vectors
///
/// # Arguments
///
/// * `iter`: iterator of vectors to copy into rows
///
/// returns: Matrix<T, M, N>
///
/// # Examples
///
/// ```
/// # use vector_victor::Matrix;
/// let my_matrix = Matrix::new([[1,2,3],[4,5,6]]);
/// let transpose : Matrix<_,3,2>= Matrix::from_rows(my_matrix.cols());
/// assert_eq!(transpose, Matrix::new([[1,4],[2,5],[3,6]]))
/// ```
#[must_use]
pub fn from_rows<I>(iter: I) -> Self
where where
Self: Default, Self: Default,
I: Iterator<Item = Vector<T, N>> + Copy, I: IntoIterator<Item = Vector<T, N>>,
{ {
let mut result = Self::default(); let mut result = Self::default();
for (m, row) in iter.enumerate().filter(|(m, _)| *m <= M) { for (m, row) in iter.into_iter().enumerate().take(M) {
result.set_row(m, &row) result.set_row(m, &row)
} }
result result
} }
fn from_cols<I>(iter: &I) -> Self /// Create a matrix from an iterator of vectors
///
/// # Arguments
///
/// * `iter`: iterator of vectors to copy into columns
///
/// returns: Matrix<T, M, N>
///
/// # Examples
///
/// ```
/// # use vector_victor::Matrix;
/// let my_matrix = Matrix::new([[1,2,3],[4,5,6]]);
/// let transpose : Matrix<_,3,2>= Matrix::from_cols(my_matrix.rows());
/// assert_eq!(transpose, Matrix::new([[1,4],[2,5],[3,6]]))
/// ```
#[must_use]
pub fn from_cols<I>(iter: I) -> Self
where where
Self: Default, Self: Default,
I: Iterator<Item = Vector<T, M>> + Copy, I: IntoIterator<Item = Vector<T, M>>,
{ {
let mut result = Self::default(); let mut result = Self::default();
for (n, col) in iter.enumerate().filter(|(n, _)| *n <= N) { for (n, col) in iter.into_iter().enumerate().take(N) {
result.set_col(n, &col) result.set_col(n, &col)
} }
result result
} }
fn elements<'a>(&'a self) -> impl Iterator<Item = &T> + 'a { /// Returns an iterator over the elements of the matrix in row-major order.
///
/// # Examples
/// ```
/// # use vector_victor::Matrix;
/// let my_matrix = Matrix::new([[1,2],[3,4]]);
/// assert!(vec![1,2,3,4].iter().eq(my_matrix.elements()))
/// ```
#[must_use]
pub fn elements<'a>(&'a self) -> impl Iterator<Item = &T> + 'a {
self.data.iter().flatten() self.data.iter().flatten()
} }
fn elements_mut<'a>(&'a mut self) -> impl Iterator<Item = &mut T> + 'a { /// Returns a mutable iterator over the elements of the matrix in row-major order.
#[must_use]
pub fn elements_mut<'a>(&'a mut self) -> impl Iterator<Item = &mut T> + 'a {
self.data.iter_mut().flatten() self.data.iter_mut().flatten()
} }
fn get(&self, index: impl Index2D) -> Option<&T> { /// Returns a reference to the element at that position in the matrix or `None` if out of bounds.
///
/// # Examples
///
/// ```
/// # use vector_victor::Matrix;
/// let my_matrix = Matrix::new([[1,2],[3,4]]);
///
/// // element at index 2 is the same as the element at (row 1, column 0).
/// assert_eq!(my_matrix.get(2), my_matrix.get((1,0)));
/// // index 4 is out of range, so get(4) returns None.
/// assert_eq!(my_matrix.get(4), None);
/// ```
#[inline]
#[must_use]
pub fn get(&self, index: impl Index2D) -> Option<&T> {
let (m, n) = index.to_2d(M, N)?; let (m, n) = index.to_2d(M, N)?;
Some(&self.data[m][n]) Some(&self.data[m][n])
} }
fn get_mut(&mut self, index: impl Index2D) -> Option<&mut T> { #[inline]
#[must_use]
pub fn get_mut(&mut self, index: impl Index2D) -> Option<&mut T> {
let (m, n) = index.to_2d(M, N)?; let (m, n) = index.to_2d(M, N)?;
Some(&mut self.data[m][n]) Some(&mut self.data[m][n])
} }
fn row(&self, m: usize) -> Option<Vector<T, N>> { #[inline]
#[must_use]
pub fn row(&self, m: usize) -> Option<Vector<T, N>> {
if m < M { if m < M {
Some(Vector::<T, N>::new_vector(self.data[m])) Some(Vector::<T, N>::vec(self.data[m]))
} else { } else {
None None
} }
} }
fn set_row(&mut self, m: usize, val: &Vector<T, N>) { #[inline]
pub fn set_row(&mut self, m: usize, val: &Vector<T, N>) {
assert!( assert!(
m < M, m < M,
"Row index {} out of bounds for {}x{} matrix", "Row index {} out of bounds for {}x{} matrix",
@ -99,15 +204,18 @@ impl<T: Scalar, const M: usize, const N: usize> Matrix<T, M, N> {
} }
} }
fn col(&self, n: usize) -> Option<Vector<T, M>> { #[inline]
#[must_use]
pub fn col(&self, n: usize) -> Option<Vector<T, M>> {
if n < N { if n < N {
Some(Vector::<T, M>::new_vector(self.data.map(|r| r[n]))) Some(Vector::<T, M>::vec(self.data.map(|r| r[n])))
} else { } else {
None None
} }
} }
fn set_col(&mut self, n: usize, val: &Vector<T, M>) { #[inline]
pub fn set_col(&mut self, n: usize, val: &Vector<T, M>) {
assert!( assert!(
n < N, n < N,
"Column index {} out of bounds for {}x{} matrix", "Column index {} out of bounds for {}x{} matrix",
@ -121,37 +229,100 @@ impl<T: Scalar, const M: usize, const N: usize> Matrix<T, M, N> {
} }
} }
fn rows<'a>(&'a self) -> impl Iterator<Item = Vector<T, N>> + 'a { #[must_use]
pub fn rows<'a>(&'a self) -> impl Iterator<Item = Vector<T, N>> + 'a {
(0..M).map(|m| self.row(m).expect("invalid row reached while iterating")) (0..M).map(|m| self.row(m).expect("invalid row reached while iterating"))
} }
fn cols<'a>(&'a self) -> impl Iterator<Item = Vector<T, M>> + 'a { #[must_use]
pub fn cols<'a>(&'a self) -> impl Iterator<Item = Vector<T, M>> + 'a {
(0..N).map(|n| self.col(n).expect("invalid column reached while iterating")) (0..N).map(|n| self.col(n).expect("invalid column reached while iterating"))
} }
} }
// constructor for column vectors // 1D vector implementations
impl<T: Scalar, const N: usize> Vector<T, N> { impl<T: Scalar, const M: usize> Matrix<T, M, 1> {
fn new_vector(data: [T; N]) -> Self { /// Create a vector from a 1D array.
return Vector::<T, N> { /// Note that vectors are always column vectors unless explicitly instantiated as row vectors
data: data.map(|e| [e]), ///
/// # Arguments
///
/// * `data`: A 1D array of elements to copy into the new vector
///
/// returns: Matrix<T, { M }, 1>
///
/// # Examples
///
/// ```
/// # use vector_victor::{Matrix, Vector};
/// let my_vector = Vector::vec([1,2,3,4]);
/// // is equivalent to
/// assert_eq!(my_vector, Matrix::new([[1],[2],[3],[4]]));
/// ```
pub fn vec(data: [T; M]) -> Self {
return Matrix::<T, M, 1> {
data: data.map(|e| [e; 1]),
}; };
} }
} }
// default constructor // Index
impl<T, const M: usize, const N: usize> Default for Matrix<T, M, N> impl<I, T, const M: usize, const N: usize> Index<I> for Matrix<T, M, N>
where where
[[T; N]; M]: Default, I: Index2D,
T: Scalar, T: Scalar,
{ {
type Output = T;
fn index(&self, index: I) -> &Self::Output {
self.get(index).expect(&*format!(
"index {:?} out of range for {}x{} Matrix",
index, M, N
))
}
}
// IndexMut
impl<I, T, const M: usize, const N: usize> IndexMut<I> for Matrix<T, M, N>
where
I: Index2D,
T: Scalar,
{
fn index_mut(&mut self, index: I) -> &mut Self::Output {
self.get_mut(index).expect(&*format!(
"index {:?} out of range for {}x{} Matrix",
index, M, N
))
}
}
// Default
impl<T: Scalar, const M: usize, const N: usize> Default for Matrix<T, M, N> {
fn default() -> Self { fn default() -> Self {
Matrix { Matrix {
data: Default::default(), data: [[T::default(); N]; M],
} }
} }
} }
impl<T: Scalar, const M: usize, const N: usize> From<[[T; N]; M]> for Matrix<T, M, N> {
fn from(data: [[T; N]; M]) -> Self {
Self::new(data)
}
}
impl<T: Scalar, const M: usize> From<[T; M]> for Vector<T, M> {
fn from(data: [T; M]) -> Self {
Self::vec(data)
}
}
impl<T: Scalar, const M: usize, const N: usize> From<T> for Matrix<T, M, N> {
fn from(scalar: T) -> Self {
Self::fill(scalar)
}
}
// deref 1x1 matrices to a scalar automatically // deref 1x1 matrices to a scalar automatically
impl<T: Scalar> Deref for Matrix<T, 1, 1> { impl<T: Scalar> Deref for Matrix<T, 1, 1> {
type Target = T; type Target = T;
@ -168,6 +339,7 @@ impl<T: Scalar> DerefMut for Matrix<T, 1, 1> {
} }
} }
// IntoIter
impl<T: Scalar, const M: usize, const N: usize> IntoIterator for Matrix<T, M, N> { impl<T: Scalar, const M: usize, const N: usize> IntoIterator for Matrix<T, M, N> {
type Item = T; type Item = T;
type IntoIter = Flatten<std::array::IntoIter<[T; N], M>>; type IntoIter = Flatten<std::array::IntoIter<[T; N], M>>;
@ -177,15 +349,53 @@ impl<T: Scalar, const M: usize, const N: usize> IntoIterator for Matrix<T, M, N>
} }
} }
impl_matrix_op!(neg, |l: L| { -l }); // FromIterator
impl_matrix_op!(!, |l: L| { !l }); impl<T: Scalar, const M: usize, const N: usize> FromIterator<T> for Matrix<T, M, N>
impl_matrix_op!(+, |l,r| {l + r}); where
impl_matrix_op!(-, |l,r| {l - r}); Self: Default,
impl_matrix_op!(*, |l,r| {l * r}); {
impl_matrix_op!(/, |l,r| {l / r}); fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
impl_matrix_op!(%, |l,r| {l % r}); let mut result: Self = Default::default();
impl_matrix_op!(&, |l,r| {l & r}); for (l, r) in zip(result.elements_mut(), iter) {
impl_matrix_op!(|, |l,r| {l | r}); *l = r;
impl_matrix_op!(^, |l,r| {l ^ r}); }
impl_matrix_op!(<<, |l,r| {l << r}); result
impl_matrix_op!(>>, |l,r| {l >> r}); }
}
impl<T: Scalar + AddAssign, const M: usize, const N: usize> Sum for Matrix<T, M, N> {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
let mut sum = Self::default();
for m in iter {
sum += m;
}
sum
}
}
impl<T: Scalar + MulAssign, const M: usize, const N: usize> Product for Matrix<T, M, N> {
fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
let mut prod = Self::default();
for m in iter {
prod *= m;
}
prod
}
}
impl_matrix_op!(neg);
impl_matrix_op!(!);
impl_matrix_op!(+);
impl_matrix_op!(-);
impl_matrix_op!(*);
impl_matrix_op!(/);
impl_matrix_op!(%);
impl_matrix_op!(&);
impl_matrix_op!(|);
impl_matrix_op!(^);
impl_matrix_op!(<<);
impl_matrix_op!(>>);

18
tests/ops.rs Normal file
View File

@ -0,0 +1,18 @@
use generic_parameterize::parameterize;
use std::fmt::Debug;
use std::ops;
use vector_victor::{Matrix, Scalar};
#[parameterize(S = (i32, f32, u32), M = [1,4], N = [1,4])]
#[test]
fn test_add<S: Scalar + From<u16> + PartialEq + Debug, const M: usize, const N: usize>()
where
Matrix<S, M, N>: ops::Add<Output = Matrix<S, M, N>>,
{
let a = Matrix::<S, M, N>::fill(S::from(1));
let b = Matrix::<S, M, N>::fill(S::from(3));
let c: Matrix<S, M, N> = a + b;
for (i, ci) in c.elements().enumerate() {
assert_eq!(*ci, S::from(4));
}
}