use crate::numbers::Numeric;
use std::ops::{Add, Mul};
pub trait VectorOps<T>
where
T: Numeric + Default + Mul<Output = T> + Add<Output = T> + Clone,
{
fn dot_product(&self, b: &[T]) -> Option<T>;
fn squared_norm(&self) -> T;
fn shrink(&self, scalar: f64) -> Option<Vec<f64>>;
fn stretch<U>(&self, scalar: U) -> Option<Vec<f64>>
where
U: Into<f64> + PartialOrd + Copy;
}
impl<T> VectorOps<T> for Vec<T>
where
T: Numeric + Default + Mul<Output = T> + Add<Output = T> + Clone,
{
fn dot_product(&self, b: &[T]) -> Option<T> {
if self.len() != b.len() {
return None;
}
Some(self.iter().zip(b).fold(T::default(), |acc, (ai, bi)| {
(ai.clone() * bi.clone()) + acc
}))
}
fn squared_norm(&self) -> T {
self.iter()
.fold(T::default(), |acc, x| (x.clone() * x.clone()) + acc)
}
fn shrink(&self, scalar: f64) -> Option<Vec<f64>> {
if !(0.0..1.0).contains(&scalar) {
return None;
}
let shrunken_vector = self.iter().map(|x| x.as_f64() * scalar).collect();
Some(shrunken_vector)
}
fn stretch<U>(&self, scalar: U) -> Option<Vec<f64>>
where
U: Into<f64> + PartialOrd + Copy,
{
let scalar: f64 = scalar.into();
if (0.0..1.0).contains(&scalar) {
return None;
}
let stretched_vector = self.iter().map(|x| x.as_f64() * scalar).collect();
Some(stretched_vector)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_dot_product_integers() {
let vec1 = vec![1, 2, 3];
let vec2 = vec![4, 5, 6];
let result = vec1.dot_product(&vec2).unwrap();
assert_eq!(result, 32);
}
#[test]
fn test_dot_product_floats() {
let vec1 = vec![1.0, 2.0, 3.0];
let vec2 = vec![4.0, 5.0, 6.0];
let result = vec1.dot_product(&vec2).unwrap();
assert!(((result - 32.0) as f32).abs() < f32::EPSILON);
}
#[test]
fn test_dot_product_empty_vectors() {
let vec1: Vec<i32> = vec![];
let vec2: Vec<i32> = vec![];
let result = vec1.dot_product(&vec2).unwrap();
assert_eq!(result, 0);
}
#[test]
fn test_dot_product_mismatched_lengths() {
let vec1 = vec![1, 2];
let vec2 = vec![3, 4, 5];
let result = vec1.dot_product(&vec2);
assert_eq!(result, None);
}
#[test]
fn test_dot_product_single_element() {
let vec1 = vec![7];
let vec2 = vec![3];
let result = vec1.dot_product(&vec2).unwrap();
assert_eq!(result, 21);
}
#[test]
fn test_dot_product_with_zeros() {
let vec1 = vec![0, 0, 0];
let vec2 = vec![1, 2, 3];
let result = vec1.dot_product(&vec2).unwrap();
assert_eq!(result, 0);
}
#[test]
fn test_dot_product_negative_numbers() {
let vec1 = vec![-1, -2, -3];
let vec2 = vec![4, 5, 6];
let result = vec1.dot_product(&vec2).unwrap();
assert_eq!(result, -32);
}
#[test]
fn test_squared_norm_empty() {
let v: Vec<i32> = vec![];
assert_eq!(v.squared_norm(), 0);
}
#[test]
fn test_squared_norm_single_element() {
let v = vec![5];
assert_eq!(v.squared_norm(), 25);
}
#[test]
fn test_squared_norm_multiple_integers() {
let v = vec![2, -3, 4];
assert_eq!(v.squared_norm(), 29);
}
#[test]
fn test_squared_norm_with_negatives() {
let v = vec![-1, -2, -3];
assert_eq!(v.squared_norm(), 14);
}
#[test]
fn test_squared_norm_large_values() {
let v = vec![1000, 2000, 3000];
assert_eq!(v.squared_norm(), 14_000_000);
}
#[test]
fn test_squared_norm_floats() {
let v = vec![1.0_f64, 2.5_f64, 3.2_f64];
let result = v.squared_norm();
let expected = 17.49;
let epsilon = 1e-10;
assert!((result - expected).abs() < epsilon);
}
#[test]
fn test_shrink_invalid_scalar() {
let v = vec![1_i32, 2, 3];
assert_eq!(v.shrink(1.0), None);
assert_eq!(v.shrink(-0.1), None);
}
#[test]
fn test_shrink_i8() {
let v: Vec<i8> = vec![10, 20, 30];
let scalar = 0.5;
let result = v.shrink(scalar).expect("Expected valid shrink result");
assert_eq!(result, vec![5.0, 10.0, 15.0]);
}
#[test]
fn test_shrink_i32() {
let v: Vec<i32> = vec![1000, 2000, 3000];
let scalar = 0.2;
let result = v.shrink(scalar).expect("Expected valid shrink result");
assert_eq!(result, vec![200.0, 400.0, 600.0]);
}
#[test]
fn test_shrink_i64() {
let v: Vec<i64> = vec![1000, 2000, 3000];
let scalar = 0.1;
let result = v.shrink(scalar).expect("Expected valid shrink result");
assert_eq!(result, vec![100.0, 200.0, 300.0]);
}
#[test]
fn test_shrink_u64() {
let v: Vec<u64> = vec![1000, 2000, 3000];
let scalar = 0.1;
let result = v.shrink(scalar).expect("Expected valid shrink result");
assert_eq!(result, vec![100.0, 200.0, 300.0]);
}
#[test]
fn test_shrink_f32() {
let v: Vec<f32> = vec![10.0, 20.0, 30.0];
let scalar = 0.5;
let result = v.shrink(scalar).expect("Expected valid shrink result");
assert_eq!(result, vec![5.0, 10.0, 15.0]);
}
#[test]
fn test_shrink_f64() {
let v: Vec<f64> = vec![10.0, 20.0, 30.0];
let scalar = 0.5;
let result = v.shrink(scalar).expect("Expected valid shrink result");
assert_eq!(result, vec![5.0, 10.0, 15.0]);
}
#[test]
fn test_stretch_with_scalar_in_range_returns_none() {
let vec: Vec<f64> = vec![1.0, 2.0, 3.0];
assert_eq!(vec.stretch(0.5), None);
}
#[test]
fn test_stretch_with_scalar_equal_to_one_returns_same_vector() {
let vec: Vec<f64> = vec![1.0, 2.0, 3.0];
let expected: Vec<f64> = vec![1.0, 2.0, 3.0];
assert_eq!(vec.stretch(1.0), Some(expected));
}
#[test]
fn test_stretch_with_scalar_greater_than_one() {
let vec: Vec<f64> = vec![1.0, 2.0, 3.0];
let scalar = 2.0;
let expected: Vec<f64> = vec![2.0, 4.0, 6.0];
assert_eq!(vec.stretch(scalar), Some(expected));
}
#[test]
fn test_stretch_with_integer_scalar() {
let vec: Vec<f64> = vec![1.0, 2.0, 3.0];
let scalar = 3;
let expected: Vec<f64> = vec![3.0, 6.0, 9.0];
assert_eq!(vec.stretch(scalar), Some(expected));
}
#[test]
fn test_stretch_with_negative_scalar() {
let vec: Vec<f64> = vec![1.0, 2.0, 3.0];
let scalar = -2.0;
let expected: Vec<f64> = vec![-2.0, -4.0, -6.0];
assert_eq!(vec.stretch(scalar), Some(expected));
}
}