You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

671 lines
17 KiB
Rust

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

extern crate core;
use index::Index2D;
use num_traits::{Bounded, One, Zero};
use std::cmp::min;
use std::fmt::Debug;
use std::iter::{zip, Flatten};
use std::ops::{Index, IndexMut};
pub mod decompose;
pub mod index;
mod math;
mod ops;
mod util;
/** A 2D array of values which can be operated upon.
Matrices have a fixed size known at compile time */
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Matrix<T, const M: usize, const N: usize>
where
T: Copy,
{
data: [[T; N]; M], // Row-Major order
}
/// An alias for a [Matrix] with a single column
pub type Vector<T, const N: usize> = Matrix<T, N, 1>;
// CONSTRUCTORS
// Default
impl<T: Copy + Default, const M: usize, const N: usize> Default for Matrix<T, M, N> {
fn default() -> Self {
Matrix::fill(T::default())
}
}
// Zero
impl<T: Copy + Zero, const M: usize, const N: usize> Zero for Matrix<T, M, N> {
fn zero() -> Self {
Matrix::fill(T::zero())
}
fn is_zero(&self) -> bool {
self.elements().all(|e| e.is_zero())
}
}
// One
impl<T: Copy + One, const M: usize, const N: usize> One for Matrix<T, M, N> {
fn one() -> Self {
Matrix::fill(T::one())
}
}
// min_value and max_value
// LowerBounded and UpperBounded are automatically implemented from this
impl<T: Copy + Bounded, const N: usize, const M: usize> Bounded for Matrix<T, N, M> {
fn min_value() -> Self {
Self::fill(T::min_value())
}
fn max_value() -> Self {
Self::fill(T::max_value())
}
}
// Identity
impl<T: Copy + Zero + One, const N: usize> Matrix<T, N, N> {
/** Create an identity matrix, a square matrix where the diagonals are 1 and
all other elements are 0.
for example,
$bbI = \[\[1,0,0],\[0,1,0],\[0,0,1]]$
Matrix multiplication between a matrix and the identity matrix always results in itself
$bbA xx bbI = bbA$
# Examples
```
# use vector_victor::Matrix;
let i = Matrix::<i32,3,3>::identity();
assert_eq!(i, Matrix::mat([[1, 0, 0],
[0, 1, 0],
[0, 0, 1]]))
```
Note that the identity only exists for matrices that are square, so this doesnt work:
```compile_fail
# use vector_victor::Matrix;
let i = Matrix::<i32,4,2>::identity();
``` */
#[must_use]
pub fn identity() -> Self {
let mut result = Self::zero();
for i in 0..N {
result[(i, i)] = T::one();
}
return result;
}
}
// Matrix constructors
impl<T: Copy, const M: usize, const N: usize> Matrix<T, M, N> {
/** Generate a new matrix from a 2D Array
# Arguments
* `data`: A 2D array of elements to copy into the new matrix
# Examples
```
# use vector_victor::Matrix;
let a = Matrix::mat([[1,2,3,4];4]);
``` */
#[must_use]
pub fn mat(data: [[T; N]; M]) -> Self {
assert!(M > 0, "Matrix must have at least 1 row");
assert!(N > 0, "Matrix must have at least 1 column");
Matrix::<T, M, N> { data }
}
/** Generate a new matrix from a single scalar
# Arguments
* `scalar`: Scalar value to copy into the new matrix.
# Examples
```
# use vector_victor::Matrix;
// these are equivalent
assert_eq!(Matrix::<i32,4,4>::fill(5), Matrix::mat([[5;4];4]))
``` */
#[must_use]
pub fn fill(scalar: T) -> Matrix<T, M, N> {
assert!(M > 0, "Matrix must have at least 1 row");
assert!(N > 0, "Matrix must have at least 1 column");
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
# Examples
The following is another way of performing [`Matrix::transpose()`]
```
# use vector_victor::Matrix;
let my_matrix = Matrix::mat([[1, 2, 3],
[4, 5, 6]]);
let transpose : Matrix<_,3,2>= Matrix::from_rows(my_matrix.cols());
assert_eq!(transpose, Matrix::mat([[1, 4],
[2, 5],
[3, 6]]))
``` */
#[must_use]
pub fn from_rows<I>(iter: I) -> Self
where
I: IntoIterator<Item = Vector<T, N>>,
Self: Default,
{
let mut result = Self::default();
for (m, row) in iter.into_iter().enumerate().take(M) {
result.set_row(m, &row)
}
result
}
/** Create a matrix from an iterator of vectors
# Arguments
* `iter`: iterator of vectors to copy into columns
# Examples
The following is another way of performing [`Matrix::transpose()`]
```
# use vector_victor::Matrix;
let my_matrix = Matrix::mat([[1, 2, 3],
[4, 5, 6]]);
let transpose : Matrix<_,3,2>= Matrix::from_cols(my_matrix.rows());
assert_eq!(transpose, Matrix::mat([[1, 4],
[2, 5],
[3, 6]]))
``` */
#[must_use]
pub fn from_cols<I>(iter: I) -> Self
where
I: IntoIterator<Item = Vector<T, M>>,
Self: Default,
{
let mut result = Self::default();
for (n, col) in iter.into_iter().enumerate().take(N) {
result.set_col(n, &col)
}
result
}
}
// Vector constructor
impl<T: Copy, const N: usize> Vector<T, N> {
/** Create a vector from a 1D array.
Note that vectors are always column vectors unless explicitly instantiated as row vectors
# Examples
```
# use vector_victor::{Matrix, Vector};
// these are equivalent
assert_eq!(Vector::vec([1,2,3,4]), Matrix::mat([[1],[2],[3],[4]]));
``` */
pub fn vec(data: [T; N]) -> Self {
assert!(N > 0, "Vector must have at least 1 element");
return Vector::<T, N> {
data: data.map(|e| [e]),
};
}
}
// ACCESSORS AND MUTATORS
impl<T: Copy, const M: usize, const N: usize> Matrix<T, M, N> {
/** Returns an iterator over the elements of the matrix in row-major order.
This is identical to the behavior of [`IntoIterator`](#associatedtype.IntoIter)
# Examples
```
# use vector_victor::Matrix;
let my_matrix = Matrix::mat([[1, 2],
[3, 4]]);
itertools::assert_equal(my_matrix.elements(), [1,2,3,4].iter())
``` */
#[must_use]
pub fn elements<'s>(&'s self) -> impl Iterator<Item = &'s T> + 's {
self.data.iter().flatten()
}
/** Returns a mutable iterator over the elements of the matrix in row-major order.
# Examples
```
# use vector_victor::Matrix;
let mut my_matrix = Matrix::mat([[1, 2],
[3, 4]]);
for elem in my_matrix.elements_mut() {*elem += 2;}
itertools::assert_equal(my_matrix.elements(), [3,4,5,6].iter())
``` */
#[must_use]
pub fn elements_mut<'s>(&'s mut self) -> impl Iterator<Item = &'s mut T> + 's {
self.data.iter_mut().flatten()
}
/** returns an iterator over the elements along the diagonal of a matrix
# Examples
```
# use vector_victor::Matrix;
let my_matrix = Matrix::mat([[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
[10,11,12]]);
itertools::assert_equal(my_matrix.diagonals(), [1,5,9].iter())
``` */
#[must_use]
pub fn diagonals<'s>(&'s self) -> impl Iterator<Item = &'s T> + 's {
(0..min(N, M)).map(|n| &self[(n, n)])
}
/** Returns an iterator over the elements directly below the diagonal of a matrix
# Examples
```
# use vector_victor::Matrix;
let my_matrix = Matrix::mat([[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
[10,11,12]]);
itertools::assert_equal(my_matrix.subdiagonals(), [4,8,12].iter());
``` */
#[must_use]
pub fn subdiagonals<'s>(&'s self) -> impl Iterator<Item = &'s T> + 's {
(0..min(N, M - 1)).map(|n| &self[(n + 1, n)])
}
/** Returns a reference to the element at that position in the matrix, or `None` if out of bounds.
[`Index`](#impl-Index%3CI%3E-for-Matrix%3CT,+M,+N%3E) behaves similarly,
but will panic if the index is out of bounds instead of returning an option
# Arguments
* `index`: a 1D or 2D index into the matrix. See [Index2D] for more information on matrix indexing.
# Examples
```
# use vector_victor::Matrix;
let my_matrix = Matrix::mat([[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)));
// my_matrix.get() is equivalent to my_matrix[],
// but returns an Option instead of panicking
assert_eq!(my_matrix.get(2), Some(&my_matrix[2]));
// 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)?;
Some(&self.data[m][n])
}
/** Returns a mutable reference to the element at that position in the matrix,
or `None` if out of bounds.
[`IndexMut`](#impl-IndexMut%3CI%3E-for-Matrix%3CT,+M,+N%3E) behaves similarly,
but will panic if the index is out of bounds instead of returning an option
# Arguments
* `index`: a 1D or 2D index into the matrix. See [Index2D] for more information
on matrix indexing.
# Examples
```
# use vector_victor::Matrix;
let mut my_matrix = Matrix::mat([[1, 2],
[3, 4]]);
match my_matrix.get_mut(2) {
Some(t) => *t = 5,
None => panic!()};
assert_eq!(my_matrix, Matrix::mat([[1,2],[5,4]]))
``` */
#[inline]
#[must_use]
pub 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])
}
/** Returns a row of the matrix.
# Panics
Panics if row index `m` is out of bounds.
# Examples
```
# use vector_victor::{Matrix, Vector};
let my_matrix = Matrix::mat([[1, 2],
[3, 4]]);
// row at index 1
assert_eq!(my_matrix.row(1), Vector::vec([3,4]));
``` */
#[inline]
#[must_use]
pub fn row(&self, m: usize) -> Vector<T, N> {
assert!(
m < M,
"Row index {} out of bounds for {}×{} matrix",
m,
M,
N
);
Vector::<T, N>::vec(self.data[m])
}
/** Sets a row of the matrix.
# Panics
Panics if row index `m` is out of bounds.
# Examples
```
# use vector_victor::{Matrix, Vector};
let mut my_matrix = Matrix::mat([[1, 2],
[3, 4]]);
// row at index 1
my_matrix.set_row(1, &Vector::vec([5,6]));
assert_eq!(my_matrix, Matrix::mat([[1,2],[5,6]]));
``` */
#[inline]
pub fn set_row(&mut self, m: usize, val: &Vector<T, N>) {
assert!(
m < M,
"Row index {} out of bounds for {}×{} matrix",
m,
M,
N
);
for n in 0..N {
self.data[m][n] = val.data[n][0];
}
}
/** Returns a column of the matrix.
# Panics
Panics if column index `n` is out of bounds.
# Examples
```
# use vector_victor::{Matrix, Vector};
let my_matrix = Matrix::mat([[1, 2],
[3, 4]]);
// column at index 1
assert_eq!(my_matrix.col(1), Vector::vec([2,4]));
``` */
#[inline]
#[must_use]
pub fn col(&self, n: usize) -> Vector<T, M> {
assert!(
n < N,
"Column index {} out of bounds for {}×{} matrix",
n,
M,
N
);
Vector::<T, M>::vec(self.data.map(|r| r[n]))
}
/** Sets a column of the matrix.
# Panics
Panics if column index `n` is out of bounds.
# Examples
```
# use vector_victor::{Matrix, Vector};
let mut my_matrix = Matrix::mat([[1, 2],
[3, 4]]);
// column at index 1
my_matrix.set_col(1, &Vector::vec([5,6]));
assert_eq!(my_matrix, Matrix::mat([[1,5],[3,6]]));
``` */
#[inline]
pub fn set_col(&mut self, n: usize, val: &Vector<T, M>) {
assert!(
n < N,
"Column index {} out of bounds for {}×{} matrix",
n,
M,
N
);
for m in 0..M {
self.data[m][n] = val.data[m][0];
}
}
/// Returns an iterator over the rows of the matrix, returning them as column vectors.
#[must_use]
pub fn rows<'a>(&'a self) -> impl Iterator<Item = Vector<T, N>> + 'a {
(0..M).map(|m| self.row(m))
}
/// Returns an iterator over the columns of the matrix, returning them as column vectors.
#[must_use]
pub fn cols<'a>(&'a self) -> impl Iterator<Item = Vector<T, M>> + 'a {
(0..N).map(|n| self.col(n))
}
/** Interchange two rows
# Panics
Panics if row index `m1` or `m2` are out of bounds */
pub fn pivot_row(&mut self, m1: usize, m2: usize) {
let tmp = self.row(m2);
self.set_row(m2, &self.row(m1));
self.set_row(m1, &tmp);
}
/** Interchange two columns
# Panics
Panics if column index `n1` or `n2` are out of bounds */
pub fn pivot_col(&mut self, n1: usize, n2: usize) {
let tmp = self.col(n2);
self.set_col(n2, &self.col(n1));
self.set_col(n1, &tmp);
}
/** Apply a permutation matrix to the rows of a matrix
# Arguments
* `ms`: a [`Vector`] of [`usize`] of length M. Each entry is the index of the row that will
appear in the result
# Panics
Panics if any of the row indices in `ms` is out of bounds
# Examples
```
# use vector_victor::{Matrix, Vector};
let my_matrix = Matrix::mat([[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]);
let permuted = my_matrix.permute_rows(&Vector::vec([1, 0, 2]));
assert_eq!(permuted, Matrix::mat([[4, 5, 6],
[1, 2, 3],
[7, 8, 9]]))
``` */
#[must_use]
pub fn permute_rows(&self, ms: &Vector<usize, M>) -> Self
where
T: Default,
{
Self::from_rows(ms.elements().map(|&m| self.row(m)))
}
/** Apply a permutation matrix to the columns of a matrix
# Arguments
* `ns`: a [`Vector`] of [`usize`] of length N. Each entry is the index of the column that will
appear in the result
# Panics
Panics if any of the column indices in `ns` is out of bounds */
#[must_use]
pub fn permute_cols(&self, ns: &Vector<usize, N>) -> Self
where
T: Default,
{
Self::from_cols(ns.elements().map(|&n| self.col(n)))
}
/** Returns the transpose $M^T$ of the matrix, or the matrix flipped across its diagonal.
# Examples
```
# use vector_victor::Matrix;
let my_matrix = Matrix::mat([[1, 2, 3],
[4, 5, 6]]);
assert_eq!(
my_matrix.transpose(),
Matrix::mat([[1, 4],
[2, 5],
[3, 6]]))
``` */
pub fn transpose(&self) -> Matrix<T, N, M>
where
Matrix<T, N, M>: Default,
{
Matrix::<T, N, M>::from_rows(self.cols())
}
}
// Index
impl<I, T, const M: usize, const N: usize> Index<I> for Matrix<T, M, N>
where
I: Index2D,
T: Copy,
{
type Output = T;
#[inline(always)]
fn index(&self, index: I) -> &Self::Output {
self.get(index).expect(&*format!(
"index {:?} out of range for {}×{} 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: Copy,
{
#[inline(always)]
fn index_mut(&mut self, index: I) -> &mut Self::Output {
self.get_mut(index).expect(&*format!(
"index {:?} out of range for {}×{} Matrix",
index, M, N
))
}
}
// CONVERSIONS
// Convert from 2D Array (equivalent to new)
impl<T: Copy, const M: usize, const N: usize> From<[[T; N]; M]> for Matrix<T, M, N> {
fn from(data: [[T; N]; M]) -> Self {
Self::mat(data)
}
}
// Convert from 1D Array (equivalent to vec)
impl<T: Copy, const M: usize> From<[T; M]> for Vector<T, M> {
fn from(data: [T; M]) -> Self {
Self::vec(data)
}
}
// Convert from scalar (equivalent to fill)
impl<T: Copy, const M: usize, const N: usize> From<T> for Matrix<T, M, N> {
fn from(scalar: T) -> Self {
Self::fill(scalar)
}
}
// IntoIter
impl<T: Copy, const M: usize, const N: usize> IntoIterator for Matrix<T, M, N> {
type Item = T;
type IntoIter = Flatten<std::array::IntoIter<[T; N], M>>;
fn into_iter(self) -> Self::IntoIter {
self.data.into_iter().flatten()
}
}
// FromIterator
impl<T: Copy, const M: usize, const N: usize> FromIterator<T> for Matrix<T, M, N>
where
Self: Default,
{
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
let mut result: Self = Default::default();
for (l, r) in zip(result.elements_mut(), iter) {
*l = r;
}
result
}
}