use crate::address_bound::AddressBound;
use crate::matrix_address::MatrixAddress;
use crate::tensor::Tensor;
use std::fmt::{Display, Formatter};
use std::io::Error;
use std::io::ErrorKind::InvalidInput;
#[derive(Clone, Debug, PartialEq)]
pub struct Matrix<T> {
data: Vec<T>,
bounds: AddressBound<MatrixAddress>,
}
impl<T> Tensor<T, MatrixAddress> for Matrix<T> {
fn new<F>(bounds: AddressBound<MatrixAddress>, address_value_converter: F) -> Matrix<T>
where
F: Fn(MatrixAddress) -> T,
{
let data: Vec<T> = bounds.iter().map(address_value_converter).collect();
Matrix { data, bounds }
}
fn get(&self, address: &MatrixAddress) -> Option<&T> {
if !self.bounds.contains_address(address) {
return None;
}
self.data.get(self.bounds.index_address(address).unwrap())
}
fn get_mut(&mut self, address: &MatrixAddress) -> Option<&mut T> {
if !self.bounds.contains_address(address) {
return None;
}
self.data
.get_mut(self.bounds.index_address(address).unwrap())
}
fn set(&mut self, address: &MatrixAddress, value: T) -> Result<(), Error> {
if !self.bounds.contains_address(address) {
return Err(Error::new(
InvalidInput,
format!("The following address is out of bounds: {address}"),
));
}
self.data[self.bounds.index_address(address).unwrap()] = value;
Ok(())
}
fn bounds(&self) -> &AddressBound<MatrixAddress> {
&self.bounds
}
}
impl<T> Matrix<T> {
fn to_display_string<T1: Display, F: Fn(&T) -> T1>(
&self,
display_func: F,
row_delimiter: &str,
column_delimiter: &str,
) -> String {
self.address_iterator()
.enumerate()
.map(|(i, address)| {
format!(
"{}{}",
display_func(self.get(&address).unwrap()),
if (i as i64 + 1) % (self.bounds.largest_possible_position.x + 1) == 0 {
row_delimiter
} else {
column_delimiter
}
)
})
.fold("".to_string(), |a: String, b: String| a + &b)
}
}
impl<'a, T: Display + 'a> Display for Matrix<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(
f,
"{}",
self.to_display_string(|x: &T| x.to_string(), " ", "\n")
)
}
}
#[cfg(test)]
mod tests {
use crate::address_bound::AddressBound;
use crate::matrix::Matrix;
use crate::matrix_address::MatrixAddress;
use crate::tensor::Tensor;
#[test]
fn display_test() {
let bound = AddressBound::new(MatrixAddress { x: 0, y: 0 }, MatrixAddress { x: 10, y: 10 });
assert_eq!(
"0 1 2 3 4 5 6 0 1 2 3\n4 5 6 0 1 2 3 4 5 6 0\n1 2 3 4 5 6 0 1 2 3 4\n5 6 0 1 2 3 4 5 6 0 1\n2 3 4 5 6 0 1 2 3 4 5\n6 0 1 2 3 4 5 6 0 1 2\n3 4 5 6 0 1 2 3 4 5 6\n0 1 2 3 4 5 6 0 1 2 3\n4 5 6 0 1 2 3 4 5 6 0\n1 2 3 4 5 6 0 1 2 3 4\n5 6 0 1 2 3 4 5 6 0 1\n",
format!(
"{}",
Matrix::new(bound.clone(), |address: MatrixAddress| bound
.index_address(&address)
.unwrap()
% 7)
)
)
}
#[test]
fn get_test() {
let bound = AddressBound {
smallest_possible_position: MatrixAddress { x: 0, y: 0 },
largest_possible_position: MatrixAddress { x: 1000, y: 1000 },
};
let matrix = Matrix::new(bound.clone(), |address| bound.index_address(&address));
matrix.address_iterator().for_each(|address| {
assert_eq!(
bound.index_address(&address),
*matrix.get(&address).unwrap()
)
})
}
#[test]
fn set_test() {
let bound = AddressBound {
smallest_possible_position: MatrixAddress { x: 0, y: 0 },
largest_possible_position: MatrixAddress { x: 1000, y: 1000 },
};
let mut matrix = Matrix::new(bound.clone(), |_address| 0usize);
matrix.address_iterator().for_each(|address| {
assert_eq!(matrix.get(&address).unwrap(), &0usize);
matrix
.set(&address, bound.index_address(&address).unwrap())
.expect("Index out of bounds error");
assert_eq!(
matrix.get(&address).unwrap(),
&(bound.index_address(&address).unwrap())
);
});
matrix.address_iterator().for_each(|address| {
assert_eq!(
bound.index_address(&address).unwrap(),
*(matrix.get(&address).unwrap())
)
})
}
#[test]
fn transform_test() {
let bound = AddressBound {
smallest_possible_position: MatrixAddress { x: 0, y: 0 },
largest_possible_position: MatrixAddress { x: 1000, y: 1000 },
};
let matrix = Matrix::new(bound.clone(), |address| {
bound.index_address(&address).unwrap()
});
let transformed_matrix: Matrix<f64> = matrix.transform(|value| *value as f64);
let transformed_by_address_matrix: Matrix<f64> =
matrix.transform_by_address(|_address, value| *value as f64);
matrix.address_iterator().for_each(|address| {
assert_eq!(
*matrix.get(&address).unwrap() as f64,
*transformed_matrix.get(&address).unwrap(),
);
assert_eq!(
*matrix.get(&address).unwrap() as f64,
*transformed_by_address_matrix.get(&address).unwrap()
);
})
}
#[test]
fn transform_in_place_test() {
let bound = AddressBound {
smallest_possible_position: MatrixAddress { x: 0, y: 0 },
largest_possible_position: MatrixAddress { x: 1000, y: 1000 },
};
let original_matrix = Matrix::new(bound.clone(), |address| {
bound.index_address(&address).unwrap() as i64
});
let mut working_matrix = original_matrix.clone();
let mut working_by_address_matrix = original_matrix.clone();
working_matrix.transform_in_place(|value| *value *= -2);
working_by_address_matrix.transform_by_address_in_place(|_address, value| *value *= -2);
assert_eq!(
original_matrix.transform::<_, i64, Matrix<i64>>(|value| *value * -2),
working_matrix
);
assert_eq!(working_matrix, working_by_address_matrix)
}
#[test]
fn equality_test() {
let bound = AddressBound {
smallest_possible_position: MatrixAddress { x: 0, y: 0 },
largest_possible_position: MatrixAddress { x: 1000, y: 1000 },
};
let m1 = Matrix::new(bound.clone(), |address| {
bound.index_address(&address).unwrap() as i32
});
let m2 = Matrix::new(bound.clone(), |address| {
bound.index_address(&address).unwrap() as i32
});
assert_eq!(m1, m2);
}
}