diff --git a/src/containers.rs b/src/containers.rs deleted file mode 100644 index f6601fa..0000000 --- a/src/containers.rs +++ /dev/null @@ -1,180 +0,0 @@ -use index::Index2D; -use std::marker::PhantomData; -use std::ops::{Deref, DerefMut}; - -pub mod index; -pub mod iter; - -/// This trait exists to allow structs like Slice2D to require Get2D, without -/// storing the dimensions of the target as part of its own generic parameters -pub trait Get2D { - type Scalar: Sized + Copy; - const HEIGHT: usize; - const WIDTH: usize; - - fn get(&self, i: I) -> Option<&Self::Scalar>; -} - -pub trait Get2DMut: Get2D { - fn get_mut(&mut self, i: I) -> Option<&mut Self::Scalar>; -} - -pub trait Get2DSized: Get2D {} - -/// A 2D owning array of T -#[derive(Debug, Clone, Copy)] -pub struct Array2D -where - T: Copy + 'static, -{ - pub data: [[T; N]; M], -} - -impl Default for Array2D -where - T: Default + Copy, -{ - fn default() -> Self { - Array2D { - data: [[T::default(); N]; M], - } - } -} - -impl Get2D for Array2D { - type Scalar = T; - const HEIGHT: usize = M; - const WIDTH: usize = N; - - fn get(&self, i: I) -> Option<&Self::Scalar> { - let (r, c) = i.to_2d(Self::WIDTH); - self.data.get(r)?.get(c) - } -} - -impl Get2DMut for Array2D { - fn get_mut(&mut self, i: I) -> Option<&mut Self::Scalar> { - let (r, c) = i.to_2d(Self::WIDTH); - self.data.get_mut(r)?.get_mut(c) - } -} - -impl Get2DSized for Array2D {} - -/// A 2D mutable view into a container -#[derive(Debug, Clone, Copy)] -pub struct Slice2D<'a, R, const M: usize, const N: usize> -where - R: Deref, - R::Target: Get2D + 'a, -{ - target: R, - r: usize, - c: usize, - phantom: PhantomData<&'a ()>, -} - -impl<'a, R, const M: usize, const N: usize> Slice2D<'a, R, M, N> -where - R: Deref, - R::Target: Get2D + 'a, -{ - pub fn new(target: R, r: usize, c: usize) -> Self { - Self { - target, - r, - c, - phantom: PhantomData, - } - } -} - -impl<'a, R, D, const M: usize, const N: usize> Get2D for Slice2D<'a, R, M, N> -where - R: Deref, - D: Get2D, -{ - type Scalar = <::Target as Get2D>::Scalar; - const HEIGHT: usize = M; - const WIDTH: usize = N; - - fn get(&self, i: I) -> Option<&Self::Scalar> { - self.target - .get(i.to_2d_offset(Self::WIDTH, Self::HEIGHT, self.r, self.c)?) - } -} - -impl<'a, R, D, const M: usize, const N: usize> Get2DMut for Slice2D<'a, R, M, N> -where - R: Deref + DerefMut, - D: Get2DMut, -{ - fn get_mut(&mut self, i: I) -> Option<&mut Self::Scalar> { - self.target - .get_mut(i.to_2d_offset(Self::WIDTH, Self::HEIGHT, self.r, self.c)?) - } -} - -impl<'a, R, D, const M: usize, const N: usize> Get2DSized for Slice2D<'a, R, M, N> -where - R: Deref, - D: Get2D, -{ -} - -// A transposition of a 2D container -#[derive(Debug, Clone, Copy)] -pub struct Transpose<'a, R> -where - R: Deref, - R::Target: Get2D + 'a, -{ - target: R, - phantom: std::marker::PhantomData<&'a ()>, -} - -impl<'a, R> Transpose<'a, R> -where - R: Deref, - R::Target: Get2D + 'a, -{ - fn new(target: R) -> Self { - Self { - target, - phantom: PhantomData, - } - } -} - -impl<'a, R, D> Get2D for Transpose<'a, R> -where - R: Deref, - D: Get2D, -{ - type Scalar = D::Scalar; - const HEIGHT: usize = D::WIDTH; - const WIDTH: usize = D::HEIGHT; - - fn get(&self, i: I) -> Option<&Self::Scalar> { - let (r, c) = i.to_2d(Self::WIDTH); - self.target.get((c, r)) - } -} - -impl<'a, R, D> Get2DMut for Transpose<'a, R> -where - R: DerefMut, - D: Get2DMut, -{ - fn get_mut(&mut self, i: I) -> Option<&mut Self::Scalar> { - let (r, c) = i.to_2d(Self::WIDTH); - self.target.get_mut((c, r)) - } -} - -impl<'a, R, D, const M: usize, const N: usize> Get2DSized for Transpose<'a, R> -where - R: Deref, - D: Get2DSized, -{ -} diff --git a/src/containers/index.rs b/src/containers/index.rs deleted file mode 100644 index 40ab86b..0000000 --- a/src/containers/index.rs +++ /dev/null @@ -1,34 +0,0 @@ -pub trait Index2D: Copy { - fn to_1d(&self, width: usize) -> usize { - let (r, c) = self.to_2d(width); - r * width + c - } - - fn to_2d(&self, width: usize) -> (usize, usize); - - fn to_2d_offset( - &self, - width: usize, - height: usize, - r: usize, - c: usize, - ) -> Option<(usize, usize)> { - let (row, col) = self.to_2d(width); - if row >= height || col >= width { - return None; - }; - Some((row + r, col + c)) - } -} - -impl Index2D for usize { - fn to_2d(&self, width: usize) -> (usize, usize) { - (*self / width, *self % width) - } -} - -impl Index2D for (usize, usize) { - fn to_2d(&self, _: usize) -> (usize, usize) { - *self - } -} diff --git a/src/containers/iter.rs b/src/containers/iter.rs deleted file mode 100644 index 71b8bef..0000000 --- a/src/containers/iter.rs +++ /dev/null @@ -1,39 +0,0 @@ -use super::Get2D; -use std::iter::FusedIterator; - -#[derive(Debug, Copy, Clone)] -pub(crate) struct ElementIter<'a, D> -where - D: Get2D, -{ - data: &'a D, - index: usize, -} - -impl<'a, D: Get2D> ElementIter<'a, D> { - pub(crate) fn new(data: &'a D) -> ElementIter { - ElementIter { data, index: 0 } - } -} - -impl<'a, D: Get2D> Iterator for ElementIter<'a, D> { - type Item = &'a D::Scalar; - - fn next(&mut self) -> Option { - let result = self.data.get(self.index); - self.index += 1; - result - } - - fn size_hint(&self) -> (usize, Option) { - let total = D::WIDTH * D::HEIGHT; - (total - self.index, Some(total - self.index)) - } -} - -impl<'a, D: Get2D> ExactSizeIterator for ElementIter<'a, D> { - fn len(&self) -> usize { - self.index - D::WIDTH * D::HEIGHT - } -} -impl<'a, D: Get2D> FusedIterator for ElementIter<'a, D> {} diff --git a/src/index.rs b/src/index.rs new file mode 100644 index 0000000..c63c05a --- /dev/null +++ b/src/index.rs @@ -0,0 +1,26 @@ +pub trait Index2D: Copy { + fn to_1d(self, height: usize, width: usize) -> Option { + let (r, c) = self.to_2d(height, width)?; + Some(r * width + c) + } + + fn to_2d(self, height: usize, width: usize) -> Option<(usize, usize)>; +} + +impl Index2D for usize { + fn to_2d(self, height: usize, width: usize) -> Option<(usize, usize)> { + match self < (height * width) { + true => Some((self / width, self % width)), + false => None, + } + } +} + +impl Index2D for (usize, usize) { + fn to_2d(self, height: usize, width: usize) -> Option<(usize, usize)> { + match self.0 < height && self.1 < width { + true => Some(self), + false => None, + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 6be022b..f723672 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,2 +1,3 @@ -mod containers; +pub mod index; mod matrix; +mod macros; diff --git a/src/macros/mod.rs b/src/macros/mod.rs new file mode 100644 index 0000000..01eafd2 --- /dev/null +++ b/src/macros/mod.rs @@ -0,0 +1 @@ +pub mod ops; diff --git a/src/macros/ops.rs b/src/macros/ops.rs new file mode 100644 index 0000000..38c4410 --- /dev/null +++ b/src/macros/ops.rs @@ -0,0 +1,226 @@ +// borrowed from the auto_ops crate +#[doc(hidden)] +#[macro_export] +macro_rules! impl_matrix_op { + (neg, $f:expr) => { + $crate::_impl_op_m_internal_ex!(Neg, neg, $f); + }; + (!, $f:expr) => { + $crate::_impl_op_m_internal_ex!(Not, not, $f); + }; + (+, $f:expr) => { + $crate::_impl_op_mm_internal_ex!(Add, add, $f); + $crate::_impl_opassign_mm_internal_ex!(Add, AddAssign, add_assign, $f); + }; + (-, $f:expr) => { + $crate::_impl_op_mm_internal_ex!(Sub, sub, $f); + $crate::_impl_opassign_mm_internal_ex!(Sub, SubAssign, sub_assign, $f); + }; + (*, $f:expr) => { + $crate::_impl_op_mm_internal_ex!(Mul, mul, $f); + $crate::_impl_op_ms_internal_ex!(Mul, mul, $f); + $crate::_impl_opassign_mm_internal_ex!(Mul, MulAssign, mul_assign, $f); + $crate::_impl_opassign_ms_internal_ex!(Mul, MulAssign, mul_assign, $f); + }; + (/, $f:expr) => { + $crate::_impl_op_mm_internal_ex!(Div, div, $f); + $crate::_impl_op_ms_internal_ex!(Div, div, $f); + $crate::_impl_opassign_mm_internal_ex!(Div, DivAssign, div_assign, $f); + $crate::_impl_opassign_ms_internal_ex!(Div, DivAssign, div_assign, $f); + }; + (%, $f:expr) => { + $crate::_impl_op_mm_internal_ex!(Rem, rem, $f); + $crate::_impl_op_ms_internal_ex!(Rem, rem, $f); + $crate::_impl_opassign_mm_internal_ex!(Rem, RemAssign, rem_assign, $f); + $crate::_impl_opassign_ms_internal_ex!(Rem, RemAssign, rem_assign, $f); + }; + (&, $f:expr) => { + $crate::_impl_op_mm_internal_ex!(BitAnd, bitand, $f); + $crate::_impl_opassign_mm_internal_ex!(BitAnd, BitAndAssign, bitand_assign, $f); + }; + (|, $f:expr) => { + $crate::_impl_op_mm_internal_ex!(BitOr, bitor, $f); + $crate::_impl_opassign_mm_internal_ex!(BitOr, BitOrAssign, bitor_assign, $f); + }; + (^, $f:expr) => { + $crate::_impl_op_mm_internal_ex!(BitXor, bitxor, $f); + $crate::_impl_opassign_mm_internal_ex!(BitXor, BitXorAssign, bitxor_assign, $f); + }; + (<<, $f:expr) => { + $crate::_impl_op_ms_internal_ex!(Shl, shl, $f); + $crate::_impl_opassign_mm_internal_ex!(Shl, ShlAssign, shl_assign, $f); + }; + (>>, $f:expr) => { + $crate::_impl_op_ms_internal_ex!(Shr, shr, $f); + $crate::_impl_opassign_mm_internal_ex!(Shr, ShrAssign, shr_assign, $f); + }; +} + +#[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_rules! _impl_op_m_internal_ex { + ($ops_trait:ident, $ops_fn:ident, $f:expr) => { + $crate::_impl_op_m_internal!($ops_trait, $ops_fn, Matrix, Matrix, $f); + $crate::_impl_op_m_internal!($ops_trait, $ops_fn, &Matrix, Matrix, $f); + } +} + +#[macro_export] +macro_rules! _impl_op_mm_internal_ex { + ($ops_trait:ident, $ops_fn:ident, $f:expr) => { + $crate::_impl_op_mm_internal!($ops_trait, $ops_fn, Matrix, Matrix, Matrix, $f); + $crate::_impl_op_mm_internal!($ops_trait, $ops_fn, &Matrix, Matrix, Matrix, $f); + $crate::_impl_op_mm_internal!($ops_trait, $ops_fn, Matrix, &Matrix, Matrix, $f); + $crate::_impl_op_mm_internal!($ops_trait, $ops_fn, &Matrix, &Matrix, Matrix, $f); + } +} + +#[macro_export] +macro_rules! _impl_opassign_mm_internal_ex { + ($ops_super:ident, $ops_trait:ident, $ops_fn:ident, $f:expr) => { + $crate::_impl_opassign_mm_internal!($ops_super, $ops_trait, $ops_fn, Matrix, Matrix, Matrix, $f); + $crate::_impl_opassign_mm_internal!($ops_super, $ops_trait, $ops_fn, Matrix, &Matrix, Matrix, $f); + } +} + +#[macro_export] +macro_rules! _impl_op_ms_internal_ex { + ($ops_trait:ident, $ops_fn:ident, $f:expr) => { + $crate::_impl_op_ms_internal!($ops_trait, $ops_fn, Matrix, R, Matrix, $f); + $crate::_impl_op_ms_internal!($ops_trait, $ops_fn, &Matrix, R, Matrix, $f); + } +} + +#[macro_export] +macro_rules! _impl_opassign_ms_internal_ex { + ($ops_super:ident, $ops_trait:ident, $ops_fn:ident, $f:expr) => { + $crate::_impl_opassign_ms_internal!($ops_super, $ops_trait, $ops_fn, Matrix, R, Matrix, $f); + } +} + +#[macro_export] +macro_rules! _impl_op_mm_internal { + ($ops_trait:ident, $ops_fn:ident, $lhs:ty, $rhs:ty, $out:ty, $f:expr) => { + impl ::std::ops::$ops_trait<$rhs> for $lhs + where + L: ::std::ops::$ops_trait, + L: Scalar, + R: Scalar, + { + type Output = $out; + + fn $ops_fn(self, rhs_i: $rhs) -> Self::Output { + let mut result = self.clone(); + let op = $f; + for (l, r) in zip(result.elements_mut(), rhs_i.elements()) { + *l = op(*l, *r); + } + result + } + } + }; +} + +#[macro_export] +macro_rules! _impl_opassign_mm_internal { + ($ops_super:ident, $ops_trait:ident, $ops_fn:ident, $lhs:ty, $rhs:ty, $out:ty, $f:expr) => { + impl ::std::ops::$ops_trait<$rhs> for $lhs + where + L: ::std::ops::$ops_trait, + L: ::std::ops::$ops_super, + L: Scalar, + R: Scalar, + { + fn $ops_fn(&mut self, rhs_i: $rhs) { + let op = $f; + for (l, r) in zip(self.elements_mut(), rhs_i.elements()) { + *l = op(*l, *r); + } + } + } + }; +} + +#[macro_export] +macro_rules! _impl_op_m_internal { + ($ops_trait:ident, $ops_fn:ident, $lhs:ty, $out:ty, $f:expr) => { + impl ::std::ops::$ops_trait for $lhs + where + L: ::std::ops::$ops_trait, + 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_rules! _impl_op_ms_internal { + ($ops_trait:ident, $ops_fn:ident, $lhs:ty, $rhs:ty, $out:ty, $f:expr) => { + impl ::std::ops::$ops_trait<$rhs> for $lhs + where + L: ::std::ops::$ops_trait, + L: Scalar, + R: Scalar, + { + type Output = $out; + + fn $ops_fn(self, r: $rhs) -> Self::Output { + let mut result = self.clone(); + let op = $f; + for l in result.elements_mut() { + *l = op(*l, r); + } + result + } + } + }; +} + +#[macro_export] +macro_rules! _impl_opassign_ms_internal { + ($ops_super:ident, $ops_trait:ident, $ops_fn:ident, $lhs:ty, $rhs:ty, $out:ty, $f:expr) => { + impl ::std::ops::$ops_trait<$rhs> for $lhs + where + L: ::std::ops::$ops_trait, + L: ::std::ops::$ops_super, + L: Scalar, + R: Scalar, + { + fn $ops_fn(&mut self, r: $rhs) { + let op = $f; + for l in self.elements_mut() { + *l = op(*l, r); + } + } + } + }; +} diff --git a/src/matrix.rs b/src/matrix.rs index 0243e69..6d20e02 100644 --- a/src/matrix.rs +++ b/src/matrix.rs @@ -1,90 +1,191 @@ -use crate::containers::index::Index2D; -use crate::containers::iter::ElementIter; -use crate::containers::{Array2D, Get2D, Get2DMut, Get2DSized, Slice2D}; -use std::ops::{Add, Index, IndexMut}; +use crate::impl_matrix_op; +use crate::index::Index2D; +use std::iter::{zip, Enumerate, Flatten}; +use std::ops::{Add, Deref, DerefMut, Index, IndexMut, Range}; +use std::option::IntoIter; -type Matrix = GenericMatrix, M, N>; +pub trait Get2D { + type Scalar: Sized + Copy; + const HEIGHT: usize; + const WIDTH: usize; -#[derive(Debug, Copy, Clone)] -struct GenericMatrix, const M: usize, const N: usize> { - data: D, + fn get(&self, i: I) -> Option<&Self::Scalar>; } -impl, const M: usize, const N: usize> GenericMatrix { - fn elements(&self) -> ElementIter> { - ElementIter::new(self) - } +pub trait Get2DMut: Get2D { + fn get_mut(&mut self, i: I) -> Option<&mut Self::Scalar>; } -impl + Copy, const M: usize, const N: usize> Default for GenericMatrix +trait Scalar: Copy + 'static {} +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); +impl Scalar for &'static T where T: Scalar {} + +#[derive(Debug, Copy, Clone)] +struct Matrix where - D: Default, + T: Scalar, { - fn default() -> Self { - GenericMatrix { data: D::default() } - } + data: [[T; N]; M], } -// impl Matrix -// where -// D::Scalar: Default, -// { -// fn new(data: &[&[D::Scalar]]) -> Result {} -// } -impl, const M: usize, const N: usize> Get2D for GenericMatrix { - type Scalar = D::Scalar; - const HEIGHT: usize = D::HEIGHT; - const WIDTH: usize = D::WIDTH; +type Vector = Matrix; + +impl Matrix { + fn new(data: [[T; N]; M]) -> Self { + return Matrix:: { data }; + } + + fn from_rows(iter: &I) -> Self + where + Self: Default, + I: Iterator> + Copy, + { + let mut result = Self::default(); + for (m, row) in iter.enumerate().filter(|(m, _)| *m <= M) { + result.set_row(m, &row) + } + result + } + + fn from_cols(iter: &I) -> Self + where + Self: Default, + I: Iterator> + Copy, + { + let mut result = Self::default(); + for (n, col) in iter.enumerate().filter(|(n, _)| *n <= N) { + result.set_col(n, &col) + } + result + } + + fn elements<'a>(&'a self) -> impl Iterator + 'a { + self.data.iter().flatten() + } + + fn elements_mut<'a>(&'a mut self) -> impl Iterator + 'a { + self.data.iter_mut().flatten() + } + + fn get(&self, index: impl Index2D) -> Option<&T> { + let (m, n) = index.to_2d(M, N)?; + Some(&self.data[m][n]) + } + + fn get_mut(&mut self, index: impl Index2D) -> Option<&mut T> { + let (m, n) = index.to_2d(M, N)?; + Some(&mut self.data[m][n]) + } + + fn row(&self, m: usize) -> Option> { + if m < M { + Some(Vector::::new_vector(self.data[m])) + } else { + None + } + } + + fn set_row(&mut self, m: usize, val: &Vector) { + assert!( + m < M, + "Row index {} out of bounds for {}x{} matrix", + m, + M, + N + ); + for (n, v) in val.elements().enumerate() { + self.data[m][n] = *v; + } + } + + fn col(&self, n: usize) -> Option> { + if n < N { + Some(Vector::::new_vector(self.data.map(|r| r[n]))) + } else { + None + } + } + + fn set_col(&mut self, n: usize, val: &Vector) { + assert!( + n < N, + "Column index {} out of bounds for {}x{} matrix", + n, + M, + N + ); - fn get(&self, i: I) -> Option<&Self::Scalar> { - self.data.get(i) + for (m, v) in val.elements().enumerate() { + self.data[m][n] = *v; + } + } + + fn rows<'a>(&'a self) -> impl Iterator> + 'a { + (0..M).map(|m| self.row(m).expect("invalid row reached while iterating")) + } + + fn cols<'a>(&'a self) -> impl Iterator> + 'a { + (0..N).map(|n| self.col(n).expect("invalid column reached while iterating")) } } -impl, const M: usize, const N: usize> Get2DMut - for GenericMatrix -{ - fn get_mut(&mut self, i: I) -> Option<&mut Self::Scalar> { - self.data.get_mut(i) +// constructor for column vectors +impl Vector { + fn new_vector(data: [T; N]) -> Self { + return Vector:: { + data: data.map(|e| [e]), + }; } } -impl, I: Index2D, const M: usize, const N: usize> Index - for GenericMatrix +// default constructor +impl Default for Matrix +where + [[T; N]; M]: Default, + T: Scalar, { - type Output = D::Scalar; - - fn index(&self, index: I) -> &Self::Output { - self.get(index).expect(&*format!( - "Index {:?} out of range for {} x {} matrix", - index.to_2d(D::WIDTH), - D::HEIGHT, - D::WIDTH - )) + fn default() -> Self { + Matrix { + data: Default::default(), + } } } -impl, I: Index2D, const M: usize, const N: usize> IndexMut - for GenericMatrix -{ - fn index_mut(&mut self, index: I) -> &mut Self::Output { - self.get_mut(index).expect(&*format!( - "Index {:?} out of range for {} x {} matrix", - index.to_2d(D::WIDTH), - D::HEIGHT, - D::WIDTH - )) +// deref 1x1 matrices to a scalar automatically +impl Deref for Matrix { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.data[0][0] } } -impl, const M: usize, const N: usize> Get2DSized - for GenericMatrix -{ +// deref 1x1 matrices to a mutable scalar automatically +impl DerefMut for Matrix { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.data[0][0] + } } -fn foo() { - let mut a: Matrix = Default::default(); - let c = Slice2D::<&Matrix, 3, 3>::new(&a, 2, 2); - let b = Slice2D::<&mut Matrix, 3, 3>::new(&mut a, 1, 1); - println!("{:?} {:?}", b, c) +impl IntoIterator for Matrix { + type Item = T; + type IntoIter = Flatten>; + + fn into_iter(self) -> Self::IntoIter { + self.data.into_iter().flatten() + } } + +impl_matrix_op!(neg, |l: L| { -l }); +impl_matrix_op!(!, |l: L| { !l }); +impl_matrix_op!(+, |l,r| {l + r}); +impl_matrix_op!(-, |l,r| {l - r}); +impl_matrix_op!(*, |l,r| {l * r}); +impl_matrix_op!(/, |l,r| {l / r}); +impl_matrix_op!(%, |l,r| {l % r}); +impl_matrix_op!(&, |l,r| {l & r}); +impl_matrix_op!(|, |l,r| {l | r}); +impl_matrix_op!(^, |l,r| {l ^ r}); +impl_matrix_op!(<<, |l,r| {l << r}); +impl_matrix_op!(>>, |l,r| {l >> r});