extern crate rand;
use rand::Rng;
#[derive(Debug)]
struct Matrix {
rows: usize,
cols: usize,
val: Vec<f64>,
}
impl Matrix {
pub fn new(m: usize, n: usize, value: f64) -> Matrix {
Matrix {
rows: m,
cols: n,
val: vec![value; m * n],
}
}
fn with_val(m: usize, n: usize, val: Vec<f64>) -> Matrix{
Matrix {
rows: m,
cols: n,
val: val,
}
}
pub fn rand(m: usize, n: usize) -> Matrix {
let mut rng = rand::thread_rng();
Matrix::with_val(m, n, (0..m * n).map(|_| rng.gen()).collect())
}
pub fn apply_fn<F>(&self, f: F) -> Matrix
where F: Fn(f64) -> f64 {
Matrix {
rows: self.rows,
cols: self.cols,
val: self.val.clone().into_iter().map(|x| f(x)).collect()
}
}
pub fn scale(&self, scalar: f64) -> Matrix {
self.apply_fn((|x| x * scalar))
}
pub fn mult_vec(&self, v: &Vec<f64>) -> Result<Vec<f64>, &str> {
if v.len() != self.cols {
return Err("Vector length must equal matrix column length")
}
Ok((0..self.rows)
.map(|i| {
(0..self.cols)
.map(|j| v[j].clone() * self.val[i * self.cols + j].clone())
.sum()
})
.collect())
}
pub fn transpose(&self) -> Matrix {
let mut val = Vec::with_capacity(self.rows * self.cols);
for i in 0..self.cols {
for j in 0..self.rows {
val.push(self.val[j * self.cols + i].clone())
}
}
Matrix::with_val(self.cols, self.rows, val)
}
pub fn index(&self, m: usize, n: usize) -> Result<f64, &str> {
if m > self.rows || n > self.cols {
return Err("Index out of range")
}
Ok(self.val[m * self.cols + n].clone())
}
pub fn dimensions(&self) -> (usize, usize) {
(self.rows, self.cols)
}
pub fn mult_apply_fn<F>(&self, other: &Matrix, f: F) -> Result<Matrix, &str>
where F: Fn(f64, f64) -> f64{
if self.dimensions() != other.dimensions() {
return Err("Matricies must be same size")
}
Ok(Matrix::with_val(self.rows, self.cols, (0..self.val.len()).map(|i| f(self.val[i].clone(), other.val[i].clone())).collect()))
}
pub fn add(&self, other: &Matrix) -> Result<Matrix, &str> {
self.mult_apply_fn(other, (|x, y| x + y))
}
pub fn subtract(&self, other: &Matrix) -> Result<Matrix, &str> {
self.mult_apply_fn(other, (|x, y| x - y))
}
pub fn push(&mut self, m: usize, n: usize, val: f64) -> Result<(), &str> {
if m > self.rows || n > self.cols {
return Err("Index out of range")
}
self.val[m*self.cols+n] = val;
Ok(())
}
}
impl PartialEq for Matrix {
fn eq (&self, other: &Matrix) -> bool {
self.rows == other.rows && self.cols == other.cols && self.val == other.val
}
}
#[test]
fn test_new() {
let m = Matrix::new(2, 2, 1.0);
assert_eq!(m, Matrix{rows: 2, cols: 2, val: vec![1.0, 1.0, 1.0, 1.0]});
}
#[test]
fn test_rand() {
let m = Matrix::rand(2, 2);
assert_eq!(m.rows, 2);
assert_eq!(m.cols, 2);
assert!(0.0 <= m.val[0] && m.val[0] <= 1.0);
assert!(0.0 <= m.val[0] && m.val[1] <= 1.0);
assert!(0.0 <= m.val[0] && m.val[2] <= 1.0);
assert!(0.0 <= m.val[0] && m.val[3] <= 1.0);
}
#[test]
fn test_with_val() {
let m = Matrix::with_val(2, 2, vec![1.0, 2.0, 3.0, 4.0]);
assert_eq!(m, Matrix{rows: 2, cols: 2, val: vec![1.0, 2.0, 3.0, 4.0]});
}
#[test]
fn test_apply_fn() {
let m = Matrix::with_val(2, 2, vec![1.0, 2.0, 3.0, 4.0]).apply_fn((|x| x*x));
assert_eq!(m, Matrix{rows: 2, cols: 2, val: vec![1.0, 4.0, 9.0, 16.0]});
}
#[test]
fn test_scale() {
let m = Matrix::with_val(2, 2, vec![1.0, 2.0, 3.0, 4.0]).scale(2.0);
assert_eq!(m, Matrix{rows: 2, cols: 2, val: vec![2.0, 4.0, 6.0, 8.0]});
}
#[test]
fn test_mult_vec() {
let m = Matrix::with_val(2, 2, vec![1.0, 2.0, 3.0, 4.0]);
let v = m.mult_vec(&vec![2.0, 1.0]);
assert_eq!(v, Ok(vec![4.0, 10.0]));
}
#[test]
fn test_transpose() {
let m = Matrix::with_val(2, 2, vec![1.0, 2.0, 3.0, 4.0]);
assert_eq!(m.transpose(), Matrix::with_val(2, 2, vec![1.0, 3.0, 2.0, 4.0]));
let m = Matrix::with_val(3, 3, vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]);
assert_eq!(m.transpose(), Matrix::with_val(3, 3, vec![1.0, 4.0, 7.0, 2.0, 5.0, 8.0, 3.0, 6.0, 9.0]));
let m = Matrix::with_val(3, 2, vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
assert_eq!(m.transpose(), Matrix::with_val(2, 3, vec![1.0, 3.0, 5.0, 2.0, 4.0, 6.0]));
}
#[test]
fn test_index() {
let m = Matrix::with_val(2, 2, vec![1.0, 2.0, 3.0, 4.0]);
assert_eq!(m.index(0, 0), Ok(1.0));
assert_eq!(m.index(0, 1), Ok(2.0));
assert_eq!(m.index(1, 0), Ok(3.0));
assert_eq!(m.index(1, 1), Ok(4.0));
}
#[test]
fn test_dimensions() {
assert_eq!(Matrix::new(1, 1, 1.0).dimensions(), (1, 1));
assert_eq!(Matrix::new(1, 2, 1.0).dimensions(), (1, 2));
assert_eq!(Matrix::new(2, 1, 1.0).dimensions(), (2, 1));
assert_eq!(Matrix::new(2, 2, 1.0).dimensions(), (2, 2));
}
#[test]
fn test_add() {
let m1 = Matrix::with_val(2, 3, vec![2.0, 4.0, 5.0, 1.0, 2.0, 3.0]);
let m2 = Matrix::with_val(2, 3, vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
assert_eq!(m1.add(&m2), Ok(Matrix::with_val(2, 3, vec![3.0, 6.0, 8.0, 5.0, 7.0, 9.0])));
}
#[test]
fn test_subtraction() {
let m1 = Matrix::with_val(2, 3, vec![2.0, 4.0, 5.0, 1.0, 2.0, 3.0]);
let m2 = Matrix::with_val(2, 3, vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0]);
assert_eq!(m1.subtract(&m2), Ok(Matrix::with_val(2, 3, vec![1.0, 2.0, 2.0, -3.0, -3.0, -3.0])));
}
#[test]
fn test_push() {
let mut m = Matrix::new(3,3, 1.0);
m.push(0, 2, 2.0);
assert_eq!(m.index(0, 2), Ok(2.0));
m.push(2, 2, 2.0);
assert_eq!(m.index(2, 2), Ok(2.0));
}