Skip to content

Commit e172f8d

Browse files
committed
initial commit
0 parents  commit e172f8d

File tree

3 files changed

+307
-0
lines changed

3 files changed

+307
-0
lines changed

Cargo.toml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[package]
2+
name = "matrix"
3+
version = "0.1.0"
4+
authors = ["s0lst1ce"]
5+
edition = "2018"
6+
7+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
8+
9+
[dev-dependencies]
10+
rand = "0.8"
11+
12+
[dependencies]
13+
num = "0.3.1"
14+
thiserror = "1.0"

src/lib.rs

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
#![feature(maybe_uninit_extra)]
2+
#![feature(array_methods)]
3+
use num::traits::{One, Zero};
4+
use std::convert::From;
5+
use std::iter::Sum;
6+
use std::mem::{self, MaybeUninit};
7+
use std::ops::{Add, AddAssign, Mul, MulAssign};
8+
use std::slice::{Iter, IterMut};
9+
use thiserror::Error;
10+
11+
#[derive(Debug, PartialEq, Eq)]
12+
///C is the type of the coefficients of the Matrix
13+
///ROWS is the number of lines & COLS the number of columns
14+
pub struct Matrix<C, const ROWS: usize, const COLS: usize> {
15+
data: [[C; COLS]; ROWS],
16+
}
17+
18+
impl<C, const ROWS: usize, const COLS: usize> Matrix<C, ROWS, COLS> {
19+
///Returns an iterator of all lines of the matrix.
20+
pub fn get_lines(&self) -> Iter<[C; COLS]> {
21+
self.data.iter()
22+
}
23+
24+
pub fn get_mut_lines(&mut self) -> IterMut<[C; COLS]> {
25+
self.data.iter_mut()
26+
}
27+
28+
///Returns the n-1 line. If `index` is out of range then None.
29+
pub fn get_line(&self, index: usize) -> Option<&[C; COLS]> {
30+
self.data.get(index)
31+
}
32+
33+
pub fn get_mut_line(&mut self, index: usize) -> Option<&mut [C; COLS]> {
34+
self.data.get_mut(index)
35+
}
36+
37+
///Access a coefficient. None if the indexes are greated than the size
38+
pub fn get(&self, row: usize, col: usize) -> Option<&C> {
39+
match self.data.get(row) {
40+
None => None,
41+
Some(line) => line.get(col),
42+
}
43+
}
44+
45+
///Mutable access a coefficient. None if the indexes are greated than the size
46+
pub fn get_mut(&mut self, row: usize, col: usize) -> Option<&mut C> {
47+
match self.data.get_mut(row) {
48+
None => None,
49+
Some(line) => line.get_mut(col),
50+
}
51+
}
52+
}
53+
54+
impl<C, const ROWS: usize, const COLS: usize> From<[[C; COLS]; ROWS]> for Matrix<C, ROWS, COLS> {
55+
fn from(data: [[C; COLS]; ROWS]) -> Self {
56+
Matrix { data }
57+
}
58+
}
59+
60+
///Multiplication by a coefficient. Can never fail, works for all matrices.
61+
impl<'a, C: 'a, const ROWS: usize, const COLS: usize> MulAssign<C> for Matrix<C, ROWS, COLS>
62+
where
63+
C: MulAssign + Copy,
64+
{
65+
fn mul_assign(&mut self, coef: C) {
66+
for row in self.data.iter_mut() {
67+
for c in row.iter_mut() {
68+
*c *= coef
69+
}
70+
}
71+
}
72+
}
73+
74+
///Matrix addition, they must be of the same size
75+
impl<C, const ROWS: usize, const COLS: usize> AddAssign<Matrix<C, ROWS, COLS>>
76+
for Matrix<C, ROWS, COLS>
77+
where
78+
C: AddAssign + Copy,
79+
{
80+
fn add_assign(&mut self, other: Matrix<C, ROWS, COLS>) {
81+
self.data
82+
.iter_mut()
83+
.zip(other.data.iter())
84+
.for_each(|(row_a, row_b)| {
85+
row_a
86+
.iter_mut()
87+
.zip(row_b.iter())
88+
.for_each(|(a, b)| *a += *b)
89+
});
90+
}
91+
}
92+
93+
///Matrix product. The implementation garuantees success by checking compatibility with ROWS and COLS bounds
94+
///Creates a new Martix
95+
impl<C, const ROWS: usize, const COLS: usize, const Q: usize> Mul<Matrix<C, Q, COLS>>
96+
for Matrix<C, ROWS, Q>
97+
where
98+
//clone may be more appropriate here, is there a way not to require neither Copy nor Clone?
99+
C: Add + Mul<C, Output = C> + Sum + Clone,
100+
{
101+
type Output = Matrix<C, ROWS, COLS>;
102+
fn mul(self, other: Matrix<C, Q, COLS>) -> Self::Output {
103+
let mut m: [[MaybeUninit<C>; COLS]; ROWS] = unsafe { MaybeUninit::uninit().assume_init() };
104+
for row in 0..ROWS {
105+
for col in 0..COLS {
106+
m[row][col].write(
107+
self.data[row]
108+
.iter()
109+
.enumerate()
110+
.map(|(j, a)| a.clone() * other.data[j][col].clone())
111+
.sum(),
112+
);
113+
}
114+
}
115+
Matrix {
116+
//transmute_copy because transmute doesn't work for const generics yet
117+
data: unsafe { mem::transmute_copy::<_, [[C; COLS]; ROWS]>(&m) },
118+
}
119+
}
120+
}
121+
122+
///Some functions for Matrix that have coefficients to have nil and neutral product values.
123+
///This gives access to nil matrixes as well as the identity matrix
124+
impl<C, const SIZE: usize> Matrix<C, SIZE, SIZE>
125+
where
126+
C: One + Zero + Copy,
127+
{
128+
///Returns the identity matrix of size `SIZE`
129+
pub fn identity() -> Self {
130+
let mut m = Self::nil();
131+
for i in 0..SIZE {
132+
m.data[i][i] = C::one()
133+
}
134+
m
135+
}
136+
137+
///Returns the nil matrix of size `SIZE`
138+
pub fn nil() -> Self {
139+
[[C::zero(); SIZE]; SIZE].into()
140+
}
141+
}
142+
143+
///Matrix internal manipulation operations
144+
impl<'a, C: 'a, const SIZE: usize> Matrix<C, SIZE, SIZE>
145+
where
146+
C: Clone + MulAssign<&'a C> + AddAssign<&'a C>,
147+
{
148+
///Permutation operation between `source` and `target` rows
149+
///
150+
///This can be understood as swapping the `source` row with the `target` one
151+
pub fn permute(&mut self, source: usize, target: usize) -> Result<(), Error> {
152+
if (target >= SIZE) | (source >= SIZE) {
153+
return Err(Error::OutOfBounds);
154+
} else {
155+
self.data.as_mut_slice().swap(source, target);
156+
}
157+
Ok(())
158+
}
159+
160+
///Dilation operation on row `row`.
161+
///
162+
///Line dilation is to multiply all coefficients of a row by a factor
163+
pub fn dilate(&mut self, row: usize, factor: &'a C) -> Result<(), Error> {
164+
match self.data.get_mut(row) {
165+
None => Err(Error::OutOfBounds),
166+
Some(line) => Ok(line.iter_mut().for_each(|c| *c *= factor)),
167+
}
168+
}
169+
}
170+
171+
//Separated from the previous impl because of the need of HRTB
172+
impl<C, const SIZE: usize> Matrix<C, SIZE, SIZE>
173+
where
174+
for<'a> C: Clone + MulAssign<&'a C> + AddAssign<&'a C>,
175+
{
176+
///Transvection operation on row `source` with row `other` and `factor`
177+
///
178+
///Line transvection is to add a row to a source row for each coefficient
179+
pub fn transvect(&mut self, source: usize, other: usize) -> Result<(), Error> {
180+
if (other >= SIZE) | (source >= SIZE) {
181+
return Err(Error::OutOfBounds);
182+
} else if other == source {
183+
return Err(Error::WrongOperation);
184+
} else {
185+
let slices = self.data.as_mut_slice().split_at_mut(source.max(other));
186+
let (begin, end) = if source > other {
187+
(&mut slices.1[0], &mut slices.0[other])
188+
} else {
189+
(&mut slices.0[source], &mut slices.1[0])
190+
};
191+
begin
192+
.iter_mut()
193+
.enumerate()
194+
.for_each(|(i, c)| *c += &end[i]);
195+
}
196+
197+
Ok(())
198+
}
199+
}
200+
201+
#[derive(Debug, Error, PartialEq, Eq)]
202+
pub enum Error {
203+
#[error("invalid row: out of bounds")]
204+
OutOfBounds,
205+
#[error("there is an operation better suited for this")]
206+
WrongOperation,
207+
}

tests/op_tests.rs

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
use matrix::{Error, Matrix};
2+
3+
mod matrix_setup {
4+
use matrix::Matrix;
5+
use rand::{thread_rng, Rng};
6+
7+
pub fn setup_rand_3x2() -> Matrix<u8, 3, 2> {
8+
let mut m = [[0; 2]; 3];
9+
let mut rng = thread_rng();
10+
m.iter_mut().for_each(|a| rng.fill(a));
11+
m.into()
12+
}
13+
14+
pub fn setup_3x2() -> Matrix<u8, 3, 2> {
15+
[[1, 2], [3, 4], [5, 6]].into()
16+
}
17+
18+
pub fn setup_2x3() -> Matrix<u8, 2, 3> {
19+
[[9, 8, 7], [6, 5, 4]].into()
20+
}
21+
22+
pub fn setup_3x3() -> Matrix<u8, 3, 3> {
23+
[[1, 2, 1], [3, 4, 1], [1, 5, 6]].into()
24+
}
25+
}
26+
27+
#[test]
28+
fn add() {
29+
let mut m = matrix_setup::setup_3x2();
30+
m += matrix_setup::setup_3x2();
31+
assert_eq!(m, Matrix::from([[2, 4], [6, 8], [10, 12]]));
32+
}
33+
34+
#[test]
35+
fn mul() {
36+
let m = matrix_setup::setup_3x2();
37+
assert_eq!(
38+
m * matrix_setup::setup_2x3(),
39+
[[21, 18, 15], [51, 44, 37], [81, 70, 59]].into()
40+
);
41+
}
42+
43+
#[test]
44+
fn dilate() {
45+
let mut m = matrix_setup::setup_3x3();
46+
m.dilate(1, &2u8).unwrap();
47+
assert_eq!(m, [[1, 2, 1], [6, 8, 2], [1, 5, 6]].into());
48+
}
49+
50+
#[test]
51+
fn dilate_fail_bounds() {
52+
let mut m = matrix_setup::setup_3x3();
53+
assert_eq!(m.dilate(4, &1), Err(Error::OutOfBounds));
54+
}
55+
56+
#[test]
57+
fn transvect() {
58+
let mut m = matrix_setup::setup_3x3();
59+
m.transvect(0, 1).unwrap();
60+
assert_eq!(m, [[4, 6, 2], [3, 4, 1], [1, 5, 6]].into())
61+
}
62+
63+
#[test]
64+
fn transvect_fail_bounds() {
65+
let mut m = matrix_setup::setup_3x3();
66+
assert_eq!(m.transvect(3, 0), Err(Error::OutOfBounds));
67+
}
68+
69+
#[test]
70+
fn transvect_fail_op() {
71+
let mut m = matrix_setup::setup_3x3();
72+
assert_eq!(m.transvect(0, 0), Err(Error::WrongOperation));
73+
}
74+
75+
#[test]
76+
fn permute() {
77+
let mut m = matrix_setup::setup_3x3();
78+
m.permute(0, 1).unwrap();
79+
assert_eq!(m, [[3, 4, 1], [1, 2, 1], [1, 5, 6]].into());
80+
}
81+
82+
#[test]
83+
fn permute_fail_bounds() {
84+
let mut m = matrix_setup::setup_3x3();
85+
assert_eq!(m.permute(4, 0), Err(Error::OutOfBounds));
86+
}

0 commit comments

Comments
 (0)