use std::iter::zip;
pub struct HMatrix<T>
where
T: Copy + Into<f64>,
{
data: Vec<T>,
row_size: usize,
columm_size: usize,
}
impl<T> HMatrix<T>
where
T: Copy + Into<f64>,
{
pub fn new() -> Self {
HMatrix {
data: Vec::new(),
row_size: 0,
columm_size: 0
}
}
pub fn new_from_rows(rows: &[Vec<T>]) -> Option<Self> {
if rows.is_empty() {
return None;
}
let mut row_size_prev: Option<usize> = None;
let columm_size: usize = rows.len();
let mut data: Vec<T> = Vec::new();
for row in rows {
if row_size_prev == None {
row_size_prev = Some(row.len());
if row_size_prev == Some(0) {
return None;
}
}
if row_size_prev != Some(row.len()) {
return None;
}
for item in row {
data.push(*item);
}
}
Some(HMatrix {
data,
row_size: row_size_prev.unwrap(),
columm_size,
})
}
pub fn new_from_cols(cols: &[Vec<T>]) -> Option<Self> {
if cols.is_empty() {
return None;
}
let mut data: Vec<T> = Vec::new();
let row_size: usize = cols[0].len();
let mut columm_size_prev: Option<usize> = None;
for r_i in 0..cols[0].len() {
for c_i in 0..cols.len() {
if columm_size_prev == None {
columm_size_prev = Some(cols[c_i].len());
}
if Some(cols[c_i].len()) != columm_size_prev {
return None;
}
if cols[c_i].is_empty() {
return None;
}
data.push(cols[c_i][r_i]);
}
}
Some(HMatrix {
data,
row_size,
columm_size: cols.len(),
})
}
pub fn get(&self, row: usize, col: usize) -> Option<T> {
if row >= self.columm_size || col >= self.row_size {
return None;
}
Some(self.data[row * self.row_size + col])
}
pub fn get_mult(&self, row_range: (usize, usize), col_range: (usize, usize)) -> Option<HMatrix<T>> {
let (row_min, row_max) = row_range;
let (col_min, col_max) = col_range;
if row_min >= self.columm_size || row_max > self.columm_size || row_min >= row_max {
return None;
}
if col_min >= self.row_size || col_max > self.row_size || col_min >= col_max {
return None;
}
let mut data: Vec<T> = Vec::new();
for r in row_min..row_max {
for c in col_min..col_max {
data.push(self.data[r * self.row_size + c]);
}
}
Some(HMatrix {
data,
row_size: col_max - col_min,
columm_size: row_max - row_min,
})
}
pub fn get_row(&self, index: usize) -> Option<Vec<T>> {
let mut row: Vec<T> = Vec::new();
if index >= self.columm_size {
return None;
}
let start_index: usize = self.row_size * index;
for i in start_index..start_index+self.row_size {
row.push(self.data[i]);
}
return Some(row);
}
pub fn get_col(&self, index: usize) -> Option<Vec<T>> {
let mut columm: Vec<T> = Vec::new();
if index >= self.row_size {
return None;
}
for i in 0..self.columm_size {
columm.push(self.data[i * self.row_size + index]);
}
return Some(columm);
}
pub fn add_row(&mut self, row: Vec<T>) -> Result<(), String> {
if self.columm_size == 0 && self.row_size == 0 {
for i in row {
self.data.push(i);
self.row_size += 1;
}
self.columm_size += 1;
return Ok(());
} else {
if row.len() != self.row_size {
return Err(String::from("The row does not have the same size as the other in the Matrix. from: HMatrix, add_row()"));
}
else {
for i in row {
self.data.push(i);
}
self.columm_size += 1;
return Ok(());
}
}
}
pub fn add_col(&mut self, col: Vec<T>) -> Result<(), String> {
if col.len() != self.columm_size {
return Err(format!(
"column length {} does not match matrix row count {}",
col.len(),
self.columm_size
));
}
for r in 0..self.columm_size {
let insert_index = (r + 1) * self.row_size + r;
self.data.insert(insert_index, col[r]);
}
self.row_size += 1;
Ok(())
}
}
pub fn h_hadamard<I, T>(vec1: &[I], vec2: &[T]) -> Vec<f64>
where
T: Copy + Into<f64>,
I: Copy + Into<f64>,
{
let mut new_vec: Vec<f64> = Vec::new();
if vec1.len() != vec2.len() {
panic!("from: h_hadamard, vectors must have the same length");
}
for (a, b) in zip(vec1, vec2) {
new_vec.push((*a).into() * (*b).into())
}
return new_vec;
}
pub fn h_dot<A, B>(vec1: &[A], vec2: &[B]) -> f64
where
A: Copy + Into<f64>,
B: Copy + Into<f64>,
{
if vec1.len() != vec2.len() {
panic!("h_dot: the two vectors do not have the same length");
}
let mut sum: f64 = 0.0;
for (a, b) in zip(vec1, vec2) {
sum += (*a).into()*(*b).into();
}
return sum;
}
pub trait Magnitude {
fn h_magnitude(&self) -> f64;
}
impl<T> Magnitude for [T]
where
T: Copy + Into<f64>,
{
fn h_magnitude(&self) -> f64 {
let sum_of_squares: f64 = self.iter().map(|x| {
let v: f64 = (*x).into();
v * v
}).sum();
sum_of_squares.sqrt()
}
}
pub fn h_vector_add<T, I>(vec1: &[T], vec2: &[I]) -> Vec<f64>
where
T: Copy + Into<f64>,
I: Copy + Into<f64>,
{
if vec1.len() != vec2.len() {
panic!("from: h_vector_add, the vectors do not have the same length");
}
zip(vec1, vec2)
.map(|(a, b)| (*a).into() + (*b).into())
.collect()
}
pub fn h_vector_sub<T, I>(vec1: &[T], vec2: &[I]) -> Vec<f64>
where
T: Copy + Into<f64>,
I: Copy + Into<f64>,
{
if vec1.len() != vec2.len() {
panic!("from: h_vector_sub, the vectors do not have the same length");
}
zip(vec1, vec2)
.map(|(a, b)| (*a).into() - (*b).into())
.collect()
}
pub trait VectorScalarMultiply<S>
where
S: Copy + Into<f64>,
{
fn h_vector_scalar_mult(&self, scalar: S) -> Vec<f64>;
}
impl<S, T> VectorScalarMultiply<S> for [T]
where
S: Copy + Into<f64>,
T: Copy + Into<f64>,
{
fn h_vector_scalar_mult(&self, scalar: S) -> Vec<f64> {
self.iter().map(|x| (*x).into() * scalar.into()).collect()
}
}
pub trait VectorScalarDivision<S>
where
S: Copy + Into<f64>,
{
fn h_vector_scalar_div(&self, scalar: S) -> Vec<f64>;
}
impl<S, T> VectorScalarDivision<S> for [T]
where
S: Copy + Into<f64>,
T: Copy + Into<f64>,
{
fn h_vector_scalar_div(&self, scalar: S) -> Vec<f64> {
self.iter().map(|x| (*x).into() / scalar.into()).collect()
}
}
#[derive(Debug, Eq, PartialEq)]
pub enum Measurement {
Radians,
Degrees,
}
pub fn h_vectors_angle<T, I>(vec1: &[T], vec2: &[I], return_measurement: Measurement) -> Option<f64>
where
T: Copy + Into<f64>,
I: Copy + Into<f64>,
{
if vec1.len() != 2 || vec2.len() != 2 {
return None;
}
let vec1_magnitude: f64 = vec1.h_magnitude();
let vec2_magnitude: f64 = vec2.h_magnitude();
if vec1_magnitude == 0.0 || vec2_magnitude == 0.0 {
return None;
}
let angle_between: f64 = ((h_dot(vec1, vec2))/(vec1_magnitude*vec2_magnitude)).acos();
match return_measurement {
Measurement::Radians => return Some(angle_between),
Measurement::Degrees => return Some(angle_between.to_degrees()),
}
}
pub trait LinearTransform<T>
where
T: Copy + Into<f64>,
{
fn h_linear_transform(&self, matrix: &HMatrix<T>) -> Option<Vec<f64>>;
}
impl<T, S> LinearTransform<T> for [S]
where
T: Copy + Into<f64>,
S: Copy + Into<f64>,
{
fn h_linear_transform(&self, matrix: &HMatrix<T>) -> Option<Vec<f64>> {
if self.len() != matrix.row_size {
return None;
}
let result: Vec<f64> = (0..matrix.columm_size)
.map(|r| {
let row = matrix.get_row(r).unwrap();
h_dot(&row, self)
})
.collect();
Some(result)
}
}
pub fn h_linear_composition<T>(matrix2: &HMatrix<T>, matrix1: &HMatrix<T>) -> Option<HMatrix<f64>>
where
T: Copy + Into<f64>,
{
if matrix2.row_size != matrix1.columm_size {
return None;
}
let mut new_matrix: HMatrix<f64> = HMatrix::new();
for i in 0..matrix1.row_size {
if new_matrix.add_col(matrix1.get_col(i).unwrap().h_linear_transform(&matrix2).unwrap()).is_err() {
panic!("Critical failure, HMatrix Struct Logic has an error");
}
}
return Some(new_matrix);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_h_hadamard() {
let vec1 = vec![1.0, 2.0, 3.0];
let vec2 = vec![4.0, 5.0, 6.0];
let result = h_hadamard(&vec1, &vec2);
assert_eq!(result, vec![4.0, 10.0, 18.0]);
}
#[test]
fn test_h_dot() {
let vec1 = vec![1.0, 2.0, 3.0];
let vec2 = vec![4.0, 5.0, 6.0];
let result = h_dot(&vec1, &vec2);
assert_eq!(result, 32.0);
}
#[test]
fn test_magnitude() {
let vec = vec![3.0, 4.0];
assert_eq!(vec.h_magnitude(), 5.0);
}
#[test]
fn test_h_vector_add() {
let vec1 = vec![1.0, 2.0, 3.0];
let vec2 = vec![4.0, 5.0, 6.0];
let result = h_vector_add(&vec1, &vec2);
assert_eq!(result, vec![5.0, 7.0, 9.0]);
}
#[test]
fn test_h_vector_sub() {
let vec1 = vec![5.0, 7.0, 9.0];
let vec2 = vec![1.0, 2.0, 3.0];
let result = h_vector_sub(&vec1, &vec2);
assert_eq!(result, vec![4.0, 5.0, 6.0]);
}
#[test]
fn test_vector_scalar_mult() {
let vec = vec![1.0, 2.0, 3.0];
let result = vec.h_vector_scalar_mult(2.0);
assert_eq!(result, vec![2.0, 4.0, 6.0]);
}
#[test]
fn test_vector_scalar_div() {
let vec = vec![4.0, 6.0, 8.0];
let result = vec.h_vector_scalar_div(2.0);
assert_eq!(result, vec![2.0, 3.0, 4.0]);
}
#[test]
fn test_vectors_angle() {
let vec1: Vec<i32> = vec![0, 1];
let vec2: Vec<f64> = vec![1.0, 0.0];
assert_eq!(h_vectors_angle(&vec1, &vec2, Measurement::Degrees).unwrap_or(0.0), 90.0);
}
#[test]
fn test_h_linear_transform() {
let vec = vec![1.0, 2.0];
let matrix = HMatrix::new_from_rows(&vec![vec![1.0, 0.0], vec![0.0, 1.0]]).unwrap();
let result = vec.h_linear_transform(&matrix);
assert_eq!(result.unwrap_or_else(|| vec![]), vec![1.0, 2.0]); }
}