From f5544666f597463605307aad18822a10e87a6bda Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Fri, 9 May 2025 22:30:33 -0400 Subject: [PATCH 01/82] initial FlexVector implementation --- src/lib.rs | 2 + src/{types => }/macros.rs | 193 ++++ src/types/flexvector.rs | 1933 +++++++++++++++++++++++++++++++++++++ src/types/mod.rs | 6 +- src/types/traits.rs | 342 +++++++ src/types/vector.rs | 86 +- 6 files changed, 2559 insertions(+), 3 deletions(-) rename src/{types => }/macros.rs (75%) create mode 100644 src/types/flexvector.rs create mode 100644 src/types/traits.rs diff --git a/src/lib.rs b/src/lib.rs index f92d617..aa117b0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -893,6 +893,8 @@ #![cfg_attr(docsrs, feature(doc_cfg))] pub mod errors; +pub mod macros; pub mod types; +pub use types::flexvector::FlexVector; pub use types::vector::Vector; diff --git a/src/types/macros.rs b/src/macros.rs similarity index 75% rename from src/types/macros.rs rename to src/macros.rs index 127a524..d35ef1c 100644 --- a/src/types/macros.rs +++ b/src/macros.rs @@ -175,6 +175,199 @@ macro_rules! try_vector { }; } +#[macro_export] +macro_rules! impl_vector_unary_op { + ($VectorType:ident, $trait:ident, $method:ident, $op:tt) => { + impl std::ops::$trait for $VectorType + where + T: num::Num + Clone + Sync + Send + std::ops::Neg, + { + type Output = Self; + fn $method(self) -> Self { + let components = self.components.into_iter().map(|a| $op a).collect(); + Self { components } + } + } + }; +} + +#[macro_export] +macro_rules! impl_vector_binop { + ($VectorType:ident, $trait:ident, $method:ident, $op:tt) => { + impl std::ops::$trait for $VectorType + where + T: num::Num + Clone + Sync + Send, + { + type Output = Self; + fn $method(self, rhs: Self) -> Self { + assert_eq!(self.len(), rhs.len()); + let components = self.components.into_iter() + .zip(rhs.components) + .map(|(a, b)| a $op b) + .collect(); + Self { components } + } + } + }; +} + +#[macro_export] +macro_rules! impl_vector_binop_assign { + ($VectorType:ident, $trait:ident, $method:ident, $op:tt) => { + impl std::ops::$trait for $VectorType + where + T: num::Num + Clone + Sync + Send, + { + fn $method(&mut self, rhs: Self) { + assert_eq!(self.len(), rhs.len()); + for (a, b) in self.components.iter_mut().zip(rhs.components) { + *a = a.clone() $op b; + } + } + } + }; +} + +#[macro_export] +macro_rules! impl_vector_scalar_op { + ($VectorType:ident, $trait:ident, $method:ident, $op:tt) => { + impl std::ops::$trait for $VectorType + where + T: num::Num + Clone + Sync + Send, + { + type Output = Self; + fn $method(self, rhs: T) -> Self { + let components = self.components.into_iter().map(|a| a $op rhs.clone()).collect(); + Self { components } + } + } + }; +} + +#[macro_export] +macro_rules! impl_vector_scalar_op_assign { + ($VectorType:ident, $trait:ident, $method:ident, $op:tt) => { + impl std::ops::$trait for $VectorType + where + T: num::Num + Clone + Sync + Send, + { + fn $method(&mut self, rhs: T) { + for a in &mut self.components { + *a = a.clone() $op rhs.clone(); + } + } + } + }; +} + +#[macro_export] +macro_rules! impl_vector_scalar_div_op { + ($VectorType:ident) => { + // For f32 + impl std::ops::Div for $VectorType { + type Output = Self; + fn div(self, rhs: f32) -> Self { + let components = self.components.into_iter().map(|a| a / rhs).collect(); + Self { components } + } + } + // For f64 + impl std::ops::Div for $VectorType { + type Output = Self; + fn div(self, rhs: f64) -> Self { + let components = self.components.into_iter().map(|a| a / rhs).collect(); + Self { components } + } + } + // For Complex / f32 + impl std::ops::Div for $VectorType> { + type Output = Self; + fn div(self, rhs: f32) -> Self { + let components = self.components.into_iter().map(|a| a / rhs).collect(); + Self { components } + } + } + // For Complex / f64 + impl std::ops::Div for $VectorType> { + type Output = Self; + fn div(self, rhs: f64) -> Self { + let components = self.components.into_iter().map(|a| a / rhs).collect(); + Self { components } + } + } + // For Complex / Complex + impl std::ops::Div> for $VectorType> { + type Output = Self; + fn div(self, rhs: num::Complex) -> Self { + let components = self.components.into_iter().map(|a| a / rhs).collect(); + Self { components } + } + } + // For Complex / Complex + impl std::ops::Div> for $VectorType> { + type Output = Self; + fn div(self, rhs: num::Complex) -> Self { + let components = self.components.into_iter().map(|a| a / rhs).collect(); + Self { components } + } + } + }; +} + +#[macro_export] +macro_rules! impl_vector_scalar_div_op_assign { + ($VectorType:ident) => { + // For f32 + impl std::ops::DivAssign for $VectorType { + fn div_assign(&mut self, rhs: f32) { + for a in &mut self.components { + *a = *a / rhs; + } + } + } + // For f64 + impl std::ops::DivAssign for $VectorType { + fn div_assign(&mut self, rhs: f64) { + for a in &mut self.components { + *a = *a / rhs; + } + } + } + // For Complex / f32 + impl std::ops::DivAssign for $VectorType> { + fn div_assign(&mut self, rhs: f32) { + for a in &mut self.components { + *a = *a / rhs; + } + } + } + // For Complex / f64 + impl std::ops::DivAssign for $VectorType> { + fn div_assign(&mut self, rhs: f64) { + for a in &mut self.components { + *a = *a / rhs; + } + } + } + // For Complex / Complex + impl std::ops::DivAssign> for $VectorType> { + fn div_assign(&mut self, rhs: num::Complex) { + for a in &mut self.components { + *a = *a / rhs; + } + } + } + // For Complex / Complex + impl std::ops::DivAssign> for $VectorType> { + fn div_assign(&mut self, rhs: num::Complex) { + for a in &mut self.components { + *a = *a / rhs; + } + } + } + }; +} + #[cfg(test)] mod tests { use crate::Vector; diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs new file mode 100644 index 0000000..e290e3b --- /dev/null +++ b/src/types/flexvector.rs @@ -0,0 +1,1933 @@ +//! FlexVector type. + +use crate::{ + impl_vector_binop, impl_vector_binop_assign, impl_vector_scalar_div_op, + impl_vector_scalar_div_op_assign, impl_vector_scalar_op, impl_vector_scalar_op_assign, + impl_vector_unary_op, types::traits::VectorBase, types::traits::VectorOps, + types::traits::VectorOpsComplexFloat, types::traits::VectorOpsFloat, +}; + +use num::Complex; +use num::Num; + +/// A dynamic, heap-allocated vector type for n-dimensional real and complex scalar data. +/// +/// The length of the vector is determined at runtime and stored on the heap. +/// This type is analogous to `Vector` but supports dynamic sizing. +#[derive(Clone, Debug)] +pub struct FlexVector +where + T: Num + Clone + Sync + Send, +{ + /// Ordered n-dimensional scalar values. + pub components: Vec, +} + +// ================================ +// +// Constructors +// +// ================================ +impl FlexVector +where + T: Num + Clone + Default + Sync + Send, +{ + /// Creates a new, empty FlexVector. + pub fn new() -> Self { + Self { components: Vec::new() } + } + + /// Creates a new FlexVector with a pre-allocated capacity. + pub fn with_capacity(capacity: usize) -> Self { + Self { components: Vec::with_capacity(capacity) } + } + + /// Returns a new FlexVector of the given length, filled with zeros. + pub fn zero(len: usize) -> Self + where + T: num::Zero, + { + Self { components: vec![T::zero(); len] } + } + + /// Returns a new FlexVector of the given length, filled with ones. + pub fn one(len: usize) -> Self + where + T: num::One, + { + Self { components: vec![T::one(); len] } + } + + /// Returns a new FlexVector of the given length, filled with the given value. + pub fn filled(len: usize, value: T) -> Self { + Self { components: vec![value; len] } + } + + /// Creates a new FlexVector from a slice. + pub fn from_slice(slice: &[T]) -> Self { + Self { components: slice.to_vec() } + } + + /// Creates a FlexVector from a Vec. + pub fn from_vec(vec: Vec) -> Self { + Self { components: vec } + } +} + +// ================================ +// +// Default trait impl +// +// ================================ +impl Default for FlexVector +where + T: num::Num + Clone + Default + Sync + Send, +{ + fn default() -> Self { + Self::new() + } +} + +// ================================ +// +// Display trait impl +// +// ================================ +impl std::fmt::Display for FlexVector +where + T: num::Num + Clone + Sync + Send + std::fmt::Debug, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{:?}", self.components) + } +} + +// ================================ +// +// VectorBase trait impl +// +// ================================ +impl VectorBase for FlexVector +where + T: num::Num + Clone + Sync + Send, +{ + /// Returns an immutable slice of the FlexVector's components. + fn as_slice(&self) -> &[T] { + &self.components + } + + /// Returns a mutable slice of the FlexVector's components. + fn as_mut_slice(&mut self) -> &mut [T] { + &mut self.components[..] + } +} + +// ================================ +// +// VectorOps trait impl +// +// ================================ +// TODO: add tests +impl VectorOps for FlexVector +where + T: num::Num + Clone + Sync + Send, +{ + type Output = Vec; + + fn translate(&self, other: &Self) -> Self::Output + where + T: num::Num + Clone, + { + self.components.iter().zip(&other.components).map(|(a, b)| a.clone() + b.clone()).collect() + } +} + +// ================================ +// +// VectorOpsFloat trait impl +// +// ================================ +// TODO: add tests +impl VectorOpsFloat for FlexVector +where + T: num::Float + Clone + PartialOrd + std::iter::Sum + Sync + Send, +{ + type Output = Vec; + + fn normalize(&self) -> Self::Output { + let n = self.norm(); + assert!(n != T::zero(), "Cannot normalize a zero vector"); + self.components.iter().map(|a| *a / n).collect() + } + + fn mut_normalize(&mut self) + where + T: num::Float + Copy + std::iter::Sum, + { + let n = self.norm(); + assert!(n != T::zero(), "Cannot normalize a zero vector"); + for a in self.as_mut_slice().iter_mut() { + *a = *a / n; + } + } + + fn lerp(&self, end: &Self, weight: T) -> Self::Output + where + T: num::Float + Clone + PartialOrd, + { + assert!(weight >= T::zero() && weight <= T::one(), "weight must be in [0, 1]"); + let w = weight.clone(); + let one_minus_w = T::one() - w.clone(); + self.components + .iter() + .zip(&end.components) + .map(|(a, b)| one_minus_w.clone() * a.clone() + w.clone() * b.clone()) + .collect() + } +} + +// ================================ +// +// VectorOpsComplexFloat trait impl +// +// ================================ +// TODO: add tests +impl VectorOpsComplexFloat for FlexVector> +where + F: num::Float + Clone + PartialOrd + std::iter::Sum + Sync + Send, +{ + type Output = Vec>; + + fn normalize(&self) -> Self::Output + where + F: num::Float + Clone + std::iter::Sum, + Self::Output: std::iter::FromIterator>, + { + let n = self.norm(); + assert!(n != F::zero(), "Cannot normalize a zero vector"); + self.components.iter().map(|a| *a / n).collect() + } + + fn mut_normalize(&mut self) + where + F: num::Float + Copy + std::iter::Sum, + { + let n = self.norm(); + assert!(n != F::zero(), "Cannot normalize a zero vector"); + for a in self.as_mut_slice().iter_mut() { + *a = *a / n; + } + } + + fn lerp(&self, end: &Self, weight: F) -> Self::Output + where + F: num::Float + Clone + PartialOrd, + { + assert!(weight >= F::zero() && weight <= F::one(), "weight must be in [0, 1]"); + let w = Complex::new(weight.clone(), F::zero()); + let one_minus_w = Complex::new(F::one() - weight.clone(), F::zero()); + self.components + .iter() + .zip(&end.components) + .map(|(a, b)| one_minus_w.clone() * a.clone() + w.clone() * b.clone()) + .collect() + } +} + +// ================================ +// +// Methods +// +// ================================ +impl FlexVector +where + T: num::Num + Clone + Sync + Send, +{ + /// Adds an element to the end of the vector. + pub fn push(&mut self, value: T) { + self.components.push(value); + } + + /// Removes the last element and returns it, or None if empty. + pub fn pop(&mut self) -> Option { + self.components.pop() + } + + /// Inserts an element at position index, shifting all elements after it. + pub fn insert(&mut self, index: usize, value: T) { + self.components.insert(index, value); + } + + /// Removes and returns the element at position index. + pub fn remove(&mut self, index: usize) -> T { + self.components.remove(index) + } + + /// Resizes the vector in-place. + pub fn resize(&mut self, new_len: usize, value: T) { + self.components.resize(new_len, value); + } + + /// Clears the vector, removing all values. + pub fn clear(&mut self) { + self.components.clear(); + } + + /// Returns a mutable reference to a FlexVector index value or range, + /// or `None` if the index is out of bounds. + pub fn get_mut(&mut self, index: I) -> Option<&mut I::Output> + where + I: std::slice::SliceIndex<[T]>, + { + self.components.get_mut(index) + } + + /// Returns an iterator over mutable references to the elements. + pub fn iter_mut(&mut self) -> impl Iterator { + self.components.iter_mut() + } + + /// Returns a new FlexVector with each element mapped to a new value using the provided closure or function. + pub fn map(&self, mut f: F) -> FlexVector + where + F: FnMut(T) -> U, + U: num::Num + Clone + Sync + Send, + { + let new_components = self.components.iter().cloned().map(&mut f).collect(); + FlexVector { components: new_components } + } + + /// Applies a closure or function to each element, modifying them in place. + pub fn mut_map(&mut self, mut f: F) + where + F: FnMut(T) -> T, + { + for x in self.components.iter_mut() { + // Use clone since T may not be Copy + *x = f(x.clone()); + } + } +} + +// ================================ +// +// Operator overload trait impl +// +// ================================ +impl_vector_unary_op!(FlexVector, Neg, neg, -); + +impl_vector_binop!(FlexVector, Add, add, +); +impl_vector_binop!(FlexVector, Sub, sub, -); +impl_vector_binop!(FlexVector, Mul, mul, *); + +impl_vector_binop_assign!(FlexVector, AddAssign, add_assign, +); +impl_vector_binop_assign!(FlexVector, SubAssign, sub_assign, -); +impl_vector_binop_assign!(FlexVector, MulAssign, mul_assign, *); + +impl_vector_scalar_op!(FlexVector, Mul, mul, *); +impl_vector_scalar_op_assign!(FlexVector, MulAssign, mul_assign, *); + +impl_vector_scalar_div_op!(FlexVector); +impl_vector_scalar_div_op_assign!(FlexVector); + +#[cfg(test)] +mod tests { + use super::*; + use num::Complex; + + // ================================ + // + // Constructor tests + // + // ================================ + #[test] + fn test_new() { + let v = FlexVector::::new(); + assert_eq!(v.len(), 0); + assert!(v.is_empty()); + } + + #[test] + fn test_with_capacity() { + let v = FlexVector::::with_capacity(10); + assert_eq!(v.len(), 0); + assert!(v.components.capacity() >= 10); + } + + #[test] + fn test_with_capacity_large() { + let v = FlexVector::::with_capacity(1000); + assert_eq!(v.len(), 0); + assert!(v.components.capacity() >= 1000); + } + + #[test] + fn test_zero() { + let v = FlexVector::::zero(5); + assert_eq!(v.len(), 5); + assert!(v.iter().all(|&x| x == 0)); + } + + #[test] + fn test_zero_f64() { + let v = FlexVector::::zero(3); + assert_eq!(v.len(), 3); + assert!(v.iter().all(|&x| x == 0.0)); + } + + #[test] + fn test_zero_complex() { + let v = FlexVector::>::zero(2); + assert_eq!(v.len(), 2); + assert!(v.iter().all(|x| x.re == 0.0 && x.im == 0.0)); + } + + #[test] + fn test_one() { + let v = FlexVector::::one(4); + assert_eq!(v.len(), 4); + assert!(v.iter().all(|&x| x == 1)); + } + + #[test] + fn test_one_f64() { + let v = FlexVector::::one(2); + assert_eq!(v.len(), 2); + assert!(v.iter().all(|&x| x == 1.0)); + } + + #[test] + fn test_one_complex() { + let v = FlexVector::>::one(2); + assert_eq!(v.len(), 2); + assert!(v.iter().all(|x| x.re == 1.0 && x.im == 0.0)); + } + + #[test] + fn test_filled() { + let v = FlexVector::::filled(3, 42); + assert_eq!(v.len(), 3); + assert!(v.iter().all(|&x| x == 42)); + } + + #[test] + fn test_filled_f64() { + let v = FlexVector::::filled(3, -1.5); + assert_eq!(v.len(), 3); + assert!(v.iter().all(|&x| x == -1.5)); + } + + #[test] + fn test_filled_f32_fractional() { + let v = FlexVector::::filled(2, 2.25); + assert_eq!(v.len(), 2); + assert!(v.iter().all(|&x| x == 2.25)); + } + + #[test] + fn test_filled_nan() { + let v = FlexVector::::filled(2, f64::NAN); + assert_eq!(v.len(), 2); + assert!(v.iter().all(|&x| x.is_nan())); + } + + #[test] + fn test_filled_infinity() { + let v = FlexVector::::filled(2, f64::INFINITY); + assert_eq!(v.len(), 2); + assert!(v.iter().all(|&x| x.is_infinite() && x.is_sign_positive())); + } + + #[test] + fn test_filled_complex() { + let value = Complex::new(2.0, 3.0); + let v = FlexVector::>::filled(2, value); + assert_eq!(v.len(), 2); + assert!(v.iter().all(|x| *x == value)); + } + + #[test] + fn test_from_slice() { + let data = [1, 2, 3, 4]; + let v = FlexVector::::from_slice(&data); + assert_eq!(v.len(), 4); + assert_eq!(v.as_slice(), &data); + } + + #[test] + fn test_from_slice_immutability() { + let mut data = [1, 2, 3]; + let v = FlexVector::::from_slice(&data); + data[0] = 99; + assert_eq!(v.as_slice()[0], 1); + } + + #[test] + fn test_from_vec() { + let data = vec![5, 6, 7]; + let v = FlexVector::::from_vec(data.clone()); + assert_eq!(v.len(), 3); + assert_eq!(v.as_slice(), &data[..]); + } + + #[test] + fn test_zero_length_constructors() { + assert_eq!(FlexVector::::zero(0).len(), 0); + assert_eq!(FlexVector::::one(0).len(), 0); + assert_eq!(FlexVector::::filled(0, 123).len(), 0); + assert_eq!(FlexVector::::from_slice(&[]).len(), 0); + assert_eq!(FlexVector::::from_vec(vec![]).len(), 0); + } + + // ================================ + // + // Default trait tests + // + // ================================ + #[test] + fn test_default_i32() { + let v = FlexVector::::default(); + assert_eq!(v.len(), 0); + assert!(v.is_empty()); + } + + #[test] + fn test_default_f64() { + let v = FlexVector::::default(); + assert_eq!(v.len(), 0); + assert!(v.is_empty()); + } + + #[test] + fn test_default_complex() { + use num::Complex; + let v = FlexVector::>::default(); + assert_eq!(v.len(), 0); + assert!(v.is_empty()); + } + + // ================================ + // + // Display trait tests + // + // ================================ + #[test] + fn test_display_i32() { + let v = FlexVector::from_vec(vec![1, 2, 3]); + assert_eq!(format!("{}", v), "[1, 2, 3]"); + } + + #[test] + fn test_display_f64() { + let v = FlexVector::from_vec(vec![-1.0, 2.0, 3.0]); + assert_eq!(format!("{}", v), "[-1.0, 2.0, 3.0]"); + } + + #[test] + fn test_display_complex() { + use num::Complex; + let v = FlexVector::from_vec(vec![Complex::new(0, -1), Complex::new(3, 4)]); + assert_eq!(format!("{}", v), "[Complex { re: 0, im: -1 }, Complex { re: 3, im: 4 }]"); + } + + #[test] + fn test_display_empty() { + let v = FlexVector::::new(); + assert_eq!(format!("{}", v), "[]"); + } + + // ================================ + // + // VectorBase trait method tests + // + // ================================ + // --- as_slice --- + #[test] + fn test_as_slice() { + let v = FlexVector::from_vec(vec![1, 2, 3]); + let slice = v.as_slice(); + assert_eq!(slice, &[1, 2, 3]); + } + + #[test] + fn test_as_slice_f64() { + let v = FlexVector::from_vec(vec![1.1, 2.2, 3.3]); + let slice = v.as_slice(); + assert_eq!(slice, &[1.1, 2.2, 3.3]); + } + + #[test] + fn test_as_slice_complex() { + let v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let slice = v.as_slice(); + assert_eq!(slice, &[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0),]); + } + + // --- len --- + #[test] + fn test_len() { + let v = FlexVector::from_vec(vec![10, 20, 30, 40]); + assert_eq!(v.len(), 4); + let empty = FlexVector::::new(); + assert_eq!(empty.len(), 0); + } + + #[test] + fn test_len_f64() { + let v = FlexVector::from_vec(vec![10.0, 20.0]); + assert_eq!(v.len(), 2); + let empty = FlexVector::::new(); + assert_eq!(empty.len(), 0); + } + + #[test] + fn test_len_complex() { + let v = FlexVector::from_vec(vec![Complex::new(1.0, 0.0)]); + assert_eq!(v.len(), 1); + let empty = FlexVector::>::new(); + assert_eq!(empty.len(), 0); + } + + // --- is_empty --- + #[test] + fn test_is_empty() { + let v = FlexVector::from_vec(vec![1]); + assert!(!v.is_empty()); + let empty = FlexVector::::new(); + assert!(empty.is_empty()); + } + + #[test] + fn test_is_empty_f64() { + let v = FlexVector::from_vec(vec![1.0]); + assert!(!v.is_empty()); + let empty = FlexVector::::new(); + assert!(empty.is_empty()); + } + + #[test] + fn test_is_empty_complex() { + let v = FlexVector::from_vec(vec![Complex::new(0.0, 1.0)]); + assert!(!v.is_empty()); + let empty = FlexVector::>::new(); + assert!(empty.is_empty()); + } + + // --- get --- + #[test] + fn test_get() { + let v = FlexVector::from_vec(vec![10, 20, 30]); + assert_eq!(v.get(0), Some(&10)); + assert_eq!(v.get(2), Some(&30)); + assert_eq!(v.get(3), None); + } + + #[test] + fn test_get_f64() { + let v = FlexVector::from_vec(vec![10.5, 20.5, 30.5]); + assert_eq!(v.get(0), Some(&10.5)); + assert_eq!(v.get(2), Some(&30.5)); + assert_eq!(v.get(3), None); + } + + #[test] + fn test_get_complex() { + let v = FlexVector::from_vec(vec![Complex::new(1.0, 1.0), Complex::new(2.0, 2.0)]); + assert_eq!(v.get(0), Some(&Complex::new(1.0, 1.0))); + assert_eq!(v.get(1), Some(&Complex::new(2.0, 2.0))); + assert_eq!(v.get(2), None); + } + + // --- first --- + #[test] + fn test_first() { + let v = FlexVector::from_vec(vec![5, 6, 7]); + assert_eq!(v.first(), Some(&5)); + let empty = FlexVector::::new(); + assert_eq!(empty.first(), None); + } + + #[test] + fn test_first_f64() { + let v = FlexVector::from_vec(vec![5.5, 6.5]); + assert_eq!(v.first(), Some(&5.5)); + let empty = FlexVector::::new(); + assert_eq!(empty.first(), None); + } + + #[test] + fn test_first_complex() { + let v = FlexVector::from_vec(vec![Complex::new(7.0, 8.0), Complex::new(9.0, 10.0)]); + assert_eq!(v.first(), Some(&Complex::new(7.0, 8.0))); + let empty = FlexVector::>::new(); + assert_eq!(empty.first(), None); + } + + // --- last --- + #[test] + fn test_last() { + let v = FlexVector::from_vec(vec![1, 2, 3]); + assert_eq!(v.last(), Some(&3)); + let empty = FlexVector::::new(); + assert_eq!(empty.last(), None); + } + + #[test] + fn test_last_f64() { + let v = FlexVector::from_vec(vec![1.5, 2.5, 3.5]); + assert_eq!(v.last(), Some(&3.5)); + let empty = FlexVector::::new(); + assert_eq!(empty.last(), None); + } + + #[test] + fn test_last_complex() { + let v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + assert_eq!(v.last(), Some(&Complex::new(3.0, 4.0))); + let empty = FlexVector::>::new(); + assert_eq!(empty.last(), None); + } + + // --- iter --- + #[test] + fn test_iter() { + let v = FlexVector::from_vec(vec![1, 2, 3]); + let mut iter = v.iter(); + assert_eq!(iter.next(), Some(&1)); + assert_eq!(iter.next(), Some(&2)); + assert_eq!(iter.next(), Some(&3)); + assert_eq!(iter.next(), None); + } + + #[test] + fn test_iter_f64() { + let v = FlexVector::from_vec(vec![1.1, 2.2, 3.3]); + let collected: Vec<_> = v.iter().copied().collect(); + assert_eq!(collected, vec![1.1, 2.2, 3.3]); + } + + #[test] + fn test_iter_complex() { + let v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let collected: Vec<_> = v.iter().cloned().collect(); + assert_eq!(collected, vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0),]); + } + + // --- iter_rev --- + #[test] + fn test_iter_rev() { + let v = FlexVector::from_vec(vec![1, 2, 3]); + let collected: Vec<_> = v.iter_rev().copied().collect(); + assert_eq!(collected, vec![3, 2, 1]); + } + + #[test] + fn test_iter_rev_f64() { + let v = FlexVector::from_vec(vec![1.1, 2.2, 3.3]); + let collected: Vec<_> = v.iter_rev().copied().collect(); + assert_eq!(collected, vec![3.3, 2.2, 1.1]); + } + + #[test] + fn test_iter_rev_complex() { + let v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let collected: Vec<_> = v.iter_rev().cloned().collect(); + assert_eq!(collected, vec![Complex::new(3.0, 4.0), Complex::new(1.0, 2.0),]); + } + + // --- enumerate --- + #[test] + fn test_enumerate() { + let v = FlexVector::from_vec(vec![10, 20, 30]); + let pairs: Vec<_> = v.enumerate().collect(); + assert_eq!(pairs, vec![(0, &10), (1, &20), (2, &30)]); + } + + #[test] + fn test_enumerate_f64() { + let v = FlexVector::from_vec(vec![1.5, 2.5]); + let pairs: Vec<_> = v.enumerate().collect(); + assert_eq!(pairs, vec![(0, &1.5), (1, &2.5)]); + } + + #[test] + fn test_enumerate_complex() { + let v = FlexVector::from_vec(vec![Complex::new(0.0, 1.0), Complex::new(2.0, 3.0)]); + let pairs: Vec<_> = v.enumerate().collect(); + assert_eq!(pairs, vec![(0, &Complex::new(0.0, 1.0)), (1, &Complex::new(2.0, 3.0)),]); + } + + // --- to_vec --- + #[test] + fn test_to_vec() { + let v = FlexVector::from_vec(vec![1, 2, 3]); + let vec_copy = v.to_vec(); + assert_eq!(vec_copy, vec![1, 2, 3]); + } + + #[test] + fn test_to_vec_f64() { + let v = FlexVector::from_vec(vec![1.5, 2.5]); + let vec_copy = v.to_vec(); + assert_eq!(vec_copy, vec![1.5, 2.5]); + } + + #[test] + fn test_to_vec_complex() { + let v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let vec_copy = v.to_vec(); + assert_eq!(vec_copy, vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0),]); + } + + // --- pretty --- + #[test] + fn test_pretty() { + let v = FlexVector::from_vec(vec![1, 2, 3]); + let pretty = v.pretty(); + assert!(pretty.contains("1")); + assert!(pretty.contains("2")); + assert!(pretty.contains("3")); + assert!(pretty.starts_with("[")); + } + + #[test] + fn test_pretty_f64() { + let v = FlexVector::from_vec(vec![1.5, 2.5]); + let pretty = v.pretty(); + assert!(pretty.contains("1.5")); + assert!(pretty.contains("2.5")); + assert!(pretty.starts_with("[")); + } + + #[test] + fn test_pretty_complex() { + let v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let pretty = v.pretty(); + assert!(pretty.contains("1.0")); + assert!(pretty.contains("2.0")); + assert!(pretty.contains("3.0")); + assert!(pretty.contains("4.0")); + assert!(pretty.starts_with("[")); + } + + // --- contains --- + #[test] + fn test_contains() { + let v = FlexVector::from_vec(vec![1, 2, 3]); + assert!(v.contains(&2)); + assert!(!v.contains(&4)); + } + + #[test] + fn test_contains_f64() { + let v = FlexVector::from_vec(vec![1.1, 2.2, 3.3]); + assert!(v.contains(&2.2)); + assert!(!v.contains(&4.4)); + } + + #[test] + fn test_contains_complex() { + let v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + assert!(v.contains(&Complex::new(1.0, 2.0))); + assert!(!v.contains(&Complex::new(0.0, 0.0))); + } + + // --- starts_with --- + #[test] + fn test_starts_with() { + let v = FlexVector::from_vec(vec![1, 2, 3]); + assert!(v.starts_with(&[1, 2])); + assert!(!v.starts_with(&[2, 3])); + } + + #[test] + fn test_starts_with_f64() { + let v = FlexVector::from_vec(vec![1.1, 2.2, 3.3]); + assert!(v.starts_with(&[1.1, 2.2])); + assert!(!v.starts_with(&[2.2, 3.3])); + } + + #[test] + fn test_starts_with_complex() { + let v = FlexVector::from_vec(vec![ + Complex::new(1.0, 2.0), + Complex::new(3.0, 4.0), + Complex::new(5.0, 6.0), + ]); + assert!(v.starts_with(&[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)])); + assert!(!v.starts_with(&[Complex::new(3.0, 4.0)])); + } + + // --- ends_with --- + #[test] + fn test_ends_with() { + let v = FlexVector::from_vec(vec![1, 2, 3]); + assert!(v.ends_with(&[2, 3])); + assert!(!v.ends_with(&[1, 2])); + } + + #[test] + fn test_ends_with_f64() { + let v = FlexVector::from_vec(vec![1.1, 2.2, 3.3]); + assert!(v.ends_with(&[2.2, 3.3])); + assert!(!v.ends_with(&[1.1, 2.2])); + } + + #[test] + fn test_ends_with_complex() { + let v = FlexVector::from_vec(vec![ + Complex::new(1.0, 2.0), + Complex::new(3.0, 4.0), + Complex::new(5.0, 6.0), + ]); + assert!(v.ends_with(&[Complex::new(3.0, 4.0), Complex::new(5.0, 6.0)])); + assert!(!v.ends_with(&[Complex::new(1.0, 2.0)])); + } + + // --- position --- + #[test] + fn test_position() { + let v = FlexVector::from_vec(vec![10, 20, 30]); + assert_eq!(v.position(|&x| x == 20), Some(1)); + assert_eq!(v.position(|&x| x == 99), None); + } + + #[test] + fn test_position_f64() { + let v = FlexVector::from_vec(vec![10.0, 20.0, 30.0]); + assert_eq!(v.position(|&x| x == 20.0), Some(1)); + assert_eq!(v.position(|&x| x == 99.0), None); + } + + #[test] + fn test_position_complex() { + let v = FlexVector::from_vec(vec![ + Complex::new(1.0, 1.0), + Complex::new(2.0, 2.0), + Complex::new(3.0, 3.0), + ]); + assert_eq!(v.position(|x| *x == Complex::new(2.0, 2.0)), Some(1)); + assert_eq!(v.position(|x| *x == Complex::new(9.0, 9.0)), None); + } + + // --- rposition --- + #[test] + fn test_rposition() { + let v = FlexVector::from_vec(vec![1, 2, 3, 2]); + assert_eq!(v.rposition(|&x| x == 2), Some(3)); + assert_eq!(v.rposition(|&x| x == 99), None); + } + + #[test] + fn test_rposition_f64() { + let v = FlexVector::from_vec(vec![1.0, 2.0, 3.0, 2.0]); + assert_eq!(v.rposition(|&x| x == 2.0), Some(3)); + assert_eq!(v.rposition(|&x| x == 99.0), None); + } + + #[test] + fn test_rposition_complex() { + let v = FlexVector::from_vec(vec![ + Complex::new(1.0, 1.0), + Complex::new(2.0, 2.0), + Complex::new(3.0, 3.0), + Complex::new(2.0, 2.0), + ]); + assert_eq!(v.rposition(|x| *x == Complex::new(2.0, 2.0)), Some(3)); + assert_eq!(v.rposition(|x| *x == Complex::new(9.0, 9.0)), None); + } + + // --- windows --- + #[test] + fn test_windows() { + let v = FlexVector::from_vec(vec![1, 2, 3, 4]); + let windows: Vec<_> = v.windows(2).collect(); + assert_eq!(windows, vec![&[1, 2][..], &[2, 3][..], &[3, 4][..]]); + } + + #[test] + fn test_windows_f64() { + let v = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let windows: Vec<_> = v.windows(2).collect(); + assert_eq!(windows, vec![&[1.0, 2.0][..], &[2.0, 3.0][..]]); + } + + #[test] + fn test_windows_complex() { + let v = FlexVector::from_vec(vec![ + Complex::new(1.0, 0.0), + Complex::new(2.0, 0.0), + Complex::new(3.0, 0.0), + ]); + let windows: Vec<_> = v.windows(2).collect(); + assert_eq!( + windows, + vec![ + &[Complex::new(1.0, 0.0), Complex::new(2.0, 0.0)][..], + &[Complex::new(2.0, 0.0), Complex::new(3.0, 0.0)][..] + ] + ); + } + + // --- chunks --- + #[test] + fn test_chunks() { + let v = FlexVector::from_vec(vec![1, 2, 3, 4, 5]); + let chunks: Vec<_> = v.chunks(2).collect(); + assert_eq!(chunks, vec![&[1, 2][..], &[3, 4][..], &[5][..]]); + } + + #[test] + fn test_chunks_f64() { + let v = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let chunks: Vec<_> = v.chunks(2).collect(); + assert_eq!(chunks, vec![&[1.0, 2.0][..], &[3.0][..]]); + } + + #[test] + fn test_chunks_complex() { + let v = FlexVector::from_vec(vec![ + Complex::new(1.0, 0.0), + Complex::new(2.0, 0.0), + Complex::new(3.0, 0.0), + ]); + let chunks: Vec<_> = v.chunks(2).collect(); + assert_eq!( + chunks, + vec![ + &[Complex::new(1.0, 0.0), Complex::new(2.0, 0.0)][..], + &[Complex::new(3.0, 0.0)][..] + ] + ); + } + + // --- split_at --- + #[test] + fn test_split_at() { + let v = FlexVector::from_vec(vec![1, 2, 3, 4]); + let (left, right) = v.split_at(2); + assert_eq!(left, &[1, 2]); + assert_eq!(right, &[3, 4]); + } + + #[test] + fn test_split_at_f64() { + let v = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let (left, right) = v.split_at(1); + assert_eq!(left, &[1.0]); + assert_eq!(right, &[2.0, 3.0]); + } + + #[test] + fn test_split_at_complex() { + let v = FlexVector::from_vec(vec![Complex::new(1.0, 0.0), Complex::new(2.0, 0.0)]); + let (left, right) = v.split_at(1); + assert_eq!(left, &[Complex::new(1.0, 0.0)]); + assert_eq!(right, &[Complex::new(2.0, 0.0)]); + } + + // --- split --- + #[test] + fn test_split() { + let v = FlexVector::from_vec(vec![1, 2, 0, 3, 0, 4]); + let splits: Vec<_> = v.split(|&x| x == 0).collect(); + assert_eq!(splits, vec![&[1, 2][..], &[3][..], &[4][..]]); + } + + #[test] + fn test_split_f64() { + let v = FlexVector::from_vec(vec![1.0, 0.0, 2.0, 0.0, 3.0]); + let splits: Vec<_> = v.split(|&x| x == 0.0).collect(); + assert_eq!(splits, vec![&[1.0][..], &[2.0][..], &[3.0][..]]); + } + + #[test] + fn test_split_complex() { + let v = FlexVector::from_vec(vec![ + Complex::new(1.0, 0.0), + Complex::new(0.0, 0.0), + Complex::new(2.0, 0.0), + ]); + let splits: Vec<_> = v.split(|x| *x == Complex::new(0.0, 0.0)).collect(); + assert_eq!(splits, vec![&[Complex::new(1.0, 0.0)][..], &[Complex::new(2.0, 0.0)][..]]); + } + + // --- splitn --- + #[test] + fn test_splitn() { + let v = FlexVector::from_vec(vec![1, 0, 2, 0, 3]); + let splits: Vec<_> = v.splitn(2, |&x| x == 0).collect(); + assert_eq!(splits, vec![&[1][..], &[2, 0, 3][..]]); + } + + #[test] + fn test_splitn_f64() { + let v = FlexVector::from_vec(vec![1.0, 0.0, 2.0, 0.0, 3.0]); + let splits: Vec<_> = v.splitn(2, |&x| x == 0.0).collect(); + assert_eq!(splits, vec![&[1.0][..], &[2.0, 0.0, 3.0][..]]); + } + + #[test] + fn test_splitn_complex() { + let v = FlexVector::from_vec(vec![ + Complex::new(1.0, 0.0), + Complex::new(0.0, 0.0), + Complex::new(2.0, 0.0), + Complex::new(0.0, 0.0), + Complex::new(3.0, 0.0), + ]); + let splits: Vec<_> = v.splitn(2, |x| *x == Complex::new(0.0, 0.0)).collect(); + assert_eq!( + splits, + vec![ + &[Complex::new(1.0, 0.0)][..], + &[Complex::new(2.0, 0.0), Complex::new(0.0, 0.0), Complex::new(3.0, 0.0)][..] + ] + ); + } + + // --- rsplit --- + #[test] + fn test_rsplit() { + let v = FlexVector::from_vec(vec![1, 0, 2, 0, 3]); + let splits: Vec<_> = v.rsplit(|&x| x == 0).collect(); + assert_eq!(splits, vec![&[3][..], &[2][..], &[1][..]]); + } + + #[test] + fn test_rsplit_f64() { + let v = FlexVector::from_vec(vec![1.0, 0.0, 2.0, 0.0, 3.0]); + let splits: Vec<_> = v.rsplit(|&x| x == 0.0).collect(); + assert_eq!(splits, vec![&[3.0][..], &[2.0][..], &[1.0][..]]); + } + + #[test] + fn test_rsplit_complex() { + let v = FlexVector::from_vec(vec![ + Complex::new(1.0, 0.0), + Complex::new(0.0, 0.0), + Complex::new(2.0, 0.0), + Complex::new(0.0, 0.0), + Complex::new(3.0, 0.0), + ]); + let splits: Vec<_> = v.rsplit(|x| *x == Complex::new(0.0, 0.0)).collect(); + assert_eq!( + splits, + vec![ + &[Complex::new(3.0, 0.0)][..], + &[Complex::new(2.0, 0.0)][..], + &[Complex::new(1.0, 0.0)][..] + ] + ); + } + + // --- rsplitn --- + #[test] + fn test_rsplitn() { + let v = FlexVector::from_vec(vec![1, 0, 2, 0, 3]); + let splits: Vec<_> = v.rsplitn(2, |&x| x == 0).collect(); + assert_eq!(splits, vec![&[3][..], &[1, 0, 2][..]]); + } + + #[test] + fn test_rsplitn_f64() { + let v = FlexVector::from_vec(vec![1.0, 0.0, 2.0, 0.0, 3.0]); + let splits: Vec<_> = v.rsplitn(2, |&x| x == 0.0).collect(); + assert_eq!(splits, vec![&[3.0][..], &[1.0, 0.0, 2.0][..]]); + } + + #[test] + fn test_rsplitn_complex() { + let v = FlexVector::from_vec(vec![ + Complex::new(1.0, 0.0), + Complex::new(0.0, 0.0), + Complex::new(2.0, 0.0), + Complex::new(0.0, 0.0), + Complex::new(3.0, 0.0), + ]); + let splits: Vec<_> = v.rsplitn(2, |x| *x == Complex::new(0.0, 0.0)).collect(); + assert_eq!( + splits, + vec![ + &[Complex::new(3.0, 0.0)][..], + &[Complex::new(1.0, 0.0), Complex::new(0.0, 0.0), Complex::new(2.0, 0.0)][..] + ] + ); + } + + // --- push --- + #[test] + fn test_push() { + let mut v = FlexVector::new(); + v.push(1); + v.push(2); + assert_eq!(v.as_slice(), &[1, 2]); + } + + #[test] + fn test_push_f64() { + let mut v = FlexVector::new(); + v.push(1.1); + v.push(2.2); + assert_eq!(v.as_slice(), &[1.1, 2.2]); + } + + #[test] + fn test_push_complex() { + let mut v = FlexVector::new(); + v.push(Complex::new(1.0, 2.0)); + v.push(Complex::new(3.0, 4.0)); + assert_eq!(v.as_slice(), &[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + } + + // --- pop --- + #[test] + fn test_pop() { + let mut v = FlexVector::from_vec(vec![1, 2]); + assert_eq!(v.pop(), Some(2)); + assert_eq!(v.pop(), Some(1)); + assert_eq!(v.pop(), None); + } + + #[test] + fn test_pop_f64() { + let mut v = FlexVector::from_vec(vec![1.1, 2.2]); + assert_eq!(v.pop(), Some(2.2)); + assert_eq!(v.pop(), Some(1.1)); + assert_eq!(v.pop(), None); + } + + #[test] + fn test_pop_complex() { + let mut v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + assert_eq!(v.pop(), Some(Complex::new(3.0, 4.0))); + assert_eq!(v.pop(), Some(Complex::new(1.0, 2.0))); + assert_eq!(v.pop(), None); + } + + // --- insert --- + #[test] + fn test_insert() { + let mut v = FlexVector::from_vec(vec![1, 3]); + v.insert(1, 2); + assert_eq!(v.as_slice(), &[1, 2, 3]); + } + + #[test] + fn test_insert_f64() { + let mut v = FlexVector::from_vec(vec![1.1, 3.3]); + v.insert(1, 2.2); + assert_eq!(v.as_slice(), &[1.1, 2.2, 3.3]); + } + + #[test] + fn test_insert_complex() { + let mut v = FlexVector::from_vec(vec![Complex::new(1.0, 1.0), Complex::new(3.0, 3.0)]); + v.insert(1, Complex::new(2.0, 2.0)); + assert_eq!( + v.as_slice(), + &[Complex::new(1.0, 1.0), Complex::new(2.0, 2.0), Complex::new(3.0, 3.0)] + ); + } + + // --- remove --- + #[test] + fn test_remove() { + let mut v = FlexVector::from_vec(vec![1, 2, 3]); + assert_eq!(v.remove(1), 2); + assert_eq!(v.as_slice(), &[1, 3]); + } + + #[test] + fn test_remove_f64() { + let mut v = FlexVector::from_vec(vec![1.1, 2.2, 3.3]); + assert_eq!(v.remove(1), 2.2); + assert_eq!(v.as_slice(), &[1.1, 3.3]); + } + + #[test] + fn test_remove_complex() { + let mut v = FlexVector::from_vec(vec![ + Complex::new(1.0, 1.0), + Complex::new(2.0, 2.0), + Complex::new(3.0, 3.0), + ]); + assert_eq!(v.remove(1), Complex::new(2.0, 2.0)); + assert_eq!(v.as_slice(), &[Complex::new(1.0, 1.0), Complex::new(3.0, 3.0)]); + } + + // --- resize --- + #[test] + fn test_resize() { + let mut v = FlexVector::from_vec(vec![1, 2]); + v.resize(4, 0); + assert_eq!(v.as_slice(), &[1, 2, 0, 0]); + v.resize(2, 0); + assert_eq!(v.as_slice(), &[1, 2]); + } + + #[test] + fn test_resize_f64() { + let mut v = FlexVector::from_vec(vec![1.1, 2.2]); + v.resize(4, 0.0); + assert_eq!(v.as_slice(), &[1.1, 2.2, 0.0, 0.0]); + v.resize(1, 0.0); + assert_eq!(v.as_slice(), &[1.1]); + } + + #[test] + fn test_resize_complex() { + let mut v = FlexVector::from_vec(vec![Complex::new(1.0, 1.0)]); + v.resize(3, Complex::new(0.0, 0.0)); + assert_eq!( + v.as_slice(), + &[Complex::new(1.0, 1.0), Complex::new(0.0, 0.0), Complex::new(0.0, 0.0)] + ); + v.resize(1, Complex::new(0.0, 0.0)); + assert_eq!(v.as_slice(), &[Complex::new(1.0, 1.0)]); + } + + // --- clear --- + #[test] + fn test_clear() { + let mut v = FlexVector::from_vec(vec![1, 2, 3]); + assert!(!v.is_empty()); + v.clear(); + assert!(v.is_empty()); + } + + #[test] + fn test_clear_f64() { + let mut v = FlexVector::from_vec(vec![1.1, 2.2, 3.3]); + assert!(!v.is_empty()); + v.clear(); + assert!(v.is_empty()); + } + + #[test] + fn test_clear_complex() { + let mut v = FlexVector::from_vec(vec![Complex::new(1.0, 1.0), Complex::new(2.0, 2.0)]); + assert!(!v.is_empty()); + v.clear(); + assert!(v.is_empty()); + } + + // --- get_mut --- + #[test] + fn test_get_mut_single() { + let mut v = FlexVector::from_vec(vec![10, 20, 30]); + if let Some(x) = v.get_mut(1) { + *x = 99; + } + assert_eq!(v.as_slice(), &[10, 99, 30]); + } + + #[test] + fn test_get_mut_out_of_bounds() { + let mut v = FlexVector::from_vec(vec![1, 2, 3]); + assert!(v.get_mut(10).is_none()); + } + + #[test] + fn test_get_mut_range() { + let mut v = FlexVector::from_vec(vec![1, 2, 3, 4]); + if let Some(slice) = v.get_mut(1..3) { + slice[0] = 20; + slice[1] = 30; + } + assert_eq!(v.as_slice(), &[1, 20, 30, 4]); + } + + #[test] + fn test_get_mut_full_range() { + let mut v = FlexVector::from_vec(vec![5, 6, 7]); + if let Some(slice) = v.get_mut(..) { + for x in slice { + *x *= 2; + } + } + assert_eq!(v.as_slice(), &[10, 12, 14]); + } + + // --- iter_mut --- + #[test] + fn test_iter_mut_i32() { + let mut v = FlexVector::from_vec(vec![1, 2, 3]); + for x in v.iter_mut() { + *x *= 2; + } + assert_eq!(v.as_slice(), &[2, 4, 6]); + } + + #[test] + fn test_iter_mut_f64() { + let mut v = FlexVector::from_vec(vec![1.5, -2.0, 0.0]); + for x in v.iter_mut() { + *x += 1.0; + } + assert_eq!(v.as_slice(), &[2.5, -1.0, 1.0]); + } + + #[test] + fn test_iter_mut_complex() { + use num::Complex; + let mut v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + for x in v.iter_mut() { + x.re += 1.0; + x.im *= 2.0; + } + assert_eq!(v.as_slice(), &[Complex::new(2.0, 4.0), Complex::new(-2.0, 8.0)]); + } + + #[test] + fn test_iter_mut_empty() { + let mut v = FlexVector::::new(); + let mut count = 0; + for _ in v.iter_mut() { + count += 1; + } + assert_eq!(count, 0); + } + + // --- as_mut_slice --- + #[test] + fn test_as_mut_slice_i32() { + let mut v = FlexVector::from_vec(vec![1, 2, 3]); + let slice = v.as_mut_slice(); + slice[0] = 10; + slice[2] = 30; + assert_eq!(v.as_slice(), &[10, 2, 30]); + } + + #[test] + fn test_as_mut_slice_f64() { + let mut v = FlexVector::from_vec(vec![1.1, 2.2, 3.3]); + let slice = v.as_mut_slice(); + slice[1] = 9.9; + assert_eq!(v.as_slice(), &[1.1, 9.9, 3.3]); + } + + #[test] + fn test_as_mut_slice_complex() { + use num::Complex; + let mut v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let slice = v.as_mut_slice(); + slice[0].re = 10.0; + slice[1].im = 40.0; + assert_eq!(v.as_slice(), &[Complex::new(10.0, 2.0), Complex::new(3.0, 40.0)]); + } + + #[test] + fn test_as_mut_slice_empty() { + let mut v = FlexVector::::new(); + let slice = v.as_mut_slice(); + assert_eq!(slice.len(), 0); + } + + // --- map --- + #[test] + fn test_map_i32() { + let v = FlexVector::from_vec(vec![1, 2, 3]); + let squared = v.map(|x| x * x); + assert_eq!(squared.as_slice(), &[1, 4, 9]); + // original unchanged + assert_eq!(v.as_slice(), &[1, 2, 3]); + } + + #[test] + fn test_map_f64() { + let v: FlexVector = FlexVector::from_vec(vec![1.5, -2.0, 0.0]); + let abs = v.map(|x| x.abs()); + assert_eq!(abs.as_slice(), &[1.5, 2.0, 0.0]); + } + + #[test] + fn test_map_complex() { + use num::Complex; + let v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + let conj = v.map(|x| x.conj()); + assert_eq!(conj.as_slice(), &[Complex::new(1.0, -2.0), Complex::new(-3.0, -4.0)]); + } + + #[test] + fn test_map_empty() { + let v = FlexVector::::new(); + let mapped = v.map(|x| x + 1); + assert!(mapped.is_empty()); + } + + // used for function pointer test below + fn square(x: i32) -> i32 { + x * x + } + + #[test] + fn test_map_with_fn_pointer() { + let v = FlexVector::from_vec(vec![1, 2, 3]); + let squared = v.map(square); + assert_eq!(squared.as_slice(), &[1, 4, 9]); + } + + // --- mut_map --- + #[test] + fn test_mut_map_i32() { + let mut v = FlexVector::from_vec(vec![1, 2, 3]); + v.mut_map(|x| x * 10); + assert_eq!(v.as_slice(), &[10, 20, 30]); + } + + #[test] + fn test_mut_map_f64() { + let mut v = FlexVector::from_vec(vec![1.5, -2.0, 0.0]); + v.mut_map(|x| x + 1.0); + assert_eq!(v.as_slice(), &[2.5, -1.0, 1.0]); + } + + #[test] + fn test_mut_map_complex() { + use num::Complex; + let mut v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + v.mut_map(|x| Complex::new(x.re + 1.0, x.im * 2.0)); + assert_eq!(v.as_slice(), &[Complex::new(2.0, 4.0), Complex::new(-2.0, 8.0)]); + } + + #[test] + fn test_mut_map_empty() { + let mut v = FlexVector::::new(); + v.mut_map(|x| x + 1); + assert!(v.is_empty()); + } + + // used for function pointer test below + fn double(x: i32) -> i32 { + x * 2 + } + + #[test] + fn test_mut_map_with_fn_pointer() { + let mut v = FlexVector::from_vec(vec![1, 2, 3]); + v.mut_map(double); + assert_eq!(v.as_slice(), &[2, 4, 6]); + } + + // ================================ + // + // Unary Negation trait tests + // + // ================================ + #[test] + fn test_neg() { + let v = FlexVector::from_vec(vec![1, -2, 3]); + let neg_v = -v; + assert_eq!(neg_v.as_slice(), &[-1, 2, -3]); + } + + #[test] + fn test_neg_f64() { + let v = FlexVector::from_vec(vec![1.5, -2.5, 0.0]); + let neg_v = -v; + assert_eq!(neg_v.as_slice(), &[-1.5, 2.5, -0.0]); + } + + #[test] + fn test_neg_complex() { + let v = FlexVector::from_vec(vec![Complex::new(1.0, -2.0), Complex::new(-3.0, 4.0)]); + let neg_v = -v; + assert_eq!(neg_v.as_slice(), &[Complex::new(-1.0, 2.0), Complex::new(3.0, -4.0)]); + } + + #[test] + fn test_neg_nan() { + let v = FlexVector::from_vec(vec![f64::NAN, -f64::NAN]); + let neg_v = -v; + // Negating NaN is still NaN, but sign bit may flip + assert!(neg_v.as_slice()[0].is_nan()); + assert!(neg_v.as_slice()[1].is_nan()); + // Optionally check sign bit if desired + assert_eq!(neg_v.as_slice()[0].is_sign_negative(), true); + assert_eq!(neg_v.as_slice()[1].is_sign_positive(), true); + } + + #[test] + fn test_neg_infinity() { + let v = FlexVector::from_vec(vec![f64::INFINITY, f64::NEG_INFINITY]); + let neg_v = -v; + assert_eq!(neg_v.as_slice(), &[-f64::INFINITY, f64::INFINITY]); + } + + #[test] + fn test_add() { + let v1 = FlexVector::from_vec(vec![1, 2, 3]); + let v2 = FlexVector::from_vec(vec![4, 5, 6]); + let sum = v1 + v2; + assert_eq!(sum.as_slice(), &[5, 7, 9]); + } + + #[test] + fn test_add_f64() { + let v1 = FlexVector::from_vec(vec![1.0, 2.5]); + let v2 = FlexVector::from_vec(vec![3.0, 4.5]); + let sum = v1 + v2; + assert_eq!(sum.as_slice(), &[4.0, 7.0]); + } + + #[test] + fn test_add_complex() { + let v1 = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); + let sum = v1 + v2; + assert_eq!(sum.as_slice(), &[Complex::new(6.0, 8.0), Complex::new(10.0, 12.0)]); + } + + #[test] + fn test_add_nan_infinity() { + let v1 = FlexVector::from_vec(vec![f64::NAN, f64::INFINITY, 1.0]); + let v2 = FlexVector::from_vec(vec![1.0, 2.0, f64::INFINITY]); + let sum = v1 + v2; + assert!(sum.as_slice()[0].is_nan()); + assert_eq!(sum.as_slice()[1], f64::INFINITY); + assert_eq!(sum.as_slice()[2], f64::INFINITY); + } + + #[test] + fn test_sub() { + let v1 = FlexVector::from_vec(vec![10, 20, 30]); + let v2 = FlexVector::from_vec(vec![1, 2, 3]); + let diff = v1 - v2; + assert_eq!(diff.as_slice(), &[9, 18, 27]); + } + + #[test] + fn test_sub_f64() { + let v1 = FlexVector::from_vec(vec![5.5, 2.0]); + let v2 = FlexVector::from_vec(vec![1.5, 1.0]); + let diff = v1 - v2; + assert_eq!(diff.as_slice(), &[4.0, 1.0]); + } + + #[test] + fn test_sub_complex() { + let v1 = FlexVector::from_vec(vec![Complex::new(5.0, 7.0), Complex::new(3.0, 4.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(2.0, 1.0)]); + let diff = v1 - v2; + assert_eq!(diff.as_slice(), &[Complex::new(4.0, 5.0), Complex::new(1.0, 3.0)]); + } + + #[test] + fn test_sub_nan_infinity() { + let v1 = FlexVector::from_vec(vec![f64::NAN, f64::INFINITY, 5.0]); + let v2 = FlexVector::from_vec(vec![2.0, f64::INFINITY, f64::NAN]); + let diff = v1 - v2; + assert!(diff.as_slice()[0].is_nan()); + assert!(diff.as_slice()[1].is_nan()); // inf - inf = NaN + assert!(diff.as_slice()[2].is_nan()); + } + + #[test] + fn test_mul() { + let v1 = FlexVector::from_vec(vec![2, 3, 4]); + let v2 = FlexVector::from_vec(vec![5, 6, 7]); + let prod = v1 * v2; + assert_eq!(prod.as_slice(), &[10, 18, 28]); + } + + #[test] + fn test_mul_f64() { + let v1 = FlexVector::from_vec(vec![1.5, 2.0]); + let v2 = FlexVector::from_vec(vec![2.0, 3.0]); + let prod = v1 * v2; + assert_eq!(prod.as_slice(), &[3.0, 6.0]); + } + + #[test] + fn test_mul_complex() { + let v1 = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); + let prod = v1 * v2; + assert_eq!( + prod.as_slice(), + &[ + Complex::new(-7.0, 16.0), // (1+2i)*(5+6i) = (1*5 - 2*6) + (1*6 + 2*5)i = (5-12)+(6+10)i = -7+16i + Complex::new(-11.0, 52.0) // (3+4i)*(7+8i) = (3*7-4*8)+(3*8+4*7)i = (21-32)+(24+28)i = -11+52i + ] + ); + } + + #[test] + fn test_mul_nan_infinity() { + let v1 = FlexVector::from_vec(vec![f64::NAN, f64::INFINITY, 2.0]); + let v2 = FlexVector::from_vec(vec![3.0, 2.0, f64::INFINITY]); + let prod = v1 * v2; + assert!(prod.as_slice()[0].is_nan()); + assert_eq!(prod.as_slice()[1], f64::INFINITY); + assert_eq!(prod.as_slice()[2], f64::INFINITY); + } + + #[test] + fn test_add_assign() { + let mut v1 = FlexVector::from_vec(vec![1, 2, 3]); + let v2 = FlexVector::from_vec(vec![4, 5, 6]); + v1 += v2; + assert_eq!(v1.as_slice(), &[5, 7, 9]); + } + + #[test] + fn test_add_assign_f64() { + let mut v1 = FlexVector::from_vec(vec![1.0, 2.5]); + let v2 = FlexVector::from_vec(vec![3.0, 4.5]); + v1 += v2; + assert_eq!(v1.as_slice(), &[4.0, 7.0]); + } + + #[test] + fn test_add_assign_complex() { + let mut v1 = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); + v1 += v2; + assert_eq!(v1.as_slice(), &[Complex::new(6.0, 8.0), Complex::new(10.0, 12.0)]); + } + + #[test] + fn test_sub_assign() { + let mut v1 = FlexVector::from_vec(vec![10, 20, 30]); + let v2 = FlexVector::from_vec(vec![1, 2, 3]); + v1 -= v2; + assert_eq!(v1.as_slice(), &[9, 18, 27]); + } + + #[test] + fn test_sub_assign_f64() { + let mut v1 = FlexVector::from_vec(vec![5.5, 2.0]); + let v2 = FlexVector::from_vec(vec![1.5, 1.0]); + v1 -= v2; + assert_eq!(v1.as_slice(), &[4.0, 1.0]); + } + + #[test] + fn test_sub_assign_complex() { + let mut v1 = FlexVector::from_vec(vec![Complex::new(5.0, 7.0), Complex::new(3.0, 4.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(2.0, 1.0)]); + v1 -= v2; + assert_eq!(v1.as_slice(), &[Complex::new(4.0, 5.0), Complex::new(1.0, 3.0)]); + } + + #[test] + fn test_mul_assign() { + let mut v1 = FlexVector::from_vec(vec![2, 3, 4]); + let v2 = FlexVector::from_vec(vec![5, 6, 7]); + v1 *= v2; + assert_eq!(v1.as_slice(), &[10, 18, 28]); + } + + #[test] + fn test_mul_assign_f64() { + let mut v1 = FlexVector::from_vec(vec![1.5, 2.0]); + let v2 = FlexVector::from_vec(vec![2.0, 3.0]); + v1 *= v2; + assert_eq!(v1.as_slice(), &[3.0, 6.0]); + } + + #[test] + fn test_mul_assign_complex() { + let mut v1 = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); + v1 *= v2; + assert_eq!(v1.as_slice(), &[Complex::new(-7.0, 16.0), Complex::new(-11.0, 52.0)]); + } + + #[test] + fn test_scalar_mul() { + let v = FlexVector::from_vec(vec![2, -3, 4]); + let prod = v.clone() * 3; + assert_eq!(prod.as_slice(), &[6, -9, 12]); + } + + #[test] + fn test_scalar_mul_f64() { + let v = FlexVector::from_vec(vec![1.5, -2.0, 0.0]); + let prod = v.clone() * 2.0; + assert_eq!(prod.as_slice(), &[3.0, -4.0, 0.0]); + } + + #[test] + fn test_scalar_mul_complex() { + use num::Complex; + let v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + let scalar = Complex::new(2.0, 0.0); + let prod = v.clone() * scalar; + assert_eq!(prod.as_slice(), &[Complex::new(2.0, 4.0), Complex::new(-6.0, 8.0)]); + } + + #[test] + fn test_scalar_mul_assign() { + let mut v = FlexVector::from_vec(vec![2, -3, 4]); + v *= 3; + assert_eq!(v.as_slice(), &[6, -9, 12]); + } + + #[test] + fn test_scalar_mul_assign_f64() { + let mut v = FlexVector::from_vec(vec![1.5, -2.0, 0.0]); + v *= 2.0; + assert_eq!(v.as_slice(), &[3.0, -4.0, 0.0]); + } + + #[test] + fn test_scalar_mul_assign_complex() { + use num::Complex; + let mut v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + let scalar = Complex::new(2.0, 0.0); + v *= scalar; + assert_eq!(v.as_slice(), &[Complex::new(2.0, 4.0), Complex::new(-6.0, 8.0)]); + } + + #[test] + fn test_scalar_div_f64() { + let v: FlexVector = FlexVector::from_vec(vec![2.0, -4.0, 8.0]); + let result = v.clone() / 2.0; + assert_eq!(result.as_slice(), &[1.0, -2.0, 4.0]); + } + + #[test] + fn test_scalar_div_assign_f64() { + let mut v: FlexVector = FlexVector::from_vec(vec![2.0, -4.0, 8.0]); + v /= 2.0; + assert_eq!(v.as_slice(), &[1.0, -2.0, 4.0]); + } + + #[test] + fn test_scalar_div_complex_by_f64() { + use num::Complex; + let v: FlexVector> = + FlexVector::from_vec(vec![Complex::new(2.0, 4.0), Complex::new(-6.0, 8.0)]); + let result = v.clone() / 2.0; + assert_eq!(result.as_slice(), &[Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + } + + #[test] + fn test_scalar_div_assign_complex_by_f64() { + use num::Complex; + let mut v: FlexVector> = + FlexVector::from_vec(vec![Complex::new(2.0, 4.0), Complex::new(-6.0, 8.0)]); + v /= 2.0; + assert_eq!(v.as_slice(), &[Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + } + + #[test] + fn test_scalar_div_complex_by_complex() { + use num::Complex; + let v: FlexVector> = + FlexVector::from_vec(vec![Complex::new(2.0, 4.0), Complex::new(-6.0, 8.0)]); + let divisor = Complex::new(2.0, 0.0); + let result = v.clone() / divisor; + assert_eq!(result.as_slice(), &[Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + } + + #[test] + fn test_scalar_div_assign_complex_by_complex() { + use num::Complex; + let mut v: FlexVector> = + FlexVector::from_vec(vec![Complex::new(2.0, 4.0), Complex::new(-6.0, 8.0)]); + let divisor = Complex::new(2.0, 0.0); + v /= divisor; + assert_eq!(v.as_slice(), &[Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + } + + #[test] + fn test_scalar_div_nan_infinity() { + let v = FlexVector::from_vec(vec![f64::NAN, f64::INFINITY, 4.0]); + let result: FlexVector = v.clone() / 2.0; + assert!(result.as_slice()[0].is_nan()); + assert_eq!(result.as_slice()[1], f64::INFINITY); + assert_eq!(result.as_slice()[2], 2.0); + } + + #[test] + fn test_scalar_div_assign_nan_infinity() { + let mut v: FlexVector = FlexVector::from_vec(vec![f64::NAN, f64::INFINITY, 4.0]); + v /= 2.0; + assert!(v.as_slice()[0].is_nan()); + assert_eq!(v.as_slice()[1], f64::INFINITY); + assert_eq!(v.as_slice()[2], 2.0); + } + + // additional math operator overload edge cases + + #[test] + fn test_add_overflow_i32() { + let v1 = FlexVector::from_vec(vec![i32::MAX]); + let v2 = FlexVector::from_vec(vec![1]); + if cfg!(debug_assertions) { + // In debug mode, should panic on overflow + let result = std::panic::catch_unwind(|| { + let _ = v1.clone() + v2.clone(); + }); + assert!(result.is_err(), "Should panic on overflow in debug mode"); + } else { + // In release mode, should wrap + let sum = v1 + v2; + assert_eq!(sum.as_slice()[0], i32::MIN); + } + } + + #[test] + fn test_mul_overflow_i32() { + let v1 = FlexVector::from_vec(vec![i32::MAX]); + let v2 = FlexVector::from_vec(vec![2]); + if cfg!(debug_assertions) { + // In debug mode, should panic on overflow + let result = std::panic::catch_unwind(|| { + let _ = v1.clone() * v2.clone(); + }); + assert!(result.is_err(), "Should panic on overflow in debug mode"); + } else { + // In release mode, should wrap + let prod = v1 * v2; + assert_eq!(prod.as_slice()[0], -2); + } + } + + #[test] + fn test_divide_by_zero_f64() { + let v: FlexVector = FlexVector::from_vec(vec![1.0, -2.0, 0.0]); + let result = v.clone() / 0.0; + assert_eq!(result.as_slice()[0], f64::INFINITY); + assert_eq!(result.as_slice()[1], f64::NEG_INFINITY); + assert!(result.as_slice()[2].is_nan()); + } + + #[test] + fn test_neg_zero_f64() { + let v = FlexVector::from_vec(vec![0.0, -0.0]); + let neg_v = -v; + assert_eq!(neg_v.as_slice()[0], -0.0); + assert_eq!(neg_v.as_slice()[1], 0.0); + } + + #[test] + fn test_complex_div_by_zero() { + use num::Complex; + let v: FlexVector> = FlexVector::from_vec(vec![Complex::new(1.0, 1.0)]); + let result = v.clone() / Complex::new(0.0, 0.0); + assert!(result.as_slice()[0].re.is_nan()); + assert!(result.as_slice()[0].im.is_nan()); + } + + #[test] + fn test_empty_vector_ops() { + let v1: FlexVector = FlexVector::new(); + let v2: FlexVector = FlexVector::new(); + let sum = v1.clone() + v2.clone(); + assert!(sum.is_empty()); + let prod = v1 * v2; + assert!(prod.is_empty()); + } + + #[test] + #[should_panic] + fn test_mismatched_length_add() { + let v1 = FlexVector::from_vec(vec![1, 2]); + let v2 = FlexVector::from_vec(vec![1, 2, 3]); + let _ = v1 + v2; // should panic + } +} diff --git a/src/types/mod.rs b/src/types/mod.rs index a8c11bf..5f786f9 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -1,4 +1,8 @@ //! Library types. -pub mod macros; +pub mod flexvector; +pub mod traits; pub mod vector; + +pub use flexvector::FlexVector; +pub use vector::Vector; diff --git a/src/types/traits.rs b/src/types/traits.rs new file mode 100644 index 0000000..01889e1 --- /dev/null +++ b/src/types/traits.rs @@ -0,0 +1,342 @@ +//! Traits. + +use num::Complex; + +pub trait VectorBase { + // --- Core accessors --- + /// ... + fn as_slice(&self) -> &[T]; + + /// ... + fn as_mut_slice(&mut self) -> &mut [T]; + + /// ... + fn len(&self) -> usize { + self.as_slice().len() + } + + /// ... + fn is_empty(&self) -> bool { + self.as_slice().is_empty() + } + + // --- Element access --- + /// ... + fn get(&self, index: usize) -> Option<&T> { + self.as_slice().get(index) + } + + /// ... + fn first(&self) -> Option<&T> { + self.as_slice().first() + } + + /// ... + fn last(&self) -> Option<&T> { + self.as_slice().last() + } + + // --- Iteration --- + /// ... + fn iter(&self) -> std::slice::Iter<'_, T> { + self.as_slice().iter() + } + + /// ... + fn iter_rev(&self) -> std::iter::Rev> { + self.as_slice().iter().rev() + } + + /// ... + fn enumerate(&self) -> std::iter::Enumerate> { + self.iter().enumerate() + } + + // --- Conversion --- + /// ... + fn to_vec(&self) -> Vec + where + T: Clone, + { + self.as_slice().to_vec() + } + + /// ... + fn pretty(&self) -> String + where + T: std::fmt::Debug, + { + format!("{:#?}", self.as_slice()) + } + + // --- Search/containment --- + /// ... + fn contains(&self, x: &T) -> bool + where + T: PartialEq, + { + self.as_slice().contains(x) + } + + /// ... + fn starts_with(&self, needle: &[T]) -> bool + where + T: PartialEq, + { + self.as_slice().starts_with(needle) + } + + /// ... + fn ends_with(&self, needle: &[T]) -> bool + where + T: PartialEq, + { + self.as_slice().ends_with(needle) + } + + /// ... + fn position

(&self, predicate: P) -> Option + where + P: FnMut(&T) -> bool, + { + self.as_slice().iter().position(predicate) + } + + /// ... + fn rposition

(&self, predicate: P) -> Option + where + P: FnMut(&T) -> bool, + { + self.as_slice().iter().rposition(predicate) + } + + // --- Slicing/chunking --- + /// ... + fn windows(&self, size: usize) -> std::slice::Windows<'_, T> { + self.as_slice().windows(size) + } + + /// ... + fn chunks(&self, size: usize) -> std::slice::Chunks<'_, T> { + self.as_slice().chunks(size) + } + + /// ... + fn split_at(&self, mid: usize) -> (&[T], &[T]) { + self.as_slice().split_at(mid) + } + + /// ... + fn split(&self, pred: F) -> std::slice::Split<'_, T, F> + where + F: FnMut(&T) -> bool, + { + self.as_slice().split(pred) + } + + /// ... + fn splitn(&self, n: usize, pred: F) -> std::slice::SplitN<'_, T, F> + where + F: FnMut(&T) -> bool, + { + self.as_slice().splitn(n, pred) + } + + /// ... + fn rsplit(&self, pred: F) -> std::slice::RSplit<'_, T, F> + where + F: FnMut(&T) -> bool, + { + self.as_slice().rsplit(pred) + } + + /// ... + fn rsplitn(&self, n: usize, pred: F) -> std::slice::RSplitN<'_, T, F> + where + F: FnMut(&T) -> bool, + { + self.as_slice().rsplitn(n, pred) + } + + // --- Pointer access --- + /// ... + fn as_ptr(&self) -> *const T { + self.as_slice().as_ptr() + } +} + +pub trait VectorOps: VectorBase { + type Output; + + /// ... + fn translate(&self, other: &Self) -> Self::Output + where + T: num::Num + Copy; + + /// ... + fn mut_translate(&mut self, other: &Self) + where + T: num::Num + Copy, + { + for (a, b) in self.as_mut_slice().iter_mut().zip(other.as_slice()) { + *a = *a + *b; + } + } + + /// ... + fn dot(&self, other: &Self) -> T + where + T: num::Num + Copy + std::iter::Sum, + { + self.as_slice().iter().zip(other.as_slice()).map(|(a, b)| *a * *b).sum() + } + + /// ... + fn sum(&self) -> T + where + T: num::Num + Copy + std::iter::Sum, + { + self.as_slice().iter().copied().sum() + } + + /// ... + fn product(&self) -> T + where + T: num::Num + Copy + std::iter::Product, + { + self.as_slice().iter().copied().product() + } + + /// ... + fn min(&self) -> Option + where + T: Ord + Copy, + { + self.as_slice().iter().copied().min() + } + + /// ... + fn max(&self) -> Option + where + T: Ord + Copy, + { + self.as_slice().iter().copied().max() + } +} + +pub trait VectorOpsFloat: VectorBase { + type Output; + + /// ... + fn normalize(&self) -> Self::Output + where + T: num::Float + Clone + std::iter::Sum, + Self::Output: std::iter::FromIterator; + + /// ... + fn mut_normalize(&mut self) + where + T: num::Float + Copy + std::iter::Sum; + + /// Linear interpolation between self and end by weight in [0, 1]. + fn lerp(&self, end: &Self, weight: T) -> Self::Output + where + T: num::Float + Clone + PartialOrd; + + /// Midpoint + fn midpoint(&self, end: &Self) -> Self::Output + where + T: num::Float + Clone, + { + self.lerp(end, num::cast(0.5).unwrap()) + } + + /// Euclidean distance between self and other. + fn distance(&self, other: &Self) -> T + where + T: num::Float + Clone + std::iter::Sum, + { + self.as_slice() + .iter() + .zip(other.as_slice()) + .map(|(a, b)| (*a - *b).powi(2)) + .sum::() + .sqrt() + } + + /// Euclidean norm (magnitude) of the vector. + fn norm(&self) -> T + where + T: num::Float + Clone + std::iter::Sum, + { + self.as_slice().iter().map(|a| (*a).powi(2)).sum::().sqrt() + } + + /// Alias for norm (magnitude). + fn magnitude(&self) -> T + where + T: num::Float + Clone + std::iter::Sum, + { + self.norm() + } +} + +pub trait VectorOpsComplexFloat: VectorBase> { + type Output; + + /// ... + fn normalize(&self) -> Self::Output + where + F: num::Float + Clone + std::iter::Sum, + Self::Output: std::iter::FromIterator>; + + /// ... + fn mut_normalize(&mut self) + where + F: num::Float + Copy + std::iter::Sum; + + /// Linear interpolation between self and end by real weight in [0, 1]. + fn lerp(&self, end: &Self, weight: F) -> Self::Output + where + F: num::Float + Clone + PartialOrd; + + /// Midpoint + fn midpoint(&self, end: &Self) -> Self::Output + where + F: num::Float + Clone, + { + self.lerp(end, num::cast(0.5).unwrap()) + } + + /// Euclidean distance (L2 norm) between self and other (returns real). + fn distance(&self, other: &Self) -> F + where + F: num::Float + Clone + std::iter::Sum, + { + self.as_slice() + .iter() + .zip(other.as_slice()) + .map(|(a, b)| { + let diff = *a - *b; + diff.norm_sqr() + }) + .sum::() + .sqrt() + } + + /// Euclidean norm (magnitude) of the vector (returns real). + fn norm(&self) -> F + where + F: num::Float + Clone + std::iter::Sum, + { + self.as_slice().iter().map(|a| a.norm_sqr()).sum::().sqrt() + } + + /// Alias for norm (magnitude). + fn magnitude(&self) -> F + where + F: num::Float + Clone + std::iter::Sum, + { + self.norm() + } +} diff --git a/src/types/vector.rs b/src/types/vector.rs index 0aa8488..668aaf8 100644 --- a/src/types/vector.rs +++ b/src/types/vector.rs @@ -1,4 +1,4 @@ -//! Vector types. +//! Vector type. use std::{ borrow::{Borrow, BorrowMut}, @@ -402,6 +402,12 @@ where } } +// ================================ +// +// Default trait impl +// +// ================================ + impl Default for Vector where T: Num + Copy + Default + Sync + Send, @@ -428,7 +434,6 @@ where // Display trait impl // // ================================ - impl fmt::Display for Vector where T: Num + Copy + Default + Sync + Send + fmt::Debug, @@ -438,6 +443,42 @@ where } } +// ================================ +// +// VectorOps trait impl +// +// ================================ +// TODO: add VectorOps trait impl and remove methods that it defines here +// impl VectorOps for Vector +// where +// T: num::Num + Copy, +// { +// type Output = Vector; +// +// fn translate(&self, other: &Self) -> Self::Output +// where +// T: num::Num + Copy, +// { +// let mut result = [T::zero(); N]; +// for i in 0..N { +// result[i] = self.components[i] + other.components[i]; +// } +// Vector { components: result } +// } +// +// fn lerp(&self, end: &Self, weight: T) -> Self::Output +// where +// T: num::Num + Copy + PartialOrd, +// { +// assert!(weight >= T::zero() && weight <= T::one(), "weight must be in [0, 1]"); +// let mut new_components = [T::zero(); N]; +// for i in 0..N { +// new_components[i] = (T::one() - weight) * self.components[i] + weight * end.components[i]; +// } +// Vector { components: new_components } +// } +// } + impl Vector where T: Num + Copy + Sync + Send + std::fmt::Debug, @@ -734,6 +775,7 @@ where /// let i64_floor_v = v.to_num_cast(|x| x.floor() as i64); /// assert_eq!(i64_floor_v, Vector::::from([1, 2, 3])); /// ``` + // TODO: remove me pub fn to_num_cast(&self, closur: U) -> Vector where U: Fn(T) -> V, @@ -744,6 +786,29 @@ where Vector { components: new_components } } + /// Returns a new Vector with each element mapped to a new value using the provided closure or function. + // TODO: add tests (replacement for to_num_cast and other map_ functions) + pub fn map(&self, mut f: F) -> Vector + where + F: FnMut(T) -> U, + U: num::Num + Copy + Sync + Send + Default, + { + let mut new_components: [U; N] = [U::default(); N]; + self.components.iter().enumerate().for_each(|(i, &x)| new_components[i] = f(x)); + Vector { components: new_components } + } + + /// Applies a closure or function to each element, modifying them in place. + // TODO: add tests + pub fn mut_map(&mut self, mut f: F) + where + F: FnMut(T) -> T, + { + for x in self.components.iter_mut() { + *x = f(*x); + } + } + /// Returns a new, allocated [`array`] representation of the [`Vector`] scalar data. /// /// # Examples @@ -877,6 +942,7 @@ where /// assert_eq!(v[1], 7); /// assert_eq!(v[2], 9); /// ``` + // TODO: remove me (default to += operator overload instead) pub fn mut_add(&mut self, rhs: &Vector) -> &mut Self { self.components.iter_mut().zip(rhs).for_each(|(a, b)| *a = *a + *b); @@ -904,6 +970,7 @@ where /// assert_eq!(v[1], -3); /// assert_eq!(v[2], -3); /// ``` + // TODO: remove me (default to -= overload instead) pub fn mut_sub(&mut self, rhs: &Vector) -> &mut Self { self.components.iter_mut().zip(rhs.iter()).for_each(|(a, b)| *a = *a - *b); @@ -930,6 +997,7 @@ where /// assert_eq!(v[1], 8); /// assert_eq!(v[2], 12); /// ``` + // TODO: remove me (default to *= operator overload instead) pub fn mut_mul(&mut self, scale: T) -> &mut Self { self.components.iter_mut().for_each(|a| *a = *a * scale); @@ -987,6 +1055,7 @@ where /// assert_eq!(v[1], -2); /// assert_eq!(v[2], -3); /// ``` + // TODO: remove me (not necessary) pub fn displacement(&self, from: &Vector) -> Self { self.sub(*from) } @@ -1044,6 +1113,7 @@ where /// assert_eq!(v_s[1], 20); /// assert_eq!(v_s[2], 30); /// ``` + // TODO: remove me (not necessary) pub fn scale(&self, scale: T) -> Self { self.mul(scale) } @@ -1062,6 +1132,7 @@ where /// assert_eq!(v[1], 20); /// assert_eq!(v[2], 30); /// ``` + // TODO: remove (not necessary) pub fn mut_scale(&mut self, scale: T) -> &mut Self { self.mut_mul(scale) } @@ -1105,6 +1176,7 @@ where /// assert_eq!(v[1], 7); /// assert_eq!(v[2], 9); /// ``` + // TODO: remove me (not necessary) pub fn mut_translate(&mut self, translation_vector: &Vector) -> &mut Self { self.mut_add(translation_vector) } @@ -1128,6 +1200,7 @@ where /// /// assert_eq!(squared_v, Vector::from([1, 4, 9])); /// ``` + // TODO: remove me (replaced by map) pub fn map_closure(&self, closur: U) -> Self where U: Fn(T) -> T, @@ -1156,6 +1229,7 @@ where /// /// assert_eq!(v, Vector::from([1, 4, 9])); /// ``` + // TODO: remove me (replaced by mut_map) pub fn mut_map_closure(&mut self, mut closur: U) -> &mut Self where U: FnMut(T) -> T, @@ -1186,6 +1260,7 @@ where /// /// assert_eq!(squared_v, Vector::from([1, 4, 9])); /// ``` + // TODO: remove me (replaced by map) pub fn map_fn(&self, func: fn(T) -> T) -> Self { let mut new_components: [T; N] = [T::zero(); N]; self.components.iter().enumerate().for_each(|(i, x)| new_components[i] = func(*x)); @@ -1214,6 +1289,7 @@ where /// /// assert_eq!(v, Vector::from([1, 4, 9])); /// ``` + // TODO: remove me (replaced by mut_map) pub fn mut_map_fn(&mut self, func: fn(T) -> T) -> &mut Self { self.components.iter_mut().for_each(|x| *x = func(*x)); self @@ -1362,6 +1438,7 @@ where /// /// assert_eq!(v_zero_o, v_zero); /// ``` + // TODO: remove me (not necessary) pub fn opposite(&self) -> Self { -*self } @@ -1398,6 +1475,7 @@ where /// This method supports floating point [`Vector`] types only. Please /// see the [Numeric Type Casts](../../index.html#numeric-type-casts) /// documentation for details on casting to floating point types. + // TODO: remove me (in VectorOps trait) pub fn distance(&self, other: &Vector) -> T { (*self - *other).magnitude() } @@ -1434,6 +1512,7 @@ where /// This method supports floating point [`Vector`] types only. Please /// see the [Numeric Type Casts](../../index.html#numeric-type-casts) /// documentation for details on casting to floating point types. + // TODO: remove me (in VectorOpsFloat and VectorOpsComplexFloat traits) pub fn lerp(&self, end: &Vector, weight: T) -> Result where T: std::fmt::Debug, @@ -1482,6 +1561,7 @@ where /// This method supports floating point [`Vector`] types only. Please /// see the [Numeric Type Casts](../../index.html#numeric-type-casts) /// documentation for details on casting to floating point types. + // TODO: remove me (in VectorOpsFloat and VectorOpsComplexFloat traits) pub fn midpoint(&self, end: &Vector) -> Self where T: std::fmt::Debug, @@ -1514,6 +1594,7 @@ where /// This method supports floating point [`Vector`] types only. Please /// see the [Numeric Type Casts](../../index.html#numeric-type-casts) /// documentation for details on casting to floating point types. + // TODO: remove me (in VectorOpsFloat and VectorOpsComplexFloat traits) pub fn magnitude(&self) -> T { let x: T = self.components.iter().map(|a| *a * *a).sum(); x.sqrt() @@ -1538,6 +1619,7 @@ where /// This method supports floating point [`Vector`] types only. Please /// see the [Numeric Type Casts](../../index.html#numeric-type-casts) /// documentation for details on casting to floating point types. + // TODO: remove me (in VectorOpsFloat and VectorOpsComplexFloat traits) pub fn normalize(&self) -> Self where T: Float + Copy + Sync + Send + Sum, From 52b91d4b4c6338e7d34047767b508992f9871b2c Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Sat, 10 May 2025 09:57:55 -0400 Subject: [PATCH 02/82] expand FlexVector API, make it more ergonomic --- src/errors.rs | 16 +- src/types/flexvector.rs | 148 +++++++++++++---- src/types/traits.rs | 354 +++++++++++++++++++++++++++++++++++++--- 3 files changed, 459 insertions(+), 59 deletions(-) diff --git a/src/errors.rs b/src/errors.rs index b11e5cc..6ead27e 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,11 +1,13 @@ //! Error types. -/// Errors that occur while working with [`crate::types::vector::Vector`] +/// Errors that occur while working with vector types. #[derive(Debug)] pub enum VectorError { - /// Occurs when an operation that requires data in a [`crate::types::vector::Vector`] is - /// requested with an empty [`crate::types::vector::Vector`] + /// Occurs when an operation that requires data in a vector type is + /// requested with an empty vector type. EmptyVectorError(String), + /// Occurs when a value extends outside of a mandatory range. + OutOfRangeError(String), /// Occurs when there is invalid data during an attempt to convert /// from [`slice`] data. TryFromSliceError(String), @@ -14,6 +16,8 @@ pub enum VectorError { TryFromVecError(String), /// ValueError occurs when an invalid value is used in an operation ValueError(String), + /// Errors with invalid zero vectors + ZeroVectorError(String), } impl std::fmt::Display for VectorError { @@ -22,6 +26,9 @@ impl std::fmt::Display for VectorError { VectorError::EmptyVectorError(s) => { write!(f, "VectorError::EmptyVectorError: {s}") } + VectorError::OutOfRangeError(s) => { + write!(f, "VectorError::OutOfRangeError: {}", s) + } VectorError::TryFromVecError(s) => { write!(f, "VectorError::TryFromVecError: {s}") } @@ -31,6 +38,9 @@ impl std::fmt::Display for VectorError { VectorError::ValueError(s) => { write!(f, "VectorError::ValueError: {s}") } + VectorError::ZeroVectorError(s) => { + write!(f, "VectorError::ZeroVectorError: {}", s) + } } } } diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index e290e3b..4709516 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -4,9 +4,11 @@ use crate::{ impl_vector_binop, impl_vector_binop_assign, impl_vector_scalar_div_op, impl_vector_scalar_div_op_assign, impl_vector_scalar_op, impl_vector_scalar_op_assign, impl_vector_unary_op, types::traits::VectorBase, types::traits::VectorOps, - types::traits::VectorOpsComplexFloat, types::traits::VectorOpsFloat, + types::traits::VectorOpsComplex, types::traits::VectorOpsFloat, }; +use crate::errors::VectorError; + use num::Complex; use num::Num; @@ -140,6 +142,22 @@ where { self.components.iter().zip(&other.components).map(|(a, b)| a.clone() + b.clone()).collect() } + + fn scale(&self, scalar: T) -> Self::Output + where + T: num::Num + Copy, + Self::Output: std::iter::FromIterator, + { + self.as_slice().iter().map(|a| *a * scalar).collect() + } + + fn negate(&self) -> Self::Output + where + T: std::ops::Neg + Clone, + Self::Output: std::iter::FromIterator, + { + self.as_slice().iter().map(|a| -a.clone()).collect() + } } // ================================ @@ -154,35 +172,43 @@ where { type Output = Vec; - fn normalize(&self) -> Self::Output { + fn normalize(&self) -> Result { let n = self.norm(); - assert!(n != T::zero(), "Cannot normalize a zero vector"); - self.components.iter().map(|a| *a / n).collect() + if n == T::zero() { + return Err(VectorError::ZeroVectorError("Cannot normalize a zero vector".to_string())); + } + Ok(self.components.iter().map(|a| *a / n).collect()) } - fn mut_normalize(&mut self) + /// Returns a new vector with the same direction and the given magnitude. + fn normalize_to(&self, magnitude: T) -> Result where - T: num::Float + Copy + std::iter::Sum, + T: num::Float + Clone + std::iter::Sum, + Self::Output: std::iter::FromIterator, { let n = self.norm(); - assert!(n != T::zero(), "Cannot normalize a zero vector"); - for a in self.as_mut_slice().iter_mut() { - *a = *a / n; + if n == T::zero() { + return Err(VectorError::ZeroVectorError("Cannot normalize a zero vector".to_string())); } + let scale = magnitude / n; + Ok(self.as_slice().iter().map(|a| *a * scale).collect()) } - fn lerp(&self, end: &Self, weight: T) -> Self::Output + fn lerp(&self, end: &Self, weight: T) -> Result where T: num::Float + Clone + PartialOrd, { - assert!(weight >= T::zero() && weight <= T::one(), "weight must be in [0, 1]"); + if weight < T::zero() || weight > T::one() { + return Err(VectorError::OutOfRangeError("weight must be in [0, 1]".to_string())); + } let w = weight.clone(); let one_minus_w = T::one() - w.clone(); - self.components + Ok(self + .components .iter() .zip(&end.components) .map(|(a, b)| one_minus_w.clone() * a.clone() + w.clone() * b.clone()) - .collect() + .collect()) } } @@ -192,45 +218,52 @@ where // // ================================ // TODO: add tests -impl VectorOpsComplexFloat for FlexVector> +impl VectorOpsComplex for FlexVector> where - F: num::Float + Clone + PartialOrd + std::iter::Sum + Sync + Send, + N: num::Float + Clone + PartialOrd + std::iter::Sum + Sync + Send, { - type Output = Vec>; + type Output = Vec>; - fn normalize(&self) -> Self::Output + fn normalize(&self) -> Result where - F: num::Float + Clone + std::iter::Sum, - Self::Output: std::iter::FromIterator>, + N: num::Float + Clone + std::iter::Sum, + Self::Output: std::iter::FromIterator>, { let n = self.norm(); - assert!(n != F::zero(), "Cannot normalize a zero vector"); - self.components.iter().map(|a| *a / n).collect() + if n == N::zero() { + return Err(VectorError::ZeroVectorError("Cannot normalize a zero vector".to_string())); + } + Ok(self.components.iter().map(|a| *a / n).collect()) } - fn mut_normalize(&mut self) + fn normalize_to(&self, magnitude: N) -> Result where - F: num::Float + Copy + std::iter::Sum, + N: num::Float + Clone + std::iter::Sum, + Self::Output: std::iter::FromIterator>, { let n = self.norm(); - assert!(n != F::zero(), "Cannot normalize a zero vector"); - for a in self.as_mut_slice().iter_mut() { - *a = *a / n; + if n == N::zero() { + return Err(VectorError::ZeroVectorError("Cannot normalize a zero vector".to_string())); } + let scale = magnitude / n; + Ok(self.as_slice().iter().map(|a| *a * scale).collect()) } - fn lerp(&self, end: &Self, weight: F) -> Self::Output + fn lerp(&self, end: &Self, weight: N) -> Result where - F: num::Float + Clone + PartialOrd, + N: num::Float + Clone + PartialOrd, { - assert!(weight >= F::zero() && weight <= F::one(), "weight must be in [0, 1]"); - let w = Complex::new(weight.clone(), F::zero()); - let one_minus_w = Complex::new(F::one() - weight.clone(), F::zero()); - self.components + if weight < N::zero() || weight > N::one() { + return Err(VectorError::OutOfRangeError("weight must be in [0, 1]".to_string())); + } + let w = Complex::new(weight.clone(), N::zero()); + let one_minus_w = Complex::new(N::one() - weight.clone(), N::zero()); + Ok(self + .components .iter() .zip(&end.components) .map(|(a, b)| one_minus_w.clone() * a.clone() + w.clone() * b.clone()) - .collect() + .collect()) } } @@ -307,6 +340,21 @@ where *x = f(x.clone()); } } + + /// Cosine similarity between self and other. + pub fn cosine_similarity(&self, other: &Self) -> T + where + T: num::Float + Clone + std::iter::Sum, + { + let dot = self.dot(other); + let norm_self = self.norm(); + let norm_other = other.norm(); + if norm_self == T::zero() || norm_other == T::zero() { + T::zero() + } else { + dot / (norm_self * norm_other) + } + } } // ================================ @@ -333,6 +381,7 @@ impl_vector_scalar_div_op_assign!(FlexVector); #[cfg(test)] mod tests { use super::*; + use num::complex::ComplexFloat; use num::Complex; // ================================ @@ -1510,6 +1559,39 @@ mod tests { assert_eq!(v.as_slice(), &[2, 4, 6]); } + // --- cosine_similarity --- + #[test] + fn test_cosine_similarity_parallel() { + let v1 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let v2 = FlexVector::from_vec(vec![2.0, 4.0, 6.0]); + let cos_sim = v1.cosine_similarity(&v2); + assert!((cos_sim - 1.0).abs() < 1e-10); + } + + #[test] + fn test_cosine_similarity_orthogonal() { + let v1 = FlexVector::from_vec(vec![1.0, 0.0]); + let v2 = FlexVector::from_vec(vec![0.0, 1.0]); + let cos_sim = v1.cosine_similarity(&v2); + assert!((cos_sim - 0.0).abs() < 1e-10); + } + + #[test] + fn test_cosine_similarity_opposite() { + let v1 = FlexVector::from_vec(vec![1.0, 0.0]); + let v2 = FlexVector::from_vec(vec![-1.0, 0.0]); + let cos_sim = v1.cosine_similarity(&v2); + assert!((cos_sim + 1.0).abs() < 1e-10); + } + + #[test] + fn test_cosine_similarity_zero_vector() { + let v1 = FlexVector::from_vec(vec![0.0, 0.0]); + let v2 = FlexVector::from_vec(vec![1.0, 2.0]); + let cos_sim = v1.cosine_similarity(&v2); + assert_eq!(cos_sim, 0.0); + } + // ================================ // // Unary Negation trait tests diff --git a/src/types/traits.rs b/src/types/traits.rs index 01889e1..ee54d11 100644 --- a/src/types/traits.rs +++ b/src/types/traits.rs @@ -2,6 +2,8 @@ use num::Complex; +use crate::errors::VectorError; + pub trait VectorBase { // --- Core accessors --- /// ... @@ -183,6 +185,48 @@ pub trait VectorOps: VectorBase { } } + /// Returns a new vector scaled by the given scalar. + fn scale(&self, scalar: T) -> Self::Output + where + T: num::Num + Copy, + Self::Output: std::iter::FromIterator; + + /// Scales the vector in place by the given scalar. + fn mut_scale(&mut self, scalar: T) + where + T: num::Num + Copy, + { + for a in self.as_mut_slice().iter_mut() { + *a = *a * scalar; + } + } + + /// Returns a new vector with all elements negated. + fn negate(&self) -> Self::Output + where + T: std::ops::Neg + Clone, + Self::Output: std::iter::FromIterator; + + /// Negates all elements in place. + fn mut_negate(&mut self) + where + T: std::ops::Neg + Clone, + { + for a in self.as_mut_slice().iter_mut() { + *a = -a.clone(); + } + } + + /// Sets all elements to zero in place. + fn mut_zero(&mut self) + where + T: num::Zero + Clone, + { + for a in self.as_mut_slice().iter_mut() { + *a = T::zero(); + } + } + /// ... fn dot(&self, other: &Self) -> T where @@ -191,6 +235,18 @@ pub trait VectorOps: VectorBase { self.as_slice().iter().zip(other.as_slice()).map(|(a, b)| *a * *b).sum() } + /// Dot product as f64 (for integer and float types). + fn dot_to_f64(&self, other: &Self) -> f64 + where + T: num::ToPrimitive, + { + self.as_slice() + .iter() + .zip(other.as_slice()) + .map(|(a, b)| a.to_f64().unwrap() * b.to_f64().unwrap()) + .sum() + } + /// ... fn sum(&self) -> T where @@ -222,33 +278,101 @@ pub trait VectorOps: VectorBase { { self.as_slice().iter().copied().max() } + + /// L1 norm (sum of absolute values). + fn l1_norm(&self) -> T + where + T: num::Signed + Copy + std::iter::Sum, + { + self.as_slice().iter().map(|a| a.abs()).sum() + } + + /// L∞ norm (maximum absolute value). + fn linf_norm(&self) -> T + where + T: num::Signed + Copy + PartialOrd, + { + self.as_slice() + .iter() + .map(|a| a.abs()) + .fold(T::zero(), |acc, x| if acc > x { acc } else { x }) + } } pub trait VectorOpsFloat: VectorBase { type Output; /// ... - fn normalize(&self) -> Self::Output + fn normalize(&self) -> Result where T: num::Float + Clone + std::iter::Sum, Self::Output: std::iter::FromIterator; /// ... - fn mut_normalize(&mut self) + fn mut_normalize(&mut self) -> Result<(), VectorError> where - T: num::Float + Copy + std::iter::Sum; + T: num::Float + Copy + std::iter::Sum, + { + let n = self.norm(); + if n == T::zero() { + return Err(VectorError::ZeroVectorError("Cannot normalize a zero vector".to_string())); + } + for a in self.as_mut_slice().iter_mut() { + *a = *a / n; + } + Ok(()) + } + + /// Returns a new vector with the same direction and the given magnitude. + fn normalize_to(&self, magnitude: T) -> Result + where + T: num::Float + Clone + std::iter::Sum, + Self::Output: std::iter::FromIterator; + + /// ... + fn mut_normalize_to(&mut self, magnitude: T) -> Result<(), VectorError> + where + T: num::Float + Copy + std::iter::Sum, + { + let n = self.norm(); + if n == T::zero() { + return Err(VectorError::ZeroVectorError("Cannot normalize a zero vector".to_string())); + } + let scale = magnitude / n; + for a in self.as_mut_slice().iter_mut() { + *a = *a * scale; + } + Ok(()) + } /// Linear interpolation between self and end by weight in [0, 1]. - fn lerp(&self, end: &Self, weight: T) -> Self::Output + fn lerp(&self, end: &Self, weight: T) -> Result where T: num::Float + Clone + PartialOrd; + /// In-place linear interpolation between self and end by weight in [0, 1]. + fn mut_lerp(&mut self, end: &Self, weight: T) -> Result<(), VectorError> + where + T: num::Float + Copy + PartialOrd, + { + if weight < T::zero() || weight > T::one() { + return Err(VectorError::OutOfRangeError("weight must be in [0, 1]".to_string())); + } + let w = weight; + let one_minus_w = T::one() - w; + for (a, b) in self.as_mut_slice().iter_mut().zip(end.as_slice()) { + *a = one_minus_w * *a + w * *b; + } + Ok(()) + } + /// Midpoint fn midpoint(&self, end: &Self) -> Self::Output where T: num::Float + Clone, { - self.lerp(end, num::cast(0.5).unwrap()) + // OK to unwrap because by definition it uses an in-range weight + self.lerp(end, num::cast(0.5).unwrap()).unwrap() } /// Euclidean distance between self and other. @@ -264,6 +388,43 @@ pub trait VectorOpsFloat: VectorBase { .sqrt() } + /// Manhattan (L1) distance between self and other. + fn manhattan_distance(&self, other: &Self) -> T + where + T: num::Float + Clone + std::iter::Sum, + { + self.as_slice().iter().zip(other.as_slice()).map(|(a, b)| (*a - *b).abs()).sum() + } + + /// Chebyshev (L∞) distance between self and other. + fn chebyshev_distance(&self, other: &Self) -> T + where + T: num::Float + Clone + PartialOrd, + { + self.as_slice() + .iter() + .zip(other.as_slice()) + .map(|(a, b)| (*a - *b).abs()) + .fold(T::zero(), |acc, x| acc.max(x)) + } + + /// Minkowski (Lp) distance between self and other. + fn minkowski_distance(&self, other: &Self, p: T) -> Result + where + T: num::Float + Clone + std::iter::Sum, + { + if p < T::one() { + return Err(VectorError::OutOfRangeError("p must be >= 1".to_string())); + } + Ok(self + .as_slice() + .iter() + .zip(other.as_slice()) + .map(|(a, b)| (*a - *b).abs().powf(p.clone())) + .sum::() + .powf(T::one() / p)) + } + /// Euclidean norm (magnitude) of the vector. fn norm(&self) -> T where @@ -272,6 +433,17 @@ pub trait VectorOpsFloat: VectorBase { self.as_slice().iter().map(|a| (*a).powi(2)).sum::().sqrt() } + /// Lp norm (generalized Minkowski norm). + fn lp_norm(&self, p: T) -> Result + where + T: num::Float + Copy + std::iter::Sum, + { + if p < T::one() { + return Err(VectorError::OutOfRangeError("p must be >= 1".to_string())); + } + Ok(self.as_slice().iter().map(|a| a.abs().powf(p)).sum::().powf(T::one() / p)) + } + /// Alias for norm (magnitude). fn magnitude(&self) -> T where @@ -281,37 +453,109 @@ pub trait VectorOpsFloat: VectorBase { } } -pub trait VectorOpsComplexFloat: VectorBase> { +pub trait VectorOpsComplex: VectorBase> { type Output; /// ... - fn normalize(&self) -> Self::Output + fn normalize(&self) -> Result where - F: num::Float + Clone + std::iter::Sum, - Self::Output: std::iter::FromIterator>; + N: num::Float + Clone + std::iter::Sum, + Self::Output: std::iter::FromIterator>; /// ... - fn mut_normalize(&mut self) + fn mut_normalize(&mut self) -> Result<(), VectorError> + where + N: num::Float + Copy + std::iter::Sum, + { + let n = self.norm(); + if n == N::zero() { + return Err(VectorError::ZeroVectorError("Cannot normalize a zero vector".to_string())); + } + for a in self.as_mut_slice().iter_mut() { + *a = *a / n; + } + Ok(()) + } + + /// Returns a new vector with the same direction and the given magnitude (real). + fn normalize_to(&self, magnitude: N) -> Result where - F: num::Float + Copy + std::iter::Sum; + N: num::Float + Clone + std::iter::Sum, + Self::Output: std::iter::FromIterator>; + + /// Scales the complex vector in place to the given (real) magnitude. + fn mut_normalize_to(&mut self, magnitude: N) -> Result<(), VectorError> + where + N: num::Float + Copy + std::iter::Sum, + { + let n = self.norm(); + if n == N::zero() { + return Err(VectorError::ZeroVectorError("Cannot normalize a zero vector".to_string())); + } + let scale = magnitude / n; + for a in self.as_mut_slice().iter_mut() { + *a = *a * scale; + } + Ok(()) + } + + /// Hermitian dot product: for all complex types + fn dot(&self, other: &Self) -> Complex + where + N: num::Num + Copy + std::iter::Sum + std::ops::Neg, + { + self.as_slice().iter().zip(other.as_slice()).map(|(a, b)| *a * b.conj()).sum() + } + + /// Hermitian dot product as f64 (sums real part): for all complex types. + fn dot_to_f64(&self, other: &Self) -> f64 + where + N: num::Num + num::ToPrimitive + Copy + std::ops::Neg, + { + self.as_slice() + .iter() + .zip(other.as_slice()) + .map(|(a, b)| { + let prod = *a * b.conj(); + prod.re.to_f64().unwrap() + }) + .sum() + } /// Linear interpolation between self and end by real weight in [0, 1]. - fn lerp(&self, end: &Self, weight: F) -> Self::Output + fn lerp(&self, end: &Self, weight: N) -> Result where - F: num::Float + Clone + PartialOrd; + N: num::Float + Clone + PartialOrd; + + /// In-place linear interpolation between self and end by real weight in [0, 1]. + fn mut_lerp(&mut self, end: &Self, weight: N) -> Result<(), VectorError> + where + N: num::Float + Copy + PartialOrd, + { + if weight < N::zero() || weight > N::one() { + return Err(VectorError::OutOfRangeError("weight must be in [0, 1]".to_string())); + } + let w = Complex::new(weight, N::zero()); + let one_minus_w = Complex::new(N::one() - weight, N::zero()); + for (a, b) in self.as_mut_slice().iter_mut().zip(end.as_slice()) { + *a = one_minus_w * *a + w * *b; + } + Ok(()) + } /// Midpoint fn midpoint(&self, end: &Self) -> Self::Output where - F: num::Float + Clone, + N: num::Float + Clone, { - self.lerp(end, num::cast(0.5).unwrap()) + // OK to unwrap because by definition it uses an in-range weight + self.lerp(end, num::cast(0.5).unwrap()).unwrap() } /// Euclidean distance (L2 norm) between self and other (returns real). - fn distance(&self, other: &Self) -> F + fn distance(&self, other: &Self) -> N where - F: num::Float + Clone + std::iter::Sum, + N: num::Float + Clone + std::iter::Sum, { self.as_slice() .iter() @@ -320,22 +564,86 @@ pub trait VectorOpsComplexFloat: VectorBase> { let diff = *a - *b; diff.norm_sqr() }) - .sum::() + .sum::() .sqrt() } + /// Manhattan (L1) distance between self and other (sum of magnitudes of differences). + fn manhattan_distance(&self, other: &Self) -> N + where + N: num::Float + Clone + std::iter::Sum, + { + self.as_slice().iter().zip(other.as_slice()).map(|(a, b)| (*a - *b).norm()).sum() + } + + /// Chebyshev (L∞) distance between self and other (maximum magnitude of differences). + fn chebyshev_distance(&self, other: &Self) -> N + where + N: num::Float + Clone + PartialOrd, + { + self.as_slice() + .iter() + .zip(other.as_slice()) + .map(|(a, b)| (*a - *b).norm()) + .fold(N::zero(), |acc, x| acc.max(x)) + } + + /// Minkowski (Lp) distance between self and other. + fn minkowski_distance(&self, other: &Self, p: N) -> Result + where + N: num::Float + Clone + std::iter::Sum, + { + if p < N::one() { + return Err(VectorError::OutOfRangeError("p must be >= 1".to_string())); + } + Ok(self + .as_slice() + .iter() + .zip(other.as_slice()) + .map(|(a, b)| (*a - *b).norm().powf(p.clone())) + .sum::() + .powf(N::one() / p)) + } + /// Euclidean norm (magnitude) of the vector (returns real). - fn norm(&self) -> F + fn norm(&self) -> N + where + N: num::Float + Clone + std::iter::Sum, + { + self.as_slice().iter().map(|a| a.norm_sqr()).sum::().sqrt() + } + + /// L1 norm (sum of magnitudes). + fn l1_norm(&self) -> N where - F: num::Float + Clone + std::iter::Sum, + N: num::Float + Clone + std::iter::Sum, { - self.as_slice().iter().map(|a| a.norm_sqr()).sum::().sqrt() + self.as_slice().iter().map(|a| a.norm()).sum() + } + + /// L∞ norm (maximum magnitude). + fn linf_norm(&self) -> N + where + N: num::Float + Clone + PartialOrd, + { + self.as_slice().iter().map(|a| a.norm()).fold(N::zero(), |acc, x| acc.max(x)) + } + + /// Lp norm (generalized Minkowski norm for complex). + fn lp_norm(&self, p: N) -> Result + where + N: num::Float + Clone + std::iter::Sum, + { + if p < N::one() { + return Err(VectorError::OutOfRangeError("p must be >= 1".to_string())); + } + Ok(self.as_slice().iter().map(|a| a.norm().powf(p.clone())).sum::().powf(N::one() / p)) } /// Alias for norm (magnitude). - fn magnitude(&self) -> F + fn magnitude(&self) -> N where - F: num::Float + Clone + std::iter::Sum, + N: num::Float + Clone + std::iter::Sum, { self.norm() } From 18da0e1eda3b23c52a711a9aee1f2e09b688ab7f Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Sat, 10 May 2025 10:00:35 -0400 Subject: [PATCH 03/82] remove unnecessary clone --- src/types/flexvector.rs | 12 ++++++------ src/types/traits.rs | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index 4709516..28d9022 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -201,13 +201,13 @@ where if weight < T::zero() || weight > T::one() { return Err(VectorError::OutOfRangeError("weight must be in [0, 1]".to_string())); } - let w = weight.clone(); - let one_minus_w = T::one() - w.clone(); + let w = weight; + let one_minus_w = T::one() - w; Ok(self .components .iter() .zip(&end.components) - .map(|(a, b)| one_minus_w.clone() * a.clone() + w.clone() * b.clone()) + .map(|(a, b)| one_minus_w * *a + w * *b) .collect()) } } @@ -256,13 +256,13 @@ where if weight < N::zero() || weight > N::one() { return Err(VectorError::OutOfRangeError("weight must be in [0, 1]".to_string())); } - let w = Complex::new(weight.clone(), N::zero()); - let one_minus_w = Complex::new(N::one() - weight.clone(), N::zero()); + let w = Complex::new(weight, N::zero()); + let one_minus_w = Complex::new(N::one() - weight, N::zero()); Ok(self .components .iter() .zip(&end.components) - .map(|(a, b)| one_minus_w.clone() * a.clone() + w.clone() * b.clone()) + .map(|(a, b)| one_minus_w * *a + w * *b) .collect()) } } diff --git a/src/types/traits.rs b/src/types/traits.rs index ee54d11..0894582 100644 --- a/src/types/traits.rs +++ b/src/types/traits.rs @@ -420,7 +420,7 @@ pub trait VectorOpsFloat: VectorBase { .as_slice() .iter() .zip(other.as_slice()) - .map(|(a, b)| (*a - *b).abs().powf(p.clone())) + .map(|(a, b)| (*a - *b).abs().powf(p)) .sum::() .powf(T::one() / p)) } @@ -600,7 +600,7 @@ pub trait VectorOpsComplex: VectorBase> { .as_slice() .iter() .zip(other.as_slice()) - .map(|(a, b)| (*a - *b).norm().powf(p.clone())) + .map(|(a, b)| (*a - *b).norm().powf(p)) .sum::() .powf(N::one() / p)) } @@ -637,7 +637,7 @@ pub trait VectorOpsComplex: VectorBase> { if p < N::one() { return Err(VectorError::OutOfRangeError("p must be >= 1".to_string())); } - Ok(self.as_slice().iter().map(|a| a.norm().powf(p.clone())).sum::().powf(N::one() / p)) + Ok(self.as_slice().iter().map(|a| a.norm().powf(p)).sum::().powf(N::one() / p)) } /// Alias for norm (magnitude). From b73c365c18d32e2ea85d7a3950077e478787d315 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Sat, 10 May 2025 14:23:48 -0400 Subject: [PATCH 04/82] add inline --- src/types/flexvector.rs | 30 ++++++++++++++++++++ src/types/traits.rs | 63 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 93 insertions(+) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index 28d9022..1c98440 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -35,16 +35,19 @@ where T: Num + Clone + Default + Sync + Send, { /// Creates a new, empty FlexVector. + #[inline] pub fn new() -> Self { Self { components: Vec::new() } } /// Creates a new FlexVector with a pre-allocated capacity. + #[inline] pub fn with_capacity(capacity: usize) -> Self { Self { components: Vec::with_capacity(capacity) } } /// Returns a new FlexVector of the given length, filled with zeros. + #[inline] pub fn zero(len: usize) -> Self where T: num::Zero, @@ -53,6 +56,7 @@ where } /// Returns a new FlexVector of the given length, filled with ones. + #[inline] pub fn one(len: usize) -> Self where T: num::One, @@ -61,16 +65,19 @@ where } /// Returns a new FlexVector of the given length, filled with the given value. + #[inline] pub fn filled(len: usize, value: T) -> Self { Self { components: vec![value; len] } } /// Creates a new FlexVector from a slice. + #[inline] pub fn from_slice(slice: &[T]) -> Self { Self { components: slice.to_vec() } } /// Creates a FlexVector from a Vec. + #[inline] pub fn from_vec(vec: Vec) -> Self { Self { components: vec } } @@ -85,6 +92,7 @@ impl Default for FlexVector where T: num::Num + Clone + Default + Sync + Send, { + #[inline] fn default() -> Self { Self::new() } @@ -114,11 +122,13 @@ where T: num::Num + Clone + Sync + Send, { /// Returns an immutable slice of the FlexVector's components. + #[inline] fn as_slice(&self) -> &[T] { &self.components } /// Returns a mutable slice of the FlexVector's components. + #[inline] fn as_mut_slice(&mut self) -> &mut [T] { &mut self.components[..] } @@ -136,6 +146,7 @@ where { type Output = Vec; + #[inline] fn translate(&self, other: &Self) -> Self::Output where T: num::Num + Clone, @@ -143,6 +154,7 @@ where self.components.iter().zip(&other.components).map(|(a, b)| a.clone() + b.clone()).collect() } + #[inline] fn scale(&self, scalar: T) -> Self::Output where T: num::Num + Copy, @@ -151,6 +163,7 @@ where self.as_slice().iter().map(|a| *a * scalar).collect() } + #[inline] fn negate(&self) -> Self::Output where T: std::ops::Neg + Clone, @@ -172,6 +185,7 @@ where { type Output = Vec; + #[inline] fn normalize(&self) -> Result { let n = self.norm(); if n == T::zero() { @@ -181,6 +195,7 @@ where } /// Returns a new vector with the same direction and the given magnitude. + #[inline] fn normalize_to(&self, magnitude: T) -> Result where T: num::Float + Clone + std::iter::Sum, @@ -194,6 +209,7 @@ where Ok(self.as_slice().iter().map(|a| *a * scale).collect()) } + #[inline] fn lerp(&self, end: &Self, weight: T) -> Result where T: num::Float + Clone + PartialOrd, @@ -224,6 +240,7 @@ where { type Output = Vec>; + #[inline] fn normalize(&self) -> Result where N: num::Float + Clone + std::iter::Sum, @@ -236,6 +253,7 @@ where Ok(self.components.iter().map(|a| *a / n).collect()) } + #[inline] fn normalize_to(&self, magnitude: N) -> Result where N: num::Float + Clone + std::iter::Sum, @@ -249,6 +267,7 @@ where Ok(self.as_slice().iter().map(|a| *a * scale).collect()) } + #[inline] fn lerp(&self, end: &Self, weight: N) -> Result where N: num::Float + Clone + PartialOrd, @@ -277,37 +296,44 @@ where T: num::Num + Clone + Sync + Send, { /// Adds an element to the end of the vector. + #[inline] pub fn push(&mut self, value: T) { self.components.push(value); } /// Removes the last element and returns it, or None if empty. + #[inline] pub fn pop(&mut self) -> Option { self.components.pop() } /// Inserts an element at position index, shifting all elements after it. + #[inline] pub fn insert(&mut self, index: usize, value: T) { self.components.insert(index, value); } /// Removes and returns the element at position index. + #[inline] pub fn remove(&mut self, index: usize) -> T { self.components.remove(index) } /// Resizes the vector in-place. + #[inline] pub fn resize(&mut self, new_len: usize, value: T) { self.components.resize(new_len, value); } /// Clears the vector, removing all values. + #[inline] pub fn clear(&mut self) { self.components.clear(); } /// Returns a mutable reference to a FlexVector index value or range, /// or `None` if the index is out of bounds. + #[inline] pub fn get_mut(&mut self, index: I) -> Option<&mut I::Output> where I: std::slice::SliceIndex<[T]>, @@ -316,11 +342,13 @@ where } /// Returns an iterator over mutable references to the elements. + #[inline] pub fn iter_mut(&mut self) -> impl Iterator { self.components.iter_mut() } /// Returns a new FlexVector with each element mapped to a new value using the provided closure or function. + #[inline] pub fn map(&self, mut f: F) -> FlexVector where F: FnMut(T) -> U, @@ -331,6 +359,7 @@ where } /// Applies a closure or function to each element, modifying them in place. + #[inline] pub fn mut_map(&mut self, mut f: F) where F: FnMut(T) -> T, @@ -342,6 +371,7 @@ where } /// Cosine similarity between self and other. + #[inline] pub fn cosine_similarity(&self, other: &Self) -> T where T: num::Float + Clone + std::iter::Sum, diff --git a/src/types/traits.rs b/src/types/traits.rs index 0894582..f96ee49 100644 --- a/src/types/traits.rs +++ b/src/types/traits.rs @@ -13,49 +13,58 @@ pub trait VectorBase { fn as_mut_slice(&mut self) -> &mut [T]; /// ... + #[inline] fn len(&self) -> usize { self.as_slice().len() } /// ... + #[inline] fn is_empty(&self) -> bool { self.as_slice().is_empty() } // --- Element access --- /// ... + #[inline] fn get(&self, index: usize) -> Option<&T> { self.as_slice().get(index) } /// ... + #[inline] fn first(&self) -> Option<&T> { self.as_slice().first() } /// ... + #[inline] fn last(&self) -> Option<&T> { self.as_slice().last() } // --- Iteration --- /// ... + #[inline] fn iter(&self) -> std::slice::Iter<'_, T> { self.as_slice().iter() } /// ... + #[inline] fn iter_rev(&self) -> std::iter::Rev> { self.as_slice().iter().rev() } /// ... + #[inline] fn enumerate(&self) -> std::iter::Enumerate> { self.iter().enumerate() } // --- Conversion --- /// ... + #[inline] fn to_vec(&self) -> Vec where T: Clone, @@ -64,6 +73,7 @@ pub trait VectorBase { } /// ... + #[inline] fn pretty(&self) -> String where T: std::fmt::Debug, @@ -73,6 +83,7 @@ pub trait VectorBase { // --- Search/containment --- /// ... + #[inline] fn contains(&self, x: &T) -> bool where T: PartialEq, @@ -81,6 +92,7 @@ pub trait VectorBase { } /// ... + #[inline] fn starts_with(&self, needle: &[T]) -> bool where T: PartialEq, @@ -89,6 +101,7 @@ pub trait VectorBase { } /// ... + #[inline] fn ends_with(&self, needle: &[T]) -> bool where T: PartialEq, @@ -97,6 +110,7 @@ pub trait VectorBase { } /// ... + #[inline] fn position

(&self, predicate: P) -> Option where P: FnMut(&T) -> bool, @@ -105,6 +119,7 @@ pub trait VectorBase { } /// ... + #[inline] fn rposition

(&self, predicate: P) -> Option where P: FnMut(&T) -> bool, @@ -114,21 +129,25 @@ pub trait VectorBase { // --- Slicing/chunking --- /// ... + #[inline] fn windows(&self, size: usize) -> std::slice::Windows<'_, T> { self.as_slice().windows(size) } /// ... + #[inline] fn chunks(&self, size: usize) -> std::slice::Chunks<'_, T> { self.as_slice().chunks(size) } /// ... + #[inline] fn split_at(&self, mid: usize) -> (&[T], &[T]) { self.as_slice().split_at(mid) } /// ... + #[inline] fn split(&self, pred: F) -> std::slice::Split<'_, T, F> where F: FnMut(&T) -> bool, @@ -137,6 +156,7 @@ pub trait VectorBase { } /// ... + #[inline] fn splitn(&self, n: usize, pred: F) -> std::slice::SplitN<'_, T, F> where F: FnMut(&T) -> bool, @@ -145,6 +165,7 @@ pub trait VectorBase { } /// ... + #[inline] fn rsplit(&self, pred: F) -> std::slice::RSplit<'_, T, F> where F: FnMut(&T) -> bool, @@ -153,6 +174,7 @@ pub trait VectorBase { } /// ... + #[inline] fn rsplitn(&self, n: usize, pred: F) -> std::slice::RSplitN<'_, T, F> where F: FnMut(&T) -> bool, @@ -162,6 +184,7 @@ pub trait VectorBase { // --- Pointer access --- /// ... + #[inline] fn as_ptr(&self) -> *const T { self.as_slice().as_ptr() } @@ -171,11 +194,13 @@ pub trait VectorOps: VectorBase { type Output; /// ... + #[inline] fn translate(&self, other: &Self) -> Self::Output where T: num::Num + Copy; /// ... + #[inline] fn mut_translate(&mut self, other: &Self) where T: num::Num + Copy, @@ -186,12 +211,14 @@ pub trait VectorOps: VectorBase { } /// Returns a new vector scaled by the given scalar. + #[inline] fn scale(&self, scalar: T) -> Self::Output where T: num::Num + Copy, Self::Output: std::iter::FromIterator; /// Scales the vector in place by the given scalar. + #[inline] fn mut_scale(&mut self, scalar: T) where T: num::Num + Copy, @@ -208,6 +235,7 @@ pub trait VectorOps: VectorBase { Self::Output: std::iter::FromIterator; /// Negates all elements in place. + #[inline] fn mut_negate(&mut self) where T: std::ops::Neg + Clone, @@ -218,6 +246,7 @@ pub trait VectorOps: VectorBase { } /// Sets all elements to zero in place. + #[inline] fn mut_zero(&mut self) where T: num::Zero + Clone, @@ -228,6 +257,7 @@ pub trait VectorOps: VectorBase { } /// ... + #[inline] fn dot(&self, other: &Self) -> T where T: num::Num + Copy + std::iter::Sum, @@ -236,6 +266,7 @@ pub trait VectorOps: VectorBase { } /// Dot product as f64 (for integer and float types). + #[inline] fn dot_to_f64(&self, other: &Self) -> f64 where T: num::ToPrimitive, @@ -248,6 +279,7 @@ pub trait VectorOps: VectorBase { } /// ... + #[inline] fn sum(&self) -> T where T: num::Num + Copy + std::iter::Sum, @@ -256,6 +288,7 @@ pub trait VectorOps: VectorBase { } /// ... + #[inline] fn product(&self) -> T where T: num::Num + Copy + std::iter::Product, @@ -264,6 +297,7 @@ pub trait VectorOps: VectorBase { } /// ... + #[inline] fn min(&self) -> Option where T: Ord + Copy, @@ -272,6 +306,7 @@ pub trait VectorOps: VectorBase { } /// ... + #[inline] fn max(&self) -> Option where T: Ord + Copy, @@ -280,6 +315,7 @@ pub trait VectorOps: VectorBase { } /// L1 norm (sum of absolute values). + #[inline] fn l1_norm(&self) -> T where T: num::Signed + Copy + std::iter::Sum, @@ -288,6 +324,7 @@ pub trait VectorOps: VectorBase { } /// L∞ norm (maximum absolute value). + #[inline] fn linf_norm(&self) -> T where T: num::Signed + Copy + PartialOrd, @@ -309,6 +346,7 @@ pub trait VectorOpsFloat: VectorBase { Self::Output: std::iter::FromIterator; /// ... + #[inline] fn mut_normalize(&mut self) -> Result<(), VectorError> where T: num::Float + Copy + std::iter::Sum, @@ -330,6 +368,7 @@ pub trait VectorOpsFloat: VectorBase { Self::Output: std::iter::FromIterator; /// ... + #[inline] fn mut_normalize_to(&mut self, magnitude: T) -> Result<(), VectorError> where T: num::Float + Copy + std::iter::Sum, @@ -351,6 +390,7 @@ pub trait VectorOpsFloat: VectorBase { T: num::Float + Clone + PartialOrd; /// In-place linear interpolation between self and end by weight in [0, 1]. + #[inline] fn mut_lerp(&mut self, end: &Self, weight: T) -> Result<(), VectorError> where T: num::Float + Copy + PartialOrd, @@ -367,6 +407,7 @@ pub trait VectorOpsFloat: VectorBase { } /// Midpoint + #[inline] fn midpoint(&self, end: &Self) -> Self::Output where T: num::Float + Clone, @@ -376,6 +417,7 @@ pub trait VectorOpsFloat: VectorBase { } /// Euclidean distance between self and other. + #[inline] fn distance(&self, other: &Self) -> T where T: num::Float + Clone + std::iter::Sum, @@ -389,6 +431,7 @@ pub trait VectorOpsFloat: VectorBase { } /// Manhattan (L1) distance between self and other. + #[inline] fn manhattan_distance(&self, other: &Self) -> T where T: num::Float + Clone + std::iter::Sum, @@ -397,6 +440,7 @@ pub trait VectorOpsFloat: VectorBase { } /// Chebyshev (L∞) distance between self and other. + #[inline] fn chebyshev_distance(&self, other: &Self) -> T where T: num::Float + Clone + PartialOrd, @@ -409,6 +453,7 @@ pub trait VectorOpsFloat: VectorBase { } /// Minkowski (Lp) distance between self and other. + #[inline] fn minkowski_distance(&self, other: &Self, p: T) -> Result where T: num::Float + Clone + std::iter::Sum, @@ -426,6 +471,7 @@ pub trait VectorOpsFloat: VectorBase { } /// Euclidean norm (magnitude) of the vector. + #[inline] fn norm(&self) -> T where T: num::Float + Clone + std::iter::Sum, @@ -434,6 +480,7 @@ pub trait VectorOpsFloat: VectorBase { } /// Lp norm (generalized Minkowski norm). + #[inline] fn lp_norm(&self, p: T) -> Result where T: num::Float + Copy + std::iter::Sum, @@ -445,6 +492,7 @@ pub trait VectorOpsFloat: VectorBase { } /// Alias for norm (magnitude). + #[inline] fn magnitude(&self) -> T where T: num::Float + Clone + std::iter::Sum, @@ -463,6 +511,7 @@ pub trait VectorOpsComplex: VectorBase> { Self::Output: std::iter::FromIterator>; /// ... + #[inline] fn mut_normalize(&mut self) -> Result<(), VectorError> where N: num::Float + Copy + std::iter::Sum, @@ -484,6 +533,7 @@ pub trait VectorOpsComplex: VectorBase> { Self::Output: std::iter::FromIterator>; /// Scales the complex vector in place to the given (real) magnitude. + #[inline] fn mut_normalize_to(&mut self, magnitude: N) -> Result<(), VectorError> where N: num::Float + Copy + std::iter::Sum, @@ -500,6 +550,7 @@ pub trait VectorOpsComplex: VectorBase> { } /// Hermitian dot product: for all complex types + #[inline] fn dot(&self, other: &Self) -> Complex where N: num::Num + Copy + std::iter::Sum + std::ops::Neg, @@ -508,6 +559,7 @@ pub trait VectorOpsComplex: VectorBase> { } /// Hermitian dot product as f64 (sums real part): for all complex types. + #[inline] fn dot_to_f64(&self, other: &Self) -> f64 where N: num::Num + num::ToPrimitive + Copy + std::ops::Neg, @@ -528,6 +580,7 @@ pub trait VectorOpsComplex: VectorBase> { N: num::Float + Clone + PartialOrd; /// In-place linear interpolation between self and end by real weight in [0, 1]. + #[inline] fn mut_lerp(&mut self, end: &Self, weight: N) -> Result<(), VectorError> where N: num::Float + Copy + PartialOrd, @@ -544,6 +597,7 @@ pub trait VectorOpsComplex: VectorBase> { } /// Midpoint + #[inline] fn midpoint(&self, end: &Self) -> Self::Output where N: num::Float + Clone, @@ -553,6 +607,7 @@ pub trait VectorOpsComplex: VectorBase> { } /// Euclidean distance (L2 norm) between self and other (returns real). + #[inline] fn distance(&self, other: &Self) -> N where N: num::Float + Clone + std::iter::Sum, @@ -569,6 +624,7 @@ pub trait VectorOpsComplex: VectorBase> { } /// Manhattan (L1) distance between self and other (sum of magnitudes of differences). + #[inline] fn manhattan_distance(&self, other: &Self) -> N where N: num::Float + Clone + std::iter::Sum, @@ -577,6 +633,7 @@ pub trait VectorOpsComplex: VectorBase> { } /// Chebyshev (L∞) distance between self and other (maximum magnitude of differences). + #[inline] fn chebyshev_distance(&self, other: &Self) -> N where N: num::Float + Clone + PartialOrd, @@ -589,6 +646,7 @@ pub trait VectorOpsComplex: VectorBase> { } /// Minkowski (Lp) distance between self and other. + #[inline] fn minkowski_distance(&self, other: &Self, p: N) -> Result where N: num::Float + Clone + std::iter::Sum, @@ -606,6 +664,7 @@ pub trait VectorOpsComplex: VectorBase> { } /// Euclidean norm (magnitude) of the vector (returns real). + #[inline] fn norm(&self) -> N where N: num::Float + Clone + std::iter::Sum, @@ -614,6 +673,7 @@ pub trait VectorOpsComplex: VectorBase> { } /// L1 norm (sum of magnitudes). + #[inline] fn l1_norm(&self) -> N where N: num::Float + Clone + std::iter::Sum, @@ -622,6 +682,7 @@ pub trait VectorOpsComplex: VectorBase> { } /// L∞ norm (maximum magnitude). + #[inline] fn linf_norm(&self) -> N where N: num::Float + Clone + PartialOrd, @@ -630,6 +691,7 @@ pub trait VectorOpsComplex: VectorBase> { } /// Lp norm (generalized Minkowski norm for complex). + #[inline] fn lp_norm(&self, p: N) -> Result where N: num::Float + Clone + std::iter::Sum, @@ -641,6 +703,7 @@ pub trait VectorOpsComplex: VectorBase> { } /// Alias for norm (magnitude). + #[inline] fn magnitude(&self) -> N where N: num::Float + Clone + std::iter::Sum, From 6ed850f08af75b1d196056114c32bd7ca973eb2a Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Sat, 10 May 2025 14:26:35 -0400 Subject: [PATCH 05/82] remove inline in trait signatures --- src/types/traits.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/types/traits.rs b/src/types/traits.rs index f96ee49..0944d0e 100644 --- a/src/types/traits.rs +++ b/src/types/traits.rs @@ -194,7 +194,6 @@ pub trait VectorOps: VectorBase { type Output; /// ... - #[inline] fn translate(&self, other: &Self) -> Self::Output where T: num::Num + Copy; @@ -211,7 +210,6 @@ pub trait VectorOps: VectorBase { } /// Returns a new vector scaled by the given scalar. - #[inline] fn scale(&self, scalar: T) -> Self::Output where T: num::Num + Copy, From 18a447347771aed0d8aac674aaecf1895746fd3e Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Sat, 10 May 2025 15:49:24 -0400 Subject: [PATCH 06/82] add FlexVector FromIter trait support --- src/types/flexvector.rs | 46 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index 1c98440..243040b 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -1,5 +1,7 @@ //! FlexVector type. +use std::iter::FromIterator; + use crate::{ impl_vector_binop, impl_vector_binop_assign, impl_vector_scalar_div_op, impl_vector_scalar_div_op_assign, impl_vector_scalar_op, impl_vector_scalar_op_assign, @@ -112,6 +114,20 @@ where } } +// ================================ +// +// FromIter trait impl +// +// ================================ +impl FromIterator for FlexVector +where + T: num::Num + Clone + Sync + Send, +{ + fn from_iter>(iter: I) -> Self { + FlexVector { components: iter.into_iter().collect() } + } +} + // ================================ // // VectorBase trait impl @@ -615,6 +631,36 @@ mod tests { assert_eq!(format!("{}", v), "[]"); } + // ================================ + // + // FromIterator trait tests + // + // ================================ + #[test] + fn test_from_iter_i32() { + let v: FlexVector = (1..4).collect(); + assert_eq!(v.as_slice(), &[1, 2, 3]); + } + + #[test] + fn test_from_iter_f64() { + let v: FlexVector = vec![1.1, 2.2, 3.3].into_iter().collect(); + assert_eq!(v.as_slice(), &[1.1, 2.2, 3.3]); + } + + #[test] + fn test_from_iter_complex() { + let data = vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; + let v: FlexVector> = data.clone().into_iter().collect(); + assert_eq!(v.as_slice(), &[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + } + + #[test] + fn test_from_iter_empty() { + let v: FlexVector = Vec::::new().into_iter().collect(); + assert!(v.is_empty()); + } + // ================================ // // VectorBase trait method tests From b86f8073c861ea809f70973a56eb5fdcbb1cd698 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Sat, 10 May 2025 15:55:25 -0400 Subject: [PATCH 07/82] add cross and angle_with vector operation methods --- src/types/flexvector.rs | 46 ++++++++++++++++++++++++++++++++++++----- src/types/traits.rs | 10 +++++++++ 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index 243040b..6c82088 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -11,8 +11,7 @@ use crate::{ use crate::errors::VectorError; -use num::Complex; -use num::Num; +use num::{Complex, Num}; /// A dynamic, heap-allocated vector type for n-dimensional real and complex scalar data. /// @@ -160,7 +159,7 @@ impl VectorOps for FlexVector where T: num::Num + Clone + Sync + Send, { - type Output = Vec; + type Output = Self; #[inline] fn translate(&self, other: &Self) -> Self::Output @@ -187,6 +186,25 @@ where { self.as_slice().iter().map(|a| -a.clone()).collect() } + + /// Cross product (only for 3D vectors). + #[inline] + fn cross(&self, other: &Self) -> Result + where + T: num::Num + Copy, + Self::Output: std::iter::FromIterator, + { + if self.len() != 3 || other.len() != 3 { + return Err(VectorError::OutOfRangeError( + "Cross product is only defined for 3D vectors".to_string(), + )); + } + let a = self.as_slice(); + let b = other.as_slice(); + let result = + [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]]; + Ok(result.into_iter().collect()) + } } // ================================ @@ -199,7 +217,7 @@ impl VectorOpsFloat for FlexVector where T: num::Float + Clone + PartialOrd + std::iter::Sum + Sync + Send, { - type Output = Vec; + type Output = Self; #[inline] fn normalize(&self) -> Result { @@ -242,6 +260,24 @@ where .map(|(a, b)| one_minus_w * *a + w * *b) .collect()) } + + #[inline] + fn angle_with(&self, other: &Self) -> Result + where + T: num::Float + Clone + std::iter::Sum, + { + let norm_self = self.norm(); + let norm_other = other.norm(); + if norm_self == T::zero() || norm_other == T::zero() { + return Err(VectorError::ZeroVectorError( + "Cannot compute angle with zero vector".to_string(), + )); + } + let dot = self.dot(other); + let cos_theta = dot / (norm_self * norm_other); + let cos_theta = cos_theta.max(-T::one()).min(T::one()); + Ok(cos_theta.acos()) + } } // ================================ @@ -254,7 +290,7 @@ impl VectorOpsComplex for FlexVector> where N: num::Float + Clone + PartialOrd + std::iter::Sum + Sync + Send, { - type Output = Vec>; + type Output = Self; #[inline] fn normalize(&self) -> Result diff --git a/src/types/traits.rs b/src/types/traits.rs index 0944d0e..8b2b7e4 100644 --- a/src/types/traits.rs +++ b/src/types/traits.rs @@ -276,6 +276,12 @@ pub trait VectorOps: VectorBase { .sum() } + ///... + fn cross(&self, other: &Self) -> Result + where + T: num::Num + Copy, + Self::Output: std::iter::FromIterator; + /// ... #[inline] fn sum(&self) -> T @@ -497,6 +503,10 @@ pub trait VectorOpsFloat: VectorBase { { self.norm() } + + fn angle_with(&self, other: &Self) -> Result + where + T: num::Float + Clone + std::iter::Sum; } pub trait VectorOpsComplex: VectorBase> { From 46a09e772f514f6fafb5acd860929d9baad468cc Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Sat, 10 May 2025 19:44:50 -0400 Subject: [PATCH 08/82] expand trait impl, refactor trait bounds --- src/types/flexvector.rs | 834 +++++++++++++++++++++++++++++++++++++--- src/types/traits.rs | 13 + 2 files changed, 804 insertions(+), 43 deletions(-) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index 6c82088..d3fc129 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -1,6 +1,7 @@ //! FlexVector type. use std::iter::FromIterator; +use std::ops::{Deref, DerefMut}; use crate::{ impl_vector_binop, impl_vector_binop_assign, impl_vector_scalar_div_op, @@ -11,17 +12,14 @@ use crate::{ use crate::errors::VectorError; -use num::{Complex, Num}; +use num::{Complex, Zero}; /// A dynamic, heap-allocated vector type for n-dimensional real and complex scalar data. /// /// The length of the vector is determined at runtime and stored on the heap. /// This type is analogous to `Vector` but supports dynamic sizing. #[derive(Clone, Debug)] -pub struct FlexVector -where - T: Num + Clone + Sync + Send, -{ +pub struct FlexVector { /// Ordered n-dimensional scalar values. pub components: Vec, } @@ -31,10 +29,7 @@ where // Constructors // // ================================ -impl FlexVector -where - T: Num + Clone + Default + Sync + Send, -{ +impl FlexVector { /// Creates a new, empty FlexVector. #[inline] pub fn new() -> Self { @@ -51,7 +46,7 @@ where #[inline] pub fn zero(len: usize) -> Self where - T: num::Zero, + T: num::Zero + Clone, { Self { components: vec![T::zero(); len] } } @@ -60,20 +55,26 @@ where #[inline] pub fn one(len: usize) -> Self where - T: num::One, + T: num::One + Clone, { Self { components: vec![T::one(); len] } } /// Returns a new FlexVector of the given length, filled with the given value. #[inline] - pub fn filled(len: usize, value: T) -> Self { + pub fn filled(len: usize, value: T) -> Self + where + T: Clone, + { Self { components: vec![value; len] } } /// Creates a new FlexVector from a slice. #[inline] - pub fn from_slice(slice: &[T]) -> Self { + pub fn from_slice(slice: &[T]) -> Self + where + T: Clone, + { Self { components: slice.to_vec() } } @@ -89,10 +90,7 @@ where // Default trait impl // // ================================ -impl Default for FlexVector -where - T: num::Num + Clone + Default + Sync + Send, -{ +impl Default for FlexVector { #[inline] fn default() -> Self { Self::new() @@ -106,7 +104,7 @@ where // ================================ impl std::fmt::Display for FlexVector where - T: num::Num + Clone + Sync + Send + std::fmt::Debug, + T: std::fmt::Debug, { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{:?}", self.components) @@ -115,13 +113,10 @@ where // ================================ // -// FromIter trait impl +// FromIterator trait impl // // ================================ -impl FromIterator for FlexVector -where - T: num::Num + Clone + Sync + Send, -{ +impl FromIterator for FlexVector { fn from_iter>(iter: I) -> Self { FlexVector { components: iter.into_iter().collect() } } @@ -129,13 +124,159 @@ where // ================================ // -// VectorBase trait impl +// Deref/DerefMut trait impl // // ================================ -impl VectorBase for FlexVector +impl Deref for FlexVector { + type Target = [T]; + #[inline] + fn deref(&self) -> &Self::Target { + &self.components + } +} + +impl DerefMut for FlexVector { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.components + } +} + +// ================================ +// +// AsRef/AsMut trait impl +// +// ================================ +impl AsRef<[T]> for FlexVector { + #[inline] + fn as_ref(&self) -> &[T] { + &self.components + } +} +impl AsMut<[T]> for FlexVector { + #[inline] + fn as_mut(&mut self) -> &mut [T] { + &mut self.components + } +} + +// ================================ +// +// IntoIterator trait impl +// +// ================================ +impl IntoIterator for FlexVector { + type Item = T; + type IntoIter = std::vec::IntoIter; + fn into_iter(self) -> Self::IntoIter { + self.components.into_iter() + } +} +impl<'a, T> IntoIterator for &'a FlexVector { + type Item = &'a T; + type IntoIter = std::slice::Iter<'a, T>; + fn into_iter(self) -> Self::IntoIter { + self.components.iter() + } +} +impl<'a, T> IntoIterator for &'a mut FlexVector { + type Item = &'a mut T; + type IntoIter = std::slice::IterMut<'a, T>; + fn into_iter(self) -> Self::IntoIter { + self.components.iter_mut() + } +} + +// ================================ +// +// PartialEq/Eq trait impl +// +// ================================ +impl PartialEq for FlexVector where - T: num::Num + Clone + Sync + Send, + T: PartialEq, { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.components == other.components + } +} +impl Eq for FlexVector where T: Eq {} + +// ================================ +// +// PartialOrd/Ord trait impl +// +// ================================ +impl PartialOrd for FlexVector +where + T: PartialOrd, +{ + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + self.components.partial_cmp(&other.components) + } +} +impl Ord for FlexVector +where + T: Ord, +{ + #[inline] + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.components.cmp(&other.components) + } +} + +// ================================ +// +// Hash trait impl +// +// ================================ +impl std::hash::Hash for FlexVector +where + T: std::hash::Hash, +{ + fn hash(&self, state: &mut H) { + self.components.hash(state) + } +} + +// ================================ +// +// From trait impl +// +// ================================ +impl From> for FlexVector { + #[inline] + fn from(vec: Vec) -> Self { + FlexVector { components: vec } + } +} +impl From<&[T]> for FlexVector { + #[inline] + fn from(slice: &[T]) -> Self { + FlexVector { components: slice.to_vec() } + } +} + +// ================================ +// +// Extend trait impl +// +// ================================ +impl Extend for FlexVector { + #[inline] + fn extend>(&mut self, iter: I) { + self.components.extend(iter) + } +} + +// ================================ +// +// VectorBase trait impl +// +// ================================ +impl VectorBase for FlexVector { /// Returns an immutable slice of the FlexVector's components. #[inline] fn as_slice(&self) -> &[T] { @@ -157,7 +298,7 @@ where // TODO: add tests impl VectorOps for FlexVector where - T: num::Num + Clone + Sync + Send, + T: num::Num + Clone, { type Output = Self; @@ -215,7 +356,7 @@ where // TODO: add tests impl VectorOpsFloat for FlexVector where - T: num::Float + Clone + PartialOrd + std::iter::Sum + Sync + Send, + T: num::Float + Clone + std::iter::Sum, { type Output = Self; @@ -232,7 +373,6 @@ where #[inline] fn normalize_to(&self, magnitude: T) -> Result where - T: num::Float + Clone + std::iter::Sum, Self::Output: std::iter::FromIterator, { let n = self.norm(); @@ -246,7 +386,7 @@ where #[inline] fn lerp(&self, end: &Self, weight: T) -> Result where - T: num::Float + Clone + PartialOrd, + T: PartialOrd, { if weight < T::zero() || weight > T::one() { return Err(VectorError::OutOfRangeError("weight must be in [0, 1]".to_string())); @@ -262,10 +402,7 @@ where } #[inline] - fn angle_with(&self, other: &Self) -> Result - where - T: num::Float + Clone + std::iter::Sum, - { + fn angle_with(&self, other: &Self) -> Result { let norm_self = self.norm(); let norm_other = other.norm(); if norm_self == T::zero() || norm_other == T::zero() { @@ -278,6 +415,21 @@ where let cos_theta = cos_theta.max(-T::one()).min(T::one()); Ok(cos_theta.acos()) } + + #[inline] + fn project_onto(&self, other: &Self) -> Result + where + Self::Output: std::iter::FromIterator, + { + let denom = other.dot(other); + if denom == T::zero() { + return Err(VectorError::ZeroVectorError( + "Cannot project onto zero vector".to_string(), + )); + } + let scalar = self.dot(other) / denom; + Ok(other.as_slice().iter().map(|b| *b * scalar).collect()) + } } // ================================ @@ -288,14 +440,13 @@ where // TODO: add tests impl VectorOpsComplex for FlexVector> where - N: num::Float + Clone + PartialOrd + std::iter::Sum + Sync + Send, + N: num::Float + Clone + std::iter::Sum, { type Output = Self; #[inline] fn normalize(&self) -> Result where - N: num::Float + Clone + std::iter::Sum, Self::Output: std::iter::FromIterator>, { let n = self.norm(); @@ -308,7 +459,6 @@ where #[inline] fn normalize_to(&self, magnitude: N) -> Result where - N: num::Float + Clone + std::iter::Sum, Self::Output: std::iter::FromIterator>, { let n = self.norm(); @@ -336,6 +486,21 @@ where .map(|(a, b)| one_minus_w * *a + w * *b) .collect()) } + + #[inline] + fn project_onto(&self, other: &Self) -> Result + where + Self::Output: std::iter::FromIterator>, + { + let denom = VectorOpsComplex::dot(other, other); // Hermitian dot product + if denom == Complex::zero() { + return Err(VectorError::ZeroVectorError( + "Cannot project onto zero vector".to_string(), + )); + } + let scalar = VectorOpsComplex::dot(self, other) / denom; + Ok(other.as_slice().iter().map(|b| *b * scalar).collect()) + } } // ================================ @@ -343,10 +508,7 @@ where // Methods // // ================================ -impl FlexVector -where - T: num::Num + Clone + Sync + Send, -{ +impl FlexVector { /// Adds an element to the end of the vector. #[inline] pub fn push(&mut self, value: T) { @@ -373,7 +535,10 @@ where /// Resizes the vector in-place. #[inline] - pub fn resize(&mut self, new_len: usize, value: T) { + pub fn resize(&mut self, new_len: usize, value: T) + where + T: Clone, + { self.components.resize(new_len, value); } @@ -404,7 +569,7 @@ where pub fn map(&self, mut f: F) -> FlexVector where F: FnMut(T) -> U, - U: num::Num + Clone + Sync + Send, + T: Clone, { let new_components = self.components.iter().cloned().map(&mut f).collect(); FlexVector { components: new_components } @@ -415,6 +580,7 @@ where pub fn mut_map(&mut self, mut f: F) where F: FnMut(T) -> T, + T: Clone, { for x in self.components.iter_mut() { // Use clone since T may not be Copy @@ -465,6 +631,8 @@ mod tests { use super::*; use num::complex::ComplexFloat; use num::Complex; + use std::collections::hash_map::DefaultHasher; + use std::hash::{Hash, Hasher}; // ================================ // @@ -697,6 +865,586 @@ mod tests { assert!(v.is_empty()); } + // ================================ + // + // Deref/DerefMut trait tests + // + // ================================ + #[test] + fn test_deref_access_slice_methods_i32() { + let v = FlexVector::from_vec(vec![3, 1, 2]); + // Use sort (not implemented in FlexVector directly) + let mut sorted = v.clone(); + sorted.sort(); + assert_eq!(sorted.as_slice(), &[1, 2, 3]); + // Use binary_search (slice method, requires sorted) + assert_eq!(sorted.binary_search(&2), Ok(1)); + } + + #[test] + fn test_deref_access_slice_methods_f64() { + let v = FlexVector::from_vec(vec![3.5, 1.5, 2.5]); + // Use sort_by + let mut sorted = v.clone(); + sorted.sort_by(|a, b| a.partial_cmp(b).unwrap()); + assert_eq!(sorted.as_slice(), &[1.5, 2.5, 3.5]); + // Use rchunks + let chunks: Vec<_> = v.rchunks(2).collect(); + assert_eq!(chunks, vec![&[1.5, 2.5][..], &[3.5][..]]); + } + + #[test] + fn test_deref_access_slice_methods_complex() { + use num::Complex; + let v = FlexVector::from_vec(vec![ + Complex::new(1.0, 2.0), + Complex::new(3.0, 4.0), + Complex::new(5.0, 6.0), + ]); + // Use rotate_left + let mut rotated = v.clone(); + rotated.rotate_left(1); + assert_eq!( + rotated.as_slice(), + &[Complex::new(3.0, 4.0), Complex::new(5.0, 6.0), Complex::new(1.0, 2.0)] + ); + // Use fill + let mut filled = v.clone(); + filled.fill(Complex::new(9.0, 9.0)); + assert_eq!(filled.as_slice(), &[Complex::new(9.0, 9.0); 3]); + } + + #[test] + fn test_deref_mut_i32() { + let mut v = FlexVector::from_vec(vec![10, 20, 30]); + // Mutate via indexing + v[1] = 99; + assert_eq!(v.as_slice(), &[10, 99, 30]); + // Use reverse (slice method) + v.reverse(); + assert_eq!(v.as_slice(), &[30, 99, 10]); + } + + #[test] + fn test_deref_mut_f64() { + let mut v = FlexVector::from_vec(vec![1.5, 2.5, 3.5]); + // Mutate via indexing + v[0] = -1.5; + assert_eq!(v.as_slice(), &[-1.5, 2.5, 3.5]); + // Use fill (slice method) + v.fill(0.0); + assert_eq!(v.as_slice(), &[0.0, 0.0, 0.0]); + } + + #[test] + fn test_deref_mut_complex_f64() { + use num::Complex; + let mut v = FlexVector::from_vec(vec![ + Complex::new(1.0, 2.0), + Complex::new(3.0, 4.0), + Complex::new(5.0, 6.0), + ]); + // Mutate via indexing + v[2] = Complex::new(9.0, 9.0); + assert_eq!(v[2], Complex::new(9.0, 9.0)); + // Use rotate_right (slice method) + v.rotate_right(1); + assert_eq!( + v.as_slice(), + &[Complex::new(9.0, 9.0), Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)] + ); + } + + // ================================ + // + // AsRef/AsMut trait tests + // + // ================================ + #[test] + fn test_asref_i32() { + let v = FlexVector::from_vec(vec![1, 2, 3]); + let slice: &[i32] = v.as_ref(); + assert_eq!(slice, &[1, 2, 3]); + } + + #[test] + fn test_asref_f64() { + let v = FlexVector::from_vec(vec![1.1, 2.2, 3.3]); + let slice: &[f64] = v.as_ref(); + assert_eq!(slice, &[1.1, 2.2, 3.3]); + } + + #[test] + fn test_asref_complex_f64() { + use num::Complex; + let v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let slice: &[Complex] = v.as_ref(); + assert_eq!(slice, &[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + } + + #[test] + fn test_asmut_i32() { + let mut v = FlexVector::from_vec(vec![1, 2, 3]); + let slice: &mut [i32] = v.as_mut(); + slice[0] = 10; + slice[2] = 30; + assert_eq!(v.as_slice(), &[10, 2, 30]); + } + + #[test] + fn test_asmut_f64() { + let mut v = FlexVector::from_vec(vec![1.1, 2.2, 3.3]); + let slice: &mut [f64] = v.as_mut(); + slice[1] = 9.9; + assert_eq!(v.as_slice(), &[1.1, 9.9, 3.3]); + } + + #[test] + fn test_asmut_complex_f64() { + use num::Complex; + let mut v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let slice: &mut [Complex] = v.as_mut(); + slice[0].re = 10.0; + slice[1].im = 40.0; + assert_eq!(v.as_slice(), &[Complex::new(10.0, 2.0), Complex::new(3.0, 40.0)]); + } + + // ================================ + // + // IntoIterator trait method tests + // + // ================================ + #[test] + fn test_into_iter_i32() { + let v = FlexVector::from_vec(vec![1, 2, 3]); + let collected: Vec<_> = v.into_iter().collect(); + assert_eq!(collected, vec![1, 2, 3]); + } + + #[test] + fn test_into_iter_f64() { + let v = FlexVector::from_vec(vec![1.1, 2.2, 3.3]); + let collected: Vec<_> = v.into_iter().collect(); + assert_eq!(collected, vec![1.1, 2.2, 3.3]); + } + + #[test] + fn test_into_iter_complex_f64() { + use num::Complex; + let v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let collected: Vec<_> = v.into_iter().collect(); + assert_eq!(collected, vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + } + + #[test] + fn test_iter_ref_i32() { + let v = FlexVector::from_vec(vec![10, 20, 30]); + let collected: Vec<_> = (&v).into_iter().copied().collect(); + assert_eq!(collected, vec![10, 20, 30]); + } + + #[test] + fn test_iter_ref_f64() { + let v = FlexVector::from_vec(vec![1.5, 2.5, 3.5]); + let collected: Vec<_> = (&v).into_iter().copied().collect(); + assert_eq!(collected, vec![1.5, 2.5, 3.5]); + } + + #[test] + fn test_iter_ref_complex_f64() { + use num::Complex; + let v = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); + let collected: Vec<_> = (&v).into_iter().cloned().collect(); + assert_eq!(collected, vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); + } + + #[test] + fn test_iter_mutable_i32() { + let mut v = FlexVector::from_vec(vec![1, 2, 3]); + for x in &mut v { + *x *= 10; + } + assert_eq!(v.as_slice(), &[10, 20, 30]); + } + + #[test] + fn test_iter_mutable_f64() { + let mut v = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + for x in &mut v { + *x += 0.5; + } + assert_eq!(v.as_slice(), &[1.5, 2.5, 3.5]); + } + + #[test] + fn test_iter_mutable_complex_f64() { + use num::Complex; + let mut v = FlexVector::from_vec(vec![Complex::new(1.0, 1.0), Complex::new(2.0, 2.0)]); + for x in &mut v { + x.re *= 2.0; + x.im *= 3.0; + } + assert_eq!(v.as_slice(), &[Complex::new(2.0, 3.0), Complex::new(4.0, 6.0)]); + } + + // ================================ + // + // PartialEq/Eq trait method tests + // + // ================================ + #[test] + fn test_partial_eq_i32() { + let v1 = FlexVector::from_vec(vec![1, 2, 3]); + let v2 = FlexVector::from_vec(vec![1, 2, 3]); + let v3 = FlexVector::from_vec(vec![3, 2, 1]); + assert_eq!(v1, v2); + assert_ne!(v1, v3); + } + + #[test] + fn test_partial_eq_f64() { + let v1 = FlexVector::from_vec(vec![1.1, 2.2, 3.3]); + let v2 = FlexVector::from_vec(vec![1.1, 2.2, 3.3]); + let v3 = FlexVector::from_vec(vec![3.3, 2.2, 1.1]); + assert_eq!(v1, v2); + assert_ne!(v1, v3); + } + + #[test] + fn test_partial_eq_complex_f64() { + use num::Complex; + let v1 = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v3 = FlexVector::from_vec(vec![Complex::new(4.0, 3.0), Complex::new(2.0, 1.0)]); + assert_eq!(v1, v2); + assert_ne!(v1, v3); + } + + #[test] + fn test_partial_eq_empty() { + let v1: FlexVector = FlexVector::new(); + let v2: FlexVector = FlexVector::new(); + assert_eq!(v1, v2); + } + + #[test] + fn test_partial_eq_different_lengths() { + let v1 = FlexVector::from_vec(vec![1, 2]); + let v2 = FlexVector::from_vec(vec![1, 2, 3]); + assert_ne!(v1, v2); + } + + #[test] + fn test_partial_eq_f64_nan() { + let v1 = FlexVector::from_vec(vec![f64::NAN, 1.0]); + let v2 = FlexVector::from_vec(vec![f64::NAN, 1.0]); + // NaN != NaN, so these should not be equal + assert_ne!(v1, v2); + + let v3 = FlexVector::from_vec(vec![f64::NAN, 1.0]); + let v4 = FlexVector::from_vec(vec![f64::NAN, 2.0]); + assert_ne!(v3, v4); + } + + #[test] + fn test_partial_eq_f64_zero_negzero() { + let v1 = FlexVector::from_vec(vec![0.0, -0.0]); + let v2 = FlexVector::from_vec(vec![0.0, -0.0]); + let v3 = FlexVector::from_vec(vec![-0.0, 0.0]); + // 0.0 == -0.0 in Rust + assert_eq!(v1, v2); + assert_eq!(v1, v3); + } + + #[test] + fn test_partial_eq_f64_infinity() { + let v1 = FlexVector::from_vec(vec![f64::INFINITY, f64::NEG_INFINITY]); + let v2 = FlexVector::from_vec(vec![f64::INFINITY, f64::NEG_INFINITY]); + let v3 = FlexVector::from_vec(vec![f64::NEG_INFINITY, f64::INFINITY]); + assert_eq!(v1, v2); + assert_ne!(v1, v3); + } + + #[test] + fn test_partial_eq_complex_nan() { + use num::Complex; + let nan = f64::NAN; + let v1 = FlexVector::from_vec(vec![Complex::new(nan, 1.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(nan, 1.0)]); + // Complex::new(NaN, 1.0) != Complex::new(NaN, 1.0) + assert_ne!(v1, v2); + + let v3 = FlexVector::from_vec(vec![Complex::new(1.0, nan)]); + let v4 = FlexVector::from_vec(vec![Complex::new(1.0, nan)]); + assert_ne!(v3, v4); + } + + #[test] + fn test_partial_eq_complex_zero_negzero() { + use num::Complex; + let v1 = FlexVector::from_vec(vec![Complex::new(0.0, -0.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(-0.0, 0.0)]); + // 0.0 == -0.0 for both real and imaginary parts + assert_eq!(v1, v2); + } + + #[test] + fn test_partial_eq_complex_infinity() { + use num::Complex; + let v1 = FlexVector::from_vec(vec![Complex::new(f64::INFINITY, 1.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(f64::INFINITY, 1.0)]); + let v3 = FlexVector::from_vec(vec![Complex::new(1.0, f64::INFINITY)]); + let v4 = FlexVector::from_vec(vec![Complex::new(1.0, f64::INFINITY)]); + assert_eq!(v1, v2); + assert_eq!(v3, v4); + assert_ne!(v1, v3); + } + + #[test] + fn test_eq_trait_i32() { + let v1 = FlexVector::from_vec(vec![5, 6, 7]); + let v2 = FlexVector::from_vec(vec![5, 6, 7]); + assert!(v1.eq(&v2)); + } + + #[test] + fn test_eq_trait_f64() { + let v1 = FlexVector::from_vec(vec![0.0, -0.0]); + let v2 = FlexVector::from_vec(vec![0.0, -0.0]); + assert!(v1.eq(&v2)); + } + + #[test] + fn test_eq_trait_complex_f64() { + use num::Complex; + let v1 = FlexVector::from_vec(vec![Complex::new(0.0, 1.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(0.0, 1.0)]); + assert!(v1.eq(&v2)); + } + + // ================================ + // + // PartialOrd/Ord trait tests + // + // ================================ + #[test] + fn test_partial_ord_i32() { + let v1 = FlexVector::from_vec(vec![1, 2, 3]); + let v2 = FlexVector::from_vec(vec![1, 2, 4]); + let v3 = FlexVector::from_vec(vec![1, 2, 3]); + assert!(v1 < v2); + assert!(v2 > v1); + assert!(v1 <= v3); + assert!(v1 >= v3); + assert_eq!(v1.partial_cmp(&v2), Some(std::cmp::Ordering::Less)); + assert_eq!(v2.partial_cmp(&v1), Some(std::cmp::Ordering::Greater)); + assert_eq!(v1.partial_cmp(&v3), Some(std::cmp::Ordering::Equal)); + } + + #[test] + fn test_ord_i32() { + let v1 = FlexVector::from_vec(vec![1, 2, 3]); + let v2 = FlexVector::from_vec(vec![1, 2, 4]); + let v3 = FlexVector::from_vec(vec![1, 2, 3]); + assert_eq!(v1.cmp(&v2), std::cmp::Ordering::Less); + assert_eq!(v2.cmp(&v1), std::cmp::Ordering::Greater); + assert_eq!(v1.cmp(&v3), std::cmp::Ordering::Equal); + } + + #[test] + fn test_partial_ord_f64() { + let v1 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let v2 = FlexVector::from_vec(vec![1.0, 2.0, 4.0]); + let v3 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + assert_eq!(v1.partial_cmp(&v2), Some(std::cmp::Ordering::Less)); + assert_eq!(v2.partial_cmp(&v1), Some(std::cmp::Ordering::Greater)); + assert_eq!(v1.partial_cmp(&v3), Some(std::cmp::Ordering::Equal)); + } + + #[test] + fn test_partial_ord_f64_nan() { + let v1 = FlexVector::from_vec(vec![1.0, f64::NAN]); + let v2 = FlexVector::from_vec(vec![1.0, 2.0]); + // Comparison with NaN yields None + assert_eq!(v1.partial_cmp(&v2), None); + assert_eq!(v2.partial_cmp(&v1), None); + } + + #[test] + fn test_partial_ord_f64_infinity() { + let v1 = FlexVector::from_vec(vec![1.0, f64::INFINITY]); + let v2 = FlexVector::from_vec(vec![1.0, f64::NEG_INFINITY]); + let v3 = FlexVector::from_vec(vec![1.0, f64::INFINITY]); + let v4 = FlexVector::from_vec(vec![1.0, 1.0]); + // INFINITY > NEG_INFINITY + assert_eq!(v1.partial_cmp(&v2), Some(std::cmp::Ordering::Greater)); + assert_eq!(v2.partial_cmp(&v1), Some(std::cmp::Ordering::Less)); + // INFINITY == INFINITY + assert_eq!(v1.partial_cmp(&v3), Some(std::cmp::Ordering::Equal)); + // INFINITY > 1.0 + assert_eq!(v1.partial_cmp(&v4), Some(std::cmp::Ordering::Greater)); + assert_eq!(v4.partial_cmp(&v1), Some(std::cmp::Ordering::Less)); + } + + // ================================ + // + // Hash trait method tests + // + // ================================ + #[test] + fn test_hash_i32() { + let v1 = FlexVector::from_vec(vec![1, 2, 3]); + let v2 = FlexVector::from_vec(vec![1, 2, 3]); + let v3 = FlexVector::from_vec(vec![3, 2, 1]); + + let mut hasher1 = DefaultHasher::new(); + v1.hash(&mut hasher1); + let hash1 = hasher1.finish(); + + let mut hasher2 = DefaultHasher::new(); + v2.hash(&mut hasher2); + let hash2 = hasher2.finish(); + + let mut hasher3 = DefaultHasher::new(); + v3.hash(&mut hasher3); + let hash3 = hasher3.finish(); + + assert_eq!(hash1, hash2); + assert_ne!(hash1, hash3); + } + + #[test] + fn test_hash_complex_i32() { + use num::Complex; + let v1 = FlexVector::from_vec(vec![Complex::new(1, 2), Complex::new(3, 4)]); + let v2 = FlexVector::from_vec(vec![Complex::new(1, 2), Complex::new(3, 4)]); + let v3 = FlexVector::from_vec(vec![Complex::new(4, 3), Complex::new(2, 1)]); + + let mut hasher1 = DefaultHasher::new(); + v1.hash(&mut hasher1); + let hash1 = hasher1.finish(); + + let mut hasher2 = DefaultHasher::new(); + v2.hash(&mut hasher2); + let hash2 = hasher2.finish(); + + let mut hasher3 = DefaultHasher::new(); + v3.hash(&mut hasher3); + let hash3 = hasher3.finish(); + + assert_eq!(hash1, hash2); + assert_ne!(hash1, hash3); + } + + #[test] + fn test_hash_empty() { + let v1: FlexVector = FlexVector::new(); + let v2: FlexVector = FlexVector::new(); + + let mut hasher1 = DefaultHasher::new(); + v1.hash(&mut hasher1); + let hash1 = hasher1.finish(); + + let mut hasher2 = DefaultHasher::new(); + v2.hash(&mut hasher2); + let hash2 = hasher2.finish(); + + assert_eq!(hash1, hash2); + } + + // ================================ + // + // From trait tests + // + // ================================ + #[test] + fn test_from_vec_i32() { + let vec = vec![1, 2, 3]; + let fv: FlexVector = FlexVector::from(vec.clone()); + assert_eq!(fv.as_slice(), &vec[..]); + } + + #[test] + fn test_from_vec_f64() { + let vec = vec![1.1, 2.2, 3.3]; + let fv: FlexVector = FlexVector::from(vec.clone()); + assert_eq!(fv.as_slice(), &vec[..]); + } + + #[test] + fn test_from_vec_complex_f64() { + use num::Complex; + let vec = vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; + let fv: FlexVector> = FlexVector::from(vec.clone()); + assert_eq!(fv.as_slice(), &vec[..]); + } + + #[test] + fn test_from_slice_i32() { + let slice: &[i32] = &[4, 5, 6]; + let fv = FlexVector::from(slice); + assert_eq!(fv.as_slice(), slice); + } + + #[test] + fn test_from_slice_f64() { + let slice: &[f64] = &[4.4, 5.5, 6.6]; + let fv = FlexVector::from(slice); + assert_eq!(fv.as_slice(), slice); + } + + #[test] + fn test_from_slice_complex_f64() { + use num::Complex; + let slice: &[Complex] = &[Complex::new(7.0, 8.0), Complex::new(9.0, 10.0)]; + let fv = FlexVector::from(slice); + assert_eq!(fv.as_slice(), slice); + } + + // ================================ + // + // Extend trait tests + // + // ================================ + #[test] + fn test_extend_i32() { + let mut v = FlexVector::from_vec(vec![1, 2]); + v.extend(vec![3, 4]); + assert_eq!(v.as_slice(), &[1, 2, 3, 4]); + } + + #[test] + fn test_extend_f64() { + let mut v = FlexVector::from_vec(vec![1.1, 2.2]); + v.extend(vec![3.3, 4.4]); + assert_eq!(v.as_slice(), &[1.1, 2.2, 3.3, 4.4]); + } + + #[test] + fn test_extend_complex_f64() { + use num::Complex; + let mut v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0)]); + v.extend(vec![Complex::new(3.0, 4.0), Complex::new(5.0, 6.0)]); + assert_eq!( + v.as_slice(), + &[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0), Complex::new(5.0, 6.0)] + ); + } + + #[test] + fn test_extend_empty() { + let mut v = FlexVector::::new(); + v.extend(vec![7, 8]); + assert_eq!(v.as_slice(), &[7, 8]); + } + + #[test] + fn test_extend_with_empty() { + let mut v = FlexVector::from_vec(vec![1, 2]); + v.extend(Vec::::new()); + assert_eq!(v.as_slice(), &[1, 2]); + } + // ================================ // // VectorBase trait method tests diff --git a/src/types/traits.rs b/src/types/traits.rs index 8b2b7e4..89cd6f5 100644 --- a/src/types/traits.rs +++ b/src/types/traits.rs @@ -504,9 +504,17 @@ pub trait VectorOpsFloat: VectorBase { self.norm() } + /// ... fn angle_with(&self, other: &Self) -> Result where T: num::Float + Clone + std::iter::Sum; + + /// Projects self onto other. + /// Returns an error if `other` is the zero vector. + fn project_onto(&self, other: &Self) -> Result + where + T: num::Float + Clone + std::iter::Sum, + Self::Output: std::iter::FromIterator; } pub trait VectorOpsComplex: VectorBase> { @@ -718,4 +726,9 @@ pub trait VectorOpsComplex: VectorBase> { { self.norm() } + + fn project_onto(&self, other: &Self) -> Result + where + N: num::Float + Clone + std::iter::Sum, + Self::Output: std::iter::FromIterator>; } From 37de996d346fd3d7090922e836dc13675f6a62d1 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Sun, 11 May 2025 16:32:34 -0400 Subject: [PATCH 09/82] refactor min / max to minimum / maximum --- src/types/traits.rs | 72 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 67 insertions(+), 5 deletions(-) diff --git a/src/types/traits.rs b/src/types/traits.rs index 89cd6f5..fad1863 100644 --- a/src/types/traits.rs +++ b/src/types/traits.rs @@ -302,7 +302,7 @@ pub trait VectorOps: VectorBase { /// ... #[inline] - fn min(&self) -> Option + fn minimum(&self) -> Option where T: Ord + Copy, { @@ -311,7 +311,7 @@ pub trait VectorOps: VectorBase { /// ... #[inline] - fn max(&self) -> Option + fn maximum(&self) -> Option where T: Ord + Copy, { @@ -399,6 +399,11 @@ pub trait VectorOpsFloat: VectorBase { where T: num::Float + Copy + PartialOrd, { + if self.len() != end.len() { + return Err(VectorError::MismatchedLengthError( + "Vectors must have the same length".to_string(), + )); + } if weight < T::zero() || weight > T::one() { return Err(VectorError::OutOfRangeError("weight must be in [0, 1]".to_string())); } @@ -412,12 +417,11 @@ pub trait VectorOpsFloat: VectorBase { /// Midpoint #[inline] - fn midpoint(&self, end: &Self) -> Self::Output + fn midpoint(&self, other: &Self) -> Result where T: num::Float + Clone, { - // OK to unwrap because by definition it uses an in-range weight - self.lerp(end, num::cast(0.5).unwrap()).unwrap() + self.lerp(other, T::from(0.5).unwrap()) } /// Euclidean distance between self and other. @@ -515,6 +519,59 @@ pub trait VectorOpsFloat: VectorBase { where T: num::Float + Clone + std::iter::Sum, Self::Output: std::iter::FromIterator; + + /// Returns the minimum element, or `None` if the vector is empty. + /// + /// # NaN Handling + /// + /// If any element in the vector is `NaN`, the result of this method is not guaranteed to be meaningful. + /// The comparison `a < b` will return `false` if either `a` or `b` is `NaN`, so if a `NaN` is present, + /// it may be returned as the minimum, or it may cause a different value to be returned depending on the order of elements. + /// This matches the behavior of Rust's standard library for floating point minimum operations. + /// + /// If you want to ignore `NaN` values, filter them out before calling this method. + /// # Examples + /// ```rust + /// # use vectora::FlexVector; + /// use vectora::types::traits::VectorOpsFloat; + /// + /// let v = FlexVector::from(vec![1.5, -2.0, 3.0]); + /// assert_eq!(v.minimum(), Some(-2.0)); + /// ``` + #[inline] + fn minimum(&self) -> Option + where + T: PartialOrd + Copy, + { + self.as_slice().iter().copied().reduce(|a, b| if a < b { a } else { b }) + } + + /// Returns the maximum element, or `None` if the vector is empty. + /// + /// # NaN Handling + /// + /// If any element in the vector is `NaN`, the result of this method is not guaranteed to be meaningful. + /// The comparison `a > b` will return `false` if either `a` or `b` is `NaN`, so if a `NaN` is present, + /// it may be returned as the maximum, or it may cause a different value to be returned depending on the order of elements. + /// This matches the behavior of Rust's standard library for floating point maximum operations. + /// + /// If you want to ignore `NaN` values, filter them out before calling this method. + /// + /// # Examples + /// ``` + /// # use vectora::FlexVector; + /// use vectora::types::traits::VectorOpsFloat; + /// + /// let v = FlexVector::from(vec![1.5, -2.0, 3.0]); + /// assert_eq!(v.maximum(), Some(3.0)); + /// ``` + #[inline] + fn maximum(&self) -> Option + where + T: PartialOrd + Copy, + { + self.as_slice().iter().copied().reduce(|a, b| if a > b { a } else { b }) + } } pub trait VectorOpsComplex: VectorBase> { @@ -601,6 +658,11 @@ pub trait VectorOpsComplex: VectorBase> { where N: num::Float + Copy + PartialOrd, { + if self.len() != end.len() { + return Err(VectorError::MismatchedLengthError( + "Vectors must have the same length".to_string(), + )); + } if weight < N::zero() || weight > N::one() { return Err(VectorError::OutOfRangeError("weight must be in [0, 1]".to_string())); } From 953b815bd79f5aa62b61d9119b4881e7bb310b5c Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Sun, 11 May 2025 16:33:05 -0400 Subject: [PATCH 10/82] begin VectorOps trait tests --- src/errors.rs | 5 + src/types/flexvector.rs | 603 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 607 insertions(+), 1 deletion(-) diff --git a/src/errors.rs b/src/errors.rs index 6ead27e..8473393 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -6,6 +6,8 @@ pub enum VectorError { /// Occurs when an operation that requires data in a vector type is /// requested with an empty vector type. EmptyVectorError(String), + /// Occurs when two vectors need to be the same length to complete an operation, and are not. + MismatchedLengthError(String), /// Occurs when a value extends outside of a mandatory range. OutOfRangeError(String), /// Occurs when there is invalid data during an attempt to convert @@ -26,6 +28,9 @@ impl std::fmt::Display for VectorError { VectorError::EmptyVectorError(s) => { write!(f, "VectorError::EmptyVectorError: {s}") } + VectorError::MismatchedLengthError(s) => { + write!(f, "VectorError::MismatchedLengthError: {}", s) + } VectorError::OutOfRangeError(s) => { write!(f, "VectorError::OutOfRangeError: {}", s) } diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index d3fc129..b92caf6 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -388,6 +388,11 @@ where where T: PartialOrd, { + if self.len() != end.len() { + return Err(VectorError::MismatchedLengthError( + "Vectors must have the same length".to_string(), + )); + } if weight < T::zero() || weight > T::one() { return Err(VectorError::OutOfRangeError("weight must be in [0, 1]".to_string())); } @@ -474,6 +479,11 @@ where where N: num::Float + Clone + PartialOrd, { + if self.len() != end.len() { + return Err(VectorError::MismatchedLengthError( + "Vectors must have the same length".to_string(), + )); + } if weight < N::zero() || weight > N::one() { return Err(VectorError::OutOfRangeError("weight must be in [0, 1]".to_string())); } @@ -2394,7 +2404,6 @@ mod tests { #[test] fn test_mut_map_complex() { - use num::Complex; let mut v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); v.mut_map(|x| Complex::new(x.re + 1.0, x.im * 2.0)); assert_eq!(v.as_slice(), &[Complex::new(2.0, 4.0), Complex::new(-2.0, 8.0)]); @@ -2452,6 +2461,598 @@ mod tests { assert_eq!(cos_sim, 0.0); } + // ================================ + // + // VectorOps trait tests + // + // ================================ + + // -- translate -- + #[test] + fn test_translate_i32() { + let v1 = FlexVector::from_vec(vec![1, 2, 3]); + let v2 = FlexVector::from_vec(vec![4, 5, 6]); + let result = v1.translate(&v2); + assert_eq!(result.as_slice(), &[5, 7, 9]); + } + + #[test] + fn test_translate_f64() { + let v1 = FlexVector::from_vec(vec![1.5, 2.5, 3.5]); + let v2 = FlexVector::from_vec(vec![0.5, 1.5, 2.5]); + let result = v1.translate(&v2); + assert_eq!(result.as_slice(), &[2.0, 4.0, 6.0]); + } + + #[test] + fn test_translate_complex_f64() { + let v1 = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); + let result = v1.translate(&v2); + assert_eq!(result.as_slice(), &[Complex::new(6.0, 8.0), Complex::new(10.0, 12.0)]); + } + + // -- mut_translate -- + #[test] + fn test_mut_translate_i32() { + let mut v1 = FlexVector::from_vec(vec![1, 2, 3]); + let v2 = FlexVector::from_vec(vec![4, 5, 6]); + v1.mut_translate(&v2); + assert_eq!(v1.as_slice(), &[5, 7, 9]); + } + + #[test] + fn test_mut_translate_f64() { + let mut v1 = FlexVector::from_vec(vec![1.5, 2.5, 3.5]); + let v2 = FlexVector::from_vec(vec![0.5, 1.5, 2.5]); + v1.mut_translate(&v2); + assert_eq!(v1.as_slice(), &[2.0, 4.0, 6.0]); + } + + #[test] + fn test_mut_translate_complex_f64() { + let mut v1 = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); + v1.mut_translate(&v2); + assert_eq!(v1.as_slice(), &[Complex::new(6.0, 8.0), Complex::new(10.0, 12.0)]); + } + + // -- scale -- + #[test] + fn test_scale_i32() { + let v = FlexVector::from_vec(vec![1, 2, 3]); + let scaled = v.scale(10); + assert_eq!(scaled.as_slice(), &[10, 20, 30]); + } + + #[test] + fn test_scale_f64() { + let v = FlexVector::from_vec(vec![1.5, -2.0, 0.0]); + let scaled = v.scale(2.0); + assert_eq!(scaled.as_slice(), &[3.0, -4.0, 0.0]); + } + + #[test] + fn test_scale_complex_f64() { + use num::Complex; + let v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + let scalar = Complex::new(2.0, 0.0); + let scaled = v.scale(scalar); + assert_eq!(scaled.as_slice(), &[Complex::new(2.0, 4.0), Complex::new(-6.0, 8.0)]); + } + + // -- mut_scale -- + #[test] + fn test_mut_scale_i32() { + let mut v = FlexVector::from_vec(vec![1, 2, 3]); + v.mut_scale(10); + assert_eq!(v.as_slice(), &[10, 20, 30]); + } + + #[test] + fn test_mut_scale_f64() { + let mut v = FlexVector::from_vec(vec![1.5, -2.0, 0.0]); + v.mut_scale(2.0); + assert_eq!(v.as_slice(), &[3.0, -4.0, 0.0]); + } + + #[test] + fn test_mut_scale_complex_f64() { + let mut v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + let scalar = Complex::new(2.0, 0.0); + v.mut_scale(scalar); + assert_eq!(v.as_slice(), &[Complex::new(2.0, 4.0), Complex::new(-6.0, 8.0)]); + } + + // -- negate -- + #[test] + fn test_negate_i32() { + let v = FlexVector::from_vec(vec![1, -2, 3]); + let neg = v.negate(); + assert_eq!(neg.as_slice(), &[-1, 2, -3]); + // original unchanged + assert_eq!(v.as_slice(), &[1, -2, 3]); + } + + #[test] + fn test_negate_f64() { + let v = FlexVector::from_vec(vec![1.5, -2.5, 0.0]); + let neg = v.negate(); + assert_eq!(neg.as_slice(), &[-1.5, 2.5, -0.0]); + assert_eq!(v.as_slice(), &[1.5, -2.5, 0.0]); + } + + #[test] + fn test_negate_complex_f64() { + use num::Complex; + let v = FlexVector::from_vec(vec![Complex::new(1.0, -2.0), Complex::new(-3.0, 4.0)]); + let neg = v.negate(); + assert_eq!(neg.as_slice(), &[Complex::new(-1.0, 2.0), Complex::new(3.0, -4.0)]); + assert_eq!(v.as_slice(), &[Complex::new(1.0, -2.0), Complex::new(-3.0, 4.0)]); + } + + // -- mut_negate -- + #[test] + fn test_mut_negate_i32() { + let mut v = FlexVector::from_vec(vec![1, -2, 3]); + v.mut_negate(); + assert_eq!(v.as_slice(), &[-1, 2, -3]); + } + + #[test] + fn test_mut_negate_f64() { + let mut v = FlexVector::from_vec(vec![1.5, -2.5, 0.0]); + v.mut_negate(); + assert_eq!(v.as_slice(), &[-1.5, 2.5, -0.0]); + } + + #[test] + fn test_mut_negate_complex_f64() { + use num::Complex; + let mut v = FlexVector::from_vec(vec![Complex::new(1.0, -2.0), Complex::new(-3.0, 4.0)]); + v.mut_negate(); + assert_eq!(v.as_slice(), &[Complex::new(-1.0, 2.0), Complex::new(3.0, -4.0)]); + } + + // -- mut_zero -- + #[test] + fn test_mut_zero_i32() { + let mut v = FlexVector::from_vec(vec![1, -2, 3]); + v.mut_zero(); + assert_eq!(v.as_slice(), &[0, 0, 0]); + } + + #[test] + fn test_mut_zero_f64() { + let mut v = FlexVector::from_vec(vec![1.5, -2.5, 0.0]); + v.mut_zero(); + assert_eq!(v.as_slice(), &[0.0, 0.0, 0.0]); + } + + #[test] + fn test_mut_zero_complex_f64() { + use num::Complex; + let mut v = FlexVector::from_vec(vec![Complex::new(1.0, -2.0), Complex::new(-3.0, 4.0)]); + v.mut_zero(); + assert_eq!(v.as_slice(), &[Complex::new(0.0, 0.0), Complex::new(0.0, 0.0)]); + } + + // -- dot -- + #[test] + fn test_dot_i32() { + let v1 = FlexVector::from_vec(vec![1, 2, 3]); + let v2 = FlexVector::from_vec(vec![4, 5, 6]); + let dot = v1.dot(&v2); + assert_eq!(dot, 1 * 4 + 2 * 5 + 3 * 6); // 32 + } + + #[test] + fn test_dot_f64() { + let v1 = FlexVector::from_vec(vec![1.5, 2.0, -3.0]); + let v2 = FlexVector::from_vec(vec![2.0, 0.5, 4.0]); + let dot = v1.dot(&v2); + assert!((dot - (1.5 * 2.0 + 2.0 * 0.5 + -3.0 * 4.0)).abs() < 1e-12); + } + + // complex number dot product tested in VectorOpsComplex trait impl testing section below + + // -- dot_to_f64 -- + #[test] + fn test_dot_to_f64_i32() { + let v1 = FlexVector::from_vec(vec![1, 2, 3]); + let v2 = FlexVector::from_vec(vec![4, 5, 6]); + let dot = v1.dot_to_f64(&v2); + assert!((dot - 32.0).abs() < 1e-12); + } + + #[test] + fn test_dot_to_f64_f64() { + let v1 = FlexVector::from_vec(vec![1.5, 2.0, -3.0]); + let v2 = FlexVector::from_vec(vec![2.0, 0.5, 4.0]); + let dot = v1.dot_to_f64(&v2); + assert!((dot - (1.5 * 2.0 + 2.0 * 0.5 + -3.0 * 4.0)).abs() < 1e-12); + } + + // complex number dot_to_f64 tested in VectorOpsComplex trait impl testing section below + + // -- cross -- + #[test] + fn test_cross_i32() { + let v1 = FlexVector::from_vec(vec![1, 2, 3]); + let v2 = FlexVector::from_vec(vec![4, 5, 6]); + let cross = v1.cross(&v2).unwrap(); + // [2*6 - 3*5, 3*4 - 1*6, 1*5 - 2*4] = [12-15, 12-6, 5-8] = [-3, 6, -3] + assert_eq!(cross.as_slice(), &[-3, 6, -3]); + } + + #[test] + fn test_cross_f64() { + let v1 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let v2 = FlexVector::from_vec(vec![4.0, 5.0, 6.0]); + let cross = v1.cross(&v2).unwrap(); + assert_eq!(cross.as_slice(), &[-3.0, 6.0, -3.0]); + } + + // intentionally skipping complex number cross product testing + // due to lack of universally agreed upon definition in the + // complex vector space. + + #[test] + fn test_cross_wrong_length() { + let v1 = FlexVector::from_vec(vec![1, 2]); + let v2 = FlexVector::from_vec(vec![3, 4]); + let result = v1.cross(&v2); + assert!(result.is_err()); + } + + // -- sum -- + #[test] + fn test_sum_i32() { + let v = FlexVector::from_vec(vec![1, 2, 3, 4]); + let s = v.sum(); + assert_eq!(s, 10); + } + + #[test] + fn test_sum_f64() { + let v = FlexVector::from_vec(vec![1.5, -2.5, 3.0]); + let s = v.sum(); + assert!((s - 2.0).abs() < 1e-12); + } + + #[test] + fn test_sum_complex_f64() { + use num::Complex; + let v = FlexVector::from_vec(vec![ + Complex::new(1.0, 2.0), + Complex::new(-3.0, 4.0), + Complex::new(5.0, -6.0), + ]); + let s = v.sum(); + assert_eq!(s, Complex::new(3.0, 0.0)); + } + + // -- product -- + #[test] + fn test_product_i32() { + let v = FlexVector::from_vec(vec![2, 3, 4]); + let p = v.product(); + assert_eq!(p, 24); + } + + #[test] + fn test_product_f64() { + let v = FlexVector::from_vec(vec![1.5, -2.0, 3.0]); + let p = v.product(); + assert!((p - (1.5 * -2.0 * 3.0)).abs() < 1e-12); + } + + #[test] + fn test_product_complex_f64() { + use num::Complex; + let v = FlexVector::from_vec(vec![ + Complex::new(1.0, 2.0), + Complex::new(3.0, -1.0), + Complex::new(2.0, 0.5), + ]); + let p = v.product(); + let expected = Complex::new(1.0, 2.0) * Complex::new(3.0, -1.0) * Complex::new(2.0, 0.5); + assert!((p - expected).norm() < 1e-12); + } + + // -- minimum -- + #[test] + fn test_minimum_i32() { + let v = FlexVector::from_vec(vec![3, 1, 4, 2]); + assert_eq!(v.minimum(), Some(1)); + } + + // minimum floating point type tests in VectorOpsFloat trait impl testing section + + #[test] + fn test_minimum_empty() { + let v: FlexVector = FlexVector::new(); + assert_eq!(v.minimum(), None); + } + + // -- maximum -- + #[test] + fn test_maximum_i32() { + let v = FlexVector::from_vec(vec![3, 1, 4, 2]); + assert_eq!(v.maximum(), Some(4)); + } + + // maximum floating point type tests in VectorOpsFloat trait impl testing section + + #[test] + fn test_maximum_empty() { + let v: FlexVector = FlexVector::new(); + assert_eq!(v.maximum(), None); + } + + // -- l1_norm -- + #[test] + fn test_l1_norm_i32() { + let v = FlexVector::from_vec(vec![1, -2, 3]); + let norm = v.l1_norm(); + assert_eq!(norm, 6); + } + + #[test] + fn test_l1_norm_f64() { + let v = FlexVector::from_vec(vec![1.5, -2.5, 3.0]); + let norm = v.l1_norm(); + assert!((norm - 7.0).abs() < 1e-12); + } + + // l1_norm testing of complex number types is in the VectorOpsComplex trait impl tests below + + // -- linf_norm -- + #[test] + fn test_linf_norm_i32() { + let v = FlexVector::from_vec(vec![1, -5, 3, 2]); + let norm = v.linf_norm(); + assert_eq!(norm, 5); + } + + #[test] + fn test_linf_norm_f64() { + let v = FlexVector::from_vec(vec![1.5, -2.5, 3.0, -7.2]); + let norm = v.linf_norm(); + assert!((norm - 7.2).abs() < 1e-12); + } + + // ================================ + // + // VectorOpsFloat trait tests + // + // ================================ + + // -- normalize -- + #[test] + fn test_normalize_f64() { + let v = FlexVector::from_vec(vec![3.0, 4.0]); + let normalized = v.normalize().unwrap(); + // The norm is 5.0, so the normalized vector should be [0.6, 0.8] + assert!((normalized.as_slice()[0] - 0.6).abs() < 1e-12); + assert!((normalized.as_slice()[1] - 0.8).abs() < 1e-12); + } + + #[test] + fn test_normalize_f64_zero_vector() { + let v = FlexVector::from_vec(vec![0.0, 0.0]); + let result = v.normalize(); + assert!(result.is_err()); + } + + #[test] + fn test_normalize_f64_negative_values() { + let v = FlexVector::from_vec(vec![-3.0, -4.0]); + let normalized = v.normalize().unwrap(); + // The norm is 5.0, so the normalized vector should be [-0.6, -0.8] + assert!((normalized.as_slice()[0] + 0.6).abs() < 1e-12); + assert!((normalized.as_slice()[1] + 0.8).abs() < 1e-12); + } + + // -- mut_normalize -- + #[test] + fn test_mut_normalize_f64() { + let mut v = FlexVector::from_vec(vec![3.0, 4.0]); + v.mut_normalize().unwrap(); + // The norm is 5.0, so the normalized vector should be [0.6, 0.8] + assert!((v.as_slice()[0] - 0.6).abs() < 1e-12); + assert!((v.as_slice()[1] - 0.8).abs() < 1e-12); + } + + #[test] + fn test_mut_normalize_f64_zero_vector() { + let mut v = FlexVector::from_vec(vec![0.0, 0.0]); + let result = v.mut_normalize(); + assert!(result.is_err()); + } + + #[test] + fn test_mut_normalize_f64_negative_values() { + let mut v = FlexVector::from_vec(vec![-3.0, -4.0]); + v.mut_normalize().unwrap(); + // The norm is 5.0, so the normalized vector should be [-0.6, -0.8] + assert!((v.as_slice()[0] + 0.6).abs() < 1e-12); + assert!((v.as_slice()[1] + 0.8).abs() < 1e-12); + } + + // -- normalize_to -- + #[test] + fn test_normalize_to_f64() { + let v = FlexVector::from_vec(vec![3.0, 4.0]); + let normalized = v.normalize_to(10.0).unwrap(); + // The original norm is 5.0, so the normalized vector should be [6.0, 8.0] + assert!((normalized.as_slice()[0] - 6.0).abs() < 1e-12); + assert!((normalized.as_slice()[1] - 8.0).abs() < 1e-12); + } + + #[test] + fn test_normalize_to_f64_zero_vector() { + let v = FlexVector::from_vec(vec![0.0, 0.0]); + let result = v.normalize_to(1.0); + assert!(result.is_err()); + } + + #[test] + fn test_normalize_to_f64_negative_values() { + let v = FlexVector::from_vec(vec![-3.0, -4.0]); + let normalized = v.normalize_to(5.0).unwrap(); + // The original norm is 5.0, so the normalized vector should be [-3.0, -4.0] + assert!((normalized.as_slice()[0] + 3.0).abs() < 1e-12); + assert!((normalized.as_slice()[1] + 4.0).abs() < 1e-12); + } + + // -- mut_normalize_to -- + #[test] + fn test_mut_normalize_to_f64() { + let mut v = FlexVector::from_vec(vec![3.0, 4.0]); + v.mut_normalize_to(10.0).unwrap(); + // The original norm is 5.0, so the normalized vector should be [6.0, 8.0] + assert!((v.as_slice()[0] - 6.0).abs() < 1e-12); + assert!((v.as_slice()[1] - 8.0).abs() < 1e-12); + } + + #[test] + fn test_mut_normalize_to_f64_zero_vector() { + let mut v = FlexVector::from_vec(vec![0.0, 0.0]); + let result = v.mut_normalize_to(1.0); + assert!(result.is_err()); + } + + #[test] + fn test_mut_normalize_to_f64_negative_values() { + let mut v = FlexVector::from_vec(vec![-3.0, -4.0]); + v.mut_normalize_to(5.0).unwrap(); + // The original norm is 5.0, so the normalized vector should be [-3.0, -4.0] + assert!((v.as_slice()[0] + 3.0).abs() < 1e-12); + assert!((v.as_slice()[1] + 4.0).abs() < 1e-12); + } + + // -- lerp -- + #[test] + fn test_lerp_f64_weight_zero() { + let v1 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let v2 = FlexVector::from_vec(vec![4.0, 5.0, 6.0]); + let result = v1.lerp(&v2, 0.0).unwrap(); + // Should be equal to v1 + assert!((result.as_slice()[0] - 1.0).abs() < 1e-12); + assert!((result.as_slice()[1] - 2.0).abs() < 1e-12); + assert!((result.as_slice()[2] - 3.0).abs() < 1e-12); + } + + #[test] + fn test_lerp_f64_weight_one() { + let v1 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let v2 = FlexVector::from_vec(vec![4.0, 5.0, 6.0]); + let result = v1.lerp(&v2, 1.0).unwrap(); + // Should be equal to v2 + assert!((result.as_slice()[0] - 4.0).abs() < 1e-12); + assert!((result.as_slice()[1] - 5.0).abs() < 1e-12); + assert!((result.as_slice()[2] - 6.0).abs() < 1e-12); + } + + #[test] + fn test_lerp_f64_weight_half() { + let v1 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let v2 = FlexVector::from_vec(vec![4.0, 5.0, 6.0]); + let result = v1.lerp(&v2, 0.5).unwrap(); + // Should be the midpoint + assert!((result.as_slice()[0] - 2.5).abs() < 1e-12); + assert!((result.as_slice()[1] - 3.5).abs() < 1e-12); + assert!((result.as_slice()[2] - 4.5).abs() < 1e-12); + } + + #[test] + fn test_lerp_f64_weight_out_of_bounds() { + let v1 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let v2 = FlexVector::from_vec(vec![4.0, 5.0, 6.0]); + let result_low = v1.lerp(&v2, -0.1); + let result_high = v1.lerp(&v2, 1.1); + assert!(result_low.is_err()); + assert!(result_high.is_err()); + } + + // -- mut_lerp -- + #[test] + fn test_mut_lerp_f64_weight_zero() { + let mut v1 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let v2 = FlexVector::from_vec(vec![4.0, 5.0, 6.0]); + v1.mut_lerp(&v2, 0.0).unwrap(); + // Should be equal to original v1 + assert!((v1.as_slice()[0] - 1.0).abs() < 1e-12); + assert!((v1.as_slice()[1] - 2.0).abs() < 1e-12); + assert!((v1.as_slice()[2] - 3.0).abs() < 1e-12); + } + + #[test] + fn test_mut_lerp_f64_weight_one() { + let mut v1 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let v2 = FlexVector::from_vec(vec![4.0, 5.0, 6.0]); + v1.mut_lerp(&v2, 1.0).unwrap(); + // Should be equal to v2 + assert!((v1.as_slice()[0] - 4.0).abs() < 1e-12); + assert!((v1.as_slice()[1] - 5.0).abs() < 1e-12); + assert!((v1.as_slice()[2] - 6.0).abs() < 1e-12); + } + + #[test] + fn test_mut_lerp_f64_weight_half() { + let mut v1 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let v2 = FlexVector::from_vec(vec![4.0, 5.0, 6.0]); + v1.mut_lerp(&v2, 0.5).unwrap(); + // Should be the midpoint + assert!((v1.as_slice()[0] - 2.5).abs() < 1e-12); + assert!((v1.as_slice()[1] - 3.5).abs() < 1e-12); + assert!((v1.as_slice()[2] - 4.5).abs() < 1e-12); + } + + #[test] + fn test_mut_lerp_f64_weight_out_of_bounds() { + let mut v1 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let v2 = FlexVector::from_vec(vec![4.0, 5.0, 6.0]); + let result_low = v1.mut_lerp(&v2, -0.1); + let result_high = v1.mut_lerp(&v2, 1.1); + assert!(result_low.is_err()); + assert!(result_high.is_err()); + } + + // -- midpoint -- + #[test] + fn test_midpoint_f64() { + let v1 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let v2 = FlexVector::from_vec(vec![4.0, 5.0, 6.0]); + let midpoint = v1.midpoint(&v2).unwrap(); + // Should be the average of each component + assert!((midpoint.as_slice()[0] - 2.5).abs() < 1e-12); + assert!((midpoint.as_slice()[1] - 3.5).abs() < 1e-12); + assert!((midpoint.as_slice()[2] - 4.5).abs() < 1e-12); + } + + #[test] + fn test_midpoint_f64_negative_values() { + let v1 = FlexVector::from_vec(vec![-1.0, -2.0, -3.0]); + let v2 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let midpoint = v1.midpoint(&v2).unwrap(); + // Should be [0.0, 0.0, 0.0] + assert!((midpoint.as_slice()[0]).abs() < 1e-12); + assert!((midpoint.as_slice()[1]).abs() < 1e-12); + assert!((midpoint.as_slice()[2]).abs() < 1e-12); + } + + #[test] + fn test_midpoint_f64_mismatched_length() { + let v1 = FlexVector::from_vec(vec![1.0, 2.0]); + let v2 = FlexVector::from_vec(vec![3.0, 4.0, 5.0]); + let result = v1.midpoint(&v2); + assert!(result.is_err()); + } + + //TODO: continue tests for all methods in VectorOpsFloat trait + // ================================ // // Unary Negation trait tests From 159f8d4dbe8693ff322c299bf9f210af54d3f641 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Sun, 11 May 2025 17:33:28 -0400 Subject: [PATCH 11/82] refactor trait impl approach to support bounds checks on FlexVector types and no bounds checks on Vector types --- src/types/flexvector.rs | 74 +++++++++++++++++++++++++++++------------ src/types/mod.rs | 1 + src/types/traits.rs | 21 ++++-------- src/types/utils.rs | 23 +++++++++++++ 4 files changed, 82 insertions(+), 37 deletions(-) create mode 100644 src/types/utils.rs diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index b92caf6..d9447b0 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -10,6 +10,8 @@ use crate::{ types::traits::VectorOpsComplex, types::traits::VectorOpsFloat, }; +use crate::types::utils::{dot_impl, mut_translate_impl, translate_impl}; + use crate::errors::VectorError; use num::{Complex, Zero}; @@ -298,16 +300,31 @@ impl VectorBase for FlexVector { // TODO: add tests impl VectorOps for FlexVector where - T: num::Num + Clone, + T: num::Num + Clone + Copy, { type Output = Self; #[inline] - fn translate(&self, other: &Self) -> Self::Output - where - T: num::Num + Clone, - { - self.components.iter().zip(&other.components).map(|(a, b)| a.clone() + b.clone()).collect() + fn translate(&self, other: &Self) -> Result { + if self.len() != other.len() { + return Err(VectorError::MismatchedLengthError( + "Vectors must have the same length".to_string(), + )); + } + let mut out = FlexVector::zero(self.len()); + translate_impl(self.as_slice(), other.as_slice(), out.as_mut_slice()); + Ok(out) + } + + #[inline] + fn mut_translate(&mut self, other: &Self) -> Result<(), VectorError> { + if self.len() != other.len() { + return Err(VectorError::MismatchedLengthError( + "Vectors must have the same length".to_string(), + )); + } + mut_translate_impl(self.as_mut_slice(), other.as_slice()); + Ok(()) } #[inline] @@ -328,6 +345,19 @@ where self.as_slice().iter().map(|a| -a.clone()).collect() } + #[inline] + fn dot(&self, other: &Self) -> Result + where + T: num::Num + Copy + std::iter::Sum, + { + if self.len() != other.len() { + return Err(VectorError::MismatchedLengthError( + "Vectors must have the same length".to_string(), + )); + } + Ok(dot_impl(self.as_slice(), other.as_slice())) + } + /// Cross product (only for 3D vectors). #[inline] fn cross(&self, other: &Self) -> Result @@ -415,7 +445,7 @@ where "Cannot compute angle with zero vector".to_string(), )); } - let dot = self.dot(other); + let dot = self.dot(other)?; let cos_theta = dot / (norm_self * norm_other); let cos_theta = cos_theta.max(-T::one()).min(T::one()); Ok(cos_theta.acos()) @@ -426,13 +456,13 @@ where where Self::Output: std::iter::FromIterator, { - let denom = other.dot(other); + let denom = other.dot(other)?; if denom == T::zero() { return Err(VectorError::ZeroVectorError( "Cannot project onto zero vector".to_string(), )); } - let scalar = self.dot(other) / denom; + let scalar = self.dot(other)? / denom; Ok(other.as_slice().iter().map(|b| *b * scalar).collect()) } } @@ -600,17 +630,17 @@ impl FlexVector { /// Cosine similarity between self and other. #[inline] - pub fn cosine_similarity(&self, other: &Self) -> T + pub fn cosine_similarity(&self, other: &Self) -> Result where T: num::Float + Clone + std::iter::Sum, { - let dot = self.dot(other); + let dot = self.dot(other)?; let norm_self = self.norm(); let norm_other = other.norm(); if norm_self == T::zero() || norm_other == T::zero() { - T::zero() + Ok(T::zero()) } else { - dot / (norm_self * norm_other) + Ok(dot / (norm_self * norm_other)) } } } @@ -2433,7 +2463,7 @@ mod tests { fn test_cosine_similarity_parallel() { let v1 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![2.0, 4.0, 6.0]); - let cos_sim = v1.cosine_similarity(&v2); + let cos_sim = v1.cosine_similarity(&v2).unwrap(); assert!((cos_sim - 1.0).abs() < 1e-10); } @@ -2441,7 +2471,7 @@ mod tests { fn test_cosine_similarity_orthogonal() { let v1 = FlexVector::from_vec(vec![1.0, 0.0]); let v2 = FlexVector::from_vec(vec![0.0, 1.0]); - let cos_sim = v1.cosine_similarity(&v2); + let cos_sim = v1.cosine_similarity(&v2).unwrap(); assert!((cos_sim - 0.0).abs() < 1e-10); } @@ -2449,7 +2479,7 @@ mod tests { fn test_cosine_similarity_opposite() { let v1 = FlexVector::from_vec(vec![1.0, 0.0]); let v2 = FlexVector::from_vec(vec![-1.0, 0.0]); - let cos_sim = v1.cosine_similarity(&v2); + let cos_sim = v1.cosine_similarity(&v2).unwrap(); assert!((cos_sim + 1.0).abs() < 1e-10); } @@ -2457,7 +2487,7 @@ mod tests { fn test_cosine_similarity_zero_vector() { let v1 = FlexVector::from_vec(vec![0.0, 0.0]); let v2 = FlexVector::from_vec(vec![1.0, 2.0]); - let cos_sim = v1.cosine_similarity(&v2); + let cos_sim = v1.cosine_similarity(&v2).unwrap(); assert_eq!(cos_sim, 0.0); } @@ -2472,7 +2502,7 @@ mod tests { fn test_translate_i32() { let v1 = FlexVector::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); - let result = v1.translate(&v2); + let result = v1.translate(&v2).unwrap(); assert_eq!(result.as_slice(), &[5, 7, 9]); } @@ -2480,7 +2510,7 @@ mod tests { fn test_translate_f64() { let v1 = FlexVector::from_vec(vec![1.5, 2.5, 3.5]); let v2 = FlexVector::from_vec(vec![0.5, 1.5, 2.5]); - let result = v1.translate(&v2); + let result = v1.translate(&v2).unwrap(); assert_eq!(result.as_slice(), &[2.0, 4.0, 6.0]); } @@ -2488,7 +2518,7 @@ mod tests { fn test_translate_complex_f64() { let v1 = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); - let result = v1.translate(&v2); + let result = v1.translate(&v2).unwrap(); assert_eq!(result.as_slice(), &[Complex::new(6.0, 8.0), Complex::new(10.0, 12.0)]); } @@ -2642,7 +2672,7 @@ mod tests { fn test_dot_i32() { let v1 = FlexVector::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); - let dot = v1.dot(&v2); + let dot = v1.dot(&v2).unwrap(); assert_eq!(dot, 1 * 4 + 2 * 5 + 3 * 6); // 32 } @@ -2650,7 +2680,7 @@ mod tests { fn test_dot_f64() { let v1 = FlexVector::from_vec(vec![1.5, 2.0, -3.0]); let v2 = FlexVector::from_vec(vec![2.0, 0.5, 4.0]); - let dot = v1.dot(&v2); + let dot = v1.dot(&v2).unwrap(); assert!((dot - (1.5 * 2.0 + 2.0 * 0.5 + -3.0 * 4.0)).abs() < 1e-12); } diff --git a/src/types/mod.rs b/src/types/mod.rs index 5f786f9..2a0e52f 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -2,6 +2,7 @@ pub mod flexvector; pub mod traits; +mod utils; pub mod vector; pub use flexvector::FlexVector; diff --git a/src/types/traits.rs b/src/types/traits.rs index fad1863..8fde44b 100644 --- a/src/types/traits.rs +++ b/src/types/traits.rs @@ -194,20 +194,14 @@ pub trait VectorOps: VectorBase { type Output; /// ... - fn translate(&self, other: &Self) -> Self::Output + fn translate(&self, other: &Self) -> Result where T: num::Num + Copy; /// ... - #[inline] - fn mut_translate(&mut self, other: &Self) + fn mut_translate(&mut self, other: &Self) -> Result<(), VectorError> where - T: num::Num + Copy, - { - for (a, b) in self.as_mut_slice().iter_mut().zip(other.as_slice()) { - *a = *a + *b; - } - } + T: num::Num + Copy; /// Returns a new vector scaled by the given scalar. fn scale(&self, scalar: T) -> Self::Output @@ -255,13 +249,9 @@ pub trait VectorOps: VectorBase { } /// ... - #[inline] - fn dot(&self, other: &Self) -> T + fn dot(&self, other: &Self) -> Result where - T: num::Num + Copy + std::iter::Sum, - { - self.as_slice().iter().zip(other.as_slice()).map(|(a, b)| *a * *b).sum() - } + T: num::Num + Copy + std::iter::Sum; /// Dot product as f64 (for integer and float types). #[inline] @@ -789,6 +779,7 @@ pub trait VectorOpsComplex: VectorBase> { self.norm() } + /// ... fn project_onto(&self, other: &Self) -> Result where N: num::Float + Clone + std::iter::Sum, diff --git a/src/types/utils.rs b/src/types/utils.rs new file mode 100644 index 0000000..06c2825 --- /dev/null +++ b/src/types/utils.rs @@ -0,0 +1,23 @@ +//! Crate private core logic implementations + +#[inline] +pub(crate) fn mut_translate_impl(out: &mut [T], b: &[T]) { + for (out_elem, &b_elem) in out.iter_mut().zip(b) { + *out_elem = *out_elem + b_elem; + } +} + +#[inline] +pub(crate) fn translate_impl(a: &[T], b: &[T], out: &mut [T]) { + // Copy a into out, then add b in-place + out.copy_from_slice(a); + mut_translate_impl(out, b); +} + +#[inline] +pub(crate) fn dot_impl(a: &[T], b: &[T]) -> T +where + T: num::Num + Copy + std::iter::Sum, +{ + a.iter().zip(b.iter()).map(|(x, y)| *x * *y).sum() +} From 9f8ea758b2086c22a65226f67b1e26926b058f4d Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Sun, 11 May 2025 22:55:28 -0400 Subject: [PATCH 12/82] refactor core logic to an impl that can be used with FlexVector and Vector types --- src/types/flexvector.rs | 604 ++++++++++++++++++++++++++++++++++++++-- src/types/traits.rs | 103 ++----- src/types/utils.rs | 562 ++++++++++++++++++++++++++++++++++++- 3 files changed, 1157 insertions(+), 112 deletions(-) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index d9447b0..e6b92b9 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -10,7 +10,11 @@ use crate::{ types::traits::VectorOpsComplex, types::traits::VectorOpsFloat, }; -use crate::types::utils::{dot_impl, mut_translate_impl, translate_impl}; +use crate::types::utils::{ + angle_with_impl, chebyshev_distance_impl, cross_impl, distance_impl, dot_impl, dot_to_f64_impl, + lerp_impl, manhattan_distance_impl, minkowski_distance_impl, mut_lerp_impl, mut_translate_impl, + project_onto_impl, translate_impl, +}; use crate::errors::VectorError; @@ -358,6 +362,19 @@ where Ok(dot_impl(self.as_slice(), other.as_slice())) } + #[inline] + fn dot_to_f64(&self, other: &Self) -> Result + where + T: num::ToPrimitive, + { + if self.len() != other.len() { + return Err(VectorError::MismatchedLengthError( + "Vectors must have the same length".to_string(), + )); + } + Ok(dot_to_f64_impl(self.as_slice(), other.as_slice())) + } + /// Cross product (only for 3D vectors). #[inline] fn cross(&self, other: &Self) -> Result @@ -372,8 +389,7 @@ where } let a = self.as_slice(); let b = other.as_slice(); - let result = - [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]]; + let result = cross_impl(a, b); Ok(result.into_iter().collect()) } } @@ -383,7 +399,6 @@ where // VectorOpsFloat trait impl // // ================================ -// TODO: add tests impl VectorOpsFloat for FlexVector where T: num::Float + Clone + std::iter::Sum, @@ -416,7 +431,7 @@ where #[inline] fn lerp(&self, end: &Self, weight: T) -> Result where - T: PartialOrd, + T: num::Float + Copy, { if self.len() != end.len() { return Err(VectorError::MismatchedLengthError( @@ -426,18 +441,108 @@ where if weight < T::zero() || weight > T::one() { return Err(VectorError::OutOfRangeError("weight must be in [0, 1]".to_string())); } - let w = weight; - let one_minus_w = T::one() - w; - Ok(self - .components - .iter() - .zip(&end.components) - .map(|(a, b)| one_minus_w * *a + w * *b) - .collect()) + let mut out = FlexVector::zero(self.len()); + lerp_impl(self.as_slice(), end.as_slice(), weight, out.as_mut_slice()); + Ok(out) + } + + #[inline] + fn mut_lerp(&mut self, end: &Self, weight: T) -> Result<(), VectorError> + where + T: num::Float + Copy + PartialOrd, + { + if self.len() != end.len() { + return Err(VectorError::MismatchedLengthError( + "Vectors must have the same length".to_string(), + )); + } + if weight < T::zero() || weight > T::one() { + return Err(VectorError::OutOfRangeError("weight must be in [0, 1]".to_string())); + } + mut_lerp_impl(self.as_mut_slice(), end.as_slice(), weight); + Ok(()) } #[inline] - fn angle_with(&self, other: &Self) -> Result { + fn midpoint(&self, other: &Self) -> Result + where + T: num::Float + Clone, + { + if self.len() != other.len() { + return Err(VectorError::MismatchedLengthError( + "Vectors must have the same length".to_string(), + )); + } + let mut out = FlexVector::zero(self.len()); + lerp_impl(self.as_slice(), other.as_slice(), T::from(0.5).unwrap(), out.as_mut_slice()); + Ok(out) + } + + #[inline] + fn distance(&self, other: &Self) -> Result + where + T: num::Float + Clone + std::iter::Sum, + { + if self.len() != other.len() { + return Err(VectorError::MismatchedLengthError( + "Vectors must have the same length".to_string(), + )); + } + Ok(distance_impl(self.as_slice(), other.as_slice())) + } + + #[inline] + fn manhattan_distance(&self, other: &Self) -> Result + where + T: num::Float + Clone + std::iter::Sum, + { + if self.len() != other.len() { + return Err(VectorError::MismatchedLengthError( + "Vectors must have the same length".to_string(), + )); + } + Ok(manhattan_distance_impl(self.as_slice(), other.as_slice())) + } + + #[inline] + fn chebyshev_distance(&self, other: &Self) -> Result + where + T: num::Float + Clone + PartialOrd, + { + if self.len() != other.len() { + return Err(VectorError::MismatchedLengthError( + "Vectors must have the same length".to_string(), + )); + } + Ok(chebyshev_distance_impl(self.as_slice(), other.as_slice())) + } + + #[inline] + fn minkowski_distance(&self, other: &Self, p: T) -> Result + where + T: num::Float + Clone + std::iter::Sum, + { + if self.len() != other.len() { + return Err(VectorError::MismatchedLengthError( + "Vectors must have the same length".to_string(), + )); + } + if p < T::one() { + return Err(VectorError::OutOfRangeError("p must be >= 1".to_string())); + } + Ok(minkowski_distance_impl(self.as_slice(), other.as_slice(), p)) + } + + #[inline] + fn angle_with(&self, other: &Self) -> Result + where + T: num::Float + Clone + std::iter::Sum, + { + if self.len() != other.len() { + return Err(VectorError::MismatchedLengthError( + "Vectors must have the same length".to_string(), + )); + } let norm_self = self.norm(); let norm_other = other.norm(); if norm_self == T::zero() || norm_other == T::zero() { @@ -445,25 +550,27 @@ where "Cannot compute angle with zero vector".to_string(), )); } - let dot = self.dot(other)?; - let cos_theta = dot / (norm_self * norm_other); - let cos_theta = cos_theta.max(-T::one()).min(T::one()); - Ok(cos_theta.acos()) + Ok(angle_with_impl(self.as_slice(), other.as_slice(), norm_self, norm_other)) } #[inline] fn project_onto(&self, other: &Self) -> Result where + T: num::Float + Clone + std::iter::Sum, Self::Output: std::iter::FromIterator, { - let denom = other.dot(other)?; + if self.len() != other.len() { + return Err(VectorError::MismatchedLengthError( + "Vectors must have the same length".to_string(), + )); + } + let denom = dot_impl(other.as_slice(), other.as_slice()); if denom == T::zero() { return Err(VectorError::ZeroVectorError( "Cannot project onto zero vector".to_string(), )); } - let scalar = self.dot(other)? / denom; - Ok(other.as_slice().iter().map(|b| *b * scalar).collect()) + Ok(project_onto_impl(self.as_slice(), other.as_slice(), denom)) } } @@ -2522,12 +2629,20 @@ mod tests { assert_eq!(result.as_slice(), &[Complex::new(6.0, 8.0), Complex::new(10.0, 12.0)]); } + #[test] + fn test_translate_mismatched_lengths() { + let v1 = FlexVector::from_vec(vec![1, 2]); + let v2 = FlexVector::from_vec(vec![4, 5, 6]); + let result = v1.translate(&v2); + assert!(result.is_err()); + } + // -- mut_translate -- #[test] fn test_mut_translate_i32() { let mut v1 = FlexVector::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); - v1.mut_translate(&v2); + v1.mut_translate(&v2).unwrap(); assert_eq!(v1.as_slice(), &[5, 7, 9]); } @@ -2535,7 +2650,7 @@ mod tests { fn test_mut_translate_f64() { let mut v1 = FlexVector::from_vec(vec![1.5, 2.5, 3.5]); let v2 = FlexVector::from_vec(vec![0.5, 1.5, 2.5]); - v1.mut_translate(&v2); + v1.mut_translate(&v2).unwrap(); assert_eq!(v1.as_slice(), &[2.0, 4.0, 6.0]); } @@ -2543,10 +2658,18 @@ mod tests { fn test_mut_translate_complex_f64() { let mut v1 = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); - v1.mut_translate(&v2); + v1.mut_translate(&v2).unwrap(); assert_eq!(v1.as_slice(), &[Complex::new(6.0, 8.0), Complex::new(10.0, 12.0)]); } + #[test] + fn test_mut_translate_mismatched_lengths() { + let mut v1 = FlexVector::from_vec(vec![1, 2]); + let v2 = FlexVector::from_vec(vec![4, 5, 6]); + let result = v1.mut_translate(&v2); + assert!(result.is_err()); + } + // -- scale -- #[test] fn test_scale_i32() { @@ -2684,6 +2807,22 @@ mod tests { assert!((dot - (1.5 * 2.0 + 2.0 * 0.5 + -3.0 * 4.0)).abs() < 1e-12); } + #[test] + fn test_dot_mismatched_lengths() { + let v1 = FlexVector::from_vec(vec![1, 2]); + let v2 = FlexVector::from_vec(vec![4, 5, 6]); + let dot = v1.dot(&v2); + assert!(dot.is_err()); + } + + #[test] + fn test_dot_empty() { + let v1: FlexVector = FlexVector::from_vec(vec![]); + let v2 = FlexVector::from_vec(vec![]); + let dot = v1.dot(&v2).unwrap(); + assert_eq!(dot, 0); + } + // complex number dot product tested in VectorOpsComplex trait impl testing section below // -- dot_to_f64 -- @@ -2691,7 +2830,7 @@ mod tests { fn test_dot_to_f64_i32() { let v1 = FlexVector::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); - let dot = v1.dot_to_f64(&v2); + let dot = v1.dot_to_f64(&v2).unwrap(); assert!((dot - 32.0).abs() < 1e-12); } @@ -2699,10 +2838,18 @@ mod tests { fn test_dot_to_f64_f64() { let v1 = FlexVector::from_vec(vec![1.5, 2.0, -3.0]); let v2 = FlexVector::from_vec(vec![2.0, 0.5, 4.0]); - let dot = v1.dot_to_f64(&v2); + let dot = v1.dot_to_f64(&v2).unwrap(); assert!((dot - (1.5 * 2.0 + 2.0 * 0.5 + -3.0 * 4.0)).abs() < 1e-12); } + #[test] + fn test_dot_to_f64_mismatched_lengths() { + let v1 = FlexVector::from_vec(vec![1, 2]); + let v2 = FlexVector::from_vec(vec![4, 5, 6]); + let dot = v1.dot(&v2); + assert!(dot.is_err()); + } + // complex number dot_to_f64 tested in VectorOpsComplex trait impl testing section below // -- cross -- @@ -2728,7 +2875,23 @@ mod tests { // complex vector space. #[test] - fn test_cross_wrong_length() { + fn test_cross_wrong_length_1() { + let v1 = FlexVector::from_vec(vec![1, 2]); + let v2 = FlexVector::from_vec(vec![3, 4, 5]); + let result = v1.cross(&v2); + assert!(result.is_err()); + } + + #[test] + fn test_cross_wrong_length_2() { + let v1 = FlexVector::from_vec(vec![1, 2, 3]); + let v2 = FlexVector::from_vec(vec![3, 4]); + let result = v1.cross(&v2); + assert!(result.is_err()); + } + + #[test] + fn test_cross_wrong_length_3() { let v1 = FlexVector::from_vec(vec![1, 2]); let v2 = FlexVector::from_vec(vec![3, 4]); let result = v1.cross(&v2); @@ -3081,7 +3244,392 @@ mod tests { assert!(result.is_err()); } - //TODO: continue tests for all methods in VectorOpsFloat trait + // -- distance -- + #[test] + fn test_distance_f64_basic() { + let v1 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let v2 = FlexVector::from_vec(vec![4.0, 6.0, 8.0]); + let dist = v1.distance(&v2).unwrap(); + // sqrt((1-4)^2 + (2-6)^2 + (3-8)^2) = sqrt(9 + 16 + 25) = sqrt(50) + assert!((dist - 50f64.sqrt()).abs() < 1e-12); + } + + #[test] + fn test_distance_f64_zero() { + let v1 = FlexVector::from_vec(vec![0.0, 0.0, 0.0]); + let v2 = FlexVector::from_vec(vec![0.0, 0.0, 0.0]); + let dist = v1.distance(&v2).unwrap(); + assert_eq!(dist, 0.0); + } + + #[test] + fn test_distance_f64_negative_values() { + let v1 = FlexVector::from_vec(vec![-1.0, -2.0, -3.0]); + let v2 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let dist = v1.distance(&v2).unwrap(); + // sqrt(((-1)-1)^2 + ((-2)-2)^2 + ((-3)-3)^2) = sqrt(4 + 16 + 36) = sqrt(56) + assert!((dist - 56f64.sqrt()).abs() < 1e-12); + } + + #[test] + fn test_distance_f64_mismatched_length() { + let v1 = FlexVector::from_vec(vec![1.0, 2.0]); + let v2 = FlexVector::from_vec(vec![3.0, 4.0, 5.0]); + let result = v1.distance(&v2); + assert!(result.is_err()); + } + + // -- manhattan_distance -- + #[test] + fn test_manhattan_distance_f64_basic() { + let v1 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let v2 = FlexVector::from_vec(vec![4.0, 6.0, 8.0]); + let dist = v1.manhattan_distance(&v2).unwrap(); + // |1-4| + |2-6| + |3-8| = 3 + 4 + 5 = 12 + assert!((dist - 12.0).abs() < 1e-12); + } + + #[test] + fn test_manhattan_distance_f64_zero() { + let v1 = FlexVector::from_vec(vec![0.0, 0.0, 0.0]); + let v2 = FlexVector::from_vec(vec![0.0, 0.0, 0.0]); + let dist = v1.manhattan_distance(&v2).unwrap(); + assert_eq!(dist, 0.0); + } + + #[test] + fn test_manhattan_distance_f64_negative_values() { + let v1 = FlexVector::from_vec(vec![-1.0, -2.0, -3.0]); + let v2 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let dist = v1.manhattan_distance(&v2).unwrap(); + // |(-1)-1| + |(-2)-2| + |(-3)-3| = 2 + 4 + 6 = 12 + assert!((dist - 12.0).abs() < 1e-12); + } + + #[test] + fn test_manhattan_distance_f64_mismatched_length() { + let v1 = FlexVector::from_vec(vec![1.0, 2.0]); + let v2 = FlexVector::from_vec(vec![3.0, 4.0, 5.0]); + let result = v1.manhattan_distance(&v2); + assert!(result.is_err()); + } + + // -- chebyshev_distance -- + #[test] + fn test_chebyshev_distance_f64_basic() { + let v1 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let v2 = FlexVector::from_vec(vec![4.0, 6.0, 8.0]); + let dist = v1.chebyshev_distance(&v2).unwrap(); + // max(|1-4|, |2-6|, |3-8|) = max(3, 4, 5) = 5 + assert!((dist - 5.0).abs() < 1e-12); + } + + #[test] + fn test_chebyshev_distance_f64_zero() { + let v1 = FlexVector::from_vec(vec![0.0, 0.0, 0.0]); + let v2 = FlexVector::from_vec(vec![0.0, 0.0, 0.0]); + let dist = v1.chebyshev_distance(&v2).unwrap(); + assert_eq!(dist, 0.0); + } + + #[test] + fn test_chebyshev_distance_f64_negative_values() { + let v1 = FlexVector::from_vec(vec![-1.0, -2.0, -3.0]); + let v2 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let dist = v1.chebyshev_distance(&v2).unwrap(); + // max(|-1-1|, |-2-2|, |-3-3|) = max(2, 4, 6) = 6 + assert!((dist - 6.0).abs() < 1e-12); + } + + #[test] + fn test_chebyshev_distance_f64_mismatched_length() { + let v1 = FlexVector::from_vec(vec![1.0, 2.0]); + let v2 = FlexVector::from_vec(vec![3.0, 4.0, 5.0]); + let result = v1.chebyshev_distance(&v2); + assert!(result.is_err()); + } + + // -- minkowski_distance -- + #[test] + fn test_minkowski_distance_f64_basic() { + let v1 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let v2 = FlexVector::from_vec(vec![4.0, 6.0, 8.0]); + let dist = v1.minkowski_distance(&v2, 3.0).unwrap(); + // ((|1-4|^3 + |2-6|^3 + |3-8|^3))^(1/3) = (27 + 64 + 125)^(1/3) = (216)^(1/3) = 6 + assert!((dist - 6.0).abs() < 1e-12); + } + + #[test] + fn test_minkowski_distance_f64_p1() { + let v1 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let v2 = FlexVector::from_vec(vec![4.0, 6.0, 8.0]); + let dist = v1.minkowski_distance(&v2, 1.0).unwrap(); + // Should match manhattan distance: 3 + 4 + 5 = 12 + assert!((dist - 12.0).abs() < 1e-12); + } + + #[test] + fn test_minkowski_distance_f64_p2() { + let v1 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let v2 = FlexVector::from_vec(vec![4.0, 6.0, 8.0]); + let dist = v1.minkowski_distance(&v2, 2.0).unwrap(); + // Should match euclidean distance: sqrt(9 + 16 + 25) = sqrt(50) + assert!((dist - 50f64.sqrt()).abs() < 1e-12); + } + + #[test] + fn test_minkowski_distance_f64_empty() { + let v1: FlexVector = FlexVector::new(); + let v2: FlexVector = FlexVector::new(); + let dist = v1.minkowski_distance(&v2, 2.0).unwrap(); + assert_eq!(dist, 0.0); + } + + #[test] + fn test_minkowski_distance_f64_identical() { + let v1 = FlexVector::from_vec(vec![1.23, 4.56, 7.89]); + let v2 = FlexVector::from_vec(vec![1.23, 4.56, 7.89]); + let dist = v1.minkowski_distance(&v2, 2.0).unwrap(); + assert_eq!(dist, 0.0); + } + + #[test] + fn test_minkowski_distance_f64_partial() { + let v1 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let v2 = FlexVector::from_vec(vec![4.0, 6.0]); + let result = v1.minkowski_distance(&v2, 2.0); + assert!(result.is_err()); + } + + #[test] + fn test_minkowski_distance_f64_invalid_p() { + let v1 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let v2 = FlexVector::from_vec(vec![4.0, 6.0, 8.0]); + let result = v1.minkowski_distance(&v2, 0.5); + assert!(result.is_err()); + } + + // -- norm -- + #[test] + fn test_norm_f64_basic() { + let v = FlexVector::from_vec(vec![3.0, 4.0]); + let norm = v.norm(); + // sqrt(3^2 + 4^2) = 5 + assert!((norm - 5.0).abs() < 1e-12); + } + + #[test] + fn test_norm_f64_zero() { + let v = FlexVector::from_vec(vec![0.0, 0.0, 0.0]); + let norm = v.norm(); + assert_eq!(norm, 0.0); + } + + #[test] + fn test_norm_f64_single_element() { + let v = FlexVector::from_vec(vec![7.0]); + let norm = v.norm(); + assert_eq!(norm, 7.0); + } + + #[test] + fn test_norm_f64_negative_values() { + let v = FlexVector::from_vec(vec![-3.0, -4.0]); + let norm = v.norm(); + // sqrt((-3)^2 + (-4)^2) = 5 + assert!((norm - 5.0).abs() < 1e-12); + } + + // -- magnitude -- + #[test] + fn test_magnitude_f64_basic() { + let v = FlexVector::from_vec(vec![3.0, 4.0]); + let mag = v.magnitude(); + // sqrt(3^2 + 4^2) = 5 + assert!((mag - 5.0).abs() < 1e-12); + } + + #[test] + fn test_magnitude_f64_zero() { + let v = FlexVector::from_vec(vec![0.0, 0.0, 0.0]); + let mag = v.magnitude(); + assert_eq!(mag, 0.0); + } + + #[test] + fn test_magnitude_f64_single_element() { + let v = FlexVector::from_vec(vec![7.0]); + let mag = v.magnitude(); + assert_eq!(mag, 7.0); + } + + #[test] + fn test_magnitude_f64_negative_values() { + let v = FlexVector::from_vec(vec![-3.0, -4.0]); + let mag = v.magnitude(); + // sqrt((-3)^2 + (-4)^2) = 5 + assert!((mag - 5.0).abs() < 1e-12); + } + + // -- lp_norm -- + #[test] + fn test_lp_norm_f64_p1() { + let v = FlexVector::from_vec(vec![1.0, -2.0, 3.0]); + let norm = v.lp_norm(1.0).unwrap(); + // L1 norm: |1| + |−2| + |3| = 1 + 2 + 3 = 6 + assert!((norm - 6.0).abs() < 1e-12); + } + + #[test] + fn test_lp_norm_f64_p2() { + let v = FlexVector::from_vec(vec![3.0, 4.0]); + let norm = v.lp_norm(2.0).unwrap(); + // L2 norm: sqrt(3^2 + 4^2) = 5 + assert!((norm - 5.0).abs() < 1e-12); + } + + #[test] + fn test_lp_norm_f64_p3() { + let v = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let norm = v.lp_norm(3.0).unwrap(); + // (|1|^3 + |2|^3 + |3|^3)^(1/3) = (1 + 8 + 27)^(1/3) = 36^(1/3) + assert!((norm - 36f64.powf(1.0 / 3.0)).abs() < 1e-12); + } + + #[test] + fn test_lp_norm_f64_zero() { + let v = FlexVector::from_vec(vec![0.0, 0.0, 0.0]); + let norm = v.lp_norm(2.0).unwrap(); + assert_eq!(norm, 0.0); + } + + #[test] + fn test_lp_norm_f64_invalid_p() { + let v = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let result = v.lp_norm(0.5); + assert!(result.is_err()); + } + + // -- angle_with -- + #[test] + fn test_angle_with_f64_orthogonal() { + let v1 = FlexVector::from_vec(vec![1.0, 0.0]); + let v2 = FlexVector::from_vec(vec![0.0, 1.0]); + let angle = v1.angle_with(&v2).unwrap(); + // Orthogonal vectors: angle should be pi/2 + assert!((angle - std::f64::consts::FRAC_PI_2).abs() < 1e-12); + } + + #[test] + fn test_angle_with_f64_parallel() { + let v1 = FlexVector::from_vec(vec![1.0, 2.0]); + let v2 = FlexVector::from_vec(vec![2.0, 4.0]); + let angle = v1.angle_with(&v2).unwrap(); + // Parallel vectors: angle should be 0 (allow for floating-point error) + assert!(angle.abs() < 1e-7, "angle was {}", angle); + } + + #[test] + fn test_angle_with_f64_opposite() { + let v1 = FlexVector::from_vec(vec![1.0, 0.0]); + let v2 = FlexVector::from_vec(vec![-1.0, 0.0]); + let angle = v1.angle_with(&v2).unwrap(); + // Opposite vectors: angle should be pi + assert!((angle - std::f64::consts::PI).abs() < 1e-12); + } + + #[test] + fn test_angle_with_f64_identical() { + let v1 = FlexVector::from_vec(vec![3.0, 4.0]); + let v2 = FlexVector::from_vec(vec![3.0, 4.0]); + let angle = v1.angle_with(&v2).unwrap(); + // Identical vectors: angle should be 0 + assert!(angle.abs() < 1e-12); + } + + #[test] + fn test_angle_with_f64_arbitrary() { + let v1 = FlexVector::from_vec(vec![1.0, 2.0]); + let v2 = FlexVector::from_vec(vec![2.0, 1.0]); + let angle = v1.angle_with(&v2).unwrap(); + // Check that the angle is between 0 and pi + assert!(angle > 0.0 && angle < std::f64::consts::PI); + } + + #[test] + fn test_angle_with_f64_zero_vector() { + let v1 = FlexVector::from_vec(vec![0.0, 0.0]); + let v2 = FlexVector::from_vec(vec![1.0, 2.0]); + let result = v1.angle_with(&v2); + assert!(result.is_err()); + } + + #[test] + fn test_angle_with_f64_mismatched_length() { + let v1 = FlexVector::from_vec(vec![1.0, 2.0]); + let v2 = FlexVector::from_vec(vec![3.0, 4.0, 5.0]); + let result = v1.angle_with(&v2); + assert!(result.is_err()); + } + + // -- project_onto -- + #[test] + fn test_project_onto_f64_basic() { + let v1 = FlexVector::from_vec(vec![3.0, 4.0]); + let v2 = FlexVector::from_vec(vec![1.0, 0.0]); + let proj = v1.project_onto(&v2).unwrap(); + // Projection of [3,4] onto [1,0] is [3,0] + assert!((proj.as_slice()[0] - 3.0).abs() < 1e-12); + assert!((proj.as_slice()[1] - 0.0).abs() < 1e-12); + } + + #[test] + fn test_project_onto_f64_parallel() { + let v1 = FlexVector::from_vec(vec![2.0, 4.0]); + let v2 = FlexVector::from_vec(vec![1.0, 2.0]); + let proj = v1.project_onto(&v2).unwrap(); + // v1 is parallel to v2, so projection should be v1 itself + assert!((proj.as_slice()[0] - 2.0).abs() < 1e-12); + assert!((proj.as_slice()[1] - 4.0).abs() < 1e-12); + } + + #[test] + fn test_project_onto_f64_orthogonal() { + let v1 = FlexVector::from_vec(vec![0.0, 1.0]); + let v2 = FlexVector::from_vec(vec![1.0, 0.0]); + let proj = v1.project_onto(&v2).unwrap(); + // Orthogonal vectors: projection should be [0,0] + assert!((proj.as_slice()[0] - 0.0).abs() < 1e-12); + assert!((proj.as_slice()[1] - 0.0).abs() < 1e-12); + } + + #[test] + fn test_project_onto_f64_identical() { + let v1 = FlexVector::from_vec(vec![5.0, 5.0]); + let v2 = FlexVector::from_vec(vec![5.0, 5.0]); + let proj = v1.project_onto(&v2).unwrap(); + // Should be v1 itself + assert!((proj.as_slice()[0] - 5.0).abs() < 1e-12); + assert!((proj.as_slice()[1] - 5.0).abs() < 1e-12); + } + + #[test] + fn test_project_onto_f64_zero_vector() { + let v1 = FlexVector::from_vec(vec![1.0, 2.0]); + let v2 = FlexVector::from_vec(vec![0.0, 0.0]); + let result = v1.project_onto(&v2); + assert!(result.is_err()); + } + + #[test] + fn test_project_onto_f64_mismatched_length() { + let v1 = FlexVector::from_vec(vec![1.0, 2.0]); + let v2 = FlexVector::from_vec(vec![3.0, 4.0, 5.0]); + let result = v1.project_onto(&v2); + assert!(result.is_err()); + } + + //TODO: continue tests for all methods in VectorOpsComplex trait // ================================ // diff --git a/src/types/traits.rs b/src/types/traits.rs index 8fde44b..b48076c 100644 --- a/src/types/traits.rs +++ b/src/types/traits.rs @@ -254,17 +254,9 @@ pub trait VectorOps: VectorBase { T: num::Num + Copy + std::iter::Sum; /// Dot product as f64 (for integer and float types). - #[inline] - fn dot_to_f64(&self, other: &Self) -> f64 + fn dot_to_f64(&self, other: &Self) -> Result where - T: num::ToPrimitive, - { - self.as_slice() - .iter() - .zip(other.as_slice()) - .map(|(a, b)| a.to_f64().unwrap() * b.to_f64().unwrap()) - .sum() - } + T: num::ToPrimitive; ///... fn cross(&self, other: &Self) -> Result @@ -384,89 +376,34 @@ pub trait VectorOpsFloat: VectorBase { T: num::Float + Clone + PartialOrd; /// In-place linear interpolation between self and end by weight in [0, 1]. - #[inline] fn mut_lerp(&mut self, end: &Self, weight: T) -> Result<(), VectorError> where - T: num::Float + Copy + PartialOrd, - { - if self.len() != end.len() { - return Err(VectorError::MismatchedLengthError( - "Vectors must have the same length".to_string(), - )); - } - if weight < T::zero() || weight > T::one() { - return Err(VectorError::OutOfRangeError("weight must be in [0, 1]".to_string())); - } - let w = weight; - let one_minus_w = T::one() - w; - for (a, b) in self.as_mut_slice().iter_mut().zip(end.as_slice()) { - *a = one_minus_w * *a + w * *b; - } - Ok(()) - } + T: num::Float + Copy + PartialOrd; /// Midpoint - #[inline] fn midpoint(&self, other: &Self) -> Result where - T: num::Float + Clone, - { - self.lerp(other, T::from(0.5).unwrap()) - } + T: num::Float + Clone; /// Euclidean distance between self and other. - #[inline] - fn distance(&self, other: &Self) -> T + fn distance(&self, other: &Self) -> Result where - T: num::Float + Clone + std::iter::Sum, - { - self.as_slice() - .iter() - .zip(other.as_slice()) - .map(|(a, b)| (*a - *b).powi(2)) - .sum::() - .sqrt() - } + T: num::Float + Clone + std::iter::Sum; /// Manhattan (L1) distance between self and other. - #[inline] - fn manhattan_distance(&self, other: &Self) -> T + fn manhattan_distance(&self, other: &Self) -> Result where - T: num::Float + Clone + std::iter::Sum, - { - self.as_slice().iter().zip(other.as_slice()).map(|(a, b)| (*a - *b).abs()).sum() - } + T: num::Float + Clone + std::iter::Sum; /// Chebyshev (L∞) distance between self and other. - #[inline] - fn chebyshev_distance(&self, other: &Self) -> T + fn chebyshev_distance(&self, other: &Self) -> Result where - T: num::Float + Clone + PartialOrd, - { - self.as_slice() - .iter() - .zip(other.as_slice()) - .map(|(a, b)| (*a - *b).abs()) - .fold(T::zero(), |acc, x| acc.max(x)) - } + T: num::Float + Clone + PartialOrd; /// Minkowski (Lp) distance between self and other. - #[inline] fn minkowski_distance(&self, other: &Self, p: T) -> Result where - T: num::Float + Clone + std::iter::Sum, - { - if p < T::one() { - return Err(VectorError::OutOfRangeError("p must be >= 1".to_string())); - } - Ok(self - .as_slice() - .iter() - .zip(other.as_slice()) - .map(|(a, b)| (*a - *b).abs().powf(p)) - .sum::() - .powf(T::one() / p)) - } + T: num::Float + Clone + std::iter::Sum; /// Euclidean norm (magnitude) of the vector. #[inline] @@ -477,6 +414,15 @@ pub trait VectorOpsFloat: VectorBase { self.as_slice().iter().map(|a| (*a).powi(2)).sum::().sqrt() } + /// Alias for norm (magnitude). + #[inline] + fn magnitude(&self) -> T + where + T: num::Float + Clone + std::iter::Sum, + { + self.norm() + } + /// Lp norm (generalized Minkowski norm). #[inline] fn lp_norm(&self, p: T) -> Result @@ -489,15 +435,6 @@ pub trait VectorOpsFloat: VectorBase { Ok(self.as_slice().iter().map(|a| a.abs().powf(p)).sum::().powf(T::one() / p)) } - /// Alias for norm (magnitude). - #[inline] - fn magnitude(&self) -> T - where - T: num::Float + Clone + std::iter::Sum, - { - self.norm() - } - /// ... fn angle_with(&self, other: &Self) -> Result where diff --git a/src/types/utils.rs b/src/types/utils.rs index 06c2825..ed0f169 100644 --- a/src/types/utils.rs +++ b/src/types/utils.rs @@ -1,4 +1,4 @@ -//! Crate private core logic implementations +//! Core logic implementations #[inline] pub(crate) fn mut_translate_impl(out: &mut [T], b: &[T]) { @@ -21,3 +21,563 @@ where { a.iter().zip(b.iter()).map(|(x, y)| *x * *y).sum() } + +#[inline] +pub(crate) fn dot_to_f64_impl(a: &[T], b: &[T]) -> f64 +where + T: num::ToPrimitive, +{ + a.iter().zip(b.iter()).map(|(x, y)| x.to_f64().unwrap() * y.to_f64().unwrap()).sum() +} + +#[inline] +pub(crate) fn cross_impl(a: &[T], b: &[T]) -> [T; 3] +where + T: num::Num + Copy, +{ + [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]] +} + +#[inline] +pub(crate) fn lerp_impl(a: &[T], b: &[T], weight: T, out: &mut [T]) +where + T: num::Float + Copy, +{ + out.copy_from_slice(a); + mut_lerp_impl(out, b, weight); +} + +#[inline] +pub(crate) fn mut_lerp_impl(out: &mut [T], end: &[T], weight: T) +where + T: num::Float + Copy, +{ + let one_minus_w = T::one() - weight; + for (a, &b) in out.iter_mut().zip(end) { + *a = one_minus_w * *a + weight * b; + } +} + +#[inline] +pub(crate) fn distance_impl(a: &[T], b: &[T]) -> T +where + T: num::Float + Clone + std::iter::Sum, +{ + a.iter().zip(b.iter()).map(|(a, b)| (*a - *b).powi(2)).sum::().sqrt() +} + +#[inline] +pub(crate) fn manhattan_distance_impl(a: &[T], b: &[T]) -> T +where + T: num::Float + Clone + std::iter::Sum, +{ + a.iter().zip(b.iter()).map(|(a, b)| (*a - *b).abs()).sum() +} + +#[inline] +pub(crate) fn chebyshev_distance_impl(a: &[T], b: &[T]) -> T +where + T: num::Float + Clone + PartialOrd, +{ + a.iter().zip(b.iter()).map(|(a, b)| (*a - *b).abs()).fold(T::zero(), |acc, x| acc.max(x)) +} + +#[inline] +pub(crate) fn minkowski_distance_impl(a: &[T], b: &[T], p: T) -> T +where + T: num::Float + Clone + std::iter::Sum, +{ + a.iter().zip(b.iter()).map(|(a, b)| (*a - *b).abs().powf(p)).sum::().powf(T::one() / p) +} + +#[inline] +pub(crate) fn angle_with_impl(a: &[T], b: &[T], norm_a: T, norm_b: T) -> T +where + T: num::Float + Clone + std::iter::Sum, +{ + let dot = dot_impl(a, b); + let cos_theta = dot / (norm_a * norm_b); + let cos_theta = cos_theta.max(-T::one()).min(T::one()); + cos_theta.acos() +} + +#[inline] +pub(crate) fn project_onto_impl(a: &[T], b: &[T], denom: T) -> Out +where + T: num::Float + Clone + std::iter::Sum, + Out: std::iter::FromIterator, +{ + let scalar = dot_impl(a, b) / denom; + b.iter().map(|x| *x * scalar).collect() +} + +#[cfg(test)] +mod tests { + use super::*; + + // -- mut_translate_impl == + #[test] + fn test_mut_translate_impl_basic() { + let mut out = [1, 2, 3]; + let b = [4, 5, 6]; + mut_translate_impl(&mut out, &b); + assert_eq!(out, [5, 7, 9]); + } + + #[test] + fn test_mut_translate_impl_empty() { + let mut out: [i32; 0] = []; + let b: [i32; 0] = []; + mut_translate_impl(&mut out, &b); + assert_eq!(out, []); + } + + #[test] + fn test_mut_translate_impl_partial() { + let mut out = [1, 2, 3]; + let b = [10, 20]; + mut_translate_impl(&mut out, &b); + // Only the first two elements are updated + assert_eq!(out, [11, 22, 3]); + } + + // -- translate_impl -- + #[test] + fn test_translate_impl_basic() { + let a = [1, 2, 3]; + let b = [4, 5, 6]; + let mut out = [0; 3]; + translate_impl(&a, &b, &mut out); + assert_eq!(out, [5, 7, 9]); + } + + #[test] + fn test_translate_impl_empty() { + let a: [i32; 0] = []; + let b: [i32; 0] = []; + let mut out: [i32; 0] = []; + translate_impl(&a, &b, &mut out); + assert_eq!(out, []); + } + + #[test] + fn test_translate_impl_partial() { + let a = [1, 2, 3]; + let b = [10, 20]; + let mut out = [0; 3]; + translate_impl(&a, &b, &mut out); + // Only the first two elements are updated + assert_eq!(out, [11, 22, 3]); + } + + // -- dot_impl -- + #[test] + fn test_dot_impl_basic() { + let a = [1, 2, 3]; + let b = [4, 5, 6]; + let dot = dot_impl(&a, &b); + assert_eq!(dot, 1 * 4 + 2 * 5 + 3 * 6); + } + + #[test] + fn test_dot_impl_empty() { + let a: [i32; 0] = []; + let b: [i32; 0] = []; + let dot = dot_impl(&a, &b); + assert_eq!(dot, 0); + } + + #[test] + fn test_dot_impl_partial() { + let a = [1, 2, 3]; + let b = [10, 20]; + let dot = dot_impl(&a, &b); + // this is not a valid dot product and the reason why this function + // must be protected with vector dimension checks at compile or run time. + assert_eq!(dot, 1 * 10 + 2 * 20); + } + + // -- dot_to_f64_impl -- + #[test] + fn test_dot_to_f64_impl_basic() { + let a = [1.0f32, 2.0, 3.0]; + let b = [4.0f32, 5.0, 6.0]; + let dot = dot_to_f64_impl(&a, &b); + assert!((dot - (1.0_f64 * 4.0 + 2.0 * 5.0 + 3.0 * 6.0)).abs() < 1e-12); + } + + #[test] + fn test_dot_to_f64_impl_empty() { + let a: [f32; 0] = []; + let b: [f32; 0] = []; + let dot = dot_to_f64_impl(&a, &b); + assert_eq!(dot, 0.0_f64); + } + + #[test] + fn test_dot_to_f64_impl_partial() { + let a = [1i32, 2, 3]; + let b = [10i32, 20]; + let dot = dot_to_f64_impl(&a, &b); + assert_eq!(dot, 1.0_f64 * 10.0 + 2.0 * 20.0); + } + + #[test] + fn test_dot_to_f64_impl_mixed_types() { + let a = [1u8, 2u8, 3u8]; + let b = [4u8, 5u8, 6u8]; + let dot = dot_to_f64_impl(&a, &b); + assert_eq!(dot, 1.0_f64 * 4.0 + 2.0 * 5.0 + 3.0 * 6.0); + } + + // -- cross_impl -- + #[test] + fn test_cross_impl_basic() { + let a = [1, 2, 3]; + let b = [4, 5, 6]; + let cross = cross_impl(&a, &b); + // [2*6 - 3*5, 3*4 - 1*6, 1*5 - 2*4] = [12-15, 12-6, 5-8] = [-3, 6, -3] + assert_eq!(cross, [-3, 6, -3]); + } + + #[test] + fn test_cross_impl_zero_vector() { + let a = [0, 0, 0]; + let b = [1, 2, 3]; + let cross = cross_impl(&a, &b); + assert_eq!(cross, [0, 0, 0]); + } + + #[test] + fn test_cross_impl_parallel_vectors() { + let a = [1, 2, 3]; + let b = [2, 4, 6]; + let cross = cross_impl(&a, &b); + assert_eq!(cross, [0, 0, 0]); + } + + #[test] + fn test_cross_impl_orthogonal_vectors() { + let a = [1, 0, 0]; + let b = [0, 1, 0]; + let cross = cross_impl(&a, &b); + assert_eq!(cross, [0, 0, 1]); + } + + // -- lerp_impl -- + #[test] + fn test_lerp_impl_basic() { + let a = [1.0f32, 2.0, 3.0]; + let b = [4.0f32, 5.0, 6.0]; + let mut out = [0.0f32; 3]; + lerp_impl(&a, &b, 0.5, &mut out); + assert_eq!(out, [2.5, 3.5, 4.5]); + } + + #[test] + fn test_lerp_impl_weight_zero() { + let a = [1.0f64, 2.0, 3.0]; + let b = [4.0f64, 5.0, 6.0]; + let mut out = [0.0f64; 3]; + lerp_impl(&a, &b, 0.0, &mut out); + assert_eq!(out, [1.0, 2.0, 3.0]); + } + + #[test] + fn test_lerp_impl_weight_one() { + let a = [1.0f64, 2.0, 3.0]; + let b = [4.0f64, 5.0, 6.0]; + let mut out = [0.0f64; 3]; + lerp_impl(&a, &b, 1.0, &mut out); + assert_eq!(out, [4.0, 5.0, 6.0]); + } + + #[test] + fn test_lerp_impl_empty() { + let a: [f32; 0] = []; + let b: [f32; 0] = []; + let mut out: [f32; 0] = []; + lerp_impl(&a, &b, 0.5, &mut out); + assert_eq!(out, []); + } + + #[test] + fn test_lerp_impl_partial() { + let a = [1.0f32, 2.0, 3.0]; + let b = [4.0f32, 5.0]; + let mut out = [0.0f32; 3]; + lerp_impl(&a, &b, 0.5, &mut out); + // Only the first two elements are updated + assert_eq!(out, [2.5, 3.5, 3.0]); + } + + // -- distance_impl -- + #[test] + fn test_distance_impl_basic() { + let a = [1.0f64, 2.0, 3.0]; + let b = [4.0f64, 6.0, 8.0]; + let dist = distance_impl(&a, &b); + // sqrt((1-4)^2 + (2-6)^2 + (3-8)^2) = sqrt(9 + 16 + 25) = sqrt(50) + assert!((dist - 50f64.sqrt()).abs() < 1e-12); + } + + #[test] + fn test_distance_impl_empty() { + let a: [f32; 0] = []; + let b: [f32; 0] = []; + let dist = distance_impl(&a, &b); + assert_eq!(dist, 0.0); + } + + #[test] + fn test_distance_impl_partial() { + let a = [1.0f32, 2.0, 3.0]; + let b = [4.0f32, 6.0]; + let dist = distance_impl(&a, &b); + // Only the first two elements are used: sqrt((1-4)^2 + (2-6)^2) = sqrt(9 + 16) = 5 + assert!((dist - 5.0).abs() < 1e-6); + } + + #[test] + fn test_distance_impl_identical() { + let a = [1.23f64, 4.56, 7.89]; + let b = [1.23f64, 4.56, 7.89]; + let dist = distance_impl(&a, &b); + assert_eq!(dist, 0.0); + } + + // -- manhattan_distance_impl -- + #[test] + fn test_manhattan_distance_impl_basic() { + let a = [1.0f64, 2.0, 3.0]; + let b = [4.0f64, 6.0, 8.0]; + let dist = manhattan_distance_impl(&a, &b); + // |1-4| + |2-6| + |3-8| = 3 + 4 + 5 = 12 + assert!((dist - 12.0).abs() < 1e-12); + } + + #[test] + fn test_manhattan_distance_impl_empty() { + let a: [f32; 0] = []; + let b: [f32; 0] = []; + let dist = manhattan_distance_impl(&a, &b); + assert_eq!(dist, 0.0); + } + + #[test] + fn test_manhattan_distance_impl_partial() { + let a = [1.0f32, 2.0, 3.0]; + let b = [4.0f32, 6.0]; + let dist = manhattan_distance_impl(&a, &b); + // Only the first two elements are used: |1-4| + |2-6| = 3 + 4 = 7 + assert!((dist - 7.0).abs() < 1e-6); + } + + #[test] + fn test_manhattan_distance_impl_identical() { + let a = [1.23f64, 4.56, 7.89]; + let b = [1.23f64, 4.56, 7.89]; + let dist = manhattan_distance_impl(&a, &b); + assert_eq!(dist, 0.0); + } + + // -- chebyshev_distance_impl -- + #[test] + fn test_chebyshev_distance_impl_basic() { + let a = [1.0f64, 2.0, 3.0]; + let b = [4.0f64, 6.0, 8.0]; + let dist = chebyshev_distance_impl(&a, &b); + // max(|1-4|, |2-6|, |3-8|) = max(3, 4, 5) = 5 + assert!((dist - 5.0).abs() < 1e-12); + } + + #[test] + fn test_chebyshev_distance_impl_empty() { + let a: [f32; 0] = []; + let b: [f32; 0] = []; + let dist = chebyshev_distance_impl(&a, &b); + assert_eq!(dist, 0.0); + } + + #[test] + fn test_chebyshev_distance_impl_partial() { + let a = [1.0f32, 2.0, 3.0]; + let b = [4.0f32, 6.0]; + let dist = chebyshev_distance_impl(&a, &b); + // Only the first two elements are used: max(|1-4|, |2-6|) = max(3, 4) = 4 + assert!((dist - 4.0).abs() < 1e-6); + } + + #[test] + fn test_chebyshev_distance_impl_identical() { + let a = [1.23f64, 4.56, 7.89]; + let b = [1.23f64, 4.56, 7.89]; + let dist = chebyshev_distance_impl(&a, &b); + assert_eq!(dist, 0.0); + } + + // -- minkowski_distance_impl -- + #[test] + fn test_minkowski_distance_impl_basic() { + let a = [1.0f64, 2.0, 3.0]; + let b = [4.0f64, 6.0, 8.0]; + let p = 3.0; + let dist = minkowski_distance_impl(&a, &b, p); + // ((|1-4|^3 + |2-6|^3 + |3-8|^3))^(1/3) = (27 + 64 + 125)^(1/3) = (216)^(1/3) = 6 + assert!((dist - 6.0).abs() < 1e-12); + } + + #[test] + fn test_minkowski_distance_impl_p1() { + let a = [1.0f64, 2.0, 3.0]; + let b = [4.0f64, 6.0, 8.0]; + let dist = minkowski_distance_impl(&a, &b, 1.0); + // Should match manhattan distance: 3 + 4 + 5 = 12 + assert!((dist - 12.0).abs() < 1e-12); + } + + #[test] + fn test_minkowski_distance_impl_p2() { + let a = [1.0f64, 2.0, 3.0]; + let b = [4.0f64, 6.0, 8.0]; + let dist = minkowski_distance_impl(&a, &b, 2.0); + // Should match euclidean distance: sqrt(9 + 16 + 25) = sqrt(50) + assert!((dist - 50f64.sqrt()).abs() < 1e-12); + } + + #[test] + fn test_minkowski_distance_impl_empty() { + let a: [f32; 0] = []; + let b: [f32; 0] = []; + let dist = minkowski_distance_impl(&a, &b, 2.0); + assert_eq!(dist, 0.0); + } + + #[test] + fn test_minkowski_distance_impl_partial() { + let a = [1.0f32, 2.0, 3.0]; + let b = [4.0f32, 6.0]; + let dist = minkowski_distance_impl(&a, &b, 2.0); + // Only the first two elements: sqrt((1-4)^2 + (2-6)^2) = sqrt(9 + 16) = 5 + assert!((dist - 5.0).abs() < 1e-6); + } + + #[test] + fn test_minkowski_distance_impl_identical() { + let a = [1.23f64, 4.56, 7.89]; + let b = [1.23f64, 4.56, 7.89]; + let dist = minkowski_distance_impl(&a, &b, 2.0); + assert_eq!(dist, 0.0); + } + + // -- angle_with_impl -- + #[test] + fn test_angle_with_impl_orthogonal() { + let a = [1.0f64, 0.0]; + let b = [0.0f64, 1.0]; + let norm_a = (a[0].powi(2) + a[1].powi(2)).sqrt(); + let norm_b = (b[0].powi(2) + b[1].powi(2)).sqrt(); + let angle = angle_with_impl(&a, &b, norm_a, norm_b); + // Orthogonal vectors: angle should be pi/2 + assert!((angle - std::f64::consts::FRAC_PI_2).abs() < 1e-12); + } + + #[test] + fn test_angle_with_impl_parallel() { + let a = [1.0f64, 2.0]; + let b = [2.0f64, 4.0]; + let norm_a = (a[0].powi(2) + a[1].powi(2)).sqrt(); + let norm_b = (b[0].powi(2) + b[1].powi(2)).sqrt(); + let angle = angle_with_impl(&a, &b, norm_a, norm_b); + // Parallel vectors: angle should be 0 (allow for floating-point error) + assert!(angle.abs() < 1e-7, "angle was {}", angle); + } + + #[test] + fn test_angle_with_impl_opposite() { + let a = [1.0f64, 0.0]; + let b = [-1.0f64, 0.0]; + let norm_a = (a[0].powi(2) + a[1].powi(2)).sqrt(); + let norm_b = (b[0].powi(2) + b[1].powi(2)).sqrt(); + let angle = angle_with_impl(&a, &b, norm_a, norm_b); + // Opposite vectors: angle should be pi + assert!((angle - std::f64::consts::PI).abs() < 1e-12); + } + + #[test] + fn test_angle_with_impl_identical() { + let a = [3.0f64, 4.0]; + let b = [3.0f64, 4.0]; + let norm_a = (a[0].powi(2) + a[1].powi(2)).sqrt(); + let norm_b = (b[0].powi(2) + b[1].powi(2)).sqrt(); + let angle = angle_with_impl(&a, &b, norm_a, norm_b); + // Identical vectors: angle should be 0 + assert!(angle.abs() < 1e-12); + } + + #[test] + fn test_angle_with_impl_arbitrary() { + let a = [1.0f64, 2.0]; + let b = [2.0f64, 1.0]; + let norm_a = (a[0].powi(2) + a[1].powi(2)).sqrt(); + let norm_b = (b[0].powi(2) + b[1].powi(2)).sqrt(); + let angle = angle_with_impl(&a, &b, norm_a, norm_b); + // Check that the angle is between 0 and pi + assert!(angle > 0.0 && angle < std::f64::consts::PI); + } + + // -- project_onto_impl -- + #[test] + fn test_project_onto_impl_basic() { + let a = [3.0f64, 4.0]; + let b = [1.0f64, 0.0]; + let denom = dot_impl(&b, &b); + let proj: Vec = project_onto_impl(&a, &b, denom); + // Projection of [3,4] onto [1,0] is [3,0] + assert!((proj[0] - 3.0).abs() < 1e-12); + assert!((proj[1] - 0.0).abs() < 1e-12); + } + + #[test] + fn test_project_onto_impl_parallel() { + let a = [2.0f64, 4.0]; + let b = [1.0f64, 2.0]; + let denom = dot_impl(&b, &b); + let proj: Vec = project_onto_impl(&a, &b, denom); + // a is parallel to b, so projection should be a itself + assert!((proj[0] - 2.0).abs() < 1e-12); + assert!((proj[1] - 4.0).abs() < 1e-12); + } + + #[test] + fn test_project_onto_impl_orthogonal() { + let a = [0.0f64, 1.0]; + let b = [1.0f64, 0.0]; + let denom = dot_impl(&b, &b); + let proj: Vec = project_onto_impl(&a, &b, denom); + // Orthogonal vectors: projection should be [0,0] + assert!((proj[0] - 0.0).abs() < 1e-12); + assert!((proj[1] - 0.0).abs() < 1e-12); + } + + #[test] + fn test_project_onto_impl_identical() { + let a = [5.0f64, 5.0]; + let b = [5.0f64, 5.0]; + let denom = dot_impl(&b, &b); + let proj: Vec = project_onto_impl(&a, &b, denom); + // Should be a itself + assert!((proj[0] - 5.0).abs() < 1e-12); + assert!((proj[1] - 5.0).abs() < 1e-12); + } + + #[test] + fn test_project_onto_impl_zero_vector() { + let a = [1.0f64, 2.0]; + let b = [0.0f64, 0.0]; + let denom = dot_impl(&b, &b); + // denom is zero, so projection would be NaN or inf; check for NaN + let proj: Vec = project_onto_impl(&a, &b, denom); + assert!(proj.iter().all(|x| x.is_nan())); + } +} From 33128f98837978808312c9b97c60af8c51985f1e Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Mon, 12 May 2025 00:26:06 -0400 Subject: [PATCH 13/82] refactor minimum and maximum --- src/types/traits.rs | 61 +++------------------------------------------ 1 file changed, 4 insertions(+), 57 deletions(-) diff --git a/src/types/traits.rs b/src/types/traits.rs index b48076c..9fe78bd 100644 --- a/src/types/traits.rs +++ b/src/types/traits.rs @@ -286,18 +286,18 @@ pub trait VectorOps: VectorBase { #[inline] fn minimum(&self) -> Option where - T: Ord + Copy, + T: PartialOrd + Copy, { - self.as_slice().iter().copied().min() + self.as_slice().iter().copied().reduce(|a, b| if a < b { a } else { b }) } /// ... #[inline] fn maximum(&self) -> Option where - T: Ord + Copy, + T: PartialOrd + Copy, { - self.as_slice().iter().copied().max() + self.as_slice().iter().copied().reduce(|a, b| if a > b { a } else { b }) } /// L1 norm (sum of absolute values). @@ -446,59 +446,6 @@ pub trait VectorOpsFloat: VectorBase { where T: num::Float + Clone + std::iter::Sum, Self::Output: std::iter::FromIterator; - - /// Returns the minimum element, or `None` if the vector is empty. - /// - /// # NaN Handling - /// - /// If any element in the vector is `NaN`, the result of this method is not guaranteed to be meaningful. - /// The comparison `a < b` will return `false` if either `a` or `b` is `NaN`, so if a `NaN` is present, - /// it may be returned as the minimum, or it may cause a different value to be returned depending on the order of elements. - /// This matches the behavior of Rust's standard library for floating point minimum operations. - /// - /// If you want to ignore `NaN` values, filter them out before calling this method. - /// # Examples - /// ```rust - /// # use vectora::FlexVector; - /// use vectora::types::traits::VectorOpsFloat; - /// - /// let v = FlexVector::from(vec![1.5, -2.0, 3.0]); - /// assert_eq!(v.minimum(), Some(-2.0)); - /// ``` - #[inline] - fn minimum(&self) -> Option - where - T: PartialOrd + Copy, - { - self.as_slice().iter().copied().reduce(|a, b| if a < b { a } else { b }) - } - - /// Returns the maximum element, or `None` if the vector is empty. - /// - /// # NaN Handling - /// - /// If any element in the vector is `NaN`, the result of this method is not guaranteed to be meaningful. - /// The comparison `a > b` will return `false` if either `a` or `b` is `NaN`, so if a `NaN` is present, - /// it may be returned as the maximum, or it may cause a different value to be returned depending on the order of elements. - /// This matches the behavior of Rust's standard library for floating point maximum operations. - /// - /// If you want to ignore `NaN` values, filter them out before calling this method. - /// - /// # Examples - /// ``` - /// # use vectora::FlexVector; - /// use vectora::types::traits::VectorOpsFloat; - /// - /// let v = FlexVector::from(vec![1.5, -2.0, 3.0]); - /// assert_eq!(v.maximum(), Some(3.0)); - /// ``` - #[inline] - fn maximum(&self) -> Option - where - T: PartialOrd + Copy, - { - self.as_slice().iter().copied().reduce(|a, b| if a > b { a } else { b }) - } } pub trait VectorOpsComplex: VectorBase> { From a3f7c2659b257bda0c3bbc163ecc689600334844 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Mon, 12 May 2025 00:26:16 -0400 Subject: [PATCH 14/82] new tests --- src/types/flexvector.rs | 75 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 70 insertions(+), 5 deletions(-) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index e6b92b9..b6088d2 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -301,7 +301,6 @@ impl VectorBase for FlexVector { // VectorOps trait impl // // ================================ -// TODO: add tests impl VectorOps for FlexVector where T: num::Num + Clone + Copy, @@ -2960,7 +2959,43 @@ mod tests { assert_eq!(v.minimum(), Some(1)); } - // minimum floating point type tests in VectorOpsFloat trait impl testing section + #[test] + fn test_minimum_f64_basic() { + let v = FlexVector::from_vec(vec![1.5, -2.0, 3.0]); + assert_eq!(v.minimum(), Some(-2.0)); + } + + #[test] + fn test_minimum_f64_all_positive() { + let v = FlexVector::from_vec(vec![2.0, 4.0, 1.0, 3.0]); + assert_eq!(v.minimum(), Some(1.0)); + } + + #[test] + fn test_minimum_f64_all_negative() { + let v = FlexVector::from_vec(vec![-1.0, -2.0, -3.0]); + assert_eq!(v.minimum(), Some(-3.0)); + } + + #[test] + fn test_minimum_f64_single_element() { + let v = FlexVector::from_vec(vec![42.0]); + assert_eq!(v.minimum(), Some(42.0)); + } + + #[test] + fn test_minimum_f64_empty() { + let v: FlexVector = FlexVector::new(); + assert_eq!(v.minimum(), None); + } + + #[test] + fn test_minimum_f64_with_nan() { + let v = FlexVector::from_vec(vec![1.0, f64::NAN, 2.0]); + // The result is not guaranteed to be meaningful if NaN is present, + // but it should return Some value (could be NaN or a number). + assert!(v.minimum().is_some()); + } #[test] fn test_minimum_empty() { @@ -2975,14 +3010,44 @@ mod tests { assert_eq!(v.maximum(), Some(4)); } - // maximum floating point type tests in VectorOpsFloat trait impl testing section + #[test] + fn test_maximum_f64_basic() { + let v = FlexVector::from_vec(vec![1.5, -2.0, 3.0]); + assert_eq!(v.maximum(), Some(3.0)); + } #[test] - fn test_maximum_empty() { - let v: FlexVector = FlexVector::new(); + fn test_maximum_f64_all_positive() { + let v = FlexVector::from_vec(vec![2.0, 4.0, 1.0, 3.0]); + assert_eq!(v.maximum(), Some(4.0)); + } + + #[test] + fn test_maximum_f64_all_negative() { + let v = FlexVector::from_vec(vec![-1.0, -2.0, -3.0]); + assert_eq!(v.maximum(), Some(-1.0)); + } + + #[test] + fn test_maximum_f64_single_element() { + let v = FlexVector::from_vec(vec![42.0]); + assert_eq!(v.maximum(), Some(42.0)); + } + + #[test] + fn test_maximum_f64_empty() { + let v: FlexVector = FlexVector::new(); assert_eq!(v.maximum(), None); } + #[test] + fn test_maximum_f64_with_nan() { + let v = FlexVector::from_vec(vec![1.0, f64::NAN, 2.0]); + // The result is not guaranteed to be meaningful if NaN is present, + // but it should return Some value (could be NaN or a number). + assert!(v.maximum().is_some()); + } + // -- l1_norm -- #[test] fn test_l1_norm_i32() { From 71268ae563e620174b1db96e66d3d9ef44af7d9f Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Mon, 12 May 2025 08:42:14 -0400 Subject: [PATCH 15/82] refactor normalize and normalize_to to make core logic available across Vector and FlexVector types --- src/types/flexvector.rs | 87 ++++++++++++++++++------- src/types/traits.rs | 16 +++-- src/types/utils.rs | 139 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 209 insertions(+), 33 deletions(-) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index b6088d2..3c9f9a9 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -13,7 +13,7 @@ use crate::{ use crate::types::utils::{ angle_with_impl, chebyshev_distance_impl, cross_impl, distance_impl, dot_impl, dot_to_f64_impl, lerp_impl, manhattan_distance_impl, minkowski_distance_impl, mut_lerp_impl, mut_translate_impl, - project_onto_impl, translate_impl, + normalize_impl, normalize_to_impl, project_onto_impl, translate_impl, }; use crate::errors::VectorError; @@ -406,25 +406,21 @@ where #[inline] fn normalize(&self) -> Result { - let n = self.norm(); - if n == T::zero() { - return Err(VectorError::ZeroVectorError("Cannot normalize a zero vector".to_string())); - } - Ok(self.components.iter().map(|a| *a / n).collect()) + normalize_impl(self.as_slice(), self.norm()) } /// Returns a new vector with the same direction and the given magnitude. #[inline] fn normalize_to(&self, magnitude: T) -> Result where + T: Copy + + PartialEq + + std::ops::Div + + std::ops::Mul + + num::Zero, Self::Output: std::iter::FromIterator, { - let n = self.norm(); - if n == T::zero() { - return Err(VectorError::ZeroVectorError("Cannot normalize a zero vector".to_string())); - } - let scale = magnitude / n; - Ok(self.as_slice().iter().map(|a| *a * scale).collect()) + normalize_to_impl(self.as_slice(), self.norm(), magnitude) } #[inline] @@ -575,7 +571,7 @@ where // ================================ // -// VectorOpsComplexFloat trait impl +// VectorOpsComplex trait impl // // ================================ // TODO: add tests @@ -588,26 +584,27 @@ where #[inline] fn normalize(&self) -> Result where + Complex: Copy + PartialEq + std::ops::Div, Output = Complex> + num::Zero, Self::Output: std::iter::FromIterator>, { - let n = self.norm(); - if n == N::zero() { - return Err(VectorError::ZeroVectorError("Cannot normalize a zero vector".to_string())); - } - Ok(self.components.iter().map(|a| *a / n).collect()) + normalize_impl(self.as_slice(), Complex::new(self.norm(), N::zero())) } #[inline] fn normalize_to(&self, magnitude: N) -> Result where + Complex: Copy + + PartialEq + + std::ops::Div, Output = Complex> + + std::ops::Mul, Output = Complex> + + num::Zero, Self::Output: std::iter::FromIterator>, { - let n = self.norm(); - if n == N::zero() { - return Err(VectorError::ZeroVectorError("Cannot normalize a zero vector".to_string())); - } - let scale = magnitude / n; - Ok(self.as_slice().iter().map(|a| *a * scale).collect()) + normalize_to_impl( + self.as_slice(), + Complex::new(self.norm(), N::zero()), + Complex::new(magnitude, N::zero()), + ) } #[inline] @@ -3694,6 +3691,47 @@ mod tests { assert!(result.is_err()); } + // ================================ + // + // VectorOpsComplex trait tests + // + // ================================ + + // -- normalize -- + #[test] + fn test_normalize_complex_f64_basic() { + let v = FlexVector::from_vec(vec![Complex::new(3.0, 4.0)]); + let normalized = v.normalize().unwrap(); + // The norm is 5.0, so the normalized vector should be [0.6 + 0.8i] + assert!((normalized.as_slice()[0].re - 0.6).abs() < 1e-12); + assert!((normalized.as_slice()[0].im - 0.8).abs() < 1e-12); + } + + #[test] + fn test_normalize_complex_f64_multiple_elements() { + let v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let norm = ((1.0 * 1.0 + 2.0 * 2.0) + (3.0 * 3.0 + 4.0 * 4.0)).sqrt(); + let normalized = v.normalize().unwrap(); + assert!((normalized.as_slice()[0].re - 1.0 / norm).abs() < 1e-12); + assert!((normalized.as_slice()[0].im - 2.0 / norm).abs() < 1e-12); + assert!((normalized.as_slice()[1].re - 3.0 / norm).abs() < 1e-12); + assert!((normalized.as_slice()[1].im - 4.0 / norm).abs() < 1e-12); + } + + #[test] + fn test_normalize_complex_f64_zero_vector() { + let v = FlexVector::from_vec(vec![Complex::new(0.0, 0.0)]); + let result = v.normalize(); + assert!(result.is_err()); + } + + #[test] + fn test_normalize_complex_f64_empty() { + let v: FlexVector> = FlexVector::new(); + let normalized = v.normalize(); + assert!(normalized.is_err()); + } + //TODO: continue tests for all methods in VectorOpsComplex trait // ================================ @@ -3701,6 +3739,7 @@ mod tests { // Unary Negation trait tests // // ================================ + #[test] fn test_neg() { let v = FlexVector::from_vec(vec![1, -2, 3]); diff --git a/src/types/traits.rs b/src/types/traits.rs index 9fe78bd..cc2c3fb 100644 --- a/src/types/traits.rs +++ b/src/types/traits.rs @@ -328,7 +328,7 @@ pub trait VectorOpsFloat: VectorBase { /// ... fn normalize(&self) -> Result where - T: num::Float + Clone + std::iter::Sum, + T: Copy + PartialEq + std::ops::Div + num::Zero, Self::Output: std::iter::FromIterator; /// ... @@ -350,7 +350,11 @@ pub trait VectorOpsFloat: VectorBase { /// Returns a new vector with the same direction and the given magnitude. fn normalize_to(&self, magnitude: T) -> Result where - T: num::Float + Clone + std::iter::Sum, + T: Copy + + PartialEq + + std::ops::Div + + std::ops::Mul + + num::Zero, Self::Output: std::iter::FromIterator; /// ... @@ -454,7 +458,7 @@ pub trait VectorOpsComplex: VectorBase> { /// ... fn normalize(&self) -> Result where - N: num::Float + Clone + std::iter::Sum, + Complex: Copy + PartialEq + std::ops::Div, Output = Complex> + num::Zero, Self::Output: std::iter::FromIterator>; /// ... @@ -476,7 +480,11 @@ pub trait VectorOpsComplex: VectorBase> { /// Returns a new vector with the same direction and the given magnitude (real). fn normalize_to(&self, magnitude: N) -> Result where - N: num::Float + Clone + std::iter::Sum, + Complex: Copy + + PartialEq + + std::ops::Div, Output = Complex> + + std::ops::Mul, Output = Complex> + + num::Zero, Self::Output: std::iter::FromIterator>; /// Scales the complex vector in place to the given (real) magnitude. diff --git a/src/types/utils.rs b/src/types/utils.rs index ed0f169..b5f2f19 100644 --- a/src/types/utils.rs +++ b/src/types/utils.rs @@ -1,5 +1,7 @@ //! Core logic implementations +use crate::errors::VectorError; + #[inline] pub(crate) fn mut_translate_impl(out: &mut [T], b: &[T]) { for (out_elem, &b_elem) in out.iter_mut().zip(b) { @@ -111,8 +113,41 @@ where b.iter().map(|x| *x * scalar).collect() } +#[inline] +pub(crate) fn normalize_impl(slice: &[T], norm: T) -> Result +where + T: Copy + PartialEq + std::ops::Div + num::Zero, + Out: std::iter::FromIterator, +{ + if norm == T::zero() { + return Err(VectorError::ZeroVectorError("Cannot normalize a zero vector".to_string())); + } + Ok(slice.iter().map(|&a| a / norm).collect()) +} + +#[inline] +pub(crate) fn normalize_to_impl( + slice: &[T], + norm: T, + magnitude: T, +) -> Result +where + T: Copy + PartialEq + std::ops::Div + std::ops::Mul + num::Zero, + Out: std::iter::FromIterator, +{ + if norm == T::zero() { + return Err(VectorError::ZeroVectorError("Cannot normalize a zero vector".to_string())); + } + let scale = magnitude / norm; + Ok(slice.iter().map(|&a| a * scale).collect()) +} + #[cfg(test)] mod tests { + use num::Complex; + + use crate::FlexVector; + use super::*; // -- mut_translate_impl == @@ -532,7 +567,7 @@ mod tests { let a = [3.0f64, 4.0]; let b = [1.0f64, 0.0]; let denom = dot_impl(&b, &b); - let proj: Vec = project_onto_impl(&a, &b, denom); + let proj: FlexVector = project_onto_impl(&a, &b, denom); // Projection of [3,4] onto [1,0] is [3,0] assert!((proj[0] - 3.0).abs() < 1e-12); assert!((proj[1] - 0.0).abs() < 1e-12); @@ -543,7 +578,7 @@ mod tests { let a = [2.0f64, 4.0]; let b = [1.0f64, 2.0]; let denom = dot_impl(&b, &b); - let proj: Vec = project_onto_impl(&a, &b, denom); + let proj: FlexVector = project_onto_impl(&a, &b, denom); // a is parallel to b, so projection should be a itself assert!((proj[0] - 2.0).abs() < 1e-12); assert!((proj[1] - 4.0).abs() < 1e-12); @@ -554,7 +589,7 @@ mod tests { let a = [0.0f64, 1.0]; let b = [1.0f64, 0.0]; let denom = dot_impl(&b, &b); - let proj: Vec = project_onto_impl(&a, &b, denom); + let proj: FlexVector = project_onto_impl(&a, &b, denom); // Orthogonal vectors: projection should be [0,0] assert!((proj[0] - 0.0).abs() < 1e-12); assert!((proj[1] - 0.0).abs() < 1e-12); @@ -565,7 +600,7 @@ mod tests { let a = [5.0f64, 5.0]; let b = [5.0f64, 5.0]; let denom = dot_impl(&b, &b); - let proj: Vec = project_onto_impl(&a, &b, denom); + let proj: FlexVector = project_onto_impl(&a, &b, denom); // Should be a itself assert!((proj[0] - 5.0).abs() < 1e-12); assert!((proj[1] - 5.0).abs() < 1e-12); @@ -577,7 +612,101 @@ mod tests { let b = [0.0f64, 0.0]; let denom = dot_impl(&b, &b); // denom is zero, so projection would be NaN or inf; check for NaN - let proj: Vec = project_onto_impl(&a, &b, denom); + let proj: FlexVector = project_onto_impl(&a, &b, denom); assert!(proj.iter().all(|x| x.is_nan())); } + + // -- normalize_impl -- + #[test] + fn test_normalize_impl_f64_basic() { + let v = [3.0f64, 4.0]; + let norm = (3.0f64 * 3.0 + 4.0 * 4.0).sqrt(); + let result: FlexVector = normalize_impl(&v, norm).unwrap(); + assert!((result[0] - 0.6).abs() < 1e-12); + assert!((result[1] - 0.8).abs() < 1e-12); + } + + #[test] + fn test_normalize_impl_f64_zero_vector() { + let v = [0.0f64, 0.0]; + let norm = 0.0f64; + let result: Result, _> = normalize_impl(&v, norm); + assert!(result.is_err()); + } + + #[test] + fn test_normalize_impl_f64_single_element() { + let v = [5.0f64]; + let norm = 5.0f64; + let result: FlexVector = normalize_impl(&v, norm).unwrap(); + assert!((result[0] - 1.0).abs() < 1e-12); + } + + #[test] + fn test_normalize_impl_f64_empty() { + let v: [f64; 0] = []; + let norm = 1.0f64; + let result: FlexVector = normalize_impl(&v, norm).unwrap(); + assert!(result.is_empty()); + } + + #[test] + fn test_normalize_impl_complex_basic() { + let v = [Complex::new(3.0, 4.0)]; + let norm = Complex::new((3.0f64 * 3.0 + 4.0 * 4.0).sqrt(), 0.0); + let result: FlexVector> = normalize_impl(&v, norm).unwrap(); + assert!((result[0].re - 0.6).abs() < 1e-12); + assert!((result[0].im - 0.8).abs() < 1e-12); + } + + // -- normalize_to_impl -- + #[test] + fn test_normalize_to_impl_f64_basic() { + let v = [3.0f64, 4.0]; + let norm = (3.0f64 * 3.0 + 4.0 * 4.0).sqrt(); + let magnitude = 10.0f64; + let result: FlexVector = normalize_to_impl(&v, norm, magnitude).unwrap(); + // The normalized vector should have the same direction as v and norm 10 + let expected = [3.0 / 5.0 * 10.0, 4.0 / 5.0 * 10.0]; + assert!((result[0] - expected[0]).abs() < 1e-12); + assert!((result[1] - expected[1]).abs() < 1e-12); + } + + #[test] + fn test_normalize_to_impl_f64_zero_vector() { + let v = [0.0f64, 0.0]; + let norm = 0.0f64; + let magnitude = 10.0f64; + let result: Result, _> = normalize_to_impl(&v, norm, magnitude); + assert!(result.is_err()); + } + + #[test] + fn test_normalize_to_impl_f64_single_element() { + let v = [5.0f64]; + let norm = 5.0f64; + let magnitude = 2.0f64; + let result: FlexVector = normalize_to_impl(&v, norm, magnitude).unwrap(); + assert!((result[0] - 2.0).abs() < 1e-12); + } + + #[test] + fn test_normalize_to_impl_f64_empty() { + let v: [f64; 0] = []; + let norm = 1.0f64; + let magnitude = 2.0f64; + let result: FlexVector = normalize_to_impl(&v, norm, magnitude).unwrap(); + assert!(result.is_empty()); + } + + #[test] + fn test_normalize_to_impl_complex_basic() { + let v = [Complex::new(3.0, 4.0)]; + let norm = Complex::new((3.0f64 * 3.0 + 4.0 * 4.0).sqrt(), 0.0); + let magnitude = Complex::new(10.0, 0.0); + let result: FlexVector> = normalize_to_impl(&v, norm, magnitude).unwrap(); + let expected = Complex::new(3.0 / 5.0 * 10.0, 4.0 / 5.0 * 10.0); + assert!((result[0].re - expected.re).abs() < 1e-12); + assert!((result[0].im - expected.im).abs() < 1e-12); + } } From a041e2056d6304a4f53bdf4bb731f78a2b6bf929 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Thu, 15 May 2025 09:50:17 -0400 Subject: [PATCH 16/82] FlexVector method support expansion and trait refactoring --- src/types/flexvector.rs | 377 +++++++++++++++--------- src/types/traits.rs | 207 +++---------- src/types/utils.rs | 627 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 894 insertions(+), 317 deletions(-) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index 3c9f9a9..41a8e2a 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -11,9 +11,12 @@ use crate::{ }; use crate::types::utils::{ - angle_with_impl, chebyshev_distance_impl, cross_impl, distance_impl, dot_impl, dot_to_f64_impl, - lerp_impl, manhattan_distance_impl, minkowski_distance_impl, mut_lerp_impl, mut_translate_impl, - normalize_impl, normalize_to_impl, project_onto_impl, translate_impl, + angle_with_impl, chebyshev_distance_complex_impl, chebyshev_distance_impl, + cosine_similarity_complex_impl, cosine_similarity_impl, cross_impl, distance_complex_impl, + distance_impl, dot_impl, dot_to_f64_impl, hermitian_dot_impl, lerp_impl, + manhattan_distance_complex_impl, manhattan_distance_impl, minkowski_distance_complex_impl, + minkowski_distance_impl, mut_lerp_impl, mut_normalize_impl, mut_normalize_to_impl, + mut_translate_impl, normalize_impl, normalize_to_impl, project_onto_impl, translate_impl, }; use crate::errors::VectorError; @@ -309,11 +312,7 @@ where #[inline] fn translate(&self, other: &Self) -> Result { - if self.len() != other.len() { - return Err(VectorError::MismatchedLengthError( - "Vectors must have the same length".to_string(), - )); - } + self.check_same_length_and_raise(other)?; let mut out = FlexVector::zero(self.len()); translate_impl(self.as_slice(), other.as_slice(), out.as_mut_slice()); Ok(out) @@ -321,11 +320,7 @@ where #[inline] fn mut_translate(&mut self, other: &Self) -> Result<(), VectorError> { - if self.len() != other.len() { - return Err(VectorError::MismatchedLengthError( - "Vectors must have the same length".to_string(), - )); - } + self.check_same_length_and_raise(other)?; mut_translate_impl(self.as_mut_slice(), other.as_slice()); Ok(()) } @@ -353,11 +348,7 @@ where where T: num::Num + Copy + std::iter::Sum, { - if self.len() != other.len() { - return Err(VectorError::MismatchedLengthError( - "Vectors must have the same length".to_string(), - )); - } + self.check_same_length_and_raise(other)?; Ok(dot_impl(self.as_slice(), other.as_slice())) } @@ -366,11 +357,7 @@ where where T: num::ToPrimitive, { - if self.len() != other.len() { - return Err(VectorError::MismatchedLengthError( - "Vectors must have the same length".to_string(), - )); - } + self.check_same_length_and_raise(other)?; Ok(dot_to_f64_impl(self.as_slice(), other.as_slice())) } @@ -405,10 +392,22 @@ where type Output = Self; #[inline] - fn normalize(&self) -> Result { + fn normalize(&self) -> Result + where + T: Copy + PartialEq + std::ops::Div + num::Zero, + { normalize_impl(self.as_slice(), self.norm()) } + #[inline] + fn mut_normalize(&mut self) -> Result<(), VectorError> + where + T: Copy + PartialEq + std::ops::Div + num::Zero, + { + let norm = self.norm(); + mut_normalize_impl(self.as_mut_slice(), norm) + } + /// Returns a new vector with the same direction and the given magnitude. #[inline] fn normalize_to(&self, magnitude: T) -> Result @@ -423,16 +422,25 @@ where normalize_to_impl(self.as_slice(), self.norm(), magnitude) } + #[inline] + fn mut_normalize_to(&mut self, magnitude: T) -> Result<(), VectorError> + where + T: Copy + + PartialEq + + std::ops::Div + + std::ops::Mul + + num::Zero, + { + let n = self.norm(); + mut_normalize_to_impl(self.as_mut_slice(), n, magnitude) + } + #[inline] fn lerp(&self, end: &Self, weight: T) -> Result where T: num::Float + Copy, { - if self.len() != end.len() { - return Err(VectorError::MismatchedLengthError( - "Vectors must have the same length".to_string(), - )); - } + self.check_same_length_and_raise(end)?; if weight < T::zero() || weight > T::one() { return Err(VectorError::OutOfRangeError("weight must be in [0, 1]".to_string())); } @@ -446,11 +454,7 @@ where where T: num::Float + Copy + PartialOrd, { - if self.len() != end.len() { - return Err(VectorError::MismatchedLengthError( - "Vectors must have the same length".to_string(), - )); - } + self.check_same_length_and_raise(end)?; if weight < T::zero() || weight > T::one() { return Err(VectorError::OutOfRangeError("weight must be in [0, 1]".to_string())); } @@ -463,11 +467,7 @@ where where T: num::Float + Clone, { - if self.len() != other.len() { - return Err(VectorError::MismatchedLengthError( - "Vectors must have the same length".to_string(), - )); - } + self.check_same_length_and_raise(other)?; let mut out = FlexVector::zero(self.len()); lerp_impl(self.as_slice(), other.as_slice(), T::from(0.5).unwrap(), out.as_mut_slice()); Ok(out) @@ -478,11 +478,7 @@ where where T: num::Float + Clone + std::iter::Sum, { - if self.len() != other.len() { - return Err(VectorError::MismatchedLengthError( - "Vectors must have the same length".to_string(), - )); - } + self.check_same_length_and_raise(other)?; Ok(distance_impl(self.as_slice(), other.as_slice())) } @@ -491,11 +487,7 @@ where where T: num::Float + Clone + std::iter::Sum, { - if self.len() != other.len() { - return Err(VectorError::MismatchedLengthError( - "Vectors must have the same length".to_string(), - )); - } + self.check_same_length_and_raise(other)?; Ok(manhattan_distance_impl(self.as_slice(), other.as_slice())) } @@ -504,11 +496,7 @@ where where T: num::Float + Clone + PartialOrd, { - if self.len() != other.len() { - return Err(VectorError::MismatchedLengthError( - "Vectors must have the same length".to_string(), - )); - } + self.check_same_length_and_raise(other)?; Ok(chebyshev_distance_impl(self.as_slice(), other.as_slice())) } @@ -517,11 +505,7 @@ where where T: num::Float + Clone + std::iter::Sum, { - if self.len() != other.len() { - return Err(VectorError::MismatchedLengthError( - "Vectors must have the same length".to_string(), - )); - } + self.check_same_length_and_raise(other)?; if p < T::one() { return Err(VectorError::OutOfRangeError("p must be >= 1".to_string())); } @@ -533,11 +517,7 @@ where where T: num::Float + Clone + std::iter::Sum, { - if self.len() != other.len() { - return Err(VectorError::MismatchedLengthError( - "Vectors must have the same length".to_string(), - )); - } + self.check_same_length_and_raise(other)?; let norm_self = self.norm(); let norm_other = other.norm(); if norm_self == T::zero() || norm_other == T::zero() { @@ -554,18 +534,31 @@ where T: num::Float + Clone + std::iter::Sum, Self::Output: std::iter::FromIterator, { - if self.len() != other.len() { - return Err(VectorError::MismatchedLengthError( - "Vectors must have the same length".to_string(), - )); - } + self.check_same_length_and_raise(other)?; let denom = dot_impl(other.as_slice(), other.as_slice()); if denom == T::zero() { return Err(VectorError::ZeroVectorError( "Cannot project onto zero vector".to_string(), )); } - Ok(project_onto_impl(self.as_slice(), other.as_slice(), denom)) + let scalar = dot_impl(self.as_slice(), other.as_slice()) / denom; + Ok(project_onto_impl(other.as_slice(), scalar)) + } + + #[inline] + fn cosine_similarity(&self, other: &Self) -> Result + where + T: num::Float + Clone + std::iter::Sum + std::ops::Div, + { + self.check_same_length_and_raise(other)?; + let norm_self = self.norm(); + let norm_other = other.norm(); + if norm_self == T::zero() || norm_other == T::zero() { + return Err(VectorError::ZeroVectorError( + "Cannot compute cosine similarity with zero vector".to_string(), + )); + } + Ok(cosine_similarity_impl(self.as_slice(), other.as_slice(), norm_self, norm_other)) } } @@ -584,12 +577,21 @@ where #[inline] fn normalize(&self) -> Result where - Complex: Copy + PartialEq + std::ops::Div, Output = Complex> + num::Zero, + Complex: Copy + PartialEq + std::ops::Div, Output = Complex>, Self::Output: std::iter::FromIterator>, { normalize_impl(self.as_slice(), Complex::new(self.norm(), N::zero())) } + #[inline] + fn mut_normalize(&mut self) -> Result<(), VectorError> + where + Complex: Copy + PartialEq + std::ops::Div, Output = Complex> + num::Zero, + { + let norm = self.norm(); + mut_normalize_impl(self.as_mut_slice(), Complex::new(norm, N::zero())) + } + #[inline] fn normalize_to(&self, magnitude: N) -> Result where @@ -607,42 +609,156 @@ where ) } + #[inline] + fn mut_normalize_to(&mut self, magnitude: N) -> Result<(), VectorError> + where + Complex: Copy + + PartialEq + + std::ops::Div, Output = Complex> + + std::ops::Mul, Output = Complex> + + num::Zero, + { + let n = self.norm(); + mut_normalize_to_impl( + self.as_mut_slice(), + Complex::new(n, N::zero()), + Complex::new(magnitude, N::zero()), + ) + } + + #[inline] + fn dot(&self, other: &Self) -> Result, VectorError> + where + N: num::Num + Copy + std::iter::Sum + std::ops::Neg, + { + self.check_same_length_and_raise(other)?; + Ok(hermitian_dot_impl(self.as_slice(), other.as_slice())) + } + #[inline] fn lerp(&self, end: &Self, weight: N) -> Result where N: num::Float + Clone + PartialOrd, + Complex: Copy + + std::ops::Add> + + std::ops::Mul> + + std::ops::Sub> + + num::One, { - if self.len() != end.len() { - return Err(VectorError::MismatchedLengthError( - "Vectors must have the same length".to_string(), - )); + self.check_same_length_and_raise(end)?; + if weight < N::zero() || weight > N::one() { + return Err(VectorError::OutOfRangeError("weight must be in [0, 1]".to_string())); } + let w = Complex::new(weight, N::zero()); + let mut out = FlexVector::zero(self.len()); + lerp_impl(self.as_slice(), end.as_slice(), w, out.as_mut_slice()); + Ok(out) + } + + #[inline] + fn mut_lerp(&mut self, end: &Self, weight: N) -> Result<(), VectorError> + where + N: num::Float + Copy + PartialOrd, + Complex: Copy + + std::ops::Add> + + std::ops::Mul> + + std::ops::Sub> + + num::One, + { + self.check_same_length_and_raise(end)?; if weight < N::zero() || weight > N::one() { return Err(VectorError::OutOfRangeError("weight must be in [0, 1]".to_string())); } let w = Complex::new(weight, N::zero()); - let one_minus_w = Complex::new(N::one() - weight, N::zero()); - Ok(self - .components - .iter() - .zip(&end.components) - .map(|(a, b)| one_minus_w * *a + w * *b) - .collect()) + mut_lerp_impl(self.as_mut_slice(), end.as_slice(), w); + Ok(()) + } + + #[inline] + fn midpoint(&self, end: &Self) -> Result + where + N: num::Float + Clone, + { + self.check_same_length_and_raise(end)?; + self.lerp(end, num::cast(0.5).unwrap()) + } + + #[inline] + fn distance(&self, other: &Self) -> Result + where + N: num::Float + Clone + std::iter::Sum, + { + self.check_same_length_and_raise(other)?; + Ok(distance_complex_impl(self.as_slice(), other.as_slice())) + } + + #[inline] + fn manhattan_distance(&self, other: &Self) -> Result + where + N: num::Float + Clone + std::iter::Sum, + { + self.check_same_length_and_raise(other)?; + Ok(manhattan_distance_complex_impl(self.as_slice(), other.as_slice())) + } + + #[inline] + fn chebyshev_distance(&self, other: &Self) -> Result + where + N: num::Float + Clone + PartialOrd, + { + self.check_same_length_and_raise(other)?; + Ok(chebyshev_distance_complex_impl(self.as_slice(), other.as_slice())) + } + + #[inline] + fn minkowski_distance(&self, other: &Self, p: N) -> Result + where + N: num::Float + Clone + std::iter::Sum, + { + self.check_same_length_and_raise(other)?; + if p < N::one() { + return Err(VectorError::OutOfRangeError("p must be >= 1".to_string())); + } + Ok(minkowski_distance_complex_impl(self.as_slice(), other.as_slice(), p)) } #[inline] fn project_onto(&self, other: &Self) -> Result where + N: num::Float + Clone + std::iter::Sum + std::ops::Neg, + Complex: Copy + + std::ops::Mul> + + std::ops::Add> + + std::ops::Div, Output = Complex> + + num::Zero, Self::Output: std::iter::FromIterator>, { - let denom = VectorOpsComplex::dot(other, other); // Hermitian dot product - if denom == Complex::zero() { + self.check_same_length_and_raise(other)?; + let denom = hermitian_dot_impl(other.as_slice(), other.as_slice()); + if denom == Complex::::zero() { return Err(VectorError::ZeroVectorError( "Cannot project onto zero vector".to_string(), )); } - let scalar = VectorOpsComplex::dot(self, other) / denom; - Ok(other.as_slice().iter().map(|b| *b * scalar).collect()) + let scalar = hermitian_dot_impl(self.as_slice(), other.as_slice()) / denom; + Ok(project_onto_impl(other.as_slice(), scalar)) + } + + #[inline] + fn cosine_similarity(&self, other: &Self) -> Result, VectorError> + where + N: num::Float + Clone + std::iter::Sum + std::ops::Neg, + Complex: std::ops::Div>, + { + self.check_same_length_and_raise(other)?; + let norm_self = self.norm(); + let norm_other = other.norm(); + if norm_self == N::zero() || norm_other == N::zero() { + return Err(VectorError::ZeroVectorError( + "Cannot compute cosine similarity with zero vector".to_string(), + )); + } + Ok(cosine_similarity_complex_impl(self.as_slice(), other.as_slice(), norm_self, norm_other)) } } @@ -731,19 +847,20 @@ impl FlexVector { } } - /// Cosine similarity between self and other. + // ================================ + // + // Private methods + // + // ================================ + + /// Returns Ok(()) if self and other have the same length (i.e. vector dimensionality), + /// otherwise returns a VectorError. #[inline] - pub fn cosine_similarity(&self, other: &Self) -> Result - where - T: num::Float + Clone + std::iter::Sum, - { - let dot = self.dot(other)?; - let norm_self = self.norm(); - let norm_other = other.norm(); - if norm_self == T::zero() || norm_other == T::zero() { - Ok(T::zero()) + fn check_same_length_and_raise(&self, other: &Self) -> Result<(), VectorError> { + if self.len() != other.len() { + Err(VectorError::MismatchedLengthError("Vectors must have the same length".to_string())) } else { - Ok(dot / (norm_self * norm_other)) + Ok(()) } } } @@ -2561,39 +2678,6 @@ mod tests { assert_eq!(v.as_slice(), &[2, 4, 6]); } - // --- cosine_similarity --- - #[test] - fn test_cosine_similarity_parallel() { - let v1 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); - let v2 = FlexVector::from_vec(vec![2.0, 4.0, 6.0]); - let cos_sim = v1.cosine_similarity(&v2).unwrap(); - assert!((cos_sim - 1.0).abs() < 1e-10); - } - - #[test] - fn test_cosine_similarity_orthogonal() { - let v1 = FlexVector::from_vec(vec![1.0, 0.0]); - let v2 = FlexVector::from_vec(vec![0.0, 1.0]); - let cos_sim = v1.cosine_similarity(&v2).unwrap(); - assert!((cos_sim - 0.0).abs() < 1e-10); - } - - #[test] - fn test_cosine_similarity_opposite() { - let v1 = FlexVector::from_vec(vec![1.0, 0.0]); - let v2 = FlexVector::from_vec(vec![-1.0, 0.0]); - let cos_sim = v1.cosine_similarity(&v2).unwrap(); - assert!((cos_sim + 1.0).abs() < 1e-10); - } - - #[test] - fn test_cosine_similarity_zero_vector() { - let v1 = FlexVector::from_vec(vec![0.0, 0.0]); - let v2 = FlexVector::from_vec(vec![1.0, 2.0]); - let cos_sim = v1.cosine_similarity(&v2).unwrap(); - assert_eq!(cos_sim, 0.0); - } - // ================================ // // VectorOps trait tests @@ -3691,6 +3775,39 @@ mod tests { assert!(result.is_err()); } + // --- cosine_similarity --- + #[test] + fn test_cosine_similarity_f64_parallel() { + let v1 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let v2 = FlexVector::from_vec(vec![2.0, 4.0, 6.0]); + let cos_sim = v1.cosine_similarity(&v2).unwrap(); + assert!((cos_sim - 1.0).abs() < 1e-10); + } + + #[test] + fn test_cosine_similarity_f64_orthogonal() { + let v1 = FlexVector::from_vec(vec![1.0, 0.0]); + let v2 = FlexVector::from_vec(vec![0.0, 1.0]); + let cos_sim = v1.cosine_similarity(&v2).unwrap(); + assert!((cos_sim - 0.0).abs() < 1e-10); + } + + #[test] + fn test_cosine_similarity_f64_opposite() { + let v1 = FlexVector::from_vec(vec![1.0, 0.0]); + let v2 = FlexVector::from_vec(vec![-1.0, 0.0]); + let cos_sim = v1.cosine_similarity(&v2).unwrap(); + assert!((cos_sim + 1.0).abs() < 1e-10); + } + + #[test] + fn test_cosine_similarity_f64_zero_vector() { + let v1 = FlexVector::from_vec(vec![0.0, 0.0]); + let v2 = FlexVector::from_vec(vec![1.0, 2.0]); + let cos_sim = v1.cosine_similarity(&v2); + assert!(cos_sim.is_err()); + } + // ================================ // // VectorOpsComplex trait tests diff --git a/src/types/traits.rs b/src/types/traits.rs index cc2c3fb..93adc9f 100644 --- a/src/types/traits.rs +++ b/src/types/traits.rs @@ -328,51 +328,28 @@ pub trait VectorOpsFloat: VectorBase { /// ... fn normalize(&self) -> Result where - T: Copy + PartialEq + std::ops::Div + num::Zero, + T: Copy + PartialEq + std::ops::Div, Self::Output: std::iter::FromIterator; /// ... - #[inline] fn mut_normalize(&mut self) -> Result<(), VectorError> where - T: num::Float + Copy + std::iter::Sum, - { - let n = self.norm(); - if n == T::zero() { - return Err(VectorError::ZeroVectorError("Cannot normalize a zero vector".to_string())); - } - for a in self.as_mut_slice().iter_mut() { - *a = *a / n; - } - Ok(()) - } + T: Copy + PartialEq + std::ops::Div; /// Returns a new vector with the same direction and the given magnitude. fn normalize_to(&self, magnitude: T) -> Result where - T: Copy - + PartialEq - + std::ops::Div - + std::ops::Mul - + num::Zero, + T: Copy + PartialEq + std::ops::Div + std::ops::Mul, Self::Output: std::iter::FromIterator; /// ... - #[inline] fn mut_normalize_to(&mut self, magnitude: T) -> Result<(), VectorError> where - T: num::Float + Copy + std::iter::Sum, - { - let n = self.norm(); - if n == T::zero() { - return Err(VectorError::ZeroVectorError("Cannot normalize a zero vector".to_string())); - } - let scale = magnitude / n; - for a in self.as_mut_slice().iter_mut() { - *a = *a * scale; - } - Ok(()) - } + T: Copy + + PartialEq + + std::ops::Div + + std::ops::Mul + + num::Zero; /// Linear interpolation between self and end by weight in [0, 1]. fn lerp(&self, end: &Self, weight: T) -> Result @@ -450,6 +427,11 @@ pub trait VectorOpsFloat: VectorBase { where T: num::Float + Clone + std::iter::Sum, Self::Output: std::iter::FromIterator; + + /// ... + fn cosine_similarity(&self, other: &Self) -> Result + where + T: num::Float + Clone + std::iter::Sum + std::ops::Div; } pub trait VectorOpsComplex: VectorBase> { @@ -458,24 +440,13 @@ pub trait VectorOpsComplex: VectorBase> { /// ... fn normalize(&self) -> Result where - Complex: Copy + PartialEq + std::ops::Div, Output = Complex> + num::Zero, + Complex: Copy + PartialEq + std::ops::Div, Output = Complex>, Self::Output: std::iter::FromIterator>; /// ... - #[inline] fn mut_normalize(&mut self) -> Result<(), VectorError> where - N: num::Float + Copy + std::iter::Sum, - { - let n = self.norm(); - if n == N::zero() { - return Err(VectorError::ZeroVectorError("Cannot normalize a zero vector".to_string())); - } - for a in self.as_mut_slice().iter_mut() { - *a = *a / n; - } - Ok(()) - } + Complex: Copy + PartialEq + std::ops::Div, Output = Complex>; /// Returns a new vector with the same direction and the given magnitude (real). fn normalize_to(&self, magnitude: N) -> Result @@ -483,51 +454,22 @@ pub trait VectorOpsComplex: VectorBase> { Complex: Copy + PartialEq + std::ops::Div, Output = Complex> - + std::ops::Mul, Output = Complex> - + num::Zero, + + std::ops::Mul, Output = Complex>, Self::Output: std::iter::FromIterator>; /// Scales the complex vector in place to the given (real) magnitude. - #[inline] fn mut_normalize_to(&mut self, magnitude: N) -> Result<(), VectorError> where - N: num::Float + Copy + std::iter::Sum, - { - let n = self.norm(); - if n == N::zero() { - return Err(VectorError::ZeroVectorError("Cannot normalize a zero vector".to_string())); - } - let scale = magnitude / n; - for a in self.as_mut_slice().iter_mut() { - *a = *a * scale; - } - Ok(()) - } + Complex: Copy + + PartialEq + + std::ops::Div, Output = Complex> + + std::ops::Mul, Output = Complex> + + num::Zero; /// Hermitian dot product: for all complex types - #[inline] - fn dot(&self, other: &Self) -> Complex + fn dot(&self, other: &Self) -> Result, VectorError> where - N: num::Num + Copy + std::iter::Sum + std::ops::Neg, - { - self.as_slice().iter().zip(other.as_slice()).map(|(a, b)| *a * b.conj()).sum() - } - - /// Hermitian dot product as f64 (sums real part): for all complex types. - #[inline] - fn dot_to_f64(&self, other: &Self) -> f64 - where - N: num::Num + num::ToPrimitive + Copy + std::ops::Neg, - { - self.as_slice() - .iter() - .zip(other.as_slice()) - .map(|(a, b)| { - let prod = *a * b.conj(); - prod.re.to_f64().unwrap() - }) - .sum() - } + N: num::Num + Copy + std::iter::Sum + std::ops::Neg; /// Linear interpolation between self and end by real weight in [0, 1]. fn lerp(&self, end: &Self, weight: N) -> Result @@ -535,93 +477,34 @@ pub trait VectorOpsComplex: VectorBase> { N: num::Float + Clone + PartialOrd; /// In-place linear interpolation between self and end by real weight in [0, 1]. - #[inline] fn mut_lerp(&mut self, end: &Self, weight: N) -> Result<(), VectorError> where - N: num::Float + Copy + PartialOrd, - { - if self.len() != end.len() { - return Err(VectorError::MismatchedLengthError( - "Vectors must have the same length".to_string(), - )); - } - if weight < N::zero() || weight > N::one() { - return Err(VectorError::OutOfRangeError("weight must be in [0, 1]".to_string())); - } - let w = Complex::new(weight, N::zero()); - let one_minus_w = Complex::new(N::one() - weight, N::zero()); - for (a, b) in self.as_mut_slice().iter_mut().zip(end.as_slice()) { - *a = one_minus_w * *a + w * *b; - } - Ok(()) - } + N: num::Float + Copy + PartialOrd; /// Midpoint - #[inline] - fn midpoint(&self, end: &Self) -> Self::Output + fn midpoint(&self, end: &Self) -> Result where - N: num::Float + Clone, - { - // OK to unwrap because by definition it uses an in-range weight - self.lerp(end, num::cast(0.5).unwrap()).unwrap() - } + N: num::Float + Clone; /// Euclidean distance (L2 norm) between self and other (returns real). - #[inline] - fn distance(&self, other: &Self) -> N + fn distance(&self, other: &Self) -> Result where - N: num::Float + Clone + std::iter::Sum, - { - self.as_slice() - .iter() - .zip(other.as_slice()) - .map(|(a, b)| { - let diff = *a - *b; - diff.norm_sqr() - }) - .sum::() - .sqrt() - } + N: num::Float + Clone + std::iter::Sum; /// Manhattan (L1) distance between self and other (sum of magnitudes of differences). - #[inline] - fn manhattan_distance(&self, other: &Self) -> N + fn manhattan_distance(&self, other: &Self) -> Result where - N: num::Float + Clone + std::iter::Sum, - { - self.as_slice().iter().zip(other.as_slice()).map(|(a, b)| (*a - *b).norm()).sum() - } + N: num::Float + Clone + std::iter::Sum; /// Chebyshev (L∞) distance between self and other (maximum magnitude of differences). - #[inline] - fn chebyshev_distance(&self, other: &Self) -> N + fn chebyshev_distance(&self, other: &Self) -> Result where - N: num::Float + Clone + PartialOrd, - { - self.as_slice() - .iter() - .zip(other.as_slice()) - .map(|(a, b)| (*a - *b).norm()) - .fold(N::zero(), |acc, x| acc.max(x)) - } + N: num::Float + Clone + PartialOrd; /// Minkowski (Lp) distance between self and other. - #[inline] fn minkowski_distance(&self, other: &Self, p: N) -> Result where - N: num::Float + Clone + std::iter::Sum, - { - if p < N::one() { - return Err(VectorError::OutOfRangeError("p must be >= 1".to_string())); - } - Ok(self - .as_slice() - .iter() - .zip(other.as_slice()) - .map(|(a, b)| (*a - *b).norm().powf(p)) - .sum::() - .powf(N::one() / p)) - } + N: num::Float + Clone + std::iter::Sum; /// Euclidean norm (magnitude) of the vector (returns real). #[inline] @@ -632,6 +515,15 @@ pub trait VectorOpsComplex: VectorBase> { self.as_slice().iter().map(|a| a.norm_sqr()).sum::().sqrt() } + /// Alias for norm (magnitude). + #[inline] + fn magnitude(&self) -> N + where + N: num::Float + Clone + std::iter::Sum, + { + self.norm() + } + /// L1 norm (sum of magnitudes). #[inline] fn l1_norm(&self) -> N @@ -662,18 +554,15 @@ pub trait VectorOpsComplex: VectorBase> { Ok(self.as_slice().iter().map(|a| a.norm().powf(p)).sum::().powf(N::one() / p)) } - /// Alias for norm (magnitude). - #[inline] - fn magnitude(&self) -> N - where - N: num::Float + Clone + std::iter::Sum, - { - self.norm() - } - /// ... fn project_onto(&self, other: &Self) -> Result where N: num::Float + Clone + std::iter::Sum, Self::Output: std::iter::FromIterator>; + + /// ... + fn cosine_similarity(&self, other: &Self) -> Result, VectorError> + where + N: num::Float + Clone + std::iter::Sum + std::ops::Neg, + Complex: std::ops::Div>; } diff --git a/src/types/utils.rs b/src/types/utils.rs index b5f2f19..9198f68 100644 --- a/src/types/utils.rs +++ b/src/types/utils.rs @@ -2,6 +2,8 @@ use crate::errors::VectorError; +use num::Complex; + #[inline] pub(crate) fn mut_translate_impl(out: &mut [T], b: &[T]) { for (out_elem, &b_elem) in out.iter_mut().zip(b) { @@ -32,6 +34,14 @@ where a.iter().zip(b.iter()).map(|(x, y)| x.to_f64().unwrap() * y.to_f64().unwrap()).sum() } +#[inline] +pub(crate) fn hermitian_dot_impl(a: &[Complex], b: &[Complex]) -> Complex +where + N: num::Num + Copy + std::iter::Sum + std::ops::Neg, +{ + a.iter().zip(b.iter()).map(|(x, y)| *x * y.conj()).sum() +} + #[inline] pub(crate) fn cross_impl(a: &[T], b: &[T]) -> [T; 3] where @@ -43,16 +53,26 @@ where #[inline] pub(crate) fn lerp_impl(a: &[T], b: &[T], weight: T, out: &mut [T]) where - T: num::Float + Copy, + T: Copy + + std::ops::Add + + std::ops::Mul + + std::ops::Sub + + num::One, { - out.copy_from_slice(a); - mut_lerp_impl(out, b, weight); + let one_minus_w = T::one() - weight; + for ((out_elem, &a_elem), &b_elem) in out.iter_mut().zip(a).zip(b) { + *out_elem = one_minus_w * a_elem + weight * b_elem; + } } #[inline] pub(crate) fn mut_lerp_impl(out: &mut [T], end: &[T], weight: T) where - T: num::Float + Copy, + T: Copy + + std::ops::Add + + std::ops::Mul + + std::ops::Sub + + num::One, { let one_minus_w = T::one() - weight; for (a, &b) in out.iter_mut().zip(end) { @@ -68,6 +88,14 @@ where a.iter().zip(b.iter()).map(|(a, b)| (*a - *b).powi(2)).sum::().sqrt() } +#[inline] +pub(crate) fn distance_complex_impl(a: &[num::Complex], b: &[num::Complex]) -> N +where + N: num::Float + Clone + std::iter::Sum, +{ + a.iter().zip(b.iter()).map(|(a, b)| (*a - *b).norm_sqr()).sum::().sqrt() +} + #[inline] pub(crate) fn manhattan_distance_impl(a: &[T], b: &[T]) -> T where @@ -76,6 +104,13 @@ where a.iter().zip(b.iter()).map(|(a, b)| (*a - *b).abs()).sum() } +pub(crate) fn manhattan_distance_complex_impl(a: &[num::Complex], b: &[num::Complex]) -> N +where + N: num::Float + Clone + std::iter::Sum, +{ + a.iter().zip(b.iter()).map(|(a, b)| (*a - *b).norm()).sum() +} + #[inline] pub(crate) fn chebyshev_distance_impl(a: &[T], b: &[T]) -> T where @@ -84,6 +119,14 @@ where a.iter().zip(b.iter()).map(|(a, b)| (*a - *b).abs()).fold(T::zero(), |acc, x| acc.max(x)) } +#[inline] +pub(crate) fn chebyshev_distance_complex_impl(a: &[num::Complex], b: &[num::Complex]) -> N +where + N: num::Float + Clone + PartialOrd, +{ + a.iter().zip(b.iter()).map(|(a, b)| (*a - *b).norm()).fold(N::zero(), |acc, x| acc.max(x)) +} + #[inline] pub(crate) fn minkowski_distance_impl(a: &[T], b: &[T], p: T) -> T where @@ -92,6 +135,18 @@ where a.iter().zip(b.iter()).map(|(a, b)| (*a - *b).abs().powf(p)).sum::().powf(T::one() / p) } +#[inline] +pub(crate) fn minkowski_distance_complex_impl( + a: &[num::Complex], + b: &[num::Complex], + p: N, +) -> N +where + N: num::Float + Clone + std::iter::Sum, +{ + a.iter().zip(b.iter()).map(|(a, b)| (*a - *b).norm().powf(p)).sum::().powf(N::one() / p) +} + #[inline] pub(crate) fn angle_with_impl(a: &[T], b: &[T], norm_a: T, norm_b: T) -> T where @@ -104,12 +159,11 @@ where } #[inline] -pub(crate) fn project_onto_impl(a: &[T], b: &[T], denom: T) -> Out +pub(crate) fn project_onto_impl(b: &[T], scalar: T) -> Out where - T: num::Float + Clone + std::iter::Sum, + T: Copy + std::ops::Mul, Out: std::iter::FromIterator, { - let scalar = dot_impl(a, b) / denom; b.iter().map(|x| *x * scalar).collect() } @@ -125,6 +179,20 @@ where Ok(slice.iter().map(|&a| a / norm).collect()) } +#[inline] +pub(crate) fn mut_normalize_impl(slice: &mut [T], norm: T) -> Result<(), VectorError> +where + T: Copy + PartialEq + std::ops::Div + num::Zero, +{ + if norm == T::zero() { + return Err(VectorError::ZeroVectorError("Cannot normalize a zero vector".to_string())); + } + for a in slice.iter_mut() { + *a = *a / norm; + } + Ok(()) +} + #[inline] pub(crate) fn normalize_to_impl( slice: &[T], @@ -142,6 +210,49 @@ where Ok(slice.iter().map(|&a| a * scale).collect()) } +#[inline] +pub(crate) fn mut_normalize_to_impl( + slice: &mut [T], + norm: T, + magnitude: T, +) -> Result<(), VectorError> +where + T: Copy + PartialEq + std::ops::Div + std::ops::Mul + num::Zero, +{ + if norm == T::zero() { + return Err(VectorError::ZeroVectorError("Cannot normalize a zero vector".to_string())); + } + let scale = magnitude / norm; + for a in slice.iter_mut() { + *a = *a * scale; + } + Ok(()) +} + +#[inline] +pub(crate) fn cosine_similarity_impl(a: &[T], b: &[T], norm_a: T, norm_b: T) -> T +where + T: num::Float + Clone + std::iter::Sum + std::ops::Div, +{ + let dot = dot_impl(a, b); + dot / (norm_a * norm_b) +} + +#[inline] +pub(crate) fn cosine_similarity_complex_impl( + a: &[num::Complex], + b: &[num::Complex], + norm_a: N, + norm_b: N, +) -> num::Complex +where + N: num::Float + Clone + std::iter::Sum + std::ops::Neg, + num::Complex: std::ops::Div>, +{ + let dot = hermitian_dot_impl(a, b); + dot / num::Complex::new(norm_a * norm_b, N::zero()) +} + #[cfg(test)] mod tests { use num::Complex; @@ -265,6 +376,52 @@ mod tests { assert_eq!(dot, 1.0_f64 * 4.0 + 2.0 * 5.0 + 3.0 * 6.0); } + // -- hermitian_dot_impl -- + #[test] + fn test_hermitian_dot_impl_basic() { + let a = [Complex::new(1.0_f64, 2.0), Complex::new(3.0, 4.0)]; + let b = [Complex::new(5.0, -1.0), Complex::new(-2.0, 2.0)]; + // Hermitian dot: sum_i a_i * conj(b_i) + // conj(b_0) = 5.0 + 1.0i, conj(b_1) = -2.0 - 2.0i + // a_0 * conj(b_0) = (1+2i)*(5+1i) = 1*5 + 1*1i + 2i*5 + 2i*1i = 5 + 1i + 10i + 2i^2 = 5 + 11i - 2 = 3 + 11i + // a_1 * conj(b_1) = (3+4i)*(-2-2i) = 3*-2 + 3*-2i + 4i*-2 + 4i*-2i = -6 -6i -8i -8i^2 = -6 -14i +8 = 2 -14i + // sum = (3+11i) + (2-14i) = 5 -3i + let result = super::hermitian_dot_impl(&a, &b); + assert!((result.re - 5.0).abs() < 1e-12); + assert!((result.im + 3.0).abs() < 1e-12); + } + + #[test] + fn test_hermitian_dot_impl_empty() { + let a: [Complex; 0] = []; + let b: [Complex; 0] = []; + let result = super::hermitian_dot_impl(&a, &b); + assert_eq!(result, Complex::new(0.0, 0.0)); + } + + #[test] + fn test_hermitian_dot_impl_partial() { + let a = [Complex::new(1.0_f64, 2.0), Complex::new(3.0, 4.0)]; + let b = [Complex::new(5.0, -1.0)]; + // Only the first element is used + let result = super::hermitian_dot_impl(&a, &b); + // a_0 * conj(b_0) = (1+2i)*(5+1i) = 3 + 11i (see above) + assert!((result.re - 3.0).abs() < 1e-12); + assert!((result.im - 11.0).abs() < 1e-12); + } + + #[test] + fn test_hermitian_dot_impl_identical() { + let a = [Complex::new(2.0_f64, -3.0), Complex::new(-1.0, 4.0)]; + let b = [Complex::new(2.0, -3.0), Complex::new(-1.0, 4.0)]; + // Hermitian dot with self: sum_i a_i * conj(a_i) = sum_i |a_i|^2 (real, >= 0) + let result = super::hermitian_dot_impl(&a, &b); + let expected = + Complex::new((2.0 * 2.0 + (-3.0) * (-3.0)) + ((-1.0) * (-1.0) + 4.0 * 4.0), 0.0); + assert!((result.re - expected.re).abs() < 1e-12); + assert!((result.im - expected.im).abs() < 1e-12); + } + // -- cross_impl -- #[test] fn test_cross_impl_basic() { @@ -343,9 +500,67 @@ mod tests { let mut out = [0.0f32; 3]; lerp_impl(&a, &b, 0.5, &mut out); // Only the first two elements are updated + assert_eq!(out, [2.5, 3.5, 0.0]); + } + + // -- mut_lerp_impl -- + #[test] + fn test_mut_lerp_impl_basic() { + let mut out = [1.0f32, 2.0, 3.0]; + let end = [4.0f32, 5.0, 6.0]; + mut_lerp_impl(&mut out, &end, 0.5); + assert_eq!(out, [2.5, 3.5, 4.5]); + } + + #[test] + fn test_mut_lerp_impl_weight_zero() { + let mut out = [1.0f64, 2.0, 3.0]; + let end = [4.0f64, 5.0, 6.0]; + mut_lerp_impl(&mut out, &end, 0.0); + assert_eq!(out, [1.0, 2.0, 3.0]); + } + + #[test] + fn test_mut_lerp_impl_weight_one() { + let mut out = [1.0f64, 2.0, 3.0]; + let end = [4.0f64, 5.0, 6.0]; + mut_lerp_impl(&mut out, &end, 1.0); + assert_eq!(out, [4.0, 5.0, 6.0]); + } + + #[test] + fn test_mut_lerp_impl_empty() { + let mut out: [f32; 0] = []; + let end: [f32; 0] = []; + mut_lerp_impl(&mut out, &end, 0.5); + assert_eq!(out, []); + } + + #[test] + fn test_mut_lerp_impl_partial() { + let mut out = [1.0f32, 2.0, 3.0]; + let end = [4.0f32, 5.0]; + mut_lerp_impl(&mut out, &end, 0.5); + // Only the first two elements are updated assert_eq!(out, [2.5, 3.5, 3.0]); } + #[test] + fn test_mut_lerp_impl_complex_basic() { + let mut out = [Complex::new(1.0_f64, 2.0), Complex::new(3.0, 4.0)]; + let end = [Complex::new(5.0, -1.0), Complex::new(-2.0, 2.0)]; + let weight = Complex::new(0.5, 0.0); + mut_lerp_impl(&mut out, &end, weight); + let expected = [ + Complex::new(0.5 * 1.0 + 0.5 * 5.0, 0.5 * 2.0 + 0.5 * -1.0), + Complex::new(0.5 * 3.0 + 0.5 * -2.0, 0.5 * 4.0 + 0.5 * 2.0), + ]; + assert!((out[0].re - expected[0].re).abs() < 1e-12); + assert!((out[0].im - expected[0].im).abs() < 1e-12); + assert!((out[1].re - expected[1].re).abs() < 1e-12); + assert!((out[1].im - expected[1].im).abs() < 1e-12); + } + // -- distance_impl -- #[test] fn test_distance_impl_basic() { @@ -381,6 +596,47 @@ mod tests { assert_eq!(dist, 0.0); } + // -- distance_complex_impl -- + #[test] + fn test_distance_complex_impl_basic() { + use num::Complex; + let a = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; + let b = [Complex::new(4.0, 6.0), Complex::new(8.0, 8.0)]; + // sqrt(|1+2i - 4+6i|^2 + |3+4i - 8+8i|^2) + // = sqrt(|-3-4i|^2 + |-5-4i|^2) + // = sqrt((3^2+4^2) + (5^2+4^2)) = sqrt(25 + 41) = sqrt(66) + let dist = distance_complex_impl(&a, &b); + assert!((dist - 66f64.sqrt()).abs() < 1e-12); + } + + #[test] + fn test_distance_complex_impl_empty() { + use num::Complex; + let a: [Complex; 0] = []; + let b: [Complex; 0] = []; + let dist = distance_complex_impl(&a, &b); + assert_eq!(dist, 0.0); + } + + #[test] + fn test_distance_complex_impl_partial() { + use num::Complex; + let a = [Complex::new(1.0_f64, 2.0), Complex::new(3.0, 4.0)]; + let b = [Complex::new(4.0, 6.0)]; + // Only the first element is used: sqrt(|-3-4i|^2) = sqrt(25) = 5 + let dist = distance_complex_impl(&a, &b); + assert!((dist - 5.0).abs() < 1e-12); + } + + #[test] + fn test_distance_complex_impl_identical() { + use num::Complex; + let a = [Complex::new(1.23, 4.56), Complex::new(7.89, 0.12)]; + let b = [Complex::new(1.23, 4.56), Complex::new(7.89, 0.12)]; + let dist = distance_complex_impl(&a, &b); + assert_eq!(dist, 0.0); + } + // -- manhattan_distance_impl -- #[test] fn test_manhattan_distance_impl_basic() { @@ -416,6 +672,45 @@ mod tests { assert_eq!(dist, 0.0); } + // -- manhattan_distance_complex_impl -- + #[test] + fn test_manhattan_distance_complex_impl_basic() { + use num::Complex; + let a = [Complex::new(1.0_f64, 2.0), Complex::new(3.0, 4.0)]; + let b = [Complex::new(4.0, 6.0), Complex::new(8.0, 8.0)]; + // |1+2i - 4+6i| + |3+4i - 8+8i| = |-3-4i| + |-5-4i| = 5 + sqrt(41) + let dist = manhattan_distance_complex_impl(&a, &b); + assert!((dist - (5.0 + 41f64.sqrt())).abs() < 1e-12); + } + + #[test] + fn test_manhattan_distance_complex_impl_empty() { + use num::Complex; + let a: [Complex; 0] = []; + let b: [Complex; 0] = []; + let dist = manhattan_distance_complex_impl(&a, &b); + assert_eq!(dist, 0.0); + } + + #[test] + fn test_manhattan_distance_complex_impl_partial() { + use num::Complex; + let a = [Complex::new(1.0_f64, 2.0), Complex::new(3.0, 4.0)]; + let b = [Complex::new(4.0, 6.0)]; + // Only the first element is used: |-3-4i| = 5 + let dist = manhattan_distance_complex_impl(&a, &b); + assert!((dist - 5.0).abs() < 1e-12); + } + + #[test] + fn test_manhattan_distance_complex_impl_identical() { + use num::Complex; + let a = [Complex::new(1.23_f64, 4.56), Complex::new(7.89, 0.12)]; + let b = [Complex::new(1.23, 4.56), Complex::new(7.89, 0.12)]; + let dist = manhattan_distance_complex_impl(&a, &b); + assert_eq!(dist, 0.0); + } + // -- chebyshev_distance_impl -- #[test] fn test_chebyshev_distance_impl_basic() { @@ -451,6 +746,45 @@ mod tests { assert_eq!(dist, 0.0); } + // -- chebyshev_distance_complex_impl -- + #[test] + fn test_chebyshev_distance_complex_impl_basic() { + use num::Complex; + let a = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; + let b = [Complex::new(4.0, 6.0), Complex::new(8.0, 8.0)]; + // max(|1+2i - 4+6i|, |3+4i - 8+8i|) = max(|-3-4i|, |-5-4i|) = max(5, sqrt(41)) + let dist = chebyshev_distance_complex_impl(&a, &b); + assert!((dist - 41f64.sqrt()).abs() < 1e-12); + } + + #[test] + fn test_chebyshev_distance_complex_impl_empty() { + use num::Complex; + let a: [Complex; 0] = []; + let b: [Complex; 0] = []; + let dist = chebyshev_distance_complex_impl(&a, &b); + assert_eq!(dist, 0.0); + } + + #[test] + fn test_chebyshev_distance_complex_impl_partial() { + use num::Complex; + let a = [Complex::new(1.0_f64, 2.0), Complex::new(3.0, 4.0)]; + let b = [Complex::new(4.0, 6.0)]; + // Only the first element is used: |-3-4i| = 5 + let dist = chebyshev_distance_complex_impl(&a, &b); + assert!((dist - 5.0).abs() < 1e-12); + } + + #[test] + fn test_chebyshev_distance_complex_impl_identical() { + use num::Complex; + let a = [Complex::new(1.23, 4.56), Complex::new(7.89, 0.12)]; + let b = [Complex::new(1.23, 4.56), Complex::new(7.89, 0.12)]; + let dist = chebyshev_distance_complex_impl(&a, &b); + assert_eq!(dist, 0.0); + } + // -- minkowski_distance_impl -- #[test] fn test_minkowski_distance_impl_basic() { @@ -505,6 +839,67 @@ mod tests { assert_eq!(dist, 0.0); } + // -- minkowski_distance_complex_impl -- + #[test] + fn test_minkowski_distance_complex_impl_basic() { + use num::Complex; + let a = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; + let b = [Complex::new(4.0, 6.0), Complex::new(8.0, 8.0)]; + let p = 3.0; + // ((|-3-4i|^3 + |-5-4i|^3))^(1/3) = (5^3 + 41.sqrt()^3)^(1/3) + let expected = (5.0_f64.powf(3.0) + 41f64.sqrt().powf(3.0)).powf(1.0 / 3.0); + let dist = minkowski_distance_complex_impl(&a, &b, p); + assert!((dist - expected).abs() < 1e-12); + } + + #[test] + fn test_minkowski_distance_complex_impl_p1() { + use num::Complex; + let a = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; + let b = [Complex::new(4.0, 6.0), Complex::new(8.0, 8.0)]; + // Should match manhattan distance: 5 + sqrt(41) + let dist = minkowski_distance_complex_impl(&a, &b, 1.0); + assert!((dist - (5.0 + 41f64.sqrt())).abs() < 1e-12); + } + + #[test] + fn test_minkowski_distance_complex_impl_p2() { + use num::Complex; + let a = [Complex::new(1.0_f64, 2.0), Complex::new(3.0, 4.0)]; + let b = [Complex::new(4.0, 6.0), Complex::new(8.0, 8.0)]; + // Should match euclidean distance: sqrt(25 + 41) = sqrt(66) + let dist = minkowski_distance_complex_impl(&a, &b, 2.0); + assert!((dist - 66f64.sqrt()).abs() < 1e-12); + } + + #[test] + fn test_minkowski_distance_complex_impl_empty() { + use num::Complex; + let a: [Complex; 0] = []; + let b: [Complex; 0] = []; + let dist = minkowski_distance_complex_impl(&a, &b, 2.0); + assert_eq!(dist, 0.0); + } + + #[test] + fn test_minkowski_distance_complex_impl_partial() { + use num::Complex; + let a = [Complex::new(1.0_f64, 2.0), Complex::new(3.0, 4.0)]; + let b = [Complex::new(4.0, 6.0)]; + // Only the first element is used: |-3-4i| = 5 + let dist = minkowski_distance_complex_impl(&a, &b, 2.0); + assert!((dist - 5.0).abs() < 1e-12); + } + + #[test] + fn test_minkowski_distance_complex_impl_identical() { + use num::Complex; + let a = [Complex::new(1.23, 4.56), Complex::new(7.89, 0.12)]; + let b = [Complex::new(1.23, 4.56), Complex::new(7.89, 0.12)]; + let dist = minkowski_distance_complex_impl(&a, &b, 2.0); + assert_eq!(dist, 0.0); + } + // -- angle_with_impl -- #[test] fn test_angle_with_impl_orthogonal() { @@ -564,56 +959,52 @@ mod tests { // -- project_onto_impl -- #[test] fn test_project_onto_impl_basic() { - let a = [3.0f64, 4.0]; let b = [1.0f64, 0.0]; - let denom = dot_impl(&b, &b); - let proj: FlexVector = project_onto_impl(&a, &b, denom); - // Projection of [3,4] onto [1,0] is [3,0] + let scalar = 3.0; + let proj: FlexVector = project_onto_impl(&b, scalar); + // Projection of [3,4] onto [1,0] is [3,0], so scalar = 3, b = [1,0] assert!((proj[0] - 3.0).abs() < 1e-12); assert!((proj[1] - 0.0).abs() < 1e-12); } #[test] fn test_project_onto_impl_parallel() { - let a = [2.0f64, 4.0]; let b = [1.0f64, 2.0]; - let denom = dot_impl(&b, &b); - let proj: FlexVector = project_onto_impl(&a, &b, denom); - // a is parallel to b, so projection should be a itself + let scalar = 2.0; + let proj: FlexVector = project_onto_impl(&b, scalar); + // scalar = 2, b = [1,2] => [2,4] assert!((proj[0] - 2.0).abs() < 1e-12); assert!((proj[1] - 4.0).abs() < 1e-12); } #[test] fn test_project_onto_impl_orthogonal() { - let a = [0.0f64, 1.0]; let b = [1.0f64, 0.0]; - let denom = dot_impl(&b, &b); - let proj: FlexVector = project_onto_impl(&a, &b, denom); - // Orthogonal vectors: projection should be [0,0] + let scalar = 0.0; + let proj: FlexVector = project_onto_impl(&b, scalar); + // scalar = 0, so projection is [0,0] assert!((proj[0] - 0.0).abs() < 1e-12); assert!((proj[1] - 0.0).abs() < 1e-12); } #[test] fn test_project_onto_impl_identical() { - let a = [5.0f64, 5.0]; let b = [5.0f64, 5.0]; - let denom = dot_impl(&b, &b); - let proj: FlexVector = project_onto_impl(&a, &b, denom); - // Should be a itself + let scalar = 1.0; + let proj: FlexVector = project_onto_impl(&b, scalar); + // scalar = 1, so projection is b itself assert!((proj[0] - 5.0).abs() < 1e-12); assert!((proj[1] - 5.0).abs() < 1e-12); } #[test] fn test_project_onto_impl_zero_vector() { - let a = [1.0f64, 2.0]; let b = [0.0f64, 0.0]; - let denom = dot_impl(&b, &b); - // denom is zero, so projection would be NaN or inf; check for NaN - let proj: FlexVector = project_onto_impl(&a, &b, denom); - assert!(proj.iter().all(|x| x.is_nan())); + let scalar = 42.0; + let proj: FlexVector = project_onto_impl(&b, scalar); + // b is zero vector, so projection is [0,0] + assert!((proj[0] - 0.0).abs() < 1e-12); + assert!((proj[1] - 0.0).abs() < 1e-12); } // -- normalize_impl -- @@ -659,6 +1050,50 @@ mod tests { assert!((result[0].im - 0.8).abs() < 1e-12); } + // -- mut_normalize_impl -- + #[test] + fn test_mut_normalize_impl_f64_basic() { + let mut v = [3.0f64, 4.0]; + let norm = (3.0f64 * 3.0 + 4.0 * 4.0).sqrt(); + mut_normalize_impl(&mut v, norm).unwrap(); + assert!((v[0] - 0.6).abs() < 1e-12); + assert!((v[1] - 0.8).abs() < 1e-12); + } + + #[test] + fn test_mut_normalize_impl_f64_zero_vector() { + let mut v = [0.0f64, 0.0]; + let norm = 0.0f64; + let result = mut_normalize_impl(&mut v, norm); + assert!(result.is_err()); + } + + #[test] + fn test_mut_normalize_impl_f64_single_element() { + let mut v = [5.0f64]; + let norm = 5.0f64; + mut_normalize_impl(&mut v, norm).unwrap(); + assert!((v[0] - 1.0).abs() < 1e-12); + } + + #[test] + fn test_mut_normalize_impl_f64_empty() { + let mut v: [f64; 0] = []; + let norm = 1.0f64; + mut_normalize_impl(&mut v, norm).unwrap(); + assert!(v.is_empty()); + } + + #[test] + fn test_mut_normalize_impl_complex_basic() { + use num::Complex; + let mut v = [Complex::new(3.0, 4.0)]; + let norm = Complex::new((3.0f64 * 3.0 + 4.0 * 4.0).sqrt(), 0.0); + mut_normalize_impl(&mut v, norm).unwrap(); + assert!((v[0].re - 0.6).abs() < 1e-12); + assert!((v[0].im - 0.8).abs() < 1e-12); + } + // -- normalize_to_impl -- #[test] fn test_normalize_to_impl_f64_basic() { @@ -709,4 +1144,140 @@ mod tests { assert!((result[0].re - expected.re).abs() < 1e-12); assert!((result[0].im - expected.im).abs() < 1e-12); } + + // -- mut_normalize_to_impl -- + #[test] + fn test_mut_normalize_to_impl_f64_basic() { + let mut v = [3.0f64, 4.0]; + let norm = (3.0f64 * 3.0 + 4.0 * 4.0).sqrt(); + let magnitude = 10.0f64; + mut_normalize_to_impl(&mut v, norm, magnitude).unwrap(); + // The normalized vector should have the same direction as v and norm 10 + let expected = [3.0 / 5.0 * 10.0, 4.0 / 5.0 * 10.0]; + assert!((v[0] - expected[0]).abs() < 1e-12); + assert!((v[1] - expected[1]).abs() < 1e-12); + } + + #[test] + fn test_mut_normalize_to_impl_f64_zero_vector() { + let mut v = [0.0f64, 0.0]; + let norm = 0.0f64; + let magnitude = 10.0f64; + let result = mut_normalize_to_impl(&mut v, norm, magnitude); + assert!(result.is_err()); + } + + #[test] + fn test_mut_normalize_to_impl_f64_single_element() { + let mut v = [5.0f64]; + let norm = 5.0f64; + let magnitude = 2.0f64; + mut_normalize_to_impl(&mut v, norm, magnitude).unwrap(); + assert!((v[0] - 2.0).abs() < 1e-12); + } + + #[test] + fn test_mut_normalize_to_impl_f64_empty() { + let mut v: [f64; 0] = []; + let norm = 1.0f64; + let magnitude = 2.0f64; + mut_normalize_to_impl(&mut v, norm, magnitude).unwrap(); + assert!(v.is_empty()); + } + + #[test] + fn test_mut_normalize_to_impl_complex_basic() { + use num::Complex; + let mut v = [Complex::new(3.0, 4.0)]; + let norm = Complex::new((3.0f64 * 3.0 + 4.0 * 4.0).sqrt(), 0.0); + let magnitude = Complex::new(10.0, 0.0); + mut_normalize_to_impl(&mut v, norm, magnitude).unwrap(); + let expected = Complex::new(3.0 / 5.0 * 10.0, 4.0 / 5.0 * 10.0); + assert!((v[0].re - expected.re).abs() < 1e-12); + assert!((v[0].im - expected.im).abs() < 1e-12); + } + + // -- cosine_similarity_impl -- + #[test] + fn test_cosine_similarity_impl_basic() { + let a = [1.0f64, 0.0]; + let b = [0.0f64, 1.0]; + let norm_a = (a[0].powi(2) + a[1].powi(2)).sqrt(); + let norm_b = (b[0].powi(2) + b[1].powi(2)).sqrt(); + let cos_sim = cosine_similarity_impl(&a, &b, norm_a, norm_b); + // Orthogonal vectors: cosine similarity should be 0 + assert!((cos_sim - 0.0).abs() < 1e-12); + } + + #[test] + fn test_cosine_similarity_impl_parallel() { + let a = [1.0f64, 2.0]; + let b = [2.0f64, 4.0]; + let norm_a = (a[0].powi(2) + a[1].powi(2)).sqrt(); + let norm_b = (b[0].powi(2) + b[1].powi(2)).sqrt(); + let cos_sim = cosine_similarity_impl(&a, &b, norm_a, norm_b); + // Parallel vectors: cosine similarity should be 1 + assert!((cos_sim - 1.0).abs() < 1e-12); + } + + #[test] + fn test_cosine_similarity_impl_opposite() { + let a = [1.0f64, 0.0]; + let b = [-1.0f64, 0.0]; + let norm_a = (a[0].powi(2) + a[1].powi(2)).sqrt(); + let norm_b = (b[0].powi(2) + b[1].powi(2)).sqrt(); + let cos_sim = cosine_similarity_impl(&a, &b, norm_a, norm_b); + // Opposite vectors: cosine similarity should be -1 + assert!((cos_sim + 1.0).abs() < 1e-12); + } + + #[test] + fn test_cosine_similarity_impl_identical() { + let a = [3.0f64, 4.0]; + let b = [3.0f64, 4.0]; + let norm_a = (a[0].powi(2) + a[1].powi(2)).sqrt(); + let norm_b = (b[0].powi(2) + b[1].powi(2)).sqrt(); + let cos_sim = cosine_similarity_impl(&a, &b, norm_a, norm_b); + // Identical vectors: cosine similarity should be 1 + assert!((cos_sim - 1.0).abs() < 1e-12); + } + + // -- cosine_similarity_complex_impl -- + #[test] + fn test_cosine_similarity_complex_impl_basic() { + use num::Complex; + let a = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; + let b = [Complex::new(5.0, -1.0), Complex::new(-2.0, 2.0)]; + let norm_a = a.iter().map(|x| x.norm_sqr()).sum::().sqrt(); + let norm_b = b.iter().map(|x| x.norm_sqr()).sum::().sqrt(); + let cos_sim = cosine_similarity_complex_impl(&a, &b, norm_a, norm_b); + // Compare to direct calculation + let dot = hermitian_dot_impl(&a, &b); + let expected = dot / Complex::new(norm_a * norm_b, 0.0); + assert!((cos_sim.re - expected.re).abs() < 1e-12); + assert!((cos_sim.im - expected.im).abs() < 1e-12); + } + + #[test] + fn test_cosine_similarity_complex_impl_identical() { + use num::Complex; + let a = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; + let norm_a = a.iter().map(|x| x.norm_sqr()).sum::().sqrt(); + let cos_sim = cosine_similarity_complex_impl(&a, &a, norm_a, norm_a); + // For identical vectors, cosine similarity should be 1+0i + assert!((cos_sim.re - 1.0).abs() < 1e-12); + assert!(cos_sim.im.abs() < 1e-12); + } + + #[test] + fn test_cosine_similarity_complex_impl_zero_vector() { + use num::Complex; + let a = [Complex::new(0.0, 0.0), Complex::new(0.0, 0.0)]; + let b = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; + let norm_a = a.iter().map(|x| x.norm_sqr()).sum::().sqrt(); + let norm_b = b.iter().map(|x| x.norm_sqr()).sum::().sqrt(); + // This will produce NaN due to division by zero, but we check for zero norm in the FlexVector impl. + let cos_sim = cosine_similarity_complex_impl(&a, &b, norm_a, norm_b); + assert!(cos_sim.re.is_nan() && cos_sim.im.is_nan()); + } } From 02fc1cb4ae0e2002f5f156ee6a858bcaf05f5266 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Sat, 17 May 2025 22:17:16 -0400 Subject: [PATCH 17/82] refactor binops division macros, add flexvector initialization macro --- src/macros.rs | 447 +++++++++++++++++++++++++++------------- src/types/flexvector.rs | 165 +++++++++++++-- 2 files changed, 450 insertions(+), 162 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index d35ef1c..9dddf63 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,79 +1,20 @@ //! Macros. -/// Returns a [`crate::Vector`] with scalar data contents and order as defined in -/// the numeric type arguments. +/// Creates a [`Vector`] with the given elements or repeated value. /// -/// This macro supports standard library [`array`]-like initialization syntax of -/// a [`crate::Vector`] with the numeric type and length defined by the macro arguments. -/// -/// Import the macro before use with: -/// -/// ``` -/// use vectora::vector; -/// ``` +/// - `vector![x, y, z]` creates a Vector from the elements. +/// - `vector![elem; n]` creates a Vector of length `n` with all elements set to `elem`. /// /// # Examples /// -/// ## Integer types -/// -/// ``` -/// use vectora::{vector, Vector}; -/// -/// let v_i32_1 = vector![1_i32, 2_i32, 3_i32]; -/// let v_i32_2: Vector = vector![1, 2, 3]; -/// let v_i32_3: Vector = vector![10; 3]; -/// -/// assert_eq!(v_i32_1[0], 1_i32); -/// assert_eq!(v_i32_1[1], 2_i32); -/// assert_eq!(v_i32_1[2], 3_i32); -/// -/// assert_eq!(v_i32_2[0], 1_i32); -/// assert_eq!(v_i32_2[1], 2_i32); -/// assert_eq!(v_i32_2[2], 3_i32); -/// -/// assert_eq!(v_i32_3[0], 10_i32); -/// assert_eq!(v_i32_3[1], 10_i32); -/// assert_eq!(v_i32_3[2], 10_i32); -/// ``` -/// -/// ## Floating point types -/// -/// ``` -/// use vectora::{vector, Vector}; -/// -/// use approx::assert_relative_eq; -/// -/// let v_f64_1 = vector![1.0_f64, 2.0_f64, 3.0_f64]; -/// let v_f64_2: Vector = vector![1.0, 2.0, 3.0]; -/// let v_f64_3: Vector = vector![10.0; 3]; -/// -/// assert_relative_eq!(v_f64_1[0], 1.0_f64); -/// assert_relative_eq!(v_f64_1[1], 2.0_f64); -/// assert_relative_eq!(v_f64_1[2], 3.0_f64); -/// -/// assert_relative_eq!(v_f64_2[0], 1.0_f64); -/// assert_relative_eq!(v_f64_2[1], 2.0_f64); -/// assert_relative_eq!(v_f64_2[2], 3.0_f64); -/// -/// assert_relative_eq!(v_f64_3[0], 10.0_f64); -/// assert_relative_eq!(v_f64_3[1], 10.0_f64); -/// assert_relative_eq!(v_f64_3[2], 10.0_f64); -/// ``` -/// -/// ## Complex number types -/// /// ``` -/// use vectora::{vector, Vector}; -/// -/// use approx::assert_relative_eq; +/// use vectora::vector; /// use num::Complex; /// -/// let v_complex = vector![Complex::new(1.0_f64, 2.0_f64), Complex::new(-1.0_f64, -2.0_f64)]; -/// -/// assert_relative_eq!(v_complex[0].re, 1.0_f64); -/// assert_relative_eq!(v_complex[0].im, 2.0_f64); -/// assert_relative_eq!(v_complex[1].re, -1.0_f64); -/// assert_relative_eq!(v_complex[1].im, -2.0_f64); +/// let v = vector![1, 2, 3]; +/// let v_f64 = vector![1.0, 2.0, 3.0]; +/// let v_repeat = vector![0; 4]; +/// let v_complex = vector![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; /// ``` #[macro_export] macro_rules! vector { @@ -85,93 +26,60 @@ macro_rules! vector { ); } -/// Returns a [`crate::Vector`] with scalar data contents and order as defined in a supported -/// fallible numeric data collection type argument. -/// -/// This macro can be used with supported types that may not have known length at compile -/// time (e.g., [`Vec`] and [`slice`]). The macro takes a single numeric data collection -/// type argument. -/// -/// Import the macro before use with: -/// -/// ``` -/// use vectora::try_vector; -/// ``` -/// -/// # Errors -/// -/// The macro returns an error if the length of the argument data collection type differs from the -/// requested [`crate::Vector`] length. Errors are also propagated from argument data types. Please review -/// the crate `TryFrom` trait implementation documentation for additional details. -/// -/// ``` -/// use vectora::{try_vector, Vector}; +/// Converts a collection (like a `Vec` or slice) into a [`Vector`] of matching length, returning a `Result`. /// -/// let stdlib_vec_too_long = vec![1, 2, 3, 4, 5]; -/// let res: Result, _> = try_vector!(stdlib_vec_too_long); -/// -/// assert!(res.is_err()); -/// ``` -/// -/// ``` -/// use vectora::{try_vector, Vector}; -/// -/// let stdlib_vec_too_short = vec![1, 2]; -/// let res: Result, _> = try_vector!(stdlib_vec_too_short); -/// -/// assert!(res.is_err()); -/// ``` +/// - `try_vector!(data)` tries to create a Vector from a runtime collection, returning an error if the length does not match. /// /// # Examples /// -/// ## Integer types -/// /// ``` /// use vectora::{try_vector, Vector}; +/// use num::Complex; /// -/// let stdlib_vec_i32 = vec![1_i32, 2_i32, 3_i32]; -/// let v_i32: Vector = try_vector!(stdlib_vec_i32).unwrap(); -/// -/// assert_eq!(v_i32[0], 1_i32); -/// assert_eq!(v_i32[1], 2_i32); -/// assert_eq!(v_i32[2], 3_i32); +/// let v: Vector = try_vector!(vec![1, 2, 3]).unwrap(); +/// let v_f64: Vector = try_vector!([1.0, 2.0, 3.0]).unwrap(); +/// let v_complex: Vector, 2> = try_vector!(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]).unwrap(); /// ``` /// -/// ## Floating point types +/// /// Error example: /// /// ``` /// use vectora::{try_vector, Vector}; +/// // This will return an Err because the length does not match the Vector's size. +/// let result: Result, _> = try_vector!(vec![1, 2]); // Suppose you want Vector +/// assert!(result.is_err()); +#[macro_export] +macro_rules! try_vector { + ($elem:expr) => { + $crate::types::vector::Vector::try_from($elem) + }; +} + +/// Creates a [`FlexVector`] with the given elements or repeated value. /// -/// use approx::assert_relative_eq; -/// -/// let stdlib_vec_f64 = vec![1.0_f64, 2.0_f64, 3.0_f64]; -/// let v_f64: Vector = try_vector!(stdlib_vec_f64).unwrap(); -/// -/// assert_relative_eq!(v_f64[0], 1.0_f64); -/// assert_relative_eq!(v_f64[1], 2.0_f64); -/// assert_relative_eq!(v_f64[2], 3.0_f64); -/// ``` +/// - `flexvector![x, y, z]` creates a FlexVector from the elements. +/// - `flexvector![elem; n]` creates a FlexVector of length `n` with all elements set to `elem`. /// -/// ## Complex number types +/// # Examples /// /// ``` -/// use vectora::{try_vector, Vector}; -/// -/// use approx::assert_relative_eq; +/// use vectora::flexvector; /// use num::Complex; /// -/// let stdlib_vec_complex = vec![Complex::new(1.0_f64, 2.0_f64), Complex::new(3.0_f64, 4.0_f64)]; -/// let v_complex_f64: Vector, 2> = try_vector!(stdlib_vec_complex).unwrap(); -/// -/// assert_relative_eq!(v_complex_f64[0].re, 1.0_f64); -/// assert_relative_eq!(v_complex_f64[0].im, 2.0_f64); -/// assert_relative_eq!(v_complex_f64[1].re, 3.0_f64); -/// assert_relative_eq!(v_complex_f64[1].im, 4.0_f64); +/// let fv = flexvector![1, 2, 3]; +/// let fv_f64 = flexvector![1.0_f64, 2.0_f64, 3.0_f64]; +/// let fv_repeat = flexvector![0; 4]; +/// let fv_complex = flexvector![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; /// ``` #[macro_export] -macro_rules! try_vector { - ($elem:expr) => { - $crate::types::vector::Vector::try_from($elem) +macro_rules! flexvector { + // Repeated element syntax: flexvector![elem; n] + ($elem:expr; $n:expr) => { + $crate::types::flexvector::FlexVector::from_vec(vec![$elem; $n]) + }; + // List syntax: flexvector![x, y, z, ...] + ($($x:expr),+ $(,)?) => { + $crate::types::flexvector::FlexVector::from_vec(vec![$($x),+]) }; } @@ -180,7 +88,7 @@ macro_rules! impl_vector_unary_op { ($VectorType:ident, $trait:ident, $method:ident, $op:tt) => { impl std::ops::$trait for $VectorType where - T: num::Num + Clone + Sync + Send + std::ops::Neg, + T: num::Num + Clone + std::ops::Neg, { type Output = Self; fn $method(self) -> Self { @@ -193,14 +101,31 @@ macro_rules! impl_vector_unary_op { #[macro_export] macro_rules! impl_vector_binop { - ($VectorType:ident, $trait:ident, $method:ident, $op:tt) => { + // With length check (for FlexVector) + (check_len, $VectorType:ident, $trait:ident, $method:ident, $op:tt) => { impl std::ops::$trait for $VectorType where - T: num::Num + Clone + Sync + Send, + T: num::Num + Clone, + { + type Output = Self; + fn $method(self, rhs: Self) -> Self { + assert_eq!(self.len(), rhs.len(), "Vector length mismatch"); + let components = self.components.into_iter() + .zip(rhs.components) + .map(|(a, b)| a $op b) + .collect(); + Self { components } + } + } + }; + // Without length check (for Vector) + (no_check_len, $VectorType:ident, $trait:ident, $method:ident, $op:tt) => { + impl std::ops::$trait for $VectorType + where + T: num::Num + Clone, { type Output = Self; fn $method(self, rhs: Self) -> Self { - assert_eq!(self.len(), rhs.len()); let components = self.components.into_iter() .zip(rhs.components) .map(|(a, b)| a $op b) @@ -211,21 +136,199 @@ macro_rules! impl_vector_binop { }; } +#[macro_export] +macro_rules! impl_vector_binop_div { + // With length check (for FlexVector) + (check_len, $VectorType:ident) => { + // f32 + impl std::ops::Div for $VectorType { + type Output = Self; + fn div(self, rhs: Self) -> Self { + assert_eq!(self.len(), rhs.len(), "Vector length mismatch"); + let components = + self.components.into_iter().zip(rhs.components).map(|(a, b)| a / b).collect(); + Self { components } + } + } + // f64 + impl std::ops::Div for $VectorType { + type Output = Self; + fn div(self, rhs: Self) -> Self { + assert_eq!(self.len(), rhs.len(), "Vector length mismatch"); + let components = + self.components.into_iter().zip(rhs.components).map(|(a, b)| a / b).collect(); + Self { components } + } + } + // Complex + impl std::ops::Div for $VectorType> { + type Output = Self; + fn div(self, rhs: Self) -> Self { + assert_eq!(self.len(), rhs.len(), "Vector length mismatch"); + let components = + self.components.into_iter().zip(rhs.components).map(|(a, b)| a / b).collect(); + Self { components } + } + } + // Complex + impl std::ops::Div for $VectorType> { + type Output = Self; + fn div(self, rhs: Self) -> Self { + assert_eq!(self.len(), rhs.len(), "Vector length mismatch"); + let components = + self.components.into_iter().zip(rhs.components).map(|(a, b)| a / b).collect(); + Self { components } + } + } + }; + // Without length check (for Vector) + (no_check_len, $VectorType:ident) => { + // f32 + impl std::ops::Div for $VectorType { + type Output = Self; + fn div(self, rhs: Self) -> Self { + let components = + self.components.into_iter().zip(rhs.components).map(|(a, b)| a / b).collect(); + Self { components } + } + } + // f64 + impl std::ops::Div for $VectorType { + type Output = Self; + fn div(self, rhs: Self) -> Self { + let components = + self.components.into_iter().zip(rhs.components).map(|(a, b)| a / b).collect(); + Self { components } + } + } + // Complex + impl std::ops::Div for $VectorType> { + type Output = Self; + fn div(self, rhs: Self) -> Self { + let components = + self.components.into_iter().zip(rhs.components).map(|(a, b)| a / b).collect(); + Self { components } + } + } + // Complex + impl std::ops::Div for $VectorType> { + type Output = Self; + fn div(self, rhs: Self) -> Self { + let components = + self.components.into_iter().zip(rhs.components).map(|(a, b)| a / b).collect(); + Self { components } + } + } + }; +} + #[macro_export] macro_rules! impl_vector_binop_assign { - ($VectorType:ident, $trait:ident, $method:ident, $op:tt) => { + // With length check (for FlexVector) + (check_len, $VectorType:ident, $trait:ident, $method:ident, $op:tt) => { impl std::ops::$trait for $VectorType where - T: num::Num + Clone + Sync + Send, + T: num::Num + Clone, { fn $method(&mut self, rhs: Self) { - assert_eq!(self.len(), rhs.len()); + assert_eq!(self.len(), rhs.len(), "Vector length mismatch"); for (a, b) in self.components.iter_mut().zip(rhs.components) { *a = a.clone() $op b; } } } }; + // Without length check (for Vector) + (no_check_len, $VectorType:ident, $trait:ident, $method:ident, $op:tt) => { + impl std::ops::$trait for $VectorType + where + T: num::Num + Clone, + { + fn $method(&mut self, rhs: Self) { + for (a, b) in self.components.iter_mut().zip(rhs.components) { + *a = a.clone() $op b; + } + } + } + }; +} + +#[macro_export] +macro_rules! impl_vector_binop_div_assign { + // With length check (for FlexVector) + (check_len, $VectorType:ident) => { + // f32 + impl std::ops::DivAssign for $VectorType { + fn div_assign(&mut self, rhs: Self) { + assert_eq!(self.len(), rhs.len(), "Vector length mismatch"); + for (a, b) in self.components.iter_mut().zip(rhs.components) { + *a /= b; + } + } + } + // f64 + impl std::ops::DivAssign for $VectorType { + fn div_assign(&mut self, rhs: Self) { + assert_eq!(self.len(), rhs.len(), "Vector length mismatch"); + for (a, b) in self.components.iter_mut().zip(rhs.components) { + *a /= b; + } + } + } + // Complex + impl std::ops::DivAssign for $VectorType> { + fn div_assign(&mut self, rhs: Self) { + assert_eq!(self.len(), rhs.len(), "Vector length mismatch"); + for (a, b) in self.components.iter_mut().zip(rhs.components) { + *a /= b; + } + } + } + // Complex + impl std::ops::DivAssign for $VectorType> { + fn div_assign(&mut self, rhs: Self) { + assert_eq!(self.len(), rhs.len(), "Vector length mismatch"); + for (a, b) in self.components.iter_mut().zip(rhs.components) { + *a /= b; + } + } + } + }; + // Without length check (for Vector) + (no_check_len, $VectorType:ident) => { + // f32 + impl std::ops::DivAssign for $VectorType { + fn div_assign(&mut self, rhs: Self) { + for (a, b) in self.components.iter_mut().zip(rhs.components) { + *a /= b; + } + } + } + // f64 + impl std::ops::DivAssign for $VectorType { + fn div_assign(&mut self, rhs: Self) { + for (a, b) in self.components.iter_mut().zip(rhs.components) { + *a /= b; + } + } + } + // Complex + impl std::ops::DivAssign for $VectorType> { + fn div_assign(&mut self, rhs: Self) { + for (a, b) in self.components.iter_mut().zip(rhs.components) { + *a /= b; + } + } + } + // Complex + impl std::ops::DivAssign for $VectorType> { + fn div_assign(&mut self, rhs: Self) { + for (a, b) in self.components.iter_mut().zip(rhs.components) { + *a /= b; + } + } + } + }; } #[macro_export] @@ -233,7 +336,7 @@ macro_rules! impl_vector_scalar_op { ($VectorType:ident, $trait:ident, $method:ident, $op:tt) => { impl std::ops::$trait for $VectorType where - T: num::Num + Clone + Sync + Send, + T: num::Num + Clone, { type Output = Self; fn $method(self, rhs: T) -> Self { @@ -249,7 +352,7 @@ macro_rules! impl_vector_scalar_op_assign { ($VectorType:ident, $trait:ident, $method:ident, $op:tt) => { impl std::ops::$trait for $VectorType where - T: num::Num + Clone + Sync + Send, + T: num::Num + Clone, { fn $method(&mut self, rhs: T) { for a in &mut self.components { @@ -837,4 +940,62 @@ mod tests { assert_relative_eq!(v[1].re, 3.0_f64); assert_relative_eq!(v[1].im, 4.0_f64); } + + #[test] + fn macro_flexvector_usize() { + let v1 = flexvector![1_usize, 2_usize, 3_usize]; + let v2 = flexvector![1, 2, 3]; + let v3 = flexvector![1; 3]; + + assert_eq!(v1.len(), 3); + assert_eq!(v1[0], 1_usize); + assert_eq!(v1[1], 2_usize); + assert_eq!(v1[2], 3_usize); + + assert_eq!(v2.len(), 3); + assert_eq!(v2[0], 1_usize); + assert_eq!(v2[1], 2_usize); + assert_eq!(v2[2], 3_usize); + + assert_eq!(v3.len(), 3); + assert_eq!(v3[0], 1_usize); + assert_eq!(v3[1], 1_usize); + assert_eq!(v3[2], 1_usize); + } + + #[test] + fn macro_flexvector_f64() { + let v1 = flexvector![1.0_f64, 2.0, 3.0]; + let v2 = flexvector![1.0, 2.0, 3.0]; + let v3 = flexvector![1.0; 3]; + + assert_eq!(v1.len(), 3); + assert_eq!(v1[0], 1.0); + assert_eq!(v1[1], 2.0); + assert_eq!(v1[2], 3.0); + + assert_eq!(v2.len(), 3); + assert_eq!(v2[0], 1.0); + assert_eq!(v2[1], 2.0); + assert_eq!(v2[2], 3.0); + + assert_eq!(v3.len(), 3); + assert_eq!(v3[0], 1.0); + assert_eq!(v3[1], 1.0); + assert_eq!(v3[2], 1.0); + } + + #[test] + fn macro_flexvector_complex_f64() { + let v1 = flexvector![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; + let v2 = flexvector![Complex::new(1.0, 2.0); 2]; + + assert_eq!(v1.len(), 2); + assert_eq!(v1[0], Complex::new(1.0, 2.0)); + assert_eq!(v1[1], Complex::new(3.0, 4.0)); + + assert_eq!(v2.len(), 2); + assert_eq!(v2[0], Complex::new(1.0, 2.0)); + assert_eq!(v2[1], Complex::new(1.0, 2.0)); + } } diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index 41a8e2a..3b7fd30 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -4,10 +4,11 @@ use std::iter::FromIterator; use std::ops::{Deref, DerefMut}; use crate::{ - impl_vector_binop, impl_vector_binop_assign, impl_vector_scalar_div_op, - impl_vector_scalar_div_op_assign, impl_vector_scalar_op, impl_vector_scalar_op_assign, - impl_vector_unary_op, types::traits::VectorBase, types::traits::VectorOps, - types::traits::VectorOpsComplex, types::traits::VectorOpsFloat, + impl_vector_binop, impl_vector_binop_assign, impl_vector_binop_div, + impl_vector_binop_div_assign, impl_vector_scalar_div_op, impl_vector_scalar_div_op_assign, + impl_vector_scalar_op, impl_vector_scalar_op_assign, impl_vector_unary_op, + types::traits::VectorBase, types::traits::VectorOps, types::traits::VectorOpsComplex, + types::traits::VectorOpsFloat, }; use crate::types::utils::{ @@ -872,13 +873,15 @@ impl FlexVector { // ================================ impl_vector_unary_op!(FlexVector, Neg, neg, -); -impl_vector_binop!(FlexVector, Add, add, +); -impl_vector_binop!(FlexVector, Sub, sub, -); -impl_vector_binop!(FlexVector, Mul, mul, *); +impl_vector_binop!(check_len, FlexVector, Add, add, +); +impl_vector_binop!(check_len, FlexVector, Sub, sub, -); +impl_vector_binop!(check_len, FlexVector, Mul, mul, *); +impl_vector_binop_div!(check_len, FlexVector); -impl_vector_binop_assign!(FlexVector, AddAssign, add_assign, +); -impl_vector_binop_assign!(FlexVector, SubAssign, sub_assign, -); -impl_vector_binop_assign!(FlexVector, MulAssign, mul_assign, *); +impl_vector_binop_assign!(check_len, FlexVector, AddAssign, add_assign, +); +impl_vector_binop_assign!(check_len, FlexVector, SubAssign, sub_assign, -); +impl_vector_binop_assign!(check_len, FlexVector, MulAssign, mul_assign, *); +impl_vector_binop_div_assign!(check_len, FlexVector); impl_vector_scalar_op!(FlexVector, Mul, mul, *); impl_vector_scalar_op_assign!(FlexVector, MulAssign, mul_assign, *); @@ -3853,7 +3856,7 @@ mod tests { // ================================ // - // Unary Negation trait tests + // Operator overload trait tests // // ================================ @@ -3931,6 +3934,14 @@ mod tests { assert_eq!(sum.as_slice()[2], f64::INFINITY); } + #[test] + #[should_panic(expected = "Vector length mismatch")] + fn test_add_panic_on_mismatched_length() { + let v1 = FlexVector::from_vec(vec![1, 2]); + let v2 = FlexVector::from_vec(vec![1, 2, 3]); + let _ = v1 + v2; + } + #[test] fn test_sub() { let v1 = FlexVector::from_vec(vec![10, 20, 30]); @@ -3965,6 +3976,14 @@ mod tests { assert!(diff.as_slice()[2].is_nan()); } + #[test] + #[should_panic(expected = "Vector length mismatch")] + fn test_sub_panic_on_mismatched_length() { + let v1 = FlexVector::from_vec(vec![1, 2]); + let v2 = FlexVector::from_vec(vec![1, 2, 3]); + let _ = v1 - v2; + } + #[test] fn test_mul() { let v1 = FlexVector::from_vec(vec![2, 3, 4]); @@ -4005,6 +4024,56 @@ mod tests { assert_eq!(prod.as_slice()[2], f64::INFINITY); } + #[test] + #[should_panic(expected = "Vector length mismatch")] + fn test_mul_panic_on_mismatched_length() { + let v1 = FlexVector::from_vec(vec![1, 2]); + let v2 = FlexVector::from_vec(vec![1, 2, 3]); + let _ = v1 * v2; + } + + #[test] + fn test_elem_div_f32() { + let v1 = FlexVector::from_vec(vec![2.0f32, 4.0, 8.0]); + let v2 = FlexVector::from_vec(vec![1.0f32, 2.0, 4.0]); + let result = v1 / v2; + assert_eq!(result.as_slice(), &[2.0, 2.0, 2.0]); + } + + #[test] + fn test_elem_div_f64() { + let v1 = FlexVector::from_vec(vec![2.0f64, 4.0, 8.0]); + let v2 = FlexVector::from_vec(vec![1.0f64, 2.0, 4.0]); + let result = v1 / v2; + assert_eq!(result.as_slice(), &[2.0, 2.0, 2.0]); + } + + #[test] + fn test_elem_div_complex_f32() { + let v1 = FlexVector::from_vec(vec![Complex::new(2.0f32, 2.0), Complex::new(4.0, 0.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(1.0f32, 1.0), Complex::new(2.0, 0.0)]); + let result = v1 / v2; + assert!((result[0] - Complex::new(2.0, 0.0)).norm() < 1e-6); + assert!((result[1] - Complex::new(2.0, 0.0)).norm() < 1e-6); + } + + #[test] + fn test_elem_div_complex_f64() { + let v1 = FlexVector::from_vec(vec![Complex::new(2.0f64, 2.0), Complex::new(4.0, 0.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(1.0f64, 1.0), Complex::new(2.0, 0.0)]); + let result = v1 / v2; + assert!((result[0] - Complex::new(2.0, 0.0)).norm() < 1e-12); + assert!((result[1] - Complex::new(2.0, 0.0)).norm() < 1e-12); + } + + #[test] + #[should_panic(expected = "Vector length mismatch")] + fn test_elem_div_panic_on_mismatched_length() { + let v1 = FlexVector::from_vec(vec![1.0f64, 2.0]); + let v2 = FlexVector::from_vec(vec![1.0f64, 2.0, 3.0]); + let _ = v1 / v2; + } + #[test] fn test_add_assign() { let mut v1 = FlexVector::from_vec(vec![1, 2, 3]); @@ -4029,6 +4098,14 @@ mod tests { assert_eq!(v1.as_slice(), &[Complex::new(6.0, 8.0), Complex::new(10.0, 12.0)]); } + #[test] + #[should_panic(expected = "Vector length mismatch")] + fn test_add_assign_panic_on_mismatched_length() { + let mut v1 = FlexVector::from_vec(vec![1, 2]); + let v2 = FlexVector::from_vec(vec![1, 2, 3]); + v1 += v2; + } + #[test] fn test_sub_assign() { let mut v1 = FlexVector::from_vec(vec![10, 20, 30]); @@ -4053,6 +4130,14 @@ mod tests { assert_eq!(v1.as_slice(), &[Complex::new(4.0, 5.0), Complex::new(1.0, 3.0)]); } + #[test] + #[should_panic(expected = "Vector length mismatch")] + fn test_sub_assign_panic_on_mismatched_length() { + let mut v1 = FlexVector::from_vec(vec![1, 2]); + let v2 = FlexVector::from_vec(vec![1, 2, 3]); + v1 -= v2; + } + #[test] fn test_mul_assign() { let mut v1 = FlexVector::from_vec(vec![2, 3, 4]); @@ -4077,6 +4162,56 @@ mod tests { assert_eq!(v1.as_slice(), &[Complex::new(-7.0, 16.0), Complex::new(-11.0, 52.0)]); } + #[test] + #[should_panic(expected = "Vector length mismatch")] + fn test_mul_assign_panic_on_mismatched_length() { + let mut v1 = FlexVector::from_vec(vec![1, 2]); + let v2 = FlexVector::from_vec(vec![1, 2, 3]); + v1 *= v2; + } + + #[test] + fn test_elem_div_assign_f32() { + let mut v1 = FlexVector::from_vec(vec![2.0f32, 4.0, 8.0]); + let v2 = FlexVector::from_vec(vec![1.0f32, 2.0, 4.0]); + v1 /= v2; + assert_eq!(v1.as_slice(), &[2.0, 2.0, 2.0]); + } + + #[test] + fn test_elem_div_assign_f64() { + let mut v1 = FlexVector::from_vec(vec![2.0f64, 4.0, 8.0]); + let v2 = FlexVector::from_vec(vec![1.0f64, 2.0, 4.0]); + v1 /= v2; + assert_eq!(v1.as_slice(), &[2.0, 2.0, 2.0]); + } + + #[test] + fn test_elem_div_assign_complex_f32() { + let mut v1 = FlexVector::from_vec(vec![Complex::new(2.0f32, 2.0), Complex::new(4.0, 0.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(1.0f32, 1.0), Complex::new(2.0, 0.0)]); + v1 /= v2; + assert!((v1[0] - Complex::new(2.0, 0.0)).norm() < 1e-6); + assert!((v1[1] - Complex::new(2.0, 0.0)).norm() < 1e-6); + } + + #[test] + fn test_elem_div_assign_complex_f64() { + let mut v1 = FlexVector::from_vec(vec![Complex::new(2.0f64, 2.0), Complex::new(4.0, 0.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(1.0f64, 1.0), Complex::new(2.0, 0.0)]); + v1 /= v2; + assert!((v1[0] - Complex::new(2.0, 0.0)).norm() < 1e-12); + assert!((v1[1] - Complex::new(2.0, 0.0)).norm() < 1e-12); + } + + #[test] + #[should_panic(expected = "Vector length mismatch")] + fn test_elem_div_assign_panic_on_mismatched_length() { + let mut v1 = FlexVector::from_vec(vec![1.0f64, 2.0]); + let v2 = FlexVector::from_vec(vec![1.0f64, 2.0, 3.0]); + v1 /= v2; + } + #[test] fn test_scalar_mul() { let v = FlexVector::from_vec(vec![2, -3, 4]); @@ -4264,12 +4399,4 @@ mod tests { let prod = v1 * v2; assert!(prod.is_empty()); } - - #[test] - #[should_panic] - fn test_mismatched_length_add() { - let v1 = FlexVector::from_vec(vec![1, 2]); - let v2 = FlexVector::from_vec(vec![1, 2, 3]); - let _ = v1 + v2; // should panic - } } From 76216864eb4544572bfef684584345609e61864f Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Sat, 17 May 2025 23:27:29 -0400 Subject: [PATCH 18/82] add FlexVector try_from_iter fallible init method and try_flexvector macro --- src/macros.rs | 64 +++++++++++++++++++++++++++++++++++++++++ src/types/flexvector.rs | 61 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+) diff --git a/src/macros.rs b/src/macros.rs index 9dddf63..b15b17b 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -83,6 +83,30 @@ macro_rules! flexvector { }; } +/// Fallibly constructs a [`FlexVector`] from an iterator of `Result`, propagating the first error. +/// +/// - `try_flexvector!(iter)` collects all `Ok(T)` values into a FlexVector, or returns the first `Err(E)` encountered. +/// +/// # Examples +/// +/// ``` +/// use vectora::try_flexvector; +/// +/// let data = vec!["1", "2", "oops"]; +/// let fv = try_flexvector!(data.iter().map(|s| s.parse::())); +/// assert!(fv.is_err()); +/// +/// let data = vec!["1", "2", "3"]; +/// let fv = try_flexvector!(data.iter().map(|s| s.parse::())).unwrap(); +/// assert_eq!(fv.as_slice(), &[1, 2, 3]); +/// ``` +#[macro_export] +macro_rules! try_flexvector { + ($iter:expr) => { + $crate::types::flexvector::FlexVector::try_from_iter($iter) + }; +} + #[macro_export] macro_rules! impl_vector_unary_op { ($VectorType:ident, $trait:ident, $method:ident, $op:tt) => { @@ -473,6 +497,7 @@ macro_rules! impl_vector_scalar_div_op_assign { #[cfg(test)] mod tests { + use crate::types::traits::VectorBase; use crate::Vector; #[allow(unused_imports)] use approx::{assert_relative_eq, assert_relative_ne}; @@ -998,4 +1023,43 @@ mod tests { assert_eq!(v2[0], Complex::new(1.0, 2.0)); assert_eq!(v2[1], Complex::new(1.0, 2.0)); } + + #[test] + fn macro_try_flexvector_success() { + let data: Vec> = vec![Ok(1), Ok(2), Ok(3)]; + let fv = try_flexvector!(data).unwrap(); + assert_eq!(fv.as_slice(), &[1, 2, 3]); + } + + #[test] + fn macro_try_flexvector_parse_success() { + let data = vec!["1", "2", "3"]; + let fv = try_flexvector!(data.iter().map(|s| s.parse::())).unwrap(); + assert_eq!(fv.as_slice(), &[1, 2, 3]); + } + + #[test] + fn macro_try_flexvector_parse_error() { + let data = vec!["1", "oops", "3"]; + let result = try_flexvector!(data.iter().map(|s| s.parse::())); + assert!(result.is_err()); + } + + #[test] + fn macro_try_flexvector_custom_error() { + #[derive(Debug, PartialEq)] + enum MyError { + Fail, + } + let data = vec![Ok(1), Err(MyError::Fail), Ok(3)]; + let result = try_flexvector!(data); + assert_eq!(result.unwrap_err(), MyError::Fail); + } + + #[test] + fn macro_try_flexvector_empty() { + let data: Vec> = vec![]; + let fv = try_flexvector!(data).unwrap(); + assert!(fv.is_empty()); + } } diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index 3b7fd30..fcb11d4 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -132,6 +132,22 @@ impl FromIterator for FlexVector { } } +// ================================ +// +// try_from_iter method +// +// ================================ +impl FlexVector { + /// Fallible error-propagating construction from an iterator of Results. + pub fn try_from_iter(iter: I) -> Result + where + I: IntoIterator>, + { + let components: Result, E> = iter.into_iter().collect(); + components.map(|vec| FlexVector { components: vec }) + } +} + // ================================ // // Deref/DerefMut trait impl @@ -1128,6 +1144,51 @@ mod tests { assert!(v.is_empty()); } + // ================================ + // + // try_from_iter method tests + // + // ================================ + + #[test] + fn test_try_from_iter_success() { + let data: Vec> = vec![Ok(1i32), Ok(2i32), Ok(3i32)]; + let fv = FlexVector::::try_from_iter(data).unwrap(); + assert_eq!(fv.as_slice(), &[1, 2, 3]); + } + + #[test] + fn test_try_from_iter_parse_success() { + let data = vec!["1", "2", "3"]; + let fv = FlexVector::::try_from_iter(data.iter().map(|s| s.parse())).unwrap(); + assert_eq!(fv.as_slice(), &[1, 2, 3]); + } + + #[test] + fn test_try_from_iter_parse_error() { + let data = vec!["1", "oops", "3"]; + let result = FlexVector::::try_from_iter(data.iter().map(|s| s.parse::())); + assert!(result.is_err()); + } + + #[test] + fn test_try_from_iter_custom_error() { + #[derive(Debug, PartialEq)] + enum MyError { + Fail, + } + let data = vec![Ok(1), Err(MyError::Fail), Ok(3)]; + let result = FlexVector::::try_from_iter(data); + assert_eq!(result.unwrap_err(), MyError::Fail); + } + + #[test] + fn test_try_from_iter_empty() { + let data: Vec> = vec![]; + let fv = FlexVector::::try_from_iter(data).unwrap(); + assert!(fv.is_empty()); + } + // ================================ // // Deref/DerefMut trait tests From 3e3531eb4d81a9b090c3019411ab070daaf2a852 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Sat, 17 May 2025 23:28:24 -0400 Subject: [PATCH 19/82] remove unnecessary clone on negate method --- src/types/flexvector.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index fcb11d4..1793b54 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -357,7 +357,7 @@ where T: std::ops::Neg + Clone, Self::Output: std::iter::FromIterator, { - self.as_slice().iter().map(|a| -a.clone()).collect() + self.as_slice().iter().map(|a| -*a).collect() } #[inline] From 9673a91cfe820e1f8bdb6125dcb92680c7fafa9f Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Sun, 18 May 2025 00:21:56 -0400 Subject: [PATCH 20/82] add prelude with core traits, types, macros in scope --- src/lib.rs | 26 ++++++++++++++++++++++++++ src/macros.rs | 8 ++++---- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index aa117b0..7c1146d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -898,3 +898,29 @@ pub mod types; pub use types::flexvector::FlexVector; pub use types::vector::Vector; + +/// The vectora prelude: import this module to bring all core traits, types, and macros into scope. +/// +/// # Example +/// ``` +/// use vectora::prelude::*; +/// +/// let v = vector![1, 2, 3]; +/// let fv = flexvector![1.0, 2.0, 3.0]; +/// let v2 = Vector::::from([4, 5, 6]); +/// let fv2 = FlexVector::from_vec(vec![4.0, 5.0, 6.0]); +/// ``` +/// +/// This prelude includes: +/// - Core traits (`VectorBase`, `VectorOps`, `VectorOpsFloat`, `VectorOpsComplex`) +/// - The main types (`Vector`, `FlexVector`) +/// - User-facing macros (`vector!`, `flexvector!`, `try_vector!`, `try_flexvector!`) +pub mod prelude { + // Traits + pub use crate::types::traits::{VectorBase, VectorOps, VectorOpsComplex, VectorOpsFloat}; + // Types + pub use crate::types::flexvector::FlexVector; + pub use crate::types::vector::Vector; + // Macros + pub use crate::{flexvector, try_flexvector, try_vector, vector}; +} diff --git a/src/macros.rs b/src/macros.rs index b15b17b..5a120ce 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -33,7 +33,7 @@ macro_rules! vector { /// # Examples /// /// ``` -/// use vectora::{try_vector, Vector}; +/// use vectora::prelude::*; /// use num::Complex; /// /// let v: Vector = try_vector!(vec![1, 2, 3]).unwrap(); @@ -44,7 +44,7 @@ macro_rules! vector { /// /// Error example: /// /// ``` -/// use vectora::{try_vector, Vector}; +/// use vectora::prelude::*; /// // This will return an Err because the length does not match the Vector's size. /// let result: Result, _> = try_vector!(vec![1, 2]); // Suppose you want Vector /// assert!(result.is_err()); @@ -63,7 +63,7 @@ macro_rules! try_vector { /// # Examples /// /// ``` -/// use vectora::flexvector; +/// use vectora::prelude::*; /// use num::Complex; /// /// let fv = flexvector![1, 2, 3]; @@ -90,7 +90,7 @@ macro_rules! flexvector { /// # Examples /// /// ``` -/// use vectora::try_flexvector; +/// use vectora::prelude::*; /// /// let data = vec!["1", "2", "oops"]; /// let fv = try_flexvector!(data.iter().map(|s| s.parse::())); From d450fcf85b00ac67bf62f6c8c9cb9e1bc70778a5 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Sun, 18 May 2025 00:22:25 -0400 Subject: [PATCH 21/82] add try_from_fn method on FlexVector --- src/types/flexvector.rs | 93 +++++++++++++++++++++++++++++++---------- 1 file changed, 72 insertions(+), 21 deletions(-) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index 1793b54..d15334f 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -93,6 +93,32 @@ impl FlexVector { pub fn from_vec(vec: Vec) -> Self { Self { components: vec } } + + /// Fallible error-propagating construction from an iterator of Results. + pub fn try_from_iter(iter: I) -> Result + where + I: IntoIterator>, + { + let components: Result, E> = iter.into_iter().collect(); + components.map(|vec| FlexVector { components: vec }) + } + + /// Creates a new FlexVector by calling the provided function or closure for each index. + /// + /// # Example + /// ``` + /// use vectora::prelude::*; + /// + /// let v = FlexVector::from_fn(4, |i| i * i); + /// assert_eq!(v.as_slice(), &[0, 1, 4, 9]); + /// ``` + pub fn from_fn(len: usize, mut f: F) -> Self + where + F: FnMut(usize) -> T, + { + let components = (0..len).map(|i| f(i)).collect(); + FlexVector { components } + } } // ================================ @@ -132,22 +158,6 @@ impl FromIterator for FlexVector { } } -// ================================ -// -// try_from_iter method -// -// ================================ -impl FlexVector { - /// Fallible error-propagating construction from an iterator of Results. - pub fn try_from_iter(iter: I) -> Result - where - I: IntoIterator>, - { - let components: Result, E> = iter.into_iter().collect(); - components.map(|vec| FlexVector { components: vec }) - } -} - // ================================ // // Deref/DerefMut trait impl @@ -913,6 +923,19 @@ mod tests { use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; + // ================================ + // + // Test utility functions + // + // ================================ + fn square(x: i32) -> i32 { + x * x + } + + fn square_usize(x: usize) -> usize { + x * x + } + // ================================ // // Constructor tests @@ -1048,6 +1071,39 @@ mod tests { assert_eq!(v.as_slice(), &data[..]); } + #[test] + fn test_from_fn_i32() { + let v = FlexVector::from_fn(5, |i| i as i32 * 2); + assert_eq!(v.as_slice(), &[0, 2, 4, 6, 8]); + } + + #[test] + fn test_from_fn_f64() { + let v = FlexVector::from_fn(4, |i| (i as f64).powi(2)); + assert_eq!(v.as_slice(), &[0.0, 1.0, 4.0, 9.0]); + } + + #[test] + fn test_from_fn_complex() { + let v = FlexVector::from_fn(3, |i| Complex::new(i as f64, -(i as f64))); + assert_eq!( + v.as_slice(), + &[Complex::new(0.0, 0.0), Complex::new(1.0, -1.0), Complex::new(2.0, -2.0)] + ); + } + + #[test] + fn test_from_fn_zero_length() { + let v: FlexVector = FlexVector::from_fn(0, |_| 42); + assert!(v.is_empty()); + } + + #[test] + fn test_from_fn_with_fn_pointer() { + let v = FlexVector::from_fn(4, square_usize); + assert_eq!(v.as_slice(), &[0, 1, 4, 9]); + } + #[test] fn test_zero_length_constructors() { assert_eq!(FlexVector::::zero(0).len(), 0); @@ -2689,11 +2745,6 @@ mod tests { assert!(mapped.is_empty()); } - // used for function pointer test below - fn square(x: i32) -> i32 { - x * x - } - #[test] fn test_map_with_fn_pointer() { let v = FlexVector::from_vec(vec![1, 2, 3]); From b27ef6dad9652277b0dee4fc2a2f9a767b369086 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Sun, 18 May 2025 00:45:58 -0400 Subject: [PATCH 22/82] use f directly in from_fn map --- src/types/flexvector.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index d15334f..659e78d 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -112,11 +112,11 @@ impl FlexVector { /// let v = FlexVector::from_fn(4, |i| i * i); /// assert_eq!(v.as_slice(), &[0, 1, 4, 9]); /// ``` - pub fn from_fn(len: usize, mut f: F) -> Self + pub fn from_fn(len: usize, f: F) -> Self where F: FnMut(usize) -> T, { - let components = (0..len).map(|i| f(i)).collect(); + let components = (0..len).map(f).collect(); FlexVector { components } } } From 31ba4f375353634a18255317394ad5502cb6dc19 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Sun, 18 May 2025 00:46:24 -0400 Subject: [PATCH 23/82] add FlexVector try_from_fn constructor method --- src/types/flexvector.rs | 52 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index 659e78d..dd9c493 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -103,7 +103,7 @@ impl FlexVector { components.map(|vec| FlexVector { components: vec }) } - /// Creates a new FlexVector by calling the provided function or closure for each index. + /// Creates a new [`FlexVector`] by calling the provided function or closure for each index. /// /// # Example /// ``` @@ -119,6 +119,28 @@ impl FlexVector { let components = (0..len).map(f).collect(); FlexVector { components } } + + /// Fallibly creates a new [`FlexVector`] by calling the provided function or closure for each index. + /// + /// Returns the first error encountered, or a [`FlexVector`] of all results if successful. + /// + /// # Example + /// ``` + /// use vectora::prelude::*; + /// + /// let v = FlexVector::::try_from_fn(3, |i| if i == 1 { Err("fail") } else { Ok(i as i32) }); + /// assert!(v.is_err()); + /// ``` + pub fn try_from_fn(len: usize, mut f: F) -> Result + where + F: FnMut(usize) -> Result, + { + let mut components = Vec::with_capacity(len); + for i in 0..len { + components.push(f(i)?); + } + Ok(FlexVector { components }) + } } // ================================ @@ -1104,6 +1126,34 @@ mod tests { assert_eq!(v.as_slice(), &[0, 1, 4, 9]); } + #[test] + fn test_try_from_fn_success() { + let v = FlexVector::::try_from_fn(4, |i| Ok::(i as i32 * 2)).unwrap(); + assert_eq!(v.as_slice(), &[0, 2, 4, 6]); + } + + #[test] + fn test_try_from_fn_error() { + let result = + FlexVector::::try_from_fn(5, |i| if i == 3 { Err("fail") } else { Ok(i as i32) }); + assert_eq!(result.unwrap_err(), "fail"); + } + + #[test] + fn test_try_from_fn_empty() { + let v = FlexVector::::try_from_fn(0, |_| Ok::(42)).unwrap(); + assert!(v.is_empty()); + } + + #[test] + fn test_try_from_fn_with_fn_pointer() { + fn fallible_square(i: usize) -> Result { + Ok((i * i) as i32) + } + let v = FlexVector::::try_from_fn(3, fallible_square).unwrap(); + assert_eq!(v.as_slice(), &[0, 1, 4]); + } + #[test] fn test_zero_length_constructors() { assert_eq!(FlexVector::::zero(0).len(), 0); From f5516eb240da89ff1d5981e007f24852aca3468c Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Sun, 18 May 2025 18:34:11 -0400 Subject: [PATCH 24/82] add FlexVector flatten and flatten_owned methods --- src/types/flexvector.rs | 105 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index dd9c493..287202f 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -143,6 +143,30 @@ impl FlexVector { } } +// -- more constructors -- + +impl FlexVector +where + V: IntoIterator, +{ + /// Flattens a FlexVector of iterables into a single FlexVector by concatenating all elements. + pub fn flatten(self) -> FlexVector { + let components = self.components.into_iter().flat_map(|v| v).collect(); + FlexVector { components } + } +} + +impl<'a, V, T> FlexVector +where + V: IntoIterator, + T: Clone + 'a, +{ + pub fn flatten_owned(self) -> FlexVector { + let components = self.components.into_iter().flat_map(|v| v.into_iter().cloned()).collect(); + FlexVector { components } + } +} + // ================================ // // Default trait impl @@ -1154,6 +1178,87 @@ mod tests { assert_eq!(v.as_slice(), &[0, 1, 4]); } + #[test] + fn test_flatten_flexvector_of_flexvector() { + let row1 = FlexVector::from_vec(vec![1, 2]); + let row2 = FlexVector::from_vec(vec![3, 4, 5]); + let nested = FlexVector::from_vec(vec![row1, row2]); + let flat = nested.flatten(); + assert_eq!(flat.as_slice(), &[1, 2, 3, 4, 5]); + } + + #[test] + fn test_flatten_flexvector_of_vec() { + let nested = FlexVector::from_vec(vec![vec![10, 20], vec![30]]); + let flat = nested.flatten(); + assert_eq!(flat.as_slice(), &[10, 20, 30]); + } + + #[test] + fn test_flatten_flexvector_of_array() { + let nested = FlexVector::from_vec(vec![[1, 2], [3, 4]]); + let flat = nested.flatten(); + assert_eq!(flat.as_slice(), &[1, 2, 3, 4]); + } + + #[test] + fn test_flatten_flexvector_of_slice_refs() { + let a = [7, 8]; + let b = [9]; + let nested = FlexVector::from_vec(vec![&a[..], &b[..]]); + let flat = nested.flatten(); + assert_eq!(flat.as_slice(), &[&7, &8, &9]); + } + + #[test] + fn test_flatten_empty_outer() { + let nested: FlexVector> = FlexVector::new(); + let flat = nested.flatten(); + assert!(flat.is_empty()); + } + + #[test] + fn test_flatten_with_empty_inner() { + let nested = FlexVector::from_vec(vec![vec![], vec![1, 2], vec![]]); + let flat = nested.flatten(); + assert_eq!(flat.as_slice(), &[1, 2]); + } + + #[test] + fn test_flatten_owned_flexvector_of_slice_refs() { + let a = [8, 9]; + let b = [10]; + let nested = FlexVector::from_vec(vec![&a[..], &b[..]]); + let flat = nested.flatten_owned(); + let expected: Vec = vec![8, 9, 10]; + assert_eq!(flat.as_slice(), expected.as_slice()); + let _: &[i32] = flat.as_slice(); // type check: &[i32] + } + + #[test] + fn test_flatten_owned_flexvector_of_refs() { + let x = 42; + let y = 43; + let nested = FlexVector::from_vec(vec![vec![&x, &y], vec![&x]]); + let flat = nested.flatten_owned(); + assert_eq!(flat.as_slice(), &[42, 43, 42]); + let _: &[i32] = flat.as_slice(); // type check: &[i32] + } + + #[test] + fn test_flatten_owned_empty_outer() { + let nested: FlexVector> = FlexVector::new(); + let flat = nested.flatten_owned(); + assert!(flat.is_empty()); + } + + #[test] + fn test_flatten_owned_with_empty_inner() { + let nested = FlexVector::from_vec(vec![vec![], vec![&1, &2], vec![]]); + let flat = nested.flatten_owned(); + assert_eq!(flat.as_slice(), &[1, 2]); + } + #[test] fn test_zero_length_constructors() { assert_eq!(FlexVector::::zero(0).len(), 0); From 2f83e9b81217ecac595d932503936531791ad10f Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Sun, 18 May 2025 18:46:27 -0400 Subject: [PATCH 25/82] refactor flatten call approach --- src/types/flexvector.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index 287202f..b5e3483 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -151,7 +151,7 @@ where { /// Flattens a FlexVector of iterables into a single FlexVector by concatenating all elements. pub fn flatten(self) -> FlexVector { - let components = self.components.into_iter().flat_map(|v| v).collect(); + let components = self.components.into_iter().flatten().collect(); FlexVector { components } } } From 1e8194ca5e90e2606f2eec99796d4ff21264cf00 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Sun, 18 May 2025 18:46:43 -0400 Subject: [PATCH 26/82] refactor flatten_owned name to flatten_cloned --- src/types/flexvector.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index b5e3483..caa8d70 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -161,7 +161,7 @@ where V: IntoIterator, T: Clone + 'a, { - pub fn flatten_owned(self) -> FlexVector { + pub fn flatten_cloned(self) -> FlexVector { let components = self.components.into_iter().flat_map(|v| v.into_iter().cloned()).collect(); FlexVector { components } } @@ -1225,37 +1225,37 @@ mod tests { } #[test] - fn test_flatten_owned_flexvector_of_slice_refs() { + fn test_flatten_cloned_flexvector_of_slice_refs() { let a = [8, 9]; let b = [10]; let nested = FlexVector::from_vec(vec![&a[..], &b[..]]); - let flat = nested.flatten_owned(); + let flat = nested.flatten_cloned(); let expected: Vec = vec![8, 9, 10]; assert_eq!(flat.as_slice(), expected.as_slice()); let _: &[i32] = flat.as_slice(); // type check: &[i32] } #[test] - fn test_flatten_owned_flexvector_of_refs() { + fn test_flatten_cloned_flexvector_of_refs() { let x = 42; let y = 43; let nested = FlexVector::from_vec(vec![vec![&x, &y], vec![&x]]); - let flat = nested.flatten_owned(); + let flat = nested.flatten_cloned(); assert_eq!(flat.as_slice(), &[42, 43, 42]); let _: &[i32] = flat.as_slice(); // type check: &[i32] } #[test] - fn test_flatten_owned_empty_outer() { + fn test_flatten_cloned_empty_outer() { let nested: FlexVector> = FlexVector::new(); - let flat = nested.flatten_owned(); + let flat = nested.flatten_cloned(); assert!(flat.is_empty()); } #[test] - fn test_flatten_owned_with_empty_inner() { + fn test_flatten_cloned_with_empty_inner() { let nested = FlexVector::from_vec(vec![vec![], vec![&1, &2], vec![]]); - let flat = nested.flatten_owned(); + let flat = nested.flatten_cloned(); assert_eq!(flat.as_slice(), &[1, 2]); } From 9116f1243cbe598bb3382845738fe1e3719f035e Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Sun, 18 May 2025 19:10:18 -0400 Subject: [PATCH 27/82] add FlexVector cloned method --- src/types/flexvector.rs | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index caa8d70..88860b3 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -145,6 +145,16 @@ impl FlexVector { // -- more constructors -- +impl FlexVector<&T> +where + T: Clone, +{ + /// Returns a FlexVector of owned values by cloning each referenced element. + pub fn cloned(&self) -> FlexVector { + FlexVector::from_vec(self.components.iter().map(|&x| x.clone()).collect()) + } +} + impl FlexVector where V: IntoIterator, @@ -161,6 +171,7 @@ where V: IntoIterator, T: Clone + 'a, { + /// Flattens a FlexVector of iterables into a single FlexVector by concatenating all elements as cloned elements. pub fn flatten_cloned(self) -> FlexVector { let components = self.components.into_iter().flat_map(|v| v.into_iter().cloned()).collect(); FlexVector { components } @@ -1178,6 +1189,33 @@ mod tests { assert_eq!(v.as_slice(), &[0, 1, 4]); } + #[test] + fn test_cloned_basic() { + let a = 1; + let b = 2; + let c = 3; + let refs = FlexVector::from_vec(vec![&a, &b, &c]); + let owned = refs.cloned(); + assert_eq!(owned.as_slice(), &[1, 2, 3]); + } + + #[test] + fn test_cloned_empty() { + let refs: FlexVector<&i32> = FlexVector::new(); + let owned = refs.cloned(); + assert!(owned.is_empty()); + } + + #[test] + fn test_cloned_complex() { + use num::Complex; + let a = Complex::new(1.0, 2.0); + let b = Complex::new(3.0, 4.0); + let refs = FlexVector::from_vec(vec![&a, &b]); + let owned = refs.cloned(); + assert_eq!(owned.as_slice(), &[a, b]); + } + #[test] fn test_flatten_flexvector_of_flexvector() { let row1 = FlexVector::from_vec(vec![1, 2]); From 0c5dd4ce40f046defbf77f38f75bc89415211116 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Sun, 18 May 2025 20:20:54 -0400 Subject: [PATCH 28/82] add FlexVector zip and zip_with methods --- src/types/flexvector.rs | 194 +++++++++++++++++++++++++++++++--------- 1 file changed, 152 insertions(+), 42 deletions(-) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index 88860b3..0785f84 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -143,41 +143,6 @@ impl FlexVector { } } -// -- more constructors -- - -impl FlexVector<&T> -where - T: Clone, -{ - /// Returns a FlexVector of owned values by cloning each referenced element. - pub fn cloned(&self) -> FlexVector { - FlexVector::from_vec(self.components.iter().map(|&x| x.clone()).collect()) - } -} - -impl FlexVector -where - V: IntoIterator, -{ - /// Flattens a FlexVector of iterables into a single FlexVector by concatenating all elements. - pub fn flatten(self) -> FlexVector { - let components = self.components.into_iter().flatten().collect(); - FlexVector { components } - } -} - -impl<'a, V, T> FlexVector -where - V: IntoIterator, - T: Clone + 'a, -{ - /// Flattens a FlexVector of iterables into a single FlexVector by concatenating all elements as cloned elements. - pub fn flatten_cloned(self) -> FlexVector { - let components = self.components.into_iter().flat_map(|v| v.into_iter().cloned()).collect(); - FlexVector { components } - } -} - // ================================ // // Default trait impl @@ -914,8 +879,8 @@ impl FlexVector { F: FnMut(T) -> U, T: Clone, { - let new_components = self.components.iter().cloned().map(&mut f).collect(); - FlexVector { components: new_components } + let components = self.components.iter().cloned().map(&mut f).collect(); + FlexVector { components } } /// Applies a closure or function to each element, modifying them in place. @@ -931,6 +896,29 @@ impl FlexVector { } } + /// Zips two FlexVectors into a FlexVector of pairs. + pub fn zip(self, other: FlexVector) -> FlexVector<(T, U)> { + let len = self.len().min(other.len()); + let components = self.components.into_iter().zip(other.components).take(len).collect(); + FlexVector { components } + } + + /// Zips two FlexVectors with a function, producing a FlexVector of the function's output. + pub fn zip_with(self, other: FlexVector, mut f: F) -> FlexVector + where + F: FnMut(T, U) -> R, + { + let len = self.len().min(other.len()); + let components = self + .components + .into_iter() + .zip(other.components) + .map(|(a, b)| f(a, b)) + .take(len) + .collect(); + FlexVector { components } + } + // ================================ // // Private methods @@ -949,6 +937,39 @@ impl FlexVector { } } +impl FlexVector<&T> +where + T: Clone, +{ + /// Returns a FlexVector of owned values by cloning each referenced element. + pub fn cloned(&self) -> FlexVector { + FlexVector::from_vec(self.components.iter().map(|&x| x.clone()).collect()) + } +} + +impl FlexVector +where + V: IntoIterator, +{ + /// Flattens a FlexVector of iterables into a single FlexVector by concatenating all elements. + pub fn flatten(self) -> FlexVector { + let components = self.components.into_iter().flatten().collect(); + FlexVector { components } + } +} + +impl<'a, V, T> FlexVector +where + V: IntoIterator, + T: Clone + 'a, +{ + /// Flattens a FlexVector of iterables into a single FlexVector by concatenating all elements as cloned elements. + pub fn flatten_cloned(self) -> FlexVector { + let components = self.components.into_iter().flat_map(|v| v.into_iter().cloned()).collect(); + FlexVector { components } + } +} + // ================================ // // Operator overload trait impl @@ -985,6 +1006,10 @@ mod tests { // Test utility functions // // ================================ + fn double(x: i32) -> i32 { + x * 2 + } + fn square(x: i32) -> i32 { x * x } @@ -2974,11 +2999,6 @@ mod tests { assert!(v.is_empty()); } - // used for function pointer test below - fn double(x: i32) -> i32 { - x * 2 - } - #[test] fn test_mut_map_with_fn_pointer() { let mut v = FlexVector::from_vec(vec![1, 2, 3]); @@ -2986,6 +3006,96 @@ mod tests { assert_eq!(v.as_slice(), &[2, 4, 6]); } + // --- zip --- + #[test] + fn test_zip_i32() { + let v1 = FlexVector::from_vec(vec![1, 2, 3]); + let v2 = FlexVector::from_vec(vec![4, 5, 6]); + let zipped = v1.zip(v2); + assert_eq!(zipped.as_slice(), &[(1, 4), (2, 5), (3, 6)]); + } + + #[test] + fn test_zip_f64() { + let v1 = FlexVector::from_vec(vec![1.1, 2.2, 3.3]); + let v2 = FlexVector::from_vec(vec![4.4, 5.5, 6.6]); + let zipped = v1.zip(v2); + assert_eq!(zipped.as_slice(), &[(1.1, 4.4), (2.2, 5.5), (3.3, 6.6)]); + } + + #[test] + fn test_zip_complex_f64() { + use num::Complex; + let v1 = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); + let zipped = v1.zip(v2); + assert_eq!( + zipped.as_slice(), + &[ + (Complex::new(1.0, 2.0), Complex::new(5.0, 6.0)), + (Complex::new(3.0, 4.0), Complex::new(7.0, 8.0)) + ] + ); + } + + #[test] + fn test_zip_different_lengths() { + let v1 = FlexVector::from_vec(vec![1, 2]); + let v2 = FlexVector::from_vec(vec![3, 4, 5]); + let zipped = v1.zip(v2); + assert_eq!(zipped.as_slice(), &[(1, 3), (2, 4)]); + } + + #[test] + fn test_zip_empty() { + let v1: FlexVector = FlexVector::new(); + let v2: FlexVector = FlexVector::new(); + let zipped = v1.zip(v2); + assert!(zipped.is_empty()); + } + + // --- zip_with --- + #[test] + fn test_zip_with_i32() { + let v1 = FlexVector::from_vec(vec![1, 2, 3]); + let v2 = FlexVector::from_vec(vec![4, 5, 6]); + let summed = v1.zip_with(v2, |a, b| a + b); + assert_eq!(summed.as_slice(), &[5, 7, 9]); + } + + #[test] + fn test_zip_with_f64() { + let v1 = FlexVector::from_vec(vec![1.5, 2.5, 3.5]); + let v2 = FlexVector::from_vec(vec![4.5, 5.5, 6.5]); + let prod = v1.zip_with(v2, |a, b| a * b); + assert_eq!(prod.as_slice(), &[6.75, 13.75, 22.75]); + } + + #[test] + fn test_zip_with_complex_f64() { + use num::Complex; + let v1 = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); + let sum = v1.zip_with(v2, |a, b| a + b); + assert_eq!(sum.as_slice(), &[Complex::new(6.0, 8.0), Complex::new(10.0, 12.0)]); + } + + #[test] + fn test_zip_with_different_lengths() { + let v1 = FlexVector::from_vec(vec![1, 2]); + let v2 = FlexVector::from_vec(vec![10, 20, 30]); + let zipped = v1.zip_with(v2, |a, b| a + b); + assert_eq!(zipped.as_slice(), &[11, 22]); + } + + #[test] + fn test_zip_with_empty() { + let v1: FlexVector = FlexVector::new(); + let v2: FlexVector = FlexVector::new(); + let zipped = v1.zip_with(v2, |a, b| a + b); + assert!(zipped.is_empty()); + } + // ================================ // // VectorOps trait tests From bc422dccb0ce812923ce440fe43f3cf8f097f99d Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Sun, 18 May 2025 20:59:43 -0400 Subject: [PATCH 29/82] inline new functions --- src/types/flexvector.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index 0785f84..2f797b4 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -897,6 +897,7 @@ impl FlexVector { } /// Zips two FlexVectors into a FlexVector of pairs. + #[inline] pub fn zip(self, other: FlexVector) -> FlexVector<(T, U)> { let len = self.len().min(other.len()); let components = self.components.into_iter().zip(other.components).take(len).collect(); @@ -904,6 +905,7 @@ impl FlexVector { } /// Zips two FlexVectors with a function, producing a FlexVector of the function's output. + #[inline] pub fn zip_with(self, other: FlexVector, mut f: F) -> FlexVector where F: FnMut(T, U) -> R, @@ -942,6 +944,7 @@ where T: Clone, { /// Returns a FlexVector of owned values by cloning each referenced element. + #[inline] pub fn cloned(&self) -> FlexVector { FlexVector::from_vec(self.components.iter().map(|&x| x.clone()).collect()) } @@ -952,6 +955,7 @@ where V: IntoIterator, { /// Flattens a FlexVector of iterables into a single FlexVector by concatenating all elements. + #[inline] pub fn flatten(self) -> FlexVector { let components = self.components.into_iter().flatten().collect(); FlexVector { components } @@ -964,6 +968,7 @@ where T: Clone + 'a, { /// Flattens a FlexVector of iterables into a single FlexVector by concatenating all elements as cloned elements. + #[inline] pub fn flatten_cloned(self) -> FlexVector { let components = self.components.into_iter().flat_map(|v| v.into_iter().cloned()).collect(); FlexVector { components } From 1892b586014d57aabb43d0d5e161a6b361f5f6ce Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Sun, 18 May 2025 21:19:05 -0400 Subject: [PATCH 30/82] add FlexVector flat_map method --- src/types/flexvector.rs | 102 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index 2f797b4..1067fbd 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -896,6 +896,17 @@ impl FlexVector { } } + /// Returns a new FlexVector by mapping each element to an iterator and flattening the result. + #[inline] + pub fn flat_map(self, mut f: F) -> FlexVector + where + F: FnMut(T) -> I, + I: IntoIterator, + { + let components = self.components.into_iter().flat_map(&mut f).collect(); + FlexVector { components } + } + /// Zips two FlexVectors into a FlexVector of pairs. #[inline] pub fn zip(self, other: FlexVector) -> FlexVector<(T, U)> { @@ -3011,6 +3022,97 @@ mod tests { assert_eq!(v.as_slice(), &[2, 4, 6]); } + // --- flat_map --- + #[test] + fn test_flat_map_i32_basic() { + let v = FlexVector::from_vec(vec![1, 2, 3]); + let flat = v.flat_map(|x| vec![x, x * 10]); + assert_eq!(flat.as_slice(), &[1, 10, 2, 20, 3, 30]); + } + + #[test] + fn test_flat_map_i32_empty_inner() { + let v = FlexVector::from_vec(vec![1, 2, 3]); + let flat = v.flat_map(|x| if x % 2 == 0 { vec![] } else { vec![x] }); + assert_eq!(flat.as_slice(), &[1, 3]); + } + + #[test] + fn test_flat_map_i32_option() { + let v = FlexVector::from_vec(vec![1, 2, 3, 4]); + let flat = v.flat_map(|x| if x % 2 == 0 { Some(x) } else { None }); + assert_eq!(flat.as_slice(), &[2, 4]); + } + + #[test] + fn test_flat_map_f64_basic() { + let v = FlexVector::from_vec(vec![1.0, 2.0]); + let flat = v.flat_map(|x| vec![x, x + 0.5]); + assert_eq!(flat.as_slice(), &[1.0, 1.5, 2.0, 2.5]); + } + + #[test] + fn test_flat_map_f64_empty() { + let v: FlexVector = FlexVector::new(); + let flat = v.flat_map(|x| vec![x, x + 1.0]); + assert!(flat.is_empty()); + } + + #[test] + fn test_flat_map_f64_nan_infinity() { + let v = FlexVector::from_vec(vec![f64::NAN, f64::INFINITY, 1.0]); + let flat = v.flat_map(|x| vec![x]); + assert!(flat.as_slice()[0].is_nan()); + assert_eq!(flat.as_slice()[1], f64::INFINITY); + assert_eq!(flat.as_slice()[2], 1.0); + } + + #[test] + fn test_flat_map_complex_f64_basic() { + use num::Complex; + let v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let flat = v.flat_map(|z| vec![z, z.conj()]); + assert_eq!( + flat.as_slice(), + &[ + Complex::new(1.0, 2.0), + Complex::new(1.0, -2.0), + Complex::new(3.0, 4.0), + Complex::new(3.0, -4.0) + ] + ); + } + + #[test] + fn test_flat_map_complex_f64_empty_inner() { + use num::Complex; + let v = FlexVector::from_vec(vec![Complex::new(0.0, 0.0), Complex::new(1.0, 1.0)]); + let flat = v.flat_map(|z| if z == Complex::new(0.0, 0.0) { vec![] } else { vec![z] }); + assert_eq!(flat.as_slice(), &[Complex::new(1.0, 1.0)]); + } + + #[test] + fn test_flat_map_complex_f64_option() { + use num::Complex; + let v = FlexVector::from_vec(vec![Complex::new(1.0, 0.0), Complex::new(0.0, 0.0)]); + let flat = v.flat_map(|z| if z.im == 0.0 { Some(z) } else { None }); + assert_eq!(flat.as_slice(), &[Complex::new(1.0, 0.0), Complex::new(0.0, 0.0)]); + } + + #[test] + fn test_flat_map_empty_outer() { + let v: FlexVector = FlexVector::new(); + let flat = v.flat_map(|x| vec![x]); + assert!(flat.is_empty()); + } + + #[test] + fn test_flat_map_all_empty_inner() { + let v = FlexVector::from_vec(vec![1, 2, 3]); + let flat = v.flat_map(|_| Vec::::new()); + assert!(flat.is_empty()); + } + // --- zip --- #[test] fn test_zip_i32() { From 81cdb439c6fa4f3560e73c6029eabde492b484ab Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Sun, 18 May 2025 22:00:42 -0400 Subject: [PATCH 31/82] Add FlexVector step_by method --- src/types/flexvector.rs | 108 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index 1067fbd..ec981bf 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -932,6 +932,20 @@ impl FlexVector { FlexVector { components } } + /// Returns a new FlexVector containing every `step`th element, starting from the first. + /// Returns an error if step == 0. + #[inline] + pub fn step_by(&self, step: usize) -> Result, VectorError> + where + T: Clone, + { + if step == 0 { + return Err(VectorError::ValueError("step must be non-zero".to_string())); + } + let components = self.components.iter().step_by(step).cloned().collect(); + Ok(FlexVector { components }) + } + // ================================ // // Private methods @@ -3203,6 +3217,100 @@ mod tests { assert!(zipped.is_empty()); } + // --- step_by --- + + #[test] + fn test_step_by_i32_basic() { + let v = FlexVector::from_vec(vec![1, 2, 3, 4, 5, 6]); + let stepped = v.step_by(2).unwrap(); + assert_eq!(stepped.as_slice(), &[1, 3, 5]); + } + + #[test] + fn test_step_by_i32_step_one() { + let v = FlexVector::from_vec(vec![10, 20, 30]); + let stepped = v.step_by(1).unwrap(); + assert_eq!(stepped.as_slice(), &[10, 20, 30]); + } + + #[test] + fn test_step_by_i32_step_equals_len() { + let v = FlexVector::from_vec(vec![7, 8, 9]); + let stepped = v.step_by(3).unwrap(); + assert_eq!(stepped.as_slice(), &[7]); + } + + #[test] + fn test_step_by_i32_step_greater_than_len() { + let v = FlexVector::from_vec(vec![1, 2, 3]); + let stepped = v.step_by(5).unwrap(); + assert_eq!(stepped.as_slice(), &[1]); + } + + #[test] + fn test_step_by_i32_empty() { + let v: FlexVector = FlexVector::new(); + let stepped = v.step_by(2).unwrap(); + assert!(stepped.is_empty()); + } + + #[test] + fn test_step_by_i32_zero_step() { + let v = FlexVector::from_vec(vec![1, 2, 3]); + let result = v.step_by(0); + assert!(result.is_err()); + } + + #[test] + fn test_step_by_f64_basic() { + let v = FlexVector::from_vec(vec![1.0, 2.0, 3.0, 4.0]); + let stepped = v.step_by(2).unwrap(); + assert_eq!(stepped.as_slice(), &[1.0, 3.0]); + } + + #[test] + fn test_step_by_f64_empty() { + let v: FlexVector = FlexVector::new(); + let stepped = v.step_by(3).unwrap(); + assert!(stepped.is_empty()); + } + + #[test] + fn test_step_by_f64_zero_step() { + let v = FlexVector::from_vec(vec![1.1, 2.2]); + let result = v.step_by(0); + assert!(result.is_err()); + } + + #[test] + fn test_step_by_complex_f64_basic() { + use num::Complex; + let v = FlexVector::from_vec(vec![ + Complex::new(1.0, 2.0), + Complex::new(3.0, 4.0), + Complex::new(5.0, 6.0), + Complex::new(7.0, 8.0), + ]); + let stepped = v.step_by(2).unwrap(); + assert_eq!(stepped.as_slice(), &[Complex::new(1.0, 2.0), Complex::new(5.0, 6.0)]); + } + + #[test] + fn test_step_by_complex_f64_empty() { + use num::Complex; + let v: FlexVector> = FlexVector::new(); + let stepped = v.step_by(2).unwrap(); + assert!(stepped.is_empty()); + } + + #[test] + fn test_step_by_complex_f64_zero_step() { + use num::Complex; + let v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0)]); + let result = v.step_by(0); + assert!(result.is_err()); + } + // ================================ // // VectorOps trait tests From 1fde5a9416bbcce76d68ff11d9621aa054ed9597 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Sun, 18 May 2025 22:13:52 -0400 Subject: [PATCH 32/82] Add FlexVector Borrow & BorrowMut trait impl --- src/types/flexvector.rs | 84 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index ec981bf..69aba94 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -1,5 +1,6 @@ //! FlexVector type. +use std::borrow::{Borrow, BorrowMut}; use std::iter::FromIterator; use std::ops::{Deref, DerefMut}; @@ -218,6 +219,22 @@ impl AsMut<[T]> for FlexVector { } } +// ================================ +// +// Borrow/BorrowMut trait impl +// +// ================================ +impl Borrow<[T]> for FlexVector { + fn borrow(&self) -> &[T] { + &self.components + } +} +impl BorrowMut<[T]> for FlexVector { + fn borrow_mut(&mut self) -> &mut [T] { + &mut self.components + } +} + // ================================ // // IntoIterator trait impl @@ -1637,6 +1654,73 @@ mod tests { assert_eq!(v.as_slice(), &[Complex::new(10.0, 2.0), Complex::new(3.0, 40.0)]); } + // ================================ + // + // Borrow/BorrowMut trait method tests + // + // ================================ + + #[test] + fn test_borrow_i32() { + use std::borrow::Borrow; + let v = FlexVector::from_vec(vec![1, 2, 3]); + let slice: &[i32] = v.borrow(); + assert_eq!(slice, &[1, 2, 3]); + } + + #[test] + fn test_borrow_f64() { + use std::borrow::Borrow; + let v = FlexVector::from_vec(vec![1.1, 2.2, 3.3]); + let slice: &[f64] = v.borrow(); + assert_eq!(slice, &[1.1, 2.2, 3.3]); + } + + #[test] + fn test_borrow_complex_f64() { + use num::Complex; + use std::borrow::Borrow; + let v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let slice: &[Complex] = v.borrow(); + assert_eq!(slice, &[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + } + + #[test] + fn test_borrow_mut_i32() { + use std::borrow::BorrowMut; + let mut v = FlexVector::from_vec(vec![1, 2, 3]); + { + let slice: &mut [i32] = v.borrow_mut(); + slice[0] = 10; + slice[2] = 30; + } + assert_eq!(v.as_slice(), &[10, 2, 30]); + } + + #[test] + fn test_borrow_mut_f64() { + use std::borrow::BorrowMut; + let mut v = FlexVector::from_vec(vec![1.1, 2.2, 3.3]); + { + let slice: &mut [f64] = v.borrow_mut(); + slice[1] = 9.9; + } + assert_eq!(v.as_slice(), &[1.1, 9.9, 3.3]); + } + + #[test] + fn test_borrow_mut_complex_f64() { + use num::Complex; + use std::borrow::BorrowMut; + let mut v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + { + let slice: &mut [Complex] = v.borrow_mut(); + slice[0].re = 10.0; + slice[1].im = 40.0; + } + assert_eq!(v.as_slice(), &[Complex::new(10.0, 2.0), Complex::new(3.0, 40.0)]); + } + // ================================ // // IntoIterator trait method tests From 4fec35f67be1a98e3eda6f1018d5d19cd95f17f7 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Mon, 19 May 2025 01:00:51 -0400 Subject: [PATCH 33/82] add FlexVector Index, IndexMut trait impl, including Range support --- src/types/flexvector.rs | 274 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 273 insertions(+), 1 deletion(-) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index 69aba94..e9c8dd3 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -2,7 +2,10 @@ use std::borrow::{Borrow, BorrowMut}; use std::iter::FromIterator; -use std::ops::{Deref, DerefMut}; +use std::ops::{ + Deref, DerefMut, Index, IndexMut, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, + RangeToInclusive, +}; use crate::{ impl_vector_binop, impl_vector_binop_assign, impl_vector_binop_div, @@ -334,6 +337,107 @@ impl From<&[T]> for FlexVector { } } +// ================================ +// +// Index trait impl +// +// ================================ +impl Index for FlexVector { + type Output = T; + fn index(&self, idx: usize) -> &Self::Output { + &self.components[idx] + } +} + +impl Index> for FlexVector { + type Output = [T]; + fn index(&self, range: Range) -> &Self::Output { + &self.components[range] + } +} + +impl Index> for FlexVector { + type Output = [T]; + fn index(&self, range: RangeFrom) -> &Self::Output { + &self.components[range] + } +} + +impl Index> for FlexVector { + type Output = [T]; + fn index(&self, range: RangeTo) -> &Self::Output { + &self.components[range] + } +} + +impl Index for FlexVector { + type Output = [T]; + fn index(&self, range: RangeFull) -> &Self::Output { + &self.components[range] + } +} + +impl Index> for FlexVector { + type Output = [T]; + fn index(&self, range: RangeInclusive) -> &Self::Output { + &self.components[range] + } +} + +impl Index> for FlexVector { + type Output = [T]; + fn index(&self, range: RangeToInclusive) -> &Self::Output { + &self.components[range] + } +} + +// ================================ +// +// IndexMut trait impl +// +// ================================ +impl IndexMut for FlexVector { + fn index_mut(&mut self, idx: usize) -> &mut Self::Output { + &mut self.components[idx] + } +} + +impl IndexMut> for FlexVector { + fn index_mut(&mut self, range: Range) -> &mut Self::Output { + &mut self.components[range] + } +} + +impl IndexMut> for FlexVector { + fn index_mut(&mut self, range: RangeFrom) -> &mut Self::Output { + &mut self.components[range] + } +} + +impl IndexMut> for FlexVector { + fn index_mut(&mut self, range: RangeTo) -> &mut Self::Output { + &mut self.components[range] + } +} + +impl IndexMut for FlexVector { + fn index_mut(&mut self, range: RangeFull) -> &mut Self::Output { + &mut self.components[range] + } +} + +impl IndexMut> for FlexVector { + fn index_mut(&mut self, range: RangeInclusive) -> &mut Self::Output { + &mut self.components[range] + } +} + +impl IndexMut> for FlexVector { + fn index_mut(&mut self, range: RangeToInclusive) -> &mut Self::Output { + &mut self.components[range] + } +} + // ================================ // // Extend trait impl @@ -2113,6 +2217,174 @@ mod tests { assert_eq!(fv.as_slice(), slice); } + // ================================ + // + // Index trait tests + // + // ================================ + + #[test] + fn test_index_usize() { + let v = FlexVector::from_vec(vec![10, 20, 30, 40]); + assert_eq!(v[0], 10); + assert_eq!(v[1], 20); + assert_eq!(v[3], 40); + } + + #[test] + #[should_panic] + fn test_index_usize_out_of_bounds() { + let v = FlexVector::from_vec(vec![1, 2, 3]); + let _ = v[10]; + } + + #[test] + fn test_index_range() { + let v = FlexVector::from_vec(vec![1, 2, 3, 4, 5]); + assert_eq!(&v[1..4], &[2, 3, 4]); + assert_eq!(&v[0..2], &[1, 2]); + assert_eq!(&v[2..5], &[3, 4, 5]); + } + + #[test] + fn test_index_range_from() { + let v = FlexVector::from_vec(vec![1, 2, 3, 4, 5]); + assert_eq!(&v[2..], &[3, 4, 5]); + assert_eq!(&v[0..], &[1, 2, 3, 4, 5]); + } + + #[test] + fn test_index_range_to() { + let v = FlexVector::from_vec(vec![1, 2, 3, 4, 5]); + assert_eq!(&v[..3], &[1, 2, 3]); + assert_eq!(&v[..1], &[1]); + } + + #[test] + fn test_index_range_full() { + let v = FlexVector::from_vec(vec![1, 2, 3]); + assert_eq!(&v[..], &[1, 2, 3]); + } + + #[test] + fn test_index_range_inclusive() { + let v = FlexVector::from_vec(vec![1, 2, 3, 4, 5]); + assert_eq!(&v[1..=3], &[2, 3, 4]); + assert_eq!(&v[0..=4], &[1, 2, 3, 4, 5]); + } + + #[test] + fn test_index_range_to_inclusive() { + let v = FlexVector::from_vec(vec![1, 2, 3, 4, 5]); + assert_eq!(&v[..=2], &[1, 2, 3]); + assert_eq!(&v[..=0], &[1]); + } + + #[test] + #[should_panic] + fn test_index_range_out_of_bounds() { + let v = FlexVector::from_vec(vec![1, 2, 3]); + let _ = &v[2..5]; + } + + #[test] + #[should_panic] + fn test_index_range_inclusive_out_of_bounds() { + let v = FlexVector::from_vec(vec![1, 2, 3]); + let _ = &v[1..=5]; + } + + #[test] + #[should_panic] + fn test_index_range_to_inclusive_out_of_bounds() { + let v = FlexVector::from_vec(vec![1, 2, 3]); + let _ = &v[..=5]; + } + + // ================================ + // + // IndexMut trait tests + // + // ================================ + + #[test] + fn test_index_mut_usize() { + let mut v = FlexVector::from_vec(vec![1, 2, 3]); + v[0] = 10; + v[2] = 30; + assert_eq!(v.as_slice(), &[10, 2, 30]); + } + + #[test] + #[should_panic] + fn test_index_mut_usize_out_of_bounds() { + let mut v = FlexVector::from_vec(vec![1, 2, 3]); + v[10] = 99; + } + + #[test] + fn test_index_mut_range() { + let mut v = FlexVector::from_vec(vec![1, 2, 3, 4, 5]); + v[1..4].copy_from_slice(&[20, 30, 40]); + assert_eq!(v.as_slice(), &[1, 20, 30, 40, 5]); + } + + #[test] + fn test_index_mut_range_from() { + let mut v = FlexVector::from_vec(vec![1, 2, 3, 4, 5]); + v[2..].copy_from_slice(&[99, 100, 101]); + assert_eq!(v.as_slice(), &[1, 2, 99, 100, 101]); + } + + #[test] + fn test_index_mut_range_to() { + let mut v = FlexVector::from_vec(vec![1, 2, 3, 4, 5]); + v[..3].copy_from_slice(&[7, 8, 9]); + assert_eq!(v.as_slice(), &[7, 8, 9, 4, 5]); + } + + #[test] + fn test_index_mut_range_full() { + let mut v = FlexVector::from_vec(vec![1, 2, 3]); + v[..].copy_from_slice(&[10, 20, 30]); + assert_eq!(v.as_slice(), &[10, 20, 30]); + } + + #[test] + fn test_index_mut_range_inclusive() { + let mut v = FlexVector::from_vec(vec![1, 2, 3, 4, 5]); + v[1..=3].copy_from_slice(&[21, 31, 41]); + assert_eq!(v.as_slice(), &[1, 21, 31, 41, 5]); + } + + #[test] + fn test_index_mut_range_to_inclusive() { + let mut v = FlexVector::from_vec(vec![1, 2, 3, 4, 5]); + v[..=2].copy_from_slice(&[100, 200, 300]); + assert_eq!(v.as_slice(), &[100, 200, 300, 4, 5]); + } + + #[test] + #[should_panic] + fn test_index_mut_range_out_of_bounds() { + let mut v = FlexVector::from_vec(vec![1, 2, 3]); + v[2..5].copy_from_slice(&[9, 9, 9]); + } + + #[test] + #[should_panic] + fn test_index_mut_range_inclusive_out_of_bounds() { + let mut v = FlexVector::from_vec(vec![1, 2, 3]); + v[1..=5].copy_from_slice(&[9, 9, 9, 9, 9]); + } + + #[test] + #[should_panic] + fn test_index_mut_range_to_inclusive_out_of_bounds() { + let mut v = FlexVector::from_vec(vec![1, 2, 3]); + v[..=5].copy_from_slice(&[9, 9, 9, 9, 9, 9]); + } + // ================================ // // Extend trait tests From c75a4cb8ba2e034e377df0954b04be03624140ee Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Mon, 19 May 2025 08:35:16 -0400 Subject: [PATCH 34/82] add FlexVector repeat_pattern function --- src/types/flexvector.rs | 108 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index e9c8dd3..0906627 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -99,6 +99,7 @@ impl FlexVector { } /// Fallible error-propagating construction from an iterator of Results. + #[inline] pub fn try_from_iter(iter: I) -> Result where I: IntoIterator>, @@ -116,6 +117,7 @@ impl FlexVector { /// let v = FlexVector::from_fn(4, |i| i * i); /// assert_eq!(v.as_slice(), &[0, 1, 4, 9]); /// ``` + #[inline] pub fn from_fn(len: usize, f: F) -> Self where F: FnMut(usize) -> T, @@ -135,6 +137,7 @@ impl FlexVector { /// let v = FlexVector::::try_from_fn(3, |i| if i == 1 { Err("fail") } else { Ok(i as i32) }); /// assert!(v.is_err()); /// ``` + #[inline] pub fn try_from_fn(len: usize, mut f: F) -> Result where F: FnMut(usize) -> Result, @@ -145,6 +148,22 @@ impl FlexVector { } Ok(FlexVector { components }) } + + /// Returns a new FlexVector by repeating the pattern until length `len` is reached. + /// Returns an error if `pattern` is empty and `len` > 0. + #[inline] + pub fn repeat_pattern(pattern: &[T], len: usize) -> Result + where + T: Clone, + { + if pattern.is_empty() && len > 0 { + return Err(VectorError::ValueError( + "pattern must not be empty if len > 0".to_string(), + )); + } + let components = pattern.iter().cloned().cycle().take(len).collect(); + Ok(FlexVector { components }) + } } // ================================ @@ -1365,6 +1384,95 @@ mod tests { assert_eq!(v.as_slice(), &[0, 1, 4]); } + #[test] + fn test_repeat_pattern_i32_basic() { + let pattern = [1, 2, 3]; + let v = FlexVector::repeat_pattern(&pattern, 8).unwrap(); + assert_eq!(v.as_slice(), &[1, 2, 3, 1, 2, 3, 1, 2]); + } + + #[test] + fn test_repeat_pattern_i32_exact_multiple() { + let pattern = [4, 5]; + let v = FlexVector::repeat_pattern(&pattern, 6).unwrap(); + assert_eq!(v.as_slice(), &[4, 5, 4, 5, 4, 5]); + } + + #[test] + fn test_repeat_pattern_i32_len_zero() { + let pattern = [1, 2, 3]; + let v = FlexVector::repeat_pattern(&pattern, 0).unwrap(); + assert!(v.is_empty()); + } + + #[test] + fn test_repeat_pattern_i32_pattern_empty_len_zero() { + let pattern: [i32; 0] = []; + let v = FlexVector::repeat_pattern(&pattern, 0).unwrap(); + assert!(v.is_empty()); + } + + #[test] + fn test_repeat_pattern_i32_pattern_empty_len_nonzero() { + let pattern: [i32; 0] = []; + let result = FlexVector::repeat_pattern(&pattern, 3); + assert!(result.is_err()); + } + + #[test] + fn test_repeat_pattern_f64_basic() { + let pattern = [1.5, 2.5]; + let v = FlexVector::repeat_pattern(&pattern, 5).unwrap(); + assert_eq!(v.as_slice(), &[1.5, 2.5, 1.5, 2.5, 1.5]); + } + + #[test] + fn test_repeat_pattern_f64_empty_pattern_len_zero() { + let pattern: [f64; 0] = []; + let v = FlexVector::repeat_pattern(&pattern, 0).unwrap(); + assert!(v.is_empty()); + } + + #[test] + fn test_repeat_pattern_f64_empty_pattern_len_nonzero() { + let pattern: [f64; 0] = []; + let result = FlexVector::repeat_pattern(&pattern, 2); + assert!(result.is_err()); + } + + #[test] + fn test_repeat_pattern_complex_f64_basic() { + use num::Complex; + let pattern = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; + let v = FlexVector::repeat_pattern(&pattern, 5).unwrap(); + assert_eq!( + v.as_slice(), + &[ + Complex::new(1.0, 2.0), + Complex::new(3.0, 4.0), + Complex::new(1.0, 2.0), + Complex::new(3.0, 4.0), + Complex::new(1.0, 2.0) + ] + ); + } + + #[test] + fn test_repeat_pattern_complex_f64_empty_pattern_len_zero() { + use num::Complex; + let pattern: [Complex; 0] = []; + let v = FlexVector::repeat_pattern(&pattern, 0).unwrap(); + assert!(v.is_empty()); + } + + #[test] + fn test_repeat_pattern_complex_f64_empty_pattern_len_nonzero() { + use num::Complex; + let pattern: [Complex; 0] = []; + let result = FlexVector::repeat_pattern(&pattern, 1); + assert!(result.is_err()); + } + #[test] fn test_cloned_basic() { let a = 1; From 0c67583657235aa9444b3d579dacef5fedede341 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Mon, 19 May 2025 14:12:07 -0400 Subject: [PATCH 35/82] add FlexVector create_mask, filter_by_mask --- src/types/flexvector.rs | 221 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 221 insertions(+) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index 0906627..452cda3 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -1086,6 +1086,35 @@ impl FlexVector { Ok(FlexVector { components }) } + /// Returns a boolean vector mask where each element is the result of applying the predicate to the corresponding element. + #[inline] + pub fn create_mask(&self, predicate: F) -> FlexVector + where + F: FnMut(&T) -> bool, + { + self.components.iter().map(predicate).collect() + } + + /// Returns a new FlexVector containing elements where the corresponding mask value is true. + #[inline] + pub fn filter_by_mask(&self, mask: &[bool]) -> Result, VectorError> + where + T: Clone, + { + if self.len() != mask.len() { + return Err(VectorError::MismatchedLengthError( + "Mask length must match vector length".to_string(), + )); + } + let components = self + .components + .iter() + .zip(mask) + .filter_map(|(x, &m)| if m { Some(x.clone()) } else { None }) + .collect(); + Ok(FlexVector { components }) + } + // ================================ // // Private methods @@ -3775,6 +3804,198 @@ mod tests { assert!(result.is_err()); } + // -- create_mask -- + + #[test] + fn test_create_mask_i32_greater_than() { + let v = FlexVector::from_vec(vec![1, 5, 3, 7]); + let mask = v.create_mask(|&x| x > 3); + assert_eq!(mask.as_slice(), &[false, true, false, true]); + } + + #[test] + fn test_create_mask_i32_even() { + let v = FlexVector::from_vec(vec![2, 3, 4, 5]); + let mask = v.create_mask(|&x| x % 2 == 0); + assert_eq!(mask.as_slice(), &[true, false, true, false]); + } + + #[test] + fn test_create_mask_f64_positive() { + let v = FlexVector::from_vec(vec![-1.0, 0.0, 2.5, -3.3]); + let mask = v.create_mask(|&x| x > 0.0); + assert_eq!(mask.as_slice(), &[false, false, true, false]); + } + + #[test] + fn test_create_mask_f64_nan() { + let v = FlexVector::from_vec(vec![1.0, f64::NAN, 2.0]); + let mask = v.create_mask(|&x| x.is_nan()); + assert_eq!(mask.as_slice(), &[false, true, false]); + } + + #[test] + fn test_create_mask_complex_f64_real_positive() { + use num::Complex; + let v = FlexVector::from_vec(vec![ + Complex::new(1.0, 2.0), + Complex::new(-3.0, 4.0), + Complex::new(0.0, 0.0), + ]); + let mask = v.create_mask(|z| z.re > 0.0); + assert_eq!(mask.as_slice(), &[true, false, false]); + } + + #[test] + fn test_create_mask_complex_f64_imag_nonzero() { + use num::Complex; + let v = FlexVector::from_vec(vec![ + Complex::new(1.0, 0.0), + Complex::new(0.0, 2.0), + Complex::new(3.0, 0.0), + ]); + let mask = v.create_mask(|z| z.im != 0.0); + assert_eq!(mask.as_slice(), &[false, true, false]); + } + + #[test] + fn test_create_mask_empty() { + let v: FlexVector = FlexVector::new(); + let mask = v.create_mask(|&x| x > 0); + assert!(mask.is_empty()); + } + + // -- filter_by_mask -- + + #[test] + fn test_filter_by_mask_i32_basic() { + let v = FlexVector::from_vec(vec![10, 20, 30, 40]); + let mask = vec![false, true, false, true]; + let filtered = v.filter_by_mask(&mask).unwrap(); + assert_eq!(filtered.as_slice(), &[20, 40]); + } + + #[test] + fn test_filter_by_mask_i32_all_true() { + let v = FlexVector::from_vec(vec![1, 2, 3]); + let mask = vec![true, true, true]; + let filtered = v.filter_by_mask(&mask).unwrap(); + assert_eq!(filtered.as_slice(), &[1, 2, 3]); + } + + #[test] + fn test_filter_by_mask_i32_all_false() { + let v = FlexVector::from_vec(vec![1, 2, 3]); + let mask = vec![false, false, false]; + let filtered = v.filter_by_mask(&mask).unwrap(); + assert!(filtered.is_empty()); + } + + #[test] + fn test_filter_by_mask_i32_empty() { + let v: FlexVector = FlexVector::new(); + let mask: Vec = vec![]; + let filtered = v.filter_by_mask(&mask).unwrap(); + assert!(filtered.is_empty()); + } + + #[test] + fn test_filter_by_mask_i32_mismatched_length() { + let v = FlexVector::from_vec(vec![1, 2, 3]); + let mask = vec![true, false]; + let result = v.filter_by_mask(&mask); + assert!(result.is_err()); + } + + #[test] + fn test_filter_by_mask_f64_basic() { + let v = FlexVector::from_vec(vec![1.1, 2.2, 3.3, 4.4]); + let mask = vec![true, false, true, false]; + let filtered = v.filter_by_mask(&mask).unwrap(); + assert_eq!(filtered.as_slice(), &[1.1, 3.3]); + } + + #[test] + fn test_filter_by_mask_f64_all_true() { + let v = FlexVector::from_vec(vec![1.1, 2.2]); + let mask = vec![true, true]; + let filtered = v.filter_by_mask(&mask).unwrap(); + assert_eq!(filtered.as_slice(), &[1.1, 2.2]); + } + + #[test] + fn test_filter_by_mask_f64_all_false() { + let v = FlexVector::from_vec(vec![1.1, 2.2]); + let mask = vec![false, false]; + let filtered = v.filter_by_mask(&mask).unwrap(); + assert!(filtered.is_empty()); + } + + #[test] + fn test_filter_by_mask_f64_empty() { + let v: FlexVector = FlexVector::new(); + let mask: Vec = vec![]; + let filtered = v.filter_by_mask(&mask).unwrap(); + assert!(filtered.is_empty()); + } + + #[test] + fn test_filter_by_mask_f64_mismatched_length() { + let v = FlexVector::from_vec(vec![1.1, 2.2]); + let mask = vec![true]; + let result = v.filter_by_mask(&mask); + assert!(result.is_err()); + } + + #[test] + fn test_filter_by_mask_complex_f64_basic() { + use num::Complex; + let v = FlexVector::from_vec(vec![ + Complex::new(1.0, 2.0), + Complex::new(3.0, 4.0), + Complex::new(5.0, 6.0), + ]); + let mask = vec![false, true, true]; + let filtered = v.filter_by_mask(&mask).unwrap(); + assert_eq!(filtered.as_slice(), &[Complex::new(3.0, 4.0), Complex::new(5.0, 6.0)]); + } + + #[test] + fn test_filter_by_mask_complex_f64_all_true() { + use num::Complex; + let v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let mask = vec![true, true]; + let filtered = v.filter_by_mask(&mask).unwrap(); + assert_eq!(filtered.as_slice(), &[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + } + + #[test] + fn test_filter_by_mask_complex_f64_all_false() { + use num::Complex; + let v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let mask = vec![false, false]; + let filtered = v.filter_by_mask(&mask).unwrap(); + assert!(filtered.is_empty()); + } + + #[test] + fn test_filter_by_mask_complex_f64_empty() { + use num::Complex; + let v: FlexVector> = FlexVector::new(); + let mask: Vec = vec![]; + let filtered = v.filter_by_mask(&mask).unwrap(); + assert!(filtered.is_empty()); + } + + #[test] + fn test_filter_by_mask_complex_f64_mismatched_length() { + use num::Complex; + let v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0)]); + let mask = vec![true, false]; + let result = v.filter_by_mask(&mask); + assert!(result.is_err()); + } + // ================================ // // VectorOps trait tests From 9d79ef3e0024c88d94b501eeea8d8c72f2a08e78 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Mon, 19 May 2025 15:21:15 -0400 Subject: [PATCH 36/82] update trait bounds on VectorOps trait functions --- src/types/flexvector.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index 452cda3..8ac922d 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -495,12 +495,15 @@ impl VectorBase for FlexVector { // ================================ impl VectorOps for FlexVector where - T: num::Num + Clone + Copy, + T: Clone, { type Output = Self; #[inline] - fn translate(&self, other: &Self) -> Result { + fn translate(&self, other: &Self) -> Result + where + T: num::Num + Copy, + { self.check_same_length_and_raise(other)?; let mut out = FlexVector::zero(self.len()); translate_impl(self.as_slice(), other.as_slice(), out.as_mut_slice()); @@ -508,7 +511,10 @@ where } #[inline] - fn mut_translate(&mut self, other: &Self) -> Result<(), VectorError> { + fn mut_translate(&mut self, other: &Self) -> Result<(), VectorError> + where + T: num::Num + Copy, + { self.check_same_length_and_raise(other)?; mut_translate_impl(self.as_mut_slice(), other.as_slice()); Ok(()) @@ -517,10 +523,10 @@ where #[inline] fn scale(&self, scalar: T) -> Self::Output where - T: num::Num + Copy, + T: num::Num + Clone, Self::Output: std::iter::FromIterator, { - self.as_slice().iter().map(|a| *a * scalar).collect() + self.as_slice().iter().map(|a| a.clone() * scalar.clone()).collect() } #[inline] @@ -529,7 +535,7 @@ where T: std::ops::Neg + Clone, Self::Output: std::iter::FromIterator, { - self.as_slice().iter().map(|a| -*a).collect() + self.as_slice().iter().map(|a| -(a.clone())).collect() } #[inline] From 4342d1f8f587cf00916e2114bd84de6d5efdf5e2 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Mon, 19 May 2025 15:58:40 -0400 Subject: [PATCH 37/82] Add VectorOps trait elementwise_min and elementwise_max functions, impl on FlexVector --- src/types/flexvector.rs | 136 ++++++++++++++++++++++++++++++++++++++++ src/types/traits.rs | 10 +++ 2 files changed, 146 insertions(+) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index 8ac922d..3d9dcef 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -573,6 +573,38 @@ where let result = cross_impl(a, b); Ok(result.into_iter().collect()) } + + /// Element-wise min + #[inline] + fn elementwise_min(&self, other: &Self) -> Result + where + T: PartialOrd + Clone, + { + self.check_same_length_and_raise(other)?; + let components = self + .as_slice() + .iter() + .zip(other.as_slice()) + .map(|(a, b)| if a < b { a.clone() } else { b.clone() }) + .collect(); + Ok(FlexVector { components }) + } + + /// Element-wise max + #[inline] + fn elementwise_max(&self, other: &Self) -> Result + where + T: PartialOrd + Clone, + { + self.check_same_length_and_raise(other)?; + let components = self + .as_slice() + .iter() + .zip(other.as_slice()) + .map(|(a, b)| if a > b { a.clone() } else { b.clone() }) + .collect(); + Ok(FlexVector { components }) + } } // ================================ @@ -4408,6 +4440,58 @@ mod tests { assert_eq!(v.minimum(), None); } + // -- elementwise_min -- + + #[test] + fn test_elementwise_min_i32() { + let v1 = FlexVector::from_vec(vec![1, 5, 3, 7]); + let v2 = FlexVector::from_vec(vec![2, 4, 6, 0]); + let min = v1.elementwise_min(&v2).unwrap(); + assert_eq!(min.as_slice(), &[1, 4, 3, 0]); + } + + #[test] + fn test_elementwise_min_f64() { + let v1 = FlexVector::from_vec(vec![1.5, -2.0, 3.0]); + let v2 = FlexVector::from_vec(vec![2.5, -3.0, 2.0]); + let min = v1.elementwise_min(&v2).unwrap(); + assert_eq!(min.as_slice(), &[1.5, -3.0, 2.0]); + } + + #[test] + fn test_elementwise_min_empty() { + let v1: FlexVector = FlexVector::new(); + let v2: FlexVector = FlexVector::new(); + let min = v1.elementwise_min(&v2).unwrap(); + assert!(min.is_empty()); + } + + #[test] + fn test_elementwise_min_mismatched_length() { + let v1 = FlexVector::from_vec(vec![1, 2]); + let v2 = FlexVector::from_vec(vec![3, 4, 5]); + let result = v1.elementwise_min(&v2); + assert!(result.is_err()); + } + + #[test] + fn test_elementwise_min_f64_with_nan() { + let v1 = FlexVector::from_vec(vec![1.0, f64::NAN, 3.0]); + let v2 = FlexVector::from_vec(vec![2.0, 2.0, f64::NAN]); + let min = v1.elementwise_min(&v2).unwrap(); + assert_eq!(min[0], 1.0); // min(1.0, 2.0) = 1.0 + assert_eq!(min[1], 2.0); // min(NaN, 2.0) = 2.0 (since NaN < 2.0 is false) + assert!(min[2].is_nan()); // min(3.0, NaN) = NaN (since 3.0 < NaN is false, returns NaN) + } + + #[test] + fn test_elementwise_min_f64_both_nan() { + let v1 = FlexVector::from_vec(vec![f64::NAN]); + let v2 = FlexVector::from_vec(vec![f64::NAN]); + let min = v1.elementwise_min(&v2).unwrap(); + assert!(min[0].is_nan()); + } + // -- maximum -- #[test] fn test_maximum_i32() { @@ -4453,6 +4537,58 @@ mod tests { assert!(v.maximum().is_some()); } + // -- elementwise_max -- + + #[test] + fn test_elementwise_max_i32() { + let v1 = FlexVector::from_vec(vec![1, 5, 3, 7]); + let v2 = FlexVector::from_vec(vec![2, 4, 6, 0]); + let max = v1.elementwise_max(&v2).unwrap(); + assert_eq!(max.as_slice(), &[2, 5, 6, 7]); + } + + #[test] + fn test_elementwise_max_f64() { + let v1 = FlexVector::from_vec(vec![1.5, -2.0, 3.0]); + let v2 = FlexVector::from_vec(vec![2.5, -3.0, 2.0]); + let max = v1.elementwise_max(&v2).unwrap(); + assert_eq!(max.as_slice(), &[2.5, -2.0, 3.0]); + } + + #[test] + fn test_elementwise_max_empty() { + let v1: FlexVector = FlexVector::new(); + let v2: FlexVector = FlexVector::new(); + let max = v1.elementwise_max(&v2).unwrap(); + assert!(max.is_empty()); + } + + #[test] + fn test_elementwise_max_mismatched_length() { + let v1 = FlexVector::from_vec(vec![1, 2, 3]); + let v2 = FlexVector::from_vec(vec![4, 5]); + let result = v1.elementwise_max(&v2); + assert!(result.is_err()); + } + + #[test] + fn test_elementwise_max_f64_with_nan() { + let v1 = FlexVector::from_vec(vec![1.0, f64::NAN, 3.0]); + let v2 = FlexVector::from_vec(vec![2.0, 2.0, f64::NAN]); + let max = v1.elementwise_max(&v2).unwrap(); + assert_eq!(max[0], 2.0); // max(1.0, 2.0) = 2.0 + assert_eq!(max[1], 2.0); // max(NaN, 2.0) = 2.0 (since NaN > 2.0 is false) + assert!(max[2].is_nan()); // max(3.0, NaN) = NaN (since 3.0 > NaN is false, returns NaN) + } + + #[test] + fn test_elementwise_max_f64_both_nan() { + let v1 = FlexVector::from_vec(vec![f64::NAN]); + let v2 = FlexVector::from_vec(vec![f64::NAN]); + let max = v1.elementwise_max(&v2).unwrap(); + assert!(max[0].is_nan()); + } + // -- l1_norm -- #[test] fn test_l1_norm_i32() { diff --git a/src/types/traits.rs b/src/types/traits.rs index 93adc9f..1e182e7 100644 --- a/src/types/traits.rs +++ b/src/types/traits.rs @@ -291,6 +291,11 @@ pub trait VectorOps: VectorBase { self.as_slice().iter().copied().reduce(|a, b| if a < b { a } else { b }) } + /// Element-wise minimum + fn elementwise_min(&self, other: &Self) -> Result + where + T: PartialOrd + Clone; + /// ... #[inline] fn maximum(&self) -> Option @@ -300,6 +305,11 @@ pub trait VectorOps: VectorBase { self.as_slice().iter().copied().reduce(|a, b| if a > b { a } else { b }) } + /// Element-wise maximum + fn elementwise_max(&self, other: &Self) -> Result + where + T: PartialOrd + Clone; + /// L1 norm (sum of absolute values). #[inline] fn l1_norm(&self) -> T From f07fa0bb9f58b7df8a3d3bd1026cb7c7da2a2c24 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Mon, 19 May 2025 16:26:14 -0400 Subject: [PATCH 38/82] add VectorOps elementwise_clamp function, add FlexVector impl --- src/types/flexvector.rs | 67 +++++++++++++++++++++++++++++++++++++++++ src/types/traits.rs | 21 +++++++++++++ 2 files changed, 88 insertions(+) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index 3d9dcef..1c27c4d 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -4589,6 +4589,73 @@ mod tests { assert!(max[0].is_nan()); } + // -- elementwise_clamp -- + + #[test] + fn test_elementwise_clamp_i32_basic() { + let v = FlexVector::from_vec(vec![-5, 0, 5, 10, 15]); + let clamped = v.elementwise_clamp(0, 10); + assert_eq!(clamped.as_slice(), &[0, 0, 5, 10, 10]); + } + + #[test] + fn test_elementwise_clamp_i32_all_below() { + let v = FlexVector::from_vec(vec![-3, -2, -1]); + let clamped = v.elementwise_clamp(0, 5); + assert_eq!(clamped.as_slice(), &[0, 0, 0]); + } + + #[test] + fn test_elementwise_clamp_i32_all_above() { + let v = FlexVector::from_vec(vec![11, 12, 13]); + let clamped = v.elementwise_clamp(0, 10); + assert_eq!(clamped.as_slice(), &[10, 10, 10]); + } + + #[test] + fn test_elementwise_clamp_i32_empty() { + let v: FlexVector = FlexVector::new(); + let clamped = v.elementwise_clamp(0, 10); + assert!(clamped.is_empty()); + } + + #[test] + fn test_elementwise_clamp_f64_basic() { + let v = FlexVector::from_vec(vec![-2.5, 0.0, 3.5, 7.2, 12.0]); + let clamped = v.elementwise_clamp(0.0, 10.0); + assert_eq!(clamped.as_slice(), &[0.0, 0.0, 3.5, 7.2, 10.0]); + } + + #[test] + fn test_elementwise_clamp_f64_all_below() { + let v = FlexVector::from_vec(vec![-1.1, -2.2]); + let clamped = v.elementwise_clamp(0.0, 5.0); + assert_eq!(clamped.as_slice(), &[0.0, 0.0]); + } + + #[test] + fn test_elementwise_clamp_f64_all_above() { + let v = FlexVector::from_vec(vec![11.1, 12.2]); + let clamped = v.elementwise_clamp(0.0, 10.0); + assert_eq!(clamped.as_slice(), &[10.0, 10.0]); + } + + #[test] + fn test_elementwise_clamp_f64_with_nan() { + let v = FlexVector::from_vec(vec![1.0, f64::NAN, 5.0]); + let clamped = v.elementwise_clamp(0.0, 4.0); + assert_eq!(clamped[0], 1.0); + assert!(clamped[1].is_nan()); + assert_eq!(clamped[2], 4.0); + } + + #[test] + fn test_elementwise_clamp_f64_empty() { + let v: FlexVector = FlexVector::new(); + let clamped = v.elementwise_clamp(0.0, 10.0); + assert!(clamped.is_empty()); + } + // -- l1_norm -- #[test] fn test_l1_norm_i32() { diff --git a/src/types/traits.rs b/src/types/traits.rs index 1e182e7..48c86d6 100644 --- a/src/types/traits.rs +++ b/src/types/traits.rs @@ -310,6 +310,27 @@ pub trait VectorOps: VectorBase { where T: PartialOrd + Clone; + /// Returns a new vector where each element is clamped to the [min, max] range. + #[inline] + fn elementwise_clamp(&self, min: T, max: T) -> Self::Output + where + T: PartialOrd + Clone, + Self::Output: std::iter::FromIterator, + { + self.as_slice() + .iter() + .map(|x| { + if *x < min { + min.clone() + } else if *x > max { + max.clone() + } else { + x.clone() + } + }) + .collect() + } + /// L1 norm (sum of absolute values). #[inline] fn l1_norm(&self) -> T From 6b91abe414b6091ce0ee4e1b374ec47d6c3445d8 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Mon, 19 May 2025 16:42:56 -0400 Subject: [PATCH 39/82] update project short description --- Cargo.toml | 2 +- README.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c3d15b9..944de97 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ license = "Apache-2.0" edition = "2021" keywords = ["vector", "math", "real", "imaginary", "complex"] repository = "https://github.com/chrissimpkins/vectora" -description = "A vector computation library" +description = "Fast, Flexible n-Dimensional Vector Math for Rust" readme = "README.md" exclude = [ diff --git a/README.md b/README.md index 26f874f..42da406 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -## A Rust library for vector computation +## Fast, Flexible n-Dimensional Vector Math for Rust [![Crates.io](https://img.shields.io/crates/v/vectora)](https://crates.io/crates/vectora) [![docs.rs](https://img.shields.io/docsrs/vectora)](https://docs.rs/vectora) @@ -15,7 +15,7 @@ ## About -Vectora is a library for n-dimensional vector computation with real and complex scalar types. The main library entry point is the [`Vector`](https://docs.rs/vectora/latest/vectora/types/vector/struct.Vector.html) struct. Please see the [Gettting Started Guide](https://docs.rs/vectora/latest/vectora/#getting-started) for a detailed library API overview with examples. +Vectora is a library for n-dimensional vector math with real and complex scalar types. The main library entry point is the [`Vector`](https://docs.rs/vectora/latest/vectora/types/vector/struct.Vector.html) struct. Please see the [Gettting Started Guide](https://docs.rs/vectora/latest/vectora/#getting-started) for a detailed library API overview with examples. ## User documentation From 4a161cf163393f004d6fd8bf048f322a1d6da916 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Mon, 19 May 2025 19:05:11 -0400 Subject: [PATCH 40/82] add FlexVector filter, reduce and fold methods --- src/types/flexvector.rs | 246 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 245 insertions(+), 1 deletion(-) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index 1c27c4d..e427b89 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -1069,7 +1069,6 @@ impl FlexVector { T: Clone, { for x in self.components.iter_mut() { - // Use clone since T may not be Copy *x = f(x.clone()); } } @@ -1085,6 +1084,38 @@ impl FlexVector { FlexVector { components } } + /// Returns a new FlexVector containing only the elements that satisfy the predicate. + #[inline] + pub fn filter(&self, mut predicate: F) -> FlexVector + where + F: FnMut(&T) -> bool, + T: Clone, + { + let components = self.components.iter().filter(|&x| predicate(x)).cloned().collect(); + FlexVector { components } + } + + /// Reduces the elements to a single value using the provided closure, or returns None if empty. + #[inline] + pub fn reduce(&self, mut f: F) -> Option + where + F: FnMut(T, T) -> T, + T: Clone, + { + let mut iter = self.components.iter().cloned(); + let first = iter.next()?; + Some(iter.fold(first, &mut f)) + } + + /// Folds the elements using an initial value and a closure. + #[inline] + pub fn fold(&self, init: B, mut f: F) -> B + where + F: FnMut(B, &T) -> B, + { + self.components.iter().fold(init, &mut f) + } + /// Zips two FlexVectors into a FlexVector of pairs. #[inline] pub fn zip(self, other: FlexVector) -> FlexVector<(T, U)> { @@ -3658,6 +3689,219 @@ mod tests { assert!(flat.is_empty()); } + // -- filter -- + + #[test] + fn test_filter_i32_basic() { + let v = FlexVector::from_vec(vec![1, 2, 3, 4, 5]); + let filtered = v.filter(|&x| x % 2 == 0); + assert_eq!(filtered.as_slice(), &[2, 4]); + } + + #[test] + fn test_filter_i32_all_false() { + let v = FlexVector::from_vec(vec![1, 3, 5]); + let filtered = v.filter(|&x| x > 10); + assert!(filtered.is_empty()); + } + + #[test] + fn test_filter_i32_all_true() { + let v = FlexVector::from_vec(vec![1, 2, 3]); + let filtered = v.filter(|_| true); + assert_eq!(filtered.as_slice(), &[1, 2, 3]); + } + + #[test] + fn test_filter_f64_positive() { + let v = FlexVector::from_vec(vec![-1.5, 0.0, 2.5, 3.3]); + let filtered = v.filter(|&x| x > 0.0); + assert_eq!(filtered.as_slice(), &[2.5, 3.3]); + } + + #[test] + fn test_filter_f64_nan() { + let v = FlexVector::from_vec(vec![1.0, f64::NAN, 2.0]); + let filtered = v.filter(|&x| x.is_nan()); + assert_eq!(filtered.len(), 1); + assert!(filtered[0].is_nan()); + } + + #[test] + fn test_filter_complex_f64_real_positive() { + use num::Complex; + let v = FlexVector::from_vec(vec![ + Complex::new(1.0, 2.0), + Complex::new(-3.0, 4.0), + Complex::new(0.0, 0.0), + ]); + let filtered = v.filter(|z| z.re > 0.0); + assert_eq!(filtered.as_slice(), &[Complex::new(1.0, 2.0)]); + } + + #[test] + fn test_filter_complex_f64_imag_nonzero() { + use num::Complex; + let v = FlexVector::from_vec(vec![ + Complex::new(1.0, 0.0), + Complex::new(0.0, 2.0), + Complex::new(3.0, 0.0), + ]); + let filtered = v.filter(|z| z.im != 0.0); + assert_eq!(filtered.as_slice(), &[Complex::new(0.0, 2.0)]); + } + + #[test] + fn test_filter_empty() { + let v: FlexVector = FlexVector::new(); + let filtered = v.filter(|&x| x > 0); + assert!(filtered.is_empty()); + } + + // -- reduce -- + + #[test] + fn test_reduce_i32_sum() { + let v = FlexVector::from_vec(vec![1, 2, 3, 4]); + let sum = v.reduce(|a, b| a + b); + assert_eq!(sum, Some(10)); + } + + #[test] + fn test_reduce_i32_product() { + let v = FlexVector::from_vec(vec![2, 3, 4]); + let product = v.reduce(|a, b| a * b); + assert_eq!(product, Some(24)); + } + + #[test] + fn test_reduce_i32_empty() { + let v: FlexVector = FlexVector::new(); + let result = v.reduce(|a, b| a + b); + assert_eq!(result, None); + } + + #[test] + fn test_reduce_f64_sum() { + let v = FlexVector::from_vec(vec![1.5, 2.5, 3.0]); + let sum = v.reduce(|a, b| a + b); + assert_eq!(sum, Some(7.0)); + } + + #[test] + fn test_reduce_f64_product() { + let v = FlexVector::from_vec(vec![1.5, 2.0, 3.0]); + let product = v.reduce(|a, b| a * b); + assert!((product.unwrap() - 9.0).abs() < 1e-12); + } + + #[test] + fn test_reduce_f64_empty() { + let v: FlexVector = FlexVector::new(); + let result = v.reduce(|a, b| a + b); + assert_eq!(result, None); + } + + #[test] + fn test_reduce_complex_f64_sum() { + use num::Complex; + let v = FlexVector::from_vec(vec![ + Complex::new(1.0, 2.0), + Complex::new(3.0, 4.0), + Complex::new(5.0, 6.0), + ]); + let sum = v.reduce(|a, b| a + b); + assert_eq!(sum, Some(Complex::new(9.0, 12.0))); + } + + #[test] + fn test_reduce_complex_f64_product() { + use num::Complex; + let v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let product = v.reduce(|a, b| a * b); + assert_eq!(product, Some(Complex::new(-5.0, 10.0))); // (1+2i)*(3+4i) = -5+10i + } + + #[test] + fn test_reduce_complex_f64_empty() { + use num::Complex; + let v: FlexVector> = FlexVector::new(); + let result = v.reduce(|a, b| a + b); + assert_eq!(result, None); + } + + // -- fold -- + + #[test] + fn test_fold_i32_sum() { + let v = FlexVector::from_vec(vec![1, 2, 3, 4]); + let sum = v.fold(0, |acc, x| acc + x); + assert_eq!(sum, 10); + } + + #[test] + fn test_fold_i32_product() { + let v = FlexVector::from_vec(vec![2, 3, 4]); + let product = v.fold(1, |acc, x| acc * x); + assert_eq!(product, 24); + } + + #[test] + fn test_fold_i32_empty() { + let v: FlexVector = FlexVector::new(); + let sum = v.fold(0, |acc, x| acc + x); + assert_eq!(sum, 0); + } + + #[test] + fn test_fold_f64_sum() { + let v = FlexVector::from_vec(vec![1.5, 2.5, 3.0]); + let sum = v.fold(0.0, |acc, x| acc + x); + assert!((sum - 7.0).abs() < 1e-12); + } + + #[test] + fn test_fold_f64_product() { + let v = FlexVector::from_vec(vec![1.5, 2.0, 3.0]); + let product = v.fold(1.0, |acc, x| acc * x); + assert!((product - 9.0).abs() < 1e-12); + } + + #[test] + fn test_fold_f64_empty() { + let v: FlexVector = FlexVector::new(); + let sum = v.fold(0.0, |acc, x| acc + x); + assert_eq!(sum, 0.0); + } + + #[test] + fn test_fold_complex_f64_sum() { + use num::Complex; + let v = FlexVector::from_vec(vec![ + Complex::new(1.0, 2.0), + Complex::new(3.0, 4.0), + Complex::new(5.0, 6.0), + ]); + let sum = v.fold(Complex::new(0.0, 0.0), |acc, x| acc + x); + assert_eq!(sum, Complex::new(9.0, 12.0)); + } + + #[test] + fn test_fold_complex_f64_product() { + use num::Complex; + let v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let product = v.fold(Complex::new(1.0, 0.0), |acc, x| acc * x); + assert_eq!(product, Complex::new(-5.0, 10.0)); // (1+0i)*(1+2i)*(3+4i) = (1+2i)*(3+4i) = -5+10i + } + + #[test] + fn test_fold_complex_f64_empty() { + use num::Complex; + let v: FlexVector> = FlexVector::new(); + let sum = v.fold(Complex::new(0.0, 0.0), |acc, x| acc + x); + assert_eq!(sum, Complex::new(0.0, 0.0)); + } + // --- zip --- #[test] fn test_zip_i32() { From f8e41996db5a4e5fd408fb7bd75802f305080e27 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Wed, 21 May 2025 23:52:54 -0400 Subject: [PATCH 41/82] update .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 9a3f7ae..947d38b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ /target/ Cargo.lock **/*.rs.bk +examples .DS_Store From 04e65f2ad5d9412e7321852e95fc6f311bdeb24e Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Wed, 21 May 2025 23:53:52 -0400 Subject: [PATCH 42/82] extensive FlexVector refactor to add Row and Column sub-types --- src/lib.rs | 23 +- src/macros.rs | 642 +++++++++--- src/types/flexvector.rs | 2063 ++++++++++++++++++++++++++------------ src/types/mod.rs | 1 + src/types/orientation.rs | 32 + src/types/traits.rs | 42 + 6 files changed, 2024 insertions(+), 779 deletions(-) create mode 100644 src/types/orientation.rs diff --git a/src/lib.rs b/src/lib.rs index 7c1146d..219b2a7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -901,26 +901,19 @@ pub use types::vector::Vector; /// The vectora prelude: import this module to bring all core traits, types, and macros into scope. /// -/// # Example -/// ``` -/// use vectora::prelude::*; -/// -/// let v = vector![1, 2, 3]; -/// let fv = flexvector![1.0, 2.0, 3.0]; -/// let v2 = Vector::::from([4, 5, 6]); -/// let fv2 = FlexVector::from_vec(vec![4.0, 5.0, 6.0]); -/// ``` -/// /// This prelude includes: -/// - Core traits (`VectorBase`, `VectorOps`, `VectorOpsFloat`, `VectorOpsComplex`) -/// - The main types (`Vector`, `FlexVector`) -/// - User-facing macros (`vector!`, `flexvector!`, `try_vector!`, `try_flexvector!`) +/// - Core traits +/// - Main types +/// - Main macros pub mod prelude { // Traits - pub use crate::types::traits::{VectorBase, VectorOps, VectorOpsComplex, VectorOpsFloat}; + pub use crate::types::traits::{ + Transposable, VectorBase, VectorHasOrientation, VectorOps, VectorOpsComplex, VectorOpsFloat, + }; // Types pub use crate::types::flexvector::FlexVector; + pub use crate::types::orientation::{Column, Row, VectorOrientation}; pub use crate::types::vector::Vector; // Macros - pub use crate::{flexvector, try_flexvector, try_vector, vector}; + pub use crate::{fv, fv_from, fv_iter, try_fv_iter, try_vector, vector}; } diff --git a/src/macros.rs b/src/macros.rs index 5a120ce..2ae7751 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -55,53 +55,161 @@ macro_rules! try_vector { }; } -/// Creates a [`FlexVector`] with the given elements or repeated value. +/// Creates a [`FlexVector`] with the list of given elements or repeated value, optionally specifying +/// vector row or column orientation. /// -/// - `flexvector![x, y, z]` creates a FlexVector from the elements. -/// - `flexvector![elem; n]` creates a FlexVector of length `n` with all elements set to `elem`. +/// - `fv![x, y, z]` creates a default (column) FlexVector. +/// - `fv![Column; x, y, z]` creates a Column FlexVector. +/// - `fv![Row; x, y, z]` creates a Row FlexVector. +/// - `fv![elem; n]` creates a default (column) FlexVector of length `n` with all elements set to `elem`. +/// - `fv![Column; elem; n]` creates a Column FlexVector of length `n` with all elements set to `elem`. +/// - `fv![Row; elem; n]` creates a Row FlexVector of length `n` with all elements set to `elem`. /// /// # Examples -/// /// ``` /// use vectora::prelude::*; -/// use num::Complex; /// -/// let fv = flexvector![1, 2, 3]; -/// let fv_f64 = flexvector![1.0_f64, 2.0_f64, 3.0_f64]; -/// let fv_repeat = flexvector![0; 4]; -/// let fv_complex = flexvector![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; +/// let fv_default: FlexVector = fv![1, 2, 3]; +/// let fv_col = fv![Column; 1, 2, 3]; +/// let fv_row = fv![Row; 1, 2, 3]; +/// let fv_default_repeat: FlexVector = fv![0; 4]; +/// let fv_col_repeat = fv![Column; 0; 4]; +/// let fv_row_repeat = fv![Row; 0; 4]; /// ``` #[macro_export] -macro_rules! flexvector { - // Repeated element syntax: flexvector![elem; n] +macro_rules! fv { + // Row vector, repeated element: fv![Row; elem; n] + (Row; $elem:expr; $n:expr) => { + $crate::types::flexvector::FlexVector::<_, $crate::types::orientation::Row>::from_vec(vec![$elem; $n]) + }; + // Column vector, repeated element: fv![Column; elem; n] + (Column; $elem:expr; $n:expr) => { + $crate::types::flexvector::FlexVector::<_, $crate::types::orientation::Column>::from_vec(vec![$elem; $n]) + }; + // Default (column) vector, repeated element: fv![elem; n] ($elem:expr; $n:expr) => { $crate::types::flexvector::FlexVector::from_vec(vec![$elem; $n]) }; - // List syntax: flexvector![x, y, z, ...] + // Row vector, list: fv![Row; x, y, z, ...] + (Row; $($x:expr),+ $(,)?) => { + $crate::types::flexvector::FlexVector::<_, $crate::types::orientation::Row>::from_vec(vec![$($x),+]) + }; + // Column vector, list: fv![Column; x, y, z, ...] + (Column; $($x:expr),+ $(,)?) => { + $crate::types::flexvector::FlexVector::<_, $crate::types::orientation::Column>::from_vec(vec![$($x),+]) + }; + // Default (column) vector, list: fv![x, y, z, ...] ($($x:expr),+ $(,)?) => { $crate::types::flexvector::FlexVector::from_vec(vec![$($x),+]) }; } -/// Fallibly constructs a [`FlexVector`] from an iterator of `Result`, propagating the first error. +/// Creates a [`FlexVector`] from a collection (slice, Vec, or Cow), optionally specifying row or column orientation. /// -/// - `try_flexvector!(iter)` collects all `Ok(T)` values into a FlexVector, or returns the first `Err(E)` encountered. +/// - `fv_from![data]` creates a default (column) FlexVector from any collection accepted by `from_cow`. +/// - `fv_from![Row; data]` creates a Row FlexVector from a collection. +/// - `fv_from![Column; data]` creates a Column FlexVector from a collection. /// /// # Examples +/// ``` +/// use vectora::prelude::*; +/// use std::borrow::Cow; +/// +/// let slice: &[i32] = &[1, 2, 3]; +/// let fv: FlexVector = fv_from![slice]; +/// +/// let vec = vec![4, 5, 6]; +/// let fv_col = fv_from![Column; vec]; +/// +/// let cow: Cow<[i32]> = Cow::Borrowed(&[7, 8, 9]); +/// let fv_row = fv_from![Row; cow]; +/// ``` +#[macro_export] +macro_rules! fv_from { + (Row; $data:expr) => { + $crate::types::flexvector::FlexVector::<_, $crate::types::orientation::Row>::from_cow($data) + }; + (Column; $data:expr) => { + $crate::types::flexvector::FlexVector::<_, $crate::types::orientation::Column>::from_cow( + $data, + ) + }; + ($data:expr) => { + $crate::types::flexvector::FlexVector::from_cow($data) + }; +} + +/// Creates a [`FlexVector`] from any iterable (such as an iterator, range, Vec, or array), +/// optionally specifying row or column orientation. +/// +/// - `fv_iter![data]` creates a default (column) FlexVector from any iterable. +/// - `fv_iter![Row; data]` creates a Row FlexVector from any iterable. +/// - `fv_iter![Column; data]` creates a Column FlexVector from any iterable. /// +/// # Examples +/// ``` +/// use vectora::prelude::*; +/// +/// // From a range +/// let fv: FlexVector = fv_iter![0..3]; +/// assert_eq!(fv.as_slice(), &[0, 1, 2]); +/// +/// // From a Vec +/// let v = vec![10, 20, 30]; +/// let fv_col = fv_iter![Column; v.clone()]; +/// assert_eq!(fv_col.as_slice(), &[10, 20, 30]); +/// +/// // From an iterator +/// let fv_row = fv_iter![Row; (1..=3).map(|x| x * 2)]; +/// assert_eq!(fv_row.as_slice(), &[2, 4, 6]); +/// ``` +#[macro_export] +macro_rules! fv_iter { + (Row; $data:expr) => { + $crate::types::flexvector::FlexVector::<_, $crate::types::orientation::Row>::from_iter( + $data, + ) + }; + (Column; $data:expr) => { + $crate::types::flexvector::FlexVector::<_, $crate::types::orientation::Column>::from_iter( + $data, + ) + }; + ($data:expr) => { + $crate::types::flexvector::FlexVector::from_iter($data) + }; +} + +/// Fallibly constructs a [`FlexVector`] from an iterator of `Result`, propagating the first error, +/// optionally specifying vector row or column orientation. +/// +/// - `try_fv_iter!(iter)` collects all `Ok(T)` values into a default (column) FlexVector, or returns the first `Err(E)` encountered. +/// - `try_fv_iter!(Column; iter)` collects into a Column FlexVector. +/// - `try_fv_iter!(Row; iter)` collects into a Row FlexVector. +/// +/// # Examples /// ``` /// use vectora::prelude::*; /// /// let data = vec!["1", "2", "oops"]; -/// let fv = try_flexvector!(data.iter().map(|s| s.parse::())); +/// let fv: Result, _> = try_fv_iter!(data.iter().map(|s| s.parse::())); /// assert!(fv.is_err()); /// /// let data = vec!["1", "2", "3"]; -/// let fv = try_flexvector!(data.iter().map(|s| s.parse::())).unwrap(); +/// let fv = try_fv_iter!(Row; data.iter().map(|s| s.parse::())).unwrap(); /// assert_eq!(fv.as_slice(), &[1, 2, 3]); /// ``` #[macro_export] -macro_rules! try_flexvector { +macro_rules! try_fv_iter { + // Row vector: try_fv!(Row; iter) + (Row; $iter:expr) => { + $crate::types::flexvector::FlexVector::<_, $crate::types::orientation::Row>::try_from_iter($iter) + }; + // Column vector: try_fv!(Column; iter) + (Column; $iter:expr) => { + $crate::types::flexvector::FlexVector::<_, $crate::types::orientation::Column>::try_from_iter($iter) + }; + // Default (column) vector: try_fv!(iter) ($iter:expr) => { $crate::types::flexvector::FlexVector::try_from_iter($iter) }; @@ -110,14 +218,15 @@ macro_rules! try_flexvector { #[macro_export] macro_rules! impl_vector_unary_op { ($VectorType:ident, $trait:ident, $method:ident, $op:tt) => { - impl std::ops::$trait for $VectorType + impl std::ops::$trait for $VectorType where T: num::Num + Clone + std::ops::Neg, { type Output = Self; + #[inline] fn $method(self) -> Self { let components = self.components.into_iter().map(|a| $op a).collect(); - Self { components } + Self { components, _orientation: PhantomData } } } }; @@ -127,34 +236,36 @@ macro_rules! impl_vector_unary_op { macro_rules! impl_vector_binop { // With length check (for FlexVector) (check_len, $VectorType:ident, $trait:ident, $method:ident, $op:tt) => { - impl std::ops::$trait for $VectorType + impl std::ops::$trait for $VectorType where T: num::Num + Clone, { type Output = Self; + #[inline] fn $method(self, rhs: Self) -> Self { assert_eq!(self.len(), rhs.len(), "Vector length mismatch"); let components = self.components.into_iter() .zip(rhs.components) .map(|(a, b)| a $op b) .collect(); - Self { components } + Self { components, _orientation: PhantomData } } } }; // Without length check (for Vector) (no_check_len, $VectorType:ident, $trait:ident, $method:ident, $op:tt) => { - impl std::ops::$trait for $VectorType + impl std::ops::$trait for $VectorType where T: num::Num + Clone, { type Output = Self; + #[inline] fn $method(self, rhs: Self) -> Self { let components = self.components.into_iter() .zip(rhs.components) .map(|(a, b)| a $op b) .collect(); - Self { components } + Self { components, _orientation: PhantomData } } } }; @@ -165,82 +276,90 @@ macro_rules! impl_vector_binop_div { // With length check (for FlexVector) (check_len, $VectorType:ident) => { // f32 - impl std::ops::Div for $VectorType { + impl std::ops::Div for $VectorType { type Output = Self; + #[inline] fn div(self, rhs: Self) -> Self { assert_eq!(self.len(), rhs.len(), "Vector length mismatch"); let components = self.components.into_iter().zip(rhs.components).map(|(a, b)| a / b).collect(); - Self { components } + Self { components, _orientation: PhantomData } } } // f64 - impl std::ops::Div for $VectorType { + impl std::ops::Div for $VectorType { type Output = Self; + #[inline] fn div(self, rhs: Self) -> Self { assert_eq!(self.len(), rhs.len(), "Vector length mismatch"); let components = self.components.into_iter().zip(rhs.components).map(|(a, b)| a / b).collect(); - Self { components } + Self { components, _orientation: PhantomData } } } // Complex - impl std::ops::Div for $VectorType> { + impl std::ops::Div for $VectorType, O> { type Output = Self; + #[inline] fn div(self, rhs: Self) -> Self { assert_eq!(self.len(), rhs.len(), "Vector length mismatch"); let components = self.components.into_iter().zip(rhs.components).map(|(a, b)| a / b).collect(); - Self { components } + Self { components, _orientation: PhantomData } } } // Complex - impl std::ops::Div for $VectorType> { + impl std::ops::Div for $VectorType, O> { type Output = Self; + #[inline] fn div(self, rhs: Self) -> Self { assert_eq!(self.len(), rhs.len(), "Vector length mismatch"); let components = self.components.into_iter().zip(rhs.components).map(|(a, b)| a / b).collect(); - Self { components } + Self { components, _orientation: PhantomData } } } }; // Without length check (for Vector) (no_check_len, $VectorType:ident) => { // f32 - impl std::ops::Div for $VectorType { + impl std::ops::Div for $VectorType { type Output = Self; + #[inline] fn div(self, rhs: Self) -> Self { let components = self.components.into_iter().zip(rhs.components).map(|(a, b)| a / b).collect(); - Self { components } + Self { components, _orientation: PhantomData } } } // f64 - impl std::ops::Div for $VectorType { + impl std::ops::Div for $VectorType { type Output = Self; + #[inline] fn div(self, rhs: Self) -> Self { let components = self.components.into_iter().zip(rhs.components).map(|(a, b)| a / b).collect(); - Self { components } + Self { components, _orientation: PhantomData } } } // Complex - impl std::ops::Div for $VectorType> { + impl std::ops::Div for $VectorType, O> { type Output = Self; + #[inline] fn div(self, rhs: Self) -> Self { let components = self.components.into_iter().zip(rhs.components).map(|(a, b)| a / b).collect(); - Self { components } + Self { components, _orientation: PhantomData } } } // Complex - impl std::ops::Div for $VectorType> { + impl std::ops::Div for $VectorType, O> { type Output = Self; + #[inline] fn div(self, rhs: Self) -> Self { let components = self.components.into_iter().zip(rhs.components).map(|(a, b)| a / b).collect(); - Self { components } + Self { components, _orientation: PhantomData } } } }; @@ -250,10 +369,11 @@ macro_rules! impl_vector_binop_div { macro_rules! impl_vector_binop_assign { // With length check (for FlexVector) (check_len, $VectorType:ident, $trait:ident, $method:ident, $op:tt) => { - impl std::ops::$trait for $VectorType + impl std::ops::$trait for $VectorType where T: num::Num + Clone, { + #[inline] fn $method(&mut self, rhs: Self) { assert_eq!(self.len(), rhs.len(), "Vector length mismatch"); for (a, b) in self.components.iter_mut().zip(rhs.components) { @@ -264,10 +384,11 @@ macro_rules! impl_vector_binop_assign { }; // Without length check (for Vector) (no_check_len, $VectorType:ident, $trait:ident, $method:ident, $op:tt) => { - impl std::ops::$trait for $VectorType + impl std::ops::$trait for $VectorType where T: num::Num + Clone, { + #[inline] fn $method(&mut self, rhs: Self) { for (a, b) in self.components.iter_mut().zip(rhs.components) { *a = a.clone() $op b; @@ -282,7 +403,8 @@ macro_rules! impl_vector_binop_div_assign { // With length check (for FlexVector) (check_len, $VectorType:ident) => { // f32 - impl std::ops::DivAssign for $VectorType { + impl std::ops::DivAssign for $VectorType { + #[inline] fn div_assign(&mut self, rhs: Self) { assert_eq!(self.len(), rhs.len(), "Vector length mismatch"); for (a, b) in self.components.iter_mut().zip(rhs.components) { @@ -291,7 +413,8 @@ macro_rules! impl_vector_binop_div_assign { } } // f64 - impl std::ops::DivAssign for $VectorType { + impl std::ops::DivAssign for $VectorType { + #[inline] fn div_assign(&mut self, rhs: Self) { assert_eq!(self.len(), rhs.len(), "Vector length mismatch"); for (a, b) in self.components.iter_mut().zip(rhs.components) { @@ -300,7 +423,8 @@ macro_rules! impl_vector_binop_div_assign { } } // Complex - impl std::ops::DivAssign for $VectorType> { + impl std::ops::DivAssign for $VectorType, O> { + #[inline] fn div_assign(&mut self, rhs: Self) { assert_eq!(self.len(), rhs.len(), "Vector length mismatch"); for (a, b) in self.components.iter_mut().zip(rhs.components) { @@ -309,7 +433,8 @@ macro_rules! impl_vector_binop_div_assign { } } // Complex - impl std::ops::DivAssign for $VectorType> { + impl std::ops::DivAssign for $VectorType, O> { + #[inline] fn div_assign(&mut self, rhs: Self) { assert_eq!(self.len(), rhs.len(), "Vector length mismatch"); for (a, b) in self.components.iter_mut().zip(rhs.components) { @@ -321,7 +446,8 @@ macro_rules! impl_vector_binop_div_assign { // Without length check (for Vector) (no_check_len, $VectorType:ident) => { // f32 - impl std::ops::DivAssign for $VectorType { + impl std::ops::DivAssign for $VectorType { + #[inline] fn div_assign(&mut self, rhs: Self) { for (a, b) in self.components.iter_mut().zip(rhs.components) { *a /= b; @@ -329,7 +455,8 @@ macro_rules! impl_vector_binop_div_assign { } } // f64 - impl std::ops::DivAssign for $VectorType { + impl std::ops::DivAssign for $VectorType { + #[inline] fn div_assign(&mut self, rhs: Self) { for (a, b) in self.components.iter_mut().zip(rhs.components) { *a /= b; @@ -337,7 +464,8 @@ macro_rules! impl_vector_binop_div_assign { } } // Complex - impl std::ops::DivAssign for $VectorType> { + impl std::ops::DivAssign for $VectorType, O> { + #[inline] fn div_assign(&mut self, rhs: Self) { for (a, b) in self.components.iter_mut().zip(rhs.components) { *a /= b; @@ -345,7 +473,8 @@ macro_rules! impl_vector_binop_div_assign { } } // Complex - impl std::ops::DivAssign for $VectorType> { + impl std::ops::DivAssign for $VectorType, O> { + #[inline] fn div_assign(&mut self, rhs: Self) { for (a, b) in self.components.iter_mut().zip(rhs.components) { *a /= b; @@ -358,14 +487,15 @@ macro_rules! impl_vector_binop_div_assign { #[macro_export] macro_rules! impl_vector_scalar_op { ($VectorType:ident, $trait:ident, $method:ident, $op:tt) => { - impl std::ops::$trait for $VectorType + impl std::ops::$trait for $VectorType where T: num::Num + Clone, { type Output = Self; + #[inline] fn $method(self, rhs: T) -> Self { let components = self.components.into_iter().map(|a| a $op rhs.clone()).collect(); - Self { components } + Self { components, _orientation: PhantomData } } } }; @@ -374,10 +504,11 @@ macro_rules! impl_vector_scalar_op { #[macro_export] macro_rules! impl_vector_scalar_op_assign { ($VectorType:ident, $trait:ident, $method:ident, $op:tt) => { - impl std::ops::$trait for $VectorType + impl std::ops::$trait for $VectorType where T: num::Num + Clone, { + #[inline] fn $method(&mut self, rhs: T) { for a in &mut self.components { *a = a.clone() $op rhs.clone(); @@ -391,51 +522,57 @@ macro_rules! impl_vector_scalar_op_assign { macro_rules! impl_vector_scalar_div_op { ($VectorType:ident) => { // For f32 - impl std::ops::Div for $VectorType { + impl std::ops::Div for $VectorType { type Output = Self; + #[inline] fn div(self, rhs: f32) -> Self { let components = self.components.into_iter().map(|a| a / rhs).collect(); - Self { components } + Self { components, _orientation: PhantomData } } } // For f64 - impl std::ops::Div for $VectorType { + impl std::ops::Div for $VectorType { type Output = Self; + #[inline] fn div(self, rhs: f64) -> Self { let components = self.components.into_iter().map(|a| a / rhs).collect(); - Self { components } + Self { components, _orientation: PhantomData } } } // For Complex / f32 - impl std::ops::Div for $VectorType> { + impl std::ops::Div for $VectorType, O> { type Output = Self; + #[inline] fn div(self, rhs: f32) -> Self { let components = self.components.into_iter().map(|a| a / rhs).collect(); - Self { components } + Self { components, _orientation: PhantomData } } } // For Complex / f64 - impl std::ops::Div for $VectorType> { + impl std::ops::Div for $VectorType, O> { type Output = Self; + #[inline] fn div(self, rhs: f64) -> Self { let components = self.components.into_iter().map(|a| a / rhs).collect(); - Self { components } + Self { components, _orientation: PhantomData } } } // For Complex / Complex - impl std::ops::Div> for $VectorType> { + impl std::ops::Div> for $VectorType, O> { type Output = Self; + #[inline] fn div(self, rhs: num::Complex) -> Self { let components = self.components.into_iter().map(|a| a / rhs).collect(); - Self { components } + Self { components, _orientation: PhantomData } } } // For Complex / Complex - impl std::ops::Div> for $VectorType> { + impl std::ops::Div> for $VectorType, O> { type Output = Self; + #[inline] fn div(self, rhs: num::Complex) -> Self { let components = self.components.into_iter().map(|a| a / rhs).collect(); - Self { components } + Self { components, _orientation: PhantomData } } } }; @@ -445,7 +582,8 @@ macro_rules! impl_vector_scalar_div_op { macro_rules! impl_vector_scalar_div_op_assign { ($VectorType:ident) => { // For f32 - impl std::ops::DivAssign for $VectorType { + impl std::ops::DivAssign for $VectorType { + #[inline] fn div_assign(&mut self, rhs: f32) { for a in &mut self.components { *a = *a / rhs; @@ -453,7 +591,8 @@ macro_rules! impl_vector_scalar_div_op_assign { } } // For f64 - impl std::ops::DivAssign for $VectorType { + impl std::ops::DivAssign for $VectorType { + #[inline] fn div_assign(&mut self, rhs: f64) { for a in &mut self.components { *a = *a / rhs; @@ -461,7 +600,8 @@ macro_rules! impl_vector_scalar_div_op_assign { } } // For Complex / f32 - impl std::ops::DivAssign for $VectorType> { + impl std::ops::DivAssign for $VectorType, O> { + #[inline] fn div_assign(&mut self, rhs: f32) { for a in &mut self.components { *a = *a / rhs; @@ -469,7 +609,8 @@ macro_rules! impl_vector_scalar_div_op_assign { } } // For Complex / f64 - impl std::ops::DivAssign for $VectorType> { + impl std::ops::DivAssign for $VectorType, O> { + #[inline] fn div_assign(&mut self, rhs: f64) { for a in &mut self.components { *a = *a / rhs; @@ -477,7 +618,8 @@ macro_rules! impl_vector_scalar_div_op_assign { } } // For Complex / Complex - impl std::ops::DivAssign> for $VectorType> { + impl std::ops::DivAssign> for $VectorType, O> { + #[inline] fn div_assign(&mut self, rhs: num::Complex) { for a in &mut self.components { *a = *a / rhs; @@ -485,7 +627,8 @@ macro_rules! impl_vector_scalar_div_op_assign { } } // For Complex / Complex - impl std::ops::DivAssign> for $VectorType> { + impl std::ops::DivAssign> for $VectorType, O> { + #[inline] fn div_assign(&mut self, rhs: num::Complex) { for a in &mut self.components { *a = *a / rhs; @@ -497,8 +640,9 @@ macro_rules! impl_vector_scalar_div_op_assign { #[cfg(test)] mod tests { + use crate::types::orientation::{Column, Row}; use crate::types::traits::VectorBase; - use crate::Vector; + use crate::{FlexVector, Vector}; #[allow(unused_imports)] use approx::{assert_relative_eq, assert_relative_ne}; #[allow(unused_imports)] @@ -966,100 +1110,330 @@ mod tests { assert_relative_eq!(v[1].im, 4.0_f64); } + // -- fv! macro -- + #[test] - fn macro_flexvector_usize() { - let v1 = flexvector![1_usize, 2_usize, 3_usize]; - let v2 = flexvector![1, 2, 3]; - let v3 = flexvector![1; 3]; + fn fv_macro_default_column_vec_i32() { + let v = fv![1, 2, 3]; + assert_eq!(v, FlexVector::::from_vec(vec![1, 2, 3])); + let v = fv![10; 4]; + assert_eq!(v, FlexVector::::from_vec(vec![10, 10, 10, 10])); + } - assert_eq!(v1.len(), 3); - assert_eq!(v1[0], 1_usize); - assert_eq!(v1[1], 2_usize); - assert_eq!(v1[2], 3_usize); + #[test] + fn fv_macro_explicit_column_vec_i32() { + let v = fv![Column; 1, 2, 3]; + assert_eq!(v, FlexVector::::from_vec(vec![1, 2, 3])); + let v = fv![Column; 7; 2]; + assert_eq!(v, FlexVector::::from_vec(vec![7, 7])); + } - assert_eq!(v2.len(), 3); - assert_eq!(v2[0], 1_usize); - assert_eq!(v2[1], 2_usize); - assert_eq!(v2[2], 3_usize); + #[test] + fn fv_macro_row_vec_i32() { + let v = fv![Row; 1, 2, 3]; + assert_eq!(v, FlexVector::::from_vec(vec![1, 2, 3])); + let v = fv![Row; 5; 3]; + assert_eq!(v, FlexVector::::from_vec(vec![5, 5, 5])); + } - assert_eq!(v3.len(), 3); - assert_eq!(v3[0], 1_usize); - assert_eq!(v3[1], 1_usize); - assert_eq!(v3[2], 1_usize); + #[test] + fn fv_macro_default_column_vec_f64() { + let v = fv![1.0_f64, 2.0, 3.0]; + assert_eq!(v, FlexVector::::from_vec(vec![1.0, 2.0, 3.0])); + let v = fv![0.5_f64; 2]; + assert_eq!(v, FlexVector::::from_vec(vec![0.5, 0.5])); } #[test] - fn macro_flexvector_f64() { - let v1 = flexvector![1.0_f64, 2.0, 3.0]; - let v2 = flexvector![1.0, 2.0, 3.0]; - let v3 = flexvector![1.0; 3]; + fn fv_macro_explicit_column_vec_f64() { + let v = fv![Column; 1.0_f64, 2.0, 3.0]; + assert_eq!(v, FlexVector::::from_vec(vec![1.0, 2.0, 3.0])); + let v = fv![Column; 2.5_f64; 3]; + assert_eq!(v, FlexVector::::from_vec(vec![2.5, 2.5, 2.5])); + } - assert_eq!(v1.len(), 3); - assert_eq!(v1[0], 1.0); - assert_eq!(v1[1], 2.0); - assert_eq!(v1[2], 3.0); + #[test] + fn fv_macro_row_vec_f64() { + let v = fv![Row; 1.0_f64, 2.0, 3.0]; + assert_eq!(v, FlexVector::::from_vec(vec![1.0, 2.0, 3.0])); + let v = fv![Row; -1.5_f64; 2]; + assert_eq!(v, FlexVector::::from_vec(vec![-1.5, -1.5])); + } - assert_eq!(v2.len(), 3); - assert_eq!(v2[0], 1.0); - assert_eq!(v2[1], 2.0); - assert_eq!(v2[2], 3.0); + #[test] + fn fv_macro_default_column_vec_complex_f64() { + let v = fv![Complex::new(1.0_f64, 2.0), Complex::new(3.0, 4.0)]; + assert_eq!( + v, + FlexVector::, Column>::from_vec(vec![ + Complex::new(1.0, 2.0), + Complex::new(3.0, 4.0) + ]) + ); + let v = fv![Complex::new(0.0_f64, 1.0); 2]; + assert_eq!( + v, + FlexVector::, Column>::from_vec(vec![ + Complex::new(0.0, 1.0), + Complex::new(0.0, 1.0) + ]) + ); + } - assert_eq!(v3.len(), 3); - assert_eq!(v3[0], 1.0); - assert_eq!(v3[1], 1.0); - assert_eq!(v3[2], 1.0); + #[test] + fn fv_macro_explicit_column_vec_complex_f64() { + let v = fv![Column; Complex::new(1.0_f64, 2.0), Complex::new(3.0, 4.0)]; + assert_eq!( + v, + FlexVector::, Column>::from_vec(vec![ + Complex::new(1.0, 2.0), + Complex::new(3.0, 4.0) + ]) + ); + let v = fv![Column; Complex::new(2.0_f64, -2.0); 3]; + assert_eq!( + v, + FlexVector::, Column>::from_vec(vec![ + Complex::new(2.0, -2.0), + Complex::new(2.0, -2.0), + Complex::new(2.0, -2.0) + ]) + ); } #[test] - fn macro_flexvector_complex_f64() { - let v1 = flexvector![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; - let v2 = flexvector![Complex::new(1.0, 2.0); 2]; + fn fv_macro_row_vec_complex_f64() { + let v = fv![Row; Complex::new(1.0_f64, 2.0), Complex::new(3.0, 4.0)]; + assert_eq!( + v, + FlexVector::, Row>::from_vec(vec![ + Complex::new(1.0, 2.0), + Complex::new(3.0, 4.0) + ]) + ); + let v = fv![Row; Complex::new(-1.0_f64, 0.0); 2]; + assert_eq!( + v, + FlexVector::, Row>::from_vec(vec![ + Complex::new(-1.0, 0.0), + Complex::new(-1.0, 0.0) + ]) + ); + } - assert_eq!(v1.len(), 2); - assert_eq!(v1[0], Complex::new(1.0, 2.0)); - assert_eq!(v1[1], Complex::new(3.0, 4.0)); + // -- fv_iter! macro -- - assert_eq!(v2.len(), 2); - assert_eq!(v2[0], Complex::new(1.0, 2.0)); - assert_eq!(v2[1], Complex::new(1.0, 2.0)); + #[test] + fn fv_iter_macro_default_column_vec_i32() { + let v = fv_iter![1..=3]; + assert_eq!(v, FlexVector::::from_vec(vec![1, 2, 3])); + + let arr = [10, 20, 30]; + let v = fv_iter![arr]; + assert_eq!(v, FlexVector::::from_vec(vec![10, 20, 30])); + + let v = fv_iter![vec![4, 5, 6]]; + assert_eq!(v, FlexVector::::from_vec(vec![4, 5, 6])); + } + + #[test] + fn fv_iter_macro_explicit_column_vec_i32() { + let v = fv_iter![Column; 1..=3]; + assert_eq!(v, FlexVector::::from_vec(vec![1, 2, 3])); + + let arr = [7, 8]; + let v = fv_iter![Column; arr]; + assert_eq!(v, FlexVector::::from_vec(vec![7, 8])); + + let v = fv_iter![Column; vec![9, 10]]; + assert_eq!(v, FlexVector::::from_vec(vec![9, 10])); + } + + #[test] + fn fv_iter_macro_row_vec_i32() { + let v = fv_iter![Row; 1..=3]; + assert_eq!(v, FlexVector::::from_vec(vec![1, 2, 3])); + + let arr = [11, 12]; + let v = fv_iter![Row; arr]; + assert_eq!(v, FlexVector::::from_vec(vec![11, 12])); + + let v = fv_iter![Row; vec![13, 14]]; + assert_eq!(v, FlexVector::::from_vec(vec![13, 14])); + } + + #[test] + fn fv_iter_macro_default_column_vec_f64() { + let v = fv_iter![0..3]; + assert_eq!(v, FlexVector::::from_vec(vec![0, 1, 2])); + + let v = fv_iter![vec![1.5, 2.5, 3.5]]; + assert_eq!(v, FlexVector::::from_vec(vec![1.5, 2.5, 3.5])); + } + + #[test] + fn fv_iter_macro_explicit_column_vec_f64() { + let v = fv_iter![Column; 0..2]; + assert_eq!(v, FlexVector::::from_vec(vec![0, 1])); + + let v = fv_iter![Column; vec![4.4, 5.5]]; + assert_eq!(v, FlexVector::::from_vec(vec![4.4, 5.5])); + } + + #[test] + fn fv_iter_macro_row_vec_f64() { + let v = fv_iter![Row; 1..=2]; + assert_eq!(v, FlexVector::::from_vec(vec![1, 2])); + + let v = fv_iter![Row; vec![-1.5, -2.5]]; + assert_eq!(v, FlexVector::::from_vec(vec![-1.5, -2.5])); + } + + #[test] + fn fv_iter_macro_default_column_vec_complex_f64() { + use num::Complex; + let v = fv_iter![vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]]; + assert_eq!( + v, + FlexVector::, Column>::from_vec(vec![ + Complex::new(1.0, 2.0), + Complex::new(3.0, 4.0) + ]) + ); + } + + #[test] + fn fv_iter_macro_explicit_column_vec_complex_f64() { + use num::Complex; + let v = fv_iter![Column; vec![Complex::new(2.0, -2.0), Complex::new(0.0, 1.0)]]; + assert_eq!( + v, + FlexVector::, Column>::from_vec(vec![ + Complex::new(2.0, -2.0), + Complex::new(0.0, 1.0) + ]) + ); + } + + #[test] + fn fv_iter_macro_row_vec_complex_f64() { + use num::Complex; + let v = fv_iter![Row; vec![Complex::new(-1.0, 0.0), Complex::new(2.0, 2.0)]]; + assert_eq!( + v, + FlexVector::, Row>::from_vec(vec![ + Complex::new(-1.0, 0.0), + Complex::new(2.0, 2.0) + ]) + ); } #[test] - fn macro_try_flexvector_success() { + fn fv_iter_macro_empty() { + let v: FlexVector = fv_iter![std::iter::empty::()]; + assert!(v.is_empty()); + + let v: FlexVector = fv_iter![Row; std::iter::empty::()]; + assert!(v.is_empty()); + } + + // -- try_fv_iter! macro -- + + #[test] + fn try_fv_iter_macro_default_column_vec_i32() { let data: Vec> = vec![Ok(1), Ok(2), Ok(3)]; - let fv = try_flexvector!(data).unwrap(); - assert_eq!(fv.as_slice(), &[1, 2, 3]); + let fv = try_fv_iter!(data).unwrap(); + assert_eq!(fv, FlexVector::::from_vec(vec![1, 2, 3])); } #[test] - fn macro_try_flexvector_parse_success() { - let data = vec!["1", "2", "3"]; - let fv = try_flexvector!(data.iter().map(|s| s.parse::())).unwrap(); - assert_eq!(fv.as_slice(), &[1, 2, 3]); + fn try_fv_iter_macro_explicit_column_vec_i32() { + let data: Vec> = vec![Ok(4), Ok(5)]; + let fv = try_fv_iter!(Column; data).unwrap(); + assert_eq!(fv, FlexVector::::from_vec(vec![4, 5])); } #[test] - fn macro_try_flexvector_parse_error() { - let data = vec!["1", "oops", "3"]; - let result = try_flexvector!(data.iter().map(|s| s.parse::())); - assert!(result.is_err()); + fn try_fv_iter_macro_row_vec_i32() { + let data: Vec> = vec![Ok(7), Ok(8), Ok(9)]; + let fv = try_fv_iter!(Row; data).unwrap(); + assert_eq!(fv, FlexVector::::from_vec(vec![7, 8, 9])); } #[test] - fn macro_try_flexvector_custom_error() { - #[derive(Debug, PartialEq)] - enum MyError { - Fail, - } - let data = vec![Ok(1), Err(MyError::Fail), Ok(3)]; - let result = try_flexvector!(data); - assert_eq!(result.unwrap_err(), MyError::Fail); + fn try_fv_iter_macro_default_column_vec_f64() { + let data: Vec> = vec![Ok(1.5), Ok(2.5)]; + let fv = try_fv_iter!(data).unwrap(); + assert_eq!(fv, FlexVector::::from_vec(vec![1.5, 2.5])); + } + + #[test] + fn try_fv_iter_macro_explicit_column_vec_f64() { + let data: Vec> = vec![Ok(3.0), Ok(4.0), Ok(5.0)]; + let fv = try_fv_iter!(Column; data).unwrap(); + assert_eq!(fv, FlexVector::::from_vec(vec![3.0, 4.0, 5.0])); + } + + #[test] + fn try_fv_iter_macro_row_vec_f64() { + let data: Vec> = vec![Ok(-1.0), Ok(-2.0)]; + let fv = try_fv_iter!(Row; data).unwrap(); + assert_eq!(fv, FlexVector::::from_vec(vec![-1.0, -2.0])); + } + + #[test] + fn try_fv_iter_macro_default_column_vec_complex_f64() { + let data: Vec, ()>> = + vec![Ok(Complex::new(1.0, 2.0)), Ok(Complex::new(3.0, 4.0))]; + let fv = try_fv_iter!(data).unwrap(); + assert_eq!( + fv, + FlexVector::, Column>::from_vec(vec![ + Complex::new(1.0, 2.0), + Complex::new(3.0, 4.0) + ]) + ); + } + + #[test] + fn try_fv_iter_macro_explicit_column_vec_complex_f64() { + let data: Vec, ()>> = + vec![Ok(Complex::new(2.0, -2.0)), Ok(Complex::new(0.0, 1.0))]; + let fv = try_fv_iter!(Column; data).unwrap(); + assert_eq!( + fv, + FlexVector::, Column>::from_vec(vec![ + Complex::new(2.0, -2.0), + Complex::new(0.0, 1.0) + ]) + ); + } + + #[test] + fn try_fv_iter_macro_row_vec_complex_f64() { + let data: Vec, ()>> = + vec![Ok(Complex::new(-1.0, 0.0)), Ok(Complex::new(2.0, 2.0))]; + let fv = try_fv_iter!(Row; data).unwrap(); + assert_eq!( + fv, + FlexVector::, Row>::from_vec(vec![ + Complex::new(-1.0, 0.0), + Complex::new(2.0, 2.0) + ]) + ); + } + + #[test] + fn try_fv_iter_macro_error_propagation() { + let data = vec![Ok(1), Err("fail"), Ok(3)]; + let result: Result, &str> = try_fv_iter!(data); + assert_eq!(result.unwrap_err(), "fail"); } #[test] - fn macro_try_flexvector_empty() { - let data: Vec> = vec![]; - let fv = try_flexvector!(data).unwrap(); + fn try_fv_iter_macro_empty() { + let data: Vec> = vec![]; + let fv: FlexVector = try_fv_iter!(data).unwrap(); assert!(fv.is_empty()); } } diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index e427b89..6009d7b 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -1,7 +1,9 @@ //! FlexVector type. -use std::borrow::{Borrow, BorrowMut}; +use std::any::TypeId; +use std::borrow::{Borrow, BorrowMut, Cow}; use std::iter::FromIterator; +use std::marker::PhantomData; use std::ops::{ Deref, DerefMut, Index, IndexMut, Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive, @@ -11,8 +13,10 @@ use crate::{ impl_vector_binop, impl_vector_binop_assign, impl_vector_binop_div, impl_vector_binop_div_assign, impl_vector_scalar_div_op, impl_vector_scalar_div_op_assign, impl_vector_scalar_op, impl_vector_scalar_op_assign, impl_vector_unary_op, - types::traits::VectorBase, types::traits::VectorOps, types::traits::VectorOpsComplex, - types::traits::VectorOpsFloat, + types::orientation::Column, types::orientation::Row, types::orientation::VectorOrientation, + types::traits::Transposable, types::traits::VectorBase, types::traits::VectorHasOrientation, + types::traits::VectorOps, types::traits::VectorOpsComplex, types::traits::VectorOpsFloat, + types::traits::VectorOrientationName, }; use crate::types::utils::{ @@ -32,28 +36,32 @@ use num::{Complex, Zero}; /// /// The length of the vector is determined at runtime and stored on the heap. /// This type is analogous to `Vector` but supports dynamic sizing. -#[derive(Clone, Debug)] -pub struct FlexVector { - /// Ordered n-dimensional scalar values. +#[derive(Clone)] +pub struct FlexVector { pub components: Vec, + _orientation: PhantomData, } +pub type FVector = FlexVector; +pub type ColFVector = FlexVector; +pub type RowFVector = FlexVector; + // ================================ // // Constructors // // ================================ -impl FlexVector { +impl FlexVector { /// Creates a new, empty FlexVector. #[inline] pub fn new() -> Self { - Self { components: Vec::new() } + Self { components: Vec::new(), _orientation: PhantomData } } /// Creates a new FlexVector with a pre-allocated capacity. #[inline] pub fn with_capacity(capacity: usize) -> Self { - Self { components: Vec::with_capacity(capacity) } + Self { components: Vec::with_capacity(capacity), _orientation: PhantomData } } /// Returns a new FlexVector of the given length, filled with zeros. @@ -62,7 +70,7 @@ impl FlexVector { where T: num::Zero + Clone, { - Self { components: vec![T::zero(); len] } + Self { components: vec![T::zero(); len], _orientation: PhantomData } } /// Returns a new FlexVector of the given length, filled with ones. @@ -71,7 +79,7 @@ impl FlexVector { where T: num::One + Clone, { - Self { components: vec![T::one(); len] } + Self { components: vec![T::one(); len], _orientation: PhantomData } } /// Returns a new FlexVector of the given length, filled with the given value. @@ -80,7 +88,7 @@ impl FlexVector { where T: Clone, { - Self { components: vec![value; len] } + Self { components: vec![value; len], _orientation: PhantomData } } /// Creates a new FlexVector from a slice. @@ -89,13 +97,22 @@ impl FlexVector { where T: Clone, { - Self { components: slice.to_vec() } + Self { components: slice.to_vec(), _orientation: PhantomData } } /// Creates a FlexVector from a Vec. #[inline] pub fn from_vec(vec: Vec) -> Self { - Self { components: vec } + Self { components: vec, _orientation: PhantomData } + } + + /// Creates a FlexVector from a Cow. + #[inline] + pub fn from_cow<'a>(data: impl Into>) -> Self + where + T: Clone + 'a, + { + Self::from(data.into()) } /// Fallible error-propagating construction from an iterator of Results. @@ -105,7 +122,7 @@ impl FlexVector { I: IntoIterator>, { let components: Result, E> = iter.into_iter().collect(); - components.map(|vec| FlexVector { components: vec }) + components.map(|vec| FlexVector { components: vec, _orientation: PhantomData }) } /// Creates a new [`FlexVector`] by calling the provided function or closure for each index. @@ -114,7 +131,7 @@ impl FlexVector { /// ``` /// use vectora::prelude::*; /// - /// let v = FlexVector::from_fn(4, |i| i * i); + /// let v = FlexVector::::from_fn(4, |i| i * i); /// assert_eq!(v.as_slice(), &[0, 1, 4, 9]); /// ``` #[inline] @@ -123,7 +140,7 @@ impl FlexVector { F: FnMut(usize) -> T, { let components = (0..len).map(f).collect(); - FlexVector { components } + FlexVector { components, _orientation: PhantomData } } /// Fallibly creates a new [`FlexVector`] by calling the provided function or closure for each index. @@ -146,7 +163,7 @@ impl FlexVector { for i in 0..len { components.push(f(i)?); } - Ok(FlexVector { components }) + Ok(FlexVector { components, _orientation: PhantomData }) } /// Returns a new FlexVector by repeating the pattern until length `len` is reached. @@ -162,7 +179,7 @@ impl FlexVector { )); } let components = pattern.iter().cloned().cycle().take(len).collect(); - Ok(FlexVector { components }) + Ok(FlexVector { components, _orientation: PhantomData }) } } @@ -171,7 +188,7 @@ impl FlexVector { // Default trait impl // // ================================ -impl Default for FlexVector { +impl Default for FlexVector { #[inline] fn default() -> Self { Self::new() @@ -183,12 +200,31 @@ impl Default for FlexVector { // Display trait impl // // ================================ -impl std::fmt::Display for FlexVector +impl std::fmt::Display for FlexVector where T: std::fmt::Debug, + O: VectorOrientationName + 'static, { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self.components) + write!(f, "{} FlexVector {:?}", O::orientation_name(), self.components) + } +} + +// ================================ +// +// Debug trait impl +// +// ================================ +impl std::fmt::Debug for FlexVector +where + T: std::fmt::Debug, + O: VectorOrientationName + 'static, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("FlexVector") + .field("orientation", &O::orientation_name()) + .field("components", &self.components) + .finish() } } @@ -197,9 +233,10 @@ where // FromIterator trait impl // // ================================ -impl FromIterator for FlexVector { +impl FromIterator for FlexVector { + #[inline] fn from_iter>(iter: I) -> Self { - FlexVector { components: iter.into_iter().collect() } + FlexVector { components: iter.into_iter().collect(), _orientation: PhantomData } } } @@ -208,7 +245,7 @@ impl FromIterator for FlexVector { // Deref/DerefMut trait impl // // ================================ -impl Deref for FlexVector { +impl Deref for FlexVector { type Target = [T]; #[inline] fn deref(&self) -> &Self::Target { @@ -216,7 +253,7 @@ impl Deref for FlexVector { } } -impl DerefMut for FlexVector { +impl DerefMut for FlexVector { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { &mut self.components @@ -228,13 +265,13 @@ impl DerefMut for FlexVector { // AsRef/AsMut trait impl // // ================================ -impl AsRef<[T]> for FlexVector { +impl AsRef<[T]> for FlexVector { #[inline] fn as_ref(&self) -> &[T] { &self.components } } -impl AsMut<[T]> for FlexVector { +impl AsMut<[T]> for FlexVector { #[inline] fn as_mut(&mut self) -> &mut [T] { &mut self.components @@ -246,12 +283,14 @@ impl AsMut<[T]> for FlexVector { // Borrow/BorrowMut trait impl // // ================================ -impl Borrow<[T]> for FlexVector { +impl Borrow<[T]> for FlexVector { + #[inline] fn borrow(&self) -> &[T] { &self.components } } -impl BorrowMut<[T]> for FlexVector { +impl BorrowMut<[T]> for FlexVector { + #[inline] fn borrow_mut(&mut self) -> &mut [T] { &mut self.components } @@ -262,23 +301,29 @@ impl BorrowMut<[T]> for FlexVector { // IntoIterator trait impl // // ================================ -impl IntoIterator for FlexVector { +impl IntoIterator for FlexVector { type Item = T; type IntoIter = std::vec::IntoIter; + + #[inline] fn into_iter(self) -> Self::IntoIter { self.components.into_iter() } } -impl<'a, T> IntoIterator for &'a FlexVector { +impl<'a, T, O> IntoIterator for &'a FlexVector { type Item = &'a T; type IntoIter = std::slice::Iter<'a, T>; + + #[inline] fn into_iter(self) -> Self::IntoIter { self.components.iter() } } -impl<'a, T> IntoIterator for &'a mut FlexVector { +impl<'a, T, O> IntoIterator for &'a mut FlexVector { type Item = &'a mut T; type IntoIter = std::slice::IterMut<'a, T>; + + #[inline] fn into_iter(self) -> Self::IntoIter { self.components.iter_mut() } @@ -289,7 +334,7 @@ impl<'a, T> IntoIterator for &'a mut FlexVector { // PartialEq/Eq trait impl // // ================================ -impl PartialEq for FlexVector +impl PartialEq for FlexVector where T: PartialEq, { @@ -298,14 +343,14 @@ where self.components == other.components } } -impl Eq for FlexVector where T: Eq {} +impl Eq for FlexVector where T: Eq {} // ================================ // // PartialOrd/Ord trait impl // // ================================ -impl PartialOrd for FlexVector +impl PartialOrd for FlexVector where T: PartialOrd, { @@ -314,7 +359,7 @@ where self.components.partial_cmp(&other.components) } } -impl Ord for FlexVector +impl Ord for FlexVector where T: Ord, { @@ -329,10 +374,11 @@ where // Hash trait impl // // ================================ -impl std::hash::Hash for FlexVector +impl std::hash::Hash for FlexVector where T: std::hash::Hash, { + #[inline] fn hash(&self, state: &mut H) { self.components.hash(state) } @@ -343,16 +389,59 @@ where // From trait impl // // ================================ -impl From> for FlexVector { +impl From> for FlexVector { #[inline] fn from(vec: Vec) -> Self { - FlexVector { components: vec } + FlexVector { components: vec, _orientation: PhantomData } } } -impl From<&[T]> for FlexVector { +impl From<&[T]> for FlexVector +where + T: Clone, +{ #[inline] fn from(slice: &[T]) -> Self { - FlexVector { components: slice.to_vec() } + FlexVector { components: slice.to_vec(), _orientation: PhantomData } + } +} + +impl From> for FlexVector +where + T: Clone, + [T]: ToOwned>, +{ + #[inline] + fn from(cow: Cow<'_, [T]>) -> Self { + match cow { + Cow::Borrowed(slice) => FlexVector::from_slice(slice), + Cow::Owned(vec) => FlexVector::from_vec(vec), + } + } +} + +impl From<&Cow<'_, [T]>> for FlexVector +where + T: Clone, +{ + #[inline] + fn from(cow: &Cow<'_, [T]>) -> Self { + match cow { + Cow::Borrowed(slice) => FlexVector::from_slice(slice), + Cow::Owned(vec) => FlexVector::from_slice(vec), + } + } +} + +impl From> for FlexVector { + #[inline] + fn from(v: FlexVector) -> Self { + FlexVector { components: v.components, _orientation: PhantomData } + } +} +impl From> for FlexVector { + #[inline] + fn from(v: FlexVector) -> Self { + FlexVector { components: v.components, _orientation: PhantomData } } } @@ -361,50 +450,64 @@ impl From<&[T]> for FlexVector { // Index trait impl // // ================================ -impl Index for FlexVector { +impl Index for FlexVector { type Output = T; + + #[inline] fn index(&self, idx: usize) -> &Self::Output { &self.components[idx] } } -impl Index> for FlexVector { +impl Index> for FlexVector { type Output = [T]; + + #[inline] fn index(&self, range: Range) -> &Self::Output { &self.components[range] } } -impl Index> for FlexVector { +impl Index> for FlexVector { type Output = [T]; + + #[inline] fn index(&self, range: RangeFrom) -> &Self::Output { &self.components[range] } } -impl Index> for FlexVector { +impl Index> for FlexVector { type Output = [T]; + + #[inline] fn index(&self, range: RangeTo) -> &Self::Output { &self.components[range] } } -impl Index for FlexVector { +impl Index for FlexVector { type Output = [T]; + + #[inline] fn index(&self, range: RangeFull) -> &Self::Output { &self.components[range] } } -impl Index> for FlexVector { +impl Index> for FlexVector { type Output = [T]; + + #[inline] fn index(&self, range: RangeInclusive) -> &Self::Output { &self.components[range] } } -impl Index> for FlexVector { +impl Index> for FlexVector { type Output = [T]; + + #[inline] fn index(&self, range: RangeToInclusive) -> &Self::Output { &self.components[range] } @@ -415,43 +518,50 @@ impl Index> for FlexVector { // IndexMut trait impl // // ================================ -impl IndexMut for FlexVector { +impl IndexMut for FlexVector { + #[inline] fn index_mut(&mut self, idx: usize) -> &mut Self::Output { &mut self.components[idx] } } -impl IndexMut> for FlexVector { +impl IndexMut> for FlexVector { + #[inline] fn index_mut(&mut self, range: Range) -> &mut Self::Output { &mut self.components[range] } } -impl IndexMut> for FlexVector { +impl IndexMut> for FlexVector { + #[inline] fn index_mut(&mut self, range: RangeFrom) -> &mut Self::Output { &mut self.components[range] } } -impl IndexMut> for FlexVector { +impl IndexMut> for FlexVector { + #[inline] fn index_mut(&mut self, range: RangeTo) -> &mut Self::Output { &mut self.components[range] } } -impl IndexMut for FlexVector { +impl IndexMut for FlexVector { + #[inline] fn index_mut(&mut self, range: RangeFull) -> &mut Self::Output { &mut self.components[range] } } -impl IndexMut> for FlexVector { +impl IndexMut> for FlexVector { + #[inline] fn index_mut(&mut self, range: RangeInclusive) -> &mut Self::Output { &mut self.components[range] } } -impl IndexMut> for FlexVector { +impl IndexMut> for FlexVector { + #[inline] fn index_mut(&mut self, range: RangeToInclusive) -> &mut Self::Output { &mut self.components[range] } @@ -462,7 +572,7 @@ impl IndexMut> for FlexVector { // Extend trait impl // // ================================ -impl Extend for FlexVector { +impl Extend for FlexVector { #[inline] fn extend>(&mut self, iter: I) { self.components.extend(iter) @@ -474,7 +584,7 @@ impl Extend for FlexVector { // VectorBase trait impl // // ================================ -impl VectorBase for FlexVector { +impl VectorBase for FlexVector { /// Returns an immutable slice of the FlexVector's components. #[inline] fn as_slice(&self) -> &[T] { @@ -488,12 +598,56 @@ impl VectorBase for FlexVector { } } +// ================================ +// +// Transposable trait impl +// +// ================================ + +impl Transposable for FlexVector { + type Transposed = FlexVector; + + #[inline] + fn transpose(self) -> Self::Transposed { + FlexVector { components: self.components, _orientation: PhantomData } + } +} + +impl Transposable for FlexVector { + type Transposed = FlexVector; + + #[inline] + fn transpose(self) -> Self::Transposed { + FlexVector { components: self.components, _orientation: PhantomData } + } +} + +// ================================ +// +// VectorHasOrientation trait impl +// +// ================================ + +impl VectorHasOrientation for FlexVector +where + O: 'static, +{ + #[inline] + fn orientation(&self) -> VectorOrientation { + if self.is_column() { + VectorOrientation::Column + } else { + VectorOrientation::Row + } + } +} + // ================================ // // VectorOps trait impl // // ================================ -impl VectorOps for FlexVector +impl VectorOps for FlexVector where T: Clone, { @@ -587,7 +741,7 @@ where .zip(other.as_slice()) .map(|(a, b)| if a < b { a.clone() } else { b.clone() }) .collect(); - Ok(FlexVector { components }) + Ok(FlexVector { components, _orientation: PhantomData }) } /// Element-wise max @@ -603,7 +757,7 @@ where .zip(other.as_slice()) .map(|(a, b)| if a > b { a.clone() } else { b.clone() }) .collect(); - Ok(FlexVector { components }) + Ok(FlexVector { components, _orientation: PhantomData }) } } @@ -612,7 +766,7 @@ where // VectorOpsFloat trait impl // // ================================ -impl VectorOpsFloat for FlexVector +impl VectorOpsFloat for FlexVector where T: num::Float + Clone + std::iter::Sum, { @@ -795,7 +949,7 @@ where // // ================================ // TODO: add tests -impl VectorOpsComplex for FlexVector> +impl VectorOpsComplex for FlexVector, O> where N: num::Float + Clone + std::iter::Sum, { @@ -994,7 +1148,55 @@ where // Methods // // ================================ -impl FlexVector { +impl FlexVector { + /// ... + #[inline] + pub fn is_row(&self) -> bool + where + O: 'static, + { + TypeId::of::() == TypeId::of::() + } + + /// ... + #[inline] + pub fn is_column(&self) -> bool + where + O: 'static, + { + TypeId::of::() == TypeId::of::() + } + + /// ... + #[inline] + pub fn as_row(&self) -> FlexVector + where + T: Clone, + { + FlexVector { components: self.components.clone(), _orientation: PhantomData } + } + + /// ... + #[inline] + pub fn as_column(&self) -> FlexVector + where + T: Clone, + { + FlexVector { components: self.components.clone(), _orientation: PhantomData } + } + + /// Consumes self and returns a Row-oriented FlexVector. + #[inline] + pub fn into_row(self) -> FlexVector { + FlexVector { components: self.components, _orientation: std::marker::PhantomData } + } + + /// Consumes self and returns a Column-oriented FlexVector. + #[inline] + pub fn into_column(self) -> FlexVector { + FlexVector { components: self.components, _orientation: std::marker::PhantomData } + } + /// Adds an element to the end of the vector. #[inline] pub fn push(&mut self, value: T) { @@ -1052,13 +1254,13 @@ impl FlexVector { /// Returns a new FlexVector with each element mapped to a new value using the provided closure or function. #[inline] - pub fn map(&self, mut f: F) -> FlexVector + pub fn map(&self, mut f: F) -> FlexVector where F: FnMut(T) -> U, T: Clone, { let components = self.components.iter().cloned().map(&mut f).collect(); - FlexVector { components } + FlexVector { components, _orientation: PhantomData } } /// Applies a closure or function to each element, modifying them in place. @@ -1075,24 +1277,24 @@ impl FlexVector { /// Returns a new FlexVector by mapping each element to an iterator and flattening the result. #[inline] - pub fn flat_map(self, mut f: F) -> FlexVector + pub fn flat_map(self, mut f: F) -> FlexVector where F: FnMut(T) -> I, I: IntoIterator, { let components = self.components.into_iter().flat_map(&mut f).collect(); - FlexVector { components } + FlexVector { components, _orientation: PhantomData } } /// Returns a new FlexVector containing only the elements that satisfy the predicate. #[inline] - pub fn filter(&self, mut predicate: F) -> FlexVector + pub fn filter(&self, mut predicate: F) -> Self where F: FnMut(&T) -> bool, T: Clone, { let components = self.components.iter().filter(|&x| predicate(x)).cloned().collect(); - FlexVector { components } + FlexVector { components, _orientation: PhantomData } } /// Reduces the elements to a single value using the provided closure, or returns None if empty. @@ -1118,15 +1320,15 @@ impl FlexVector { /// Zips two FlexVectors into a FlexVector of pairs. #[inline] - pub fn zip(self, other: FlexVector) -> FlexVector<(T, U)> { + pub fn zip(self, other: FlexVector) -> FlexVector<(T, U), O> { let len = self.len().min(other.len()); let components = self.components.into_iter().zip(other.components).take(len).collect(); - FlexVector { components } + FlexVector { components, _orientation: PhantomData } } /// Zips two FlexVectors with a function, producing a FlexVector of the function's output. #[inline] - pub fn zip_with(self, other: FlexVector, mut f: F) -> FlexVector + pub fn zip_with(self, other: FlexVector, mut f: F) -> FlexVector where F: FnMut(T, U) -> R, { @@ -1138,13 +1340,13 @@ impl FlexVector { .map(|(a, b)| f(a, b)) .take(len) .collect(); - FlexVector { components } + FlexVector { components, _orientation: PhantomData } } /// Returns a new FlexVector containing every `step`th element, starting from the first. /// Returns an error if step == 0. #[inline] - pub fn step_by(&self, step: usize) -> Result, VectorError> + pub fn step_by(&self, step: usize) -> Result where T: Clone, { @@ -1152,12 +1354,12 @@ impl FlexVector { return Err(VectorError::ValueError("step must be non-zero".to_string())); } let components = self.components.iter().step_by(step).cloned().collect(); - Ok(FlexVector { components }) + Ok(FlexVector { components, _orientation: PhantomData }) } /// Returns a boolean vector mask where each element is the result of applying the predicate to the corresponding element. #[inline] - pub fn create_mask(&self, predicate: F) -> FlexVector + pub fn create_mask(&self, predicate: F) -> FlexVector where F: FnMut(&T) -> bool, { @@ -1166,7 +1368,7 @@ impl FlexVector { /// Returns a new FlexVector containing elements where the corresponding mask value is true. #[inline] - pub fn filter_by_mask(&self, mask: &[bool]) -> Result, VectorError> + pub fn filter_by_mask(&self, mask: &[bool]) -> Result where T: Clone, { @@ -1181,7 +1383,7 @@ impl FlexVector { .zip(mask) .filter_map(|(x, &m)| if m { Some(x.clone()) } else { None }) .collect(); - Ok(FlexVector { components }) + Ok(FlexVector { components, _orientation: PhantomData }) } // ================================ @@ -1202,39 +1404,39 @@ impl FlexVector { } } -impl FlexVector<&T> +impl FlexVector<&T, O> where T: Clone, { /// Returns a FlexVector of owned values by cloning each referenced element. #[inline] - pub fn cloned(&self) -> FlexVector { + pub fn cloned(&self) -> FlexVector { FlexVector::from_vec(self.components.iter().map(|&x| x.clone()).collect()) } } -impl FlexVector +impl FlexVector where V: IntoIterator, { /// Flattens a FlexVector of iterables into a single FlexVector by concatenating all elements. #[inline] - pub fn flatten(self) -> FlexVector { + pub fn flatten(self) -> FlexVector { let components = self.components.into_iter().flatten().collect(); - FlexVector { components } + FlexVector { components, _orientation: PhantomData } } } -impl<'a, V, T> FlexVector +impl<'a, V, T, O> FlexVector where V: IntoIterator, T: Clone + 'a, { /// Flattens a FlexVector of iterables into a single FlexVector by concatenating all elements as cloned elements. #[inline] - pub fn flatten_cloned(self) -> FlexVector { + pub fn flatten_cloned(self) -> FlexVector { let components = self.components.into_iter().flat_map(|v| v.into_iter().cloned()).collect(); - FlexVector { components } + FlexVector { components, _orientation: PhantomData } } } @@ -1266,6 +1468,7 @@ mod tests { use super::*; use num::complex::ComplexFloat; use num::Complex; + use std::borrow::Cow; use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; @@ -1292,12 +1495,26 @@ mod tests { // // ================================ #[test] - fn test_new() { + fn test_new_default() { let v = FlexVector::::new(); assert_eq!(v.len(), 0); assert!(v.is_empty()); } + #[test] + fn test_new_explicit_column() { + let v: FlexVector = FlexVector::new(); + assert_eq!(v.len(), 0); + assert!(v.is_empty()); + } + + #[test] + fn test_new_explicit_row() { + let v: FlexVector = FlexVector::new(); + assert_eq!(v.len(), 0); + assert!(v.is_empty()); + } + #[test] fn test_with_capacity() { let v = FlexVector::::with_capacity(10); @@ -1421,21 +1638,87 @@ mod tests { assert_eq!(v.as_slice(), &data[..]); } + #[test] + fn test_from_cow_i32_constructor() { + use std::borrow::Cow; + // Borrowed + let slice: &[i32] = &[10, 20, 30]; + let fv = FlexVector::::from_cow(Cow::Borrowed(slice)); + assert_eq!(fv.as_slice(), slice); + + // Owned + let vec = vec![40, 50, 60]; + let fv = FlexVector::::from_cow(Cow::Owned(vec.clone())); + assert_eq!(fv.as_slice(), &vec[..]); + + // Direct slice + let fv = FlexVector::::from_cow(slice); + assert_eq!(fv.as_slice(), slice); + + // Direct Vec + let fv = FlexVector::::from_cow(vec.clone()); + assert_eq!(fv.as_slice(), &vec[..]); + } + + #[test] + fn test_from_cow_f64_constructor() { + use std::borrow::Cow; + // Borrowed + let slice: &[f64] = &[1.5, 2.5, 3.5]; + let fv = FlexVector::::from_cow(Cow::Borrowed(slice)); + assert_eq!(fv.as_slice(), slice); + + // Owned + let vec = vec![4.5, 5.5, 6.5]; + let fv = FlexVector::::from_cow(Cow::Owned(vec.clone())); + assert_eq!(fv.as_slice(), &vec[..]); + + // Direct slice + let fv = FlexVector::::from_cow(slice); + assert_eq!(fv.as_slice(), slice); + + // Direct Vec + let fv = FlexVector::::from_cow(vec.clone()); + assert_eq!(fv.as_slice(), &vec[..]); + } + + #[test] + fn test_from_cow_complex_f64_constructor() { + use std::borrow::Cow; + // Borrowed + let slice: &[Complex] = &[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; + let fv = FlexVector::>::from_cow(Cow::Borrowed(slice)); + assert_eq!(fv.as_slice(), slice); + + // Owned + let vec = vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]; + let fv = FlexVector::>::from_cow(Cow::Owned(vec.clone())); + assert_eq!(fv.as_slice(), &vec[..]); + + // Direct slice + let fv = FlexVector::>::from_cow(slice); + assert_eq!(fv.as_slice(), slice); + + // Direct Vec + let fv = FlexVector::>::from_cow(vec.clone()); + assert_eq!(fv.as_slice(), &vec[..]); + } + #[test] fn test_from_fn_i32() { - let v = FlexVector::from_fn(5, |i| i as i32 * 2); + let v = FVector::from_fn(5, |i| i as i32 * 2); assert_eq!(v.as_slice(), &[0, 2, 4, 6, 8]); } #[test] fn test_from_fn_f64() { - let v = FlexVector::from_fn(4, |i| (i as f64).powi(2)); + let v = FVector::from_fn(4, |i| (i as f64).powi(2)); assert_eq!(v.as_slice(), &[0.0, 1.0, 4.0, 9.0]); } #[test] fn test_from_fn_complex() { - let v = FlexVector::from_fn(3, |i| Complex::new(i as f64, -(i as f64))); + let v = FVector::from_fn(3, |i| Complex::new(i as f64, -(i as f64))); assert_eq!( v.as_slice(), &[Complex::new(0.0, 0.0), Complex::new(1.0, -1.0), Complex::new(2.0, -2.0)] @@ -1444,13 +1727,13 @@ mod tests { #[test] fn test_from_fn_zero_length() { - let v: FlexVector = FlexVector::from_fn(0, |_| 42); + let v: FlexVector = FVector::from_fn(0, |_| 42); assert!(v.is_empty()); } #[test] fn test_from_fn_with_fn_pointer() { - let v = FlexVector::from_fn(4, square_usize); + let v = FVector::from_fn(4, square_usize); assert_eq!(v.as_slice(), &[0, 1, 4, 9]); } @@ -1485,56 +1768,56 @@ mod tests { #[test] fn test_repeat_pattern_i32_basic() { let pattern = [1, 2, 3]; - let v = FlexVector::repeat_pattern(&pattern, 8).unwrap(); + let v = FVector::repeat_pattern(&pattern, 8).unwrap(); assert_eq!(v.as_slice(), &[1, 2, 3, 1, 2, 3, 1, 2]); } #[test] fn test_repeat_pattern_i32_exact_multiple() { let pattern = [4, 5]; - let v = FlexVector::repeat_pattern(&pattern, 6).unwrap(); + let v = FVector::repeat_pattern(&pattern, 6).unwrap(); assert_eq!(v.as_slice(), &[4, 5, 4, 5, 4, 5]); } #[test] fn test_repeat_pattern_i32_len_zero() { let pattern = [1, 2, 3]; - let v = FlexVector::repeat_pattern(&pattern, 0).unwrap(); + let v = FlexVector::::repeat_pattern(&pattern, 0).unwrap(); assert!(v.is_empty()); } #[test] fn test_repeat_pattern_i32_pattern_empty_len_zero() { let pattern: [i32; 0] = []; - let v = FlexVector::repeat_pattern(&pattern, 0).unwrap(); + let v = FVector::repeat_pattern(&pattern, 0).unwrap(); assert!(v.is_empty()); } #[test] fn test_repeat_pattern_i32_pattern_empty_len_nonzero() { let pattern: [i32; 0] = []; - let result = FlexVector::repeat_pattern(&pattern, 3); + let result = FVector::repeat_pattern(&pattern, 3); assert!(result.is_err()); } #[test] fn test_repeat_pattern_f64_basic() { let pattern = [1.5, 2.5]; - let v = FlexVector::repeat_pattern(&pattern, 5).unwrap(); + let v = FVector::repeat_pattern(&pattern, 5).unwrap(); assert_eq!(v.as_slice(), &[1.5, 2.5, 1.5, 2.5, 1.5]); } #[test] fn test_repeat_pattern_f64_empty_pattern_len_zero() { let pattern: [f64; 0] = []; - let v = FlexVector::repeat_pattern(&pattern, 0).unwrap(); + let v = FVector::repeat_pattern(&pattern, 0).unwrap(); assert!(v.is_empty()); } #[test] fn test_repeat_pattern_f64_empty_pattern_len_nonzero() { let pattern: [f64; 0] = []; - let result = FlexVector::repeat_pattern(&pattern, 2); + let result = FVector::repeat_pattern(&pattern, 2); assert!(result.is_err()); } @@ -1542,7 +1825,7 @@ mod tests { fn test_repeat_pattern_complex_f64_basic() { use num::Complex; let pattern = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; - let v = FlexVector::repeat_pattern(&pattern, 5).unwrap(); + let v = FVector::repeat_pattern(&pattern, 5).unwrap(); assert_eq!( v.as_slice(), &[ @@ -1559,7 +1842,7 @@ mod tests { fn test_repeat_pattern_complex_f64_empty_pattern_len_zero() { use num::Complex; let pattern: [Complex; 0] = []; - let v = FlexVector::repeat_pattern(&pattern, 0).unwrap(); + let v = FVector::repeat_pattern(&pattern, 0).unwrap(); assert!(v.is_empty()); } @@ -1567,16 +1850,114 @@ mod tests { fn test_repeat_pattern_complex_f64_empty_pattern_len_nonzero() { use num::Complex; let pattern: [Complex; 0] = []; - let result = FlexVector::repeat_pattern(&pattern, 1); + let result = FVector::repeat_pattern(&pattern, 1); assert!(result.is_err()); } + #[test] + fn test_transpose_i32() { + let v_row = FlexVector::::from_vec(vec![1, 2, 3]); + let v_col = v_row.transpose(); + assert_eq!(v_col, FlexVector::::from_vec(vec![1, 2, 3])); + assert!(v_col.is_column()); + assert!(!v_col.is_row()); + + let v_row2 = v_col.transpose(); + assert_eq!(v_row2, FlexVector::::from_vec(vec![1, 2, 3])); + assert!(v_row2.is_row()); + assert!(!v_row2.is_column()); + } + + #[test] + fn test_transpose_f64() { + let v_row = FlexVector::::from_vec(vec![1.5, -2.0, 0.0]); + let v_col = v_row.transpose(); + assert_eq!(v_col, FlexVector::::from_vec(vec![1.5, -2.0, 0.0])); + assert!(v_col.is_column()); + assert!(!v_col.is_row()); + + let v_row2 = v_col.transpose(); + assert_eq!(v_row2, FlexVector::::from_vec(vec![1.5, -2.0, 0.0])); + assert!(v_row2.is_row()); + assert!(!v_row2.is_column()); + } + + #[test] + fn test_transpose_complex_f64() { + use num::Complex; + let v_row = FlexVector::, Row>::from_vec(vec![ + Complex::new(1.0, 2.0), + Complex::new(3.0, 4.0), + ]); + let v_col = v_row.transpose(); + assert_eq!( + v_col, + FlexVector::, Column>::from_vec(vec![ + Complex::new(1.0, 2.0), + Complex::new(3.0, 4.0) + ]) + ); + assert!(v_col.is_column()); + assert!(!v_col.is_row()); + + let v_row2 = v_col.transpose(); + assert_eq!( + v_row2, + FlexVector::, Row>::from_vec(vec![ + Complex::new(1.0, 2.0), + Complex::new(3.0, 4.0) + ]) + ); + assert!(v_row2.is_row()); + assert!(!v_row2.is_column()); + } + + #[test] + fn test_orientation_i32_column_and_row() { + let v_col = FlexVector::::from_vec(vec![1, 2, 3]); + let v_row = FlexVector::::from_vec(vec![4, 5, 6]); + assert_eq!(v_col.orientation(), VectorOrientation::Column); + assert_eq!(v_row.orientation(), VectorOrientation::Row); + } + + #[test] + fn test_orientation_f64_column_and_row() { + let v_col = FlexVector::::from_vec(vec![1.1, 2.2]); + let v_row = FlexVector::::from_vec(vec![3.3, 4.4]); + assert_eq!(v_col.orientation(), VectorOrientation::Column); + assert_eq!(v_row.orientation(), VectorOrientation::Row); + } + + #[test] + fn test_orientation_complex_f64_column_and_row() { + use num::Complex; + let v_col = FlexVector::, Column>::from_vec(vec![Complex::new(1.0, 2.0)]); + let v_row = FlexVector::, Row>::from_vec(vec![Complex::new(3.0, 4.0)]); + assert_eq!(v_col.orientation(), VectorOrientation::Column); + assert_eq!(v_row.orientation(), VectorOrientation::Row); + } + + #[test] + fn test_orientation_empty_vectors() { + let v_col: FlexVector = FlexVector::new(); + let v_row: FlexVector = FlexVector::new(); + assert_eq!(v_col.orientation(), VectorOrientation::Column); + assert_eq!(v_row.orientation(), VectorOrientation::Row); + } + + #[test] + fn test_orientation_default_type() { + let v_default: FlexVector = FlexVector::new(); + // Default orientation is Column + assert_eq!(v_default.orientation(), VectorOrientation::Column); + } + #[test] fn test_cloned_basic() { let a = 1; let b = 2; let c = 3; - let refs = FlexVector::from_vec(vec![&a, &b, &c]); + let refs = FVector::from_vec(vec![&a, &b, &c]); let owned = refs.cloned(); assert_eq!(owned.as_slice(), &[1, 2, 3]); } @@ -1593,30 +1974,30 @@ mod tests { use num::Complex; let a = Complex::new(1.0, 2.0); let b = Complex::new(3.0, 4.0); - let refs = FlexVector::from_vec(vec![&a, &b]); + let refs = FVector::from_vec(vec![&a, &b]); let owned = refs.cloned(); assert_eq!(owned.as_slice(), &[a, b]); } #[test] fn test_flatten_flexvector_of_flexvector() { - let row1 = FlexVector::from_vec(vec![1, 2]); - let row2 = FlexVector::from_vec(vec![3, 4, 5]); - let nested = FlexVector::from_vec(vec![row1, row2]); + let row1 = FVector::from_vec(vec![1, 2]); + let row2 = FVector::from_vec(vec![3, 4, 5]); + let nested = FVector::from_vec(vec![row1, row2]); let flat = nested.flatten(); assert_eq!(flat.as_slice(), &[1, 2, 3, 4, 5]); } #[test] fn test_flatten_flexvector_of_vec() { - let nested = FlexVector::from_vec(vec![vec![10, 20], vec![30]]); + let nested = FVector::from_vec(vec![vec![10, 20], vec![30]]); let flat = nested.flatten(); assert_eq!(flat.as_slice(), &[10, 20, 30]); } #[test] fn test_flatten_flexvector_of_array() { - let nested = FlexVector::from_vec(vec![[1, 2], [3, 4]]); + let nested = FVector::from_vec(vec![[1, 2], [3, 4]]); let flat = nested.flatten(); assert_eq!(flat.as_slice(), &[1, 2, 3, 4]); } @@ -1625,7 +2006,7 @@ mod tests { fn test_flatten_flexvector_of_slice_refs() { let a = [7, 8]; let b = [9]; - let nested = FlexVector::from_vec(vec![&a[..], &b[..]]); + let nested = FVector::from_vec(vec![&a[..], &b[..]]); let flat = nested.flatten(); assert_eq!(flat.as_slice(), &[&7, &8, &9]); } @@ -1639,7 +2020,7 @@ mod tests { #[test] fn test_flatten_with_empty_inner() { - let nested = FlexVector::from_vec(vec![vec![], vec![1, 2], vec![]]); + let nested = FVector::from_vec(vec![vec![], vec![1, 2], vec![]]); let flat = nested.flatten(); assert_eq!(flat.as_slice(), &[1, 2]); } @@ -1648,7 +2029,7 @@ mod tests { fn test_flatten_cloned_flexvector_of_slice_refs() { let a = [8, 9]; let b = [10]; - let nested = FlexVector::from_vec(vec![&a[..], &b[..]]); + let nested = FVector::from_vec(vec![&a[..], &b[..]]); let flat = nested.flatten_cloned(); let expected: Vec = vec![8, 9, 10]; assert_eq!(flat.as_slice(), expected.as_slice()); @@ -1659,7 +2040,7 @@ mod tests { fn test_flatten_cloned_flexvector_of_refs() { let x = 42; let y = 43; - let nested = FlexVector::from_vec(vec![vec![&x, &y], vec![&x]]); + let nested = FVector::from_vec(vec![vec![&x, &y], vec![&x]]); let flat = nested.flatten_cloned(); assert_eq!(flat.as_slice(), &[42, 43, 42]); let _: &[i32] = flat.as_slice(); // type check: &[i32] @@ -1674,7 +2055,7 @@ mod tests { #[test] fn test_flatten_cloned_with_empty_inner() { - let nested = FlexVector::from_vec(vec![vec![], vec![&1, &2], vec![]]); + let nested = FVector::from_vec(vec![vec![], vec![&1, &2], vec![]]); let flat = nested.flatten_cloned(); assert_eq!(flat.as_slice(), &[1, 2]); } @@ -1721,28 +2102,135 @@ mod tests { // // ================================ #[test] - fn test_display_i32() { - let v = FlexVector::from_vec(vec![1, 2, 3]); - assert_eq!(format!("{}", v), "[1, 2, 3]"); + fn test_display_row_and_column_i32() { + let v_col = FlexVector::::from_vec(vec![1, 2, 3]); + let v_row = FlexVector::::from_vec(vec![4, 5, 6]); + assert_eq!(format!("{}", v_col), "Column FlexVector [1, 2, 3]"); + assert_eq!(format!("{}", v_row), "Row FlexVector [4, 5, 6]"); } #[test] - fn test_display_f64() { - let v = FlexVector::from_vec(vec![-1.0, 2.0, 3.0]); - assert_eq!(format!("{}", v), "[-1.0, 2.0, 3.0]"); + fn test_display_row_and_column_f64() { + let v_col = FlexVector::::from_vec(vec![1.1, 2.2]); + let v_row = FlexVector::::from_vec(vec![3.3, 4.4]); + assert_eq!(format!("{}", v_col), "Column FlexVector [1.1, 2.2]"); + assert_eq!(format!("{}", v_row), "Row FlexVector [3.3, 4.4]"); } #[test] - fn test_display_complex() { + fn test_display_row_and_column_complex_f64() { use num::Complex; - let v = FlexVector::from_vec(vec![Complex::new(0, -1), Complex::new(3, 4)]); - assert_eq!(format!("{}", v), "[Complex { re: 0, im: -1 }, Complex { re: 3, im: 4 }]"); + let v_col = FlexVector::, Column>::from_vec(vec![ + Complex::new(1.0, 2.0), + Complex::new(3.0, 4.0), + ]); + let v_row = FlexVector::, Row>::from_vec(vec![ + Complex::new(5.0, 6.0), + Complex::new(7.0, 8.0), + ]); + assert_eq!( + format!("{}", v_col), + "Column FlexVector [Complex { re: 1.0, im: 2.0 }, Complex { re: 3.0, im: 4.0 }]" + ); + assert_eq!( + format!("{}", v_row), + "Row FlexVector [Complex { re: 5.0, im: 6.0 }, Complex { re: 7.0, im: 8.0 }]" + ); } #[test] fn test_display_empty() { let v = FlexVector::::new(); - assert_eq!(format!("{}", v), "[]"); + assert_eq!(format!("{}", v), "Column FlexVector []"); + let v_col = FlexVector::::new(); + assert_eq!(format!("{}", v_col), "Column FlexVector []"); + let v_col = FlexVector::::new(); + assert_eq!(format!("{}", v_col), "Row FlexVector []"); + } + + // ================================ + // + // Debug trait tests + // + // ================================ + + #[test] + fn test_debug_row_and_column_i32() { + let v_col = FlexVector::::from_vec(vec![1, 2, 3]); + let v_row = FlexVector::::from_vec(vec![4, 5, 6]); + let debug_col = format!("{:?}", v_col); + let debug_row = format!("{:?}", v_row); + assert!(debug_col.contains("FlexVector")); + assert!(debug_col.contains("orientation")); + assert!(debug_col.contains("Column")); + assert!(debug_col.contains("components")); + assert!(debug_col.contains("1")); + assert!(debug_col.contains("2")); + assert!(debug_col.contains("3")); + assert!(debug_row.contains("FlexVector")); + assert!(debug_row.contains("orientation")); + assert!(debug_row.contains("Row")); + assert!(debug_row.contains("components")); + assert!(debug_row.contains("4")); + assert!(debug_row.contains("5")); + assert!(debug_row.contains("6")); + } + + #[test] + fn test_debug_row_and_column_f64() { + let v_col = FlexVector::::from_vec(vec![1.1, 2.2]); + let v_row = FlexVector::::from_vec(vec![3.3, 4.4]); + let debug_col = format!("{:?}", v_col); + let debug_row = format!("{:?}", v_row); + assert!(debug_col.contains("FlexVector")); + assert!(debug_col.contains("orientation")); + assert!(debug_col.contains("Column")); + assert!(debug_col.contains("components")); + assert!(debug_col.contains("1.1")); + assert!(debug_col.contains("2.2")); + assert!(debug_row.contains("FlexVector")); + assert!(debug_row.contains("orientation")); + assert!(debug_row.contains("Row")); + assert!(debug_row.contains("components")); + assert!(debug_row.contains("3.3")); + assert!(debug_row.contains("4.4")); + } + + #[test] + fn test_debug_row_and_column_complex_f64() { + let v_col = FlexVector::, Column>::from_vec(vec![ + Complex::new(1.0, 2.0), + Complex::new(3.0, 4.0), + ]); + let v_row = FlexVector::, Row>::from_vec(vec![ + Complex::new(5.0, 6.0), + Complex::new(7.0, 8.0), + ]); + let debug_col = format!("{:?}", v_col); + let debug_row = format!("{:?}", v_row); + assert!(debug_col.contains("FlexVector")); + assert!(debug_col.contains("orientation")); + assert!(debug_col.contains("Column")); + assert!(debug_col.contains("components")); + assert!(debug_col.contains("1.0")); + assert!(debug_col.contains("2.0")); + assert!(debug_row.contains("FlexVector")); + assert!(debug_row.contains("orientation")); + assert!(debug_row.contains("Row")); + assert!(debug_row.contains("components")); + assert!(debug_row.contains("5.0")); + assert!(debug_row.contains("6.0")); + } + + #[test] + fn test_debug_empty_i32() { + let v: FlexVector = FlexVector::new(); + let debug = format!("{:?}", v); + assert!(debug.contains("FlexVector")); + assert!(debug.contains("orientation")); + assert!(debug.contains("Column")); // Default orientation is Column + assert!(debug.contains("components")); + assert!(debug.contains("[]")); } // ================================ @@ -1827,7 +2315,7 @@ mod tests { // ================================ #[test] fn test_deref_access_slice_methods_i32() { - let v = FlexVector::from_vec(vec![3, 1, 2]); + let v = FVector::from_vec(vec![3, 1, 2]); // Use sort (not implemented in FlexVector directly) let mut sorted = v.clone(); sorted.sort(); @@ -1838,7 +2326,7 @@ mod tests { #[test] fn test_deref_access_slice_methods_f64() { - let v = FlexVector::from_vec(vec![3.5, 1.5, 2.5]); + let v = FVector::from_vec(vec![3.5, 1.5, 2.5]); // Use sort_by let mut sorted = v.clone(); sorted.sort_by(|a, b| a.partial_cmp(b).unwrap()); @@ -1851,7 +2339,7 @@ mod tests { #[test] fn test_deref_access_slice_methods_complex() { use num::Complex; - let v = FlexVector::from_vec(vec![ + let v = FVector::from_vec(vec![ Complex::new(1.0, 2.0), Complex::new(3.0, 4.0), Complex::new(5.0, 6.0), @@ -1871,7 +2359,7 @@ mod tests { #[test] fn test_deref_mut_i32() { - let mut v = FlexVector::from_vec(vec![10, 20, 30]); + let mut v = FVector::from_vec(vec![10, 20, 30]); // Mutate via indexing v[1] = 99; assert_eq!(v.as_slice(), &[10, 99, 30]); @@ -1882,7 +2370,7 @@ mod tests { #[test] fn test_deref_mut_f64() { - let mut v = FlexVector::from_vec(vec![1.5, 2.5, 3.5]); + let mut v = FVector::from_vec(vec![1.5, 2.5, 3.5]); // Mutate via indexing v[0] = -1.5; assert_eq!(v.as_slice(), &[-1.5, 2.5, 3.5]); @@ -1894,7 +2382,7 @@ mod tests { #[test] fn test_deref_mut_complex_f64() { use num::Complex; - let mut v = FlexVector::from_vec(vec![ + let mut v = FVector::from_vec(vec![ Complex::new(1.0, 2.0), Complex::new(3.0, 4.0), Complex::new(5.0, 6.0), @@ -1917,14 +2405,14 @@ mod tests { // ================================ #[test] fn test_asref_i32() { - let v = FlexVector::from_vec(vec![1, 2, 3]); + let v = FVector::from_vec(vec![1, 2, 3]); let slice: &[i32] = v.as_ref(); assert_eq!(slice, &[1, 2, 3]); } #[test] fn test_asref_f64() { - let v = FlexVector::from_vec(vec![1.1, 2.2, 3.3]); + let v = FVector::from_vec(vec![1.1, 2.2, 3.3]); let slice: &[f64] = v.as_ref(); assert_eq!(slice, &[1.1, 2.2, 3.3]); } @@ -1932,14 +2420,14 @@ mod tests { #[test] fn test_asref_complex_f64() { use num::Complex; - let v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let slice: &[Complex] = v.as_ref(); assert_eq!(slice, &[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); } #[test] fn test_asmut_i32() { - let mut v = FlexVector::from_vec(vec![1, 2, 3]); + let mut v = FVector::from_vec(vec![1, 2, 3]); let slice: &mut [i32] = v.as_mut(); slice[0] = 10; slice[2] = 30; @@ -1948,7 +2436,7 @@ mod tests { #[test] fn test_asmut_f64() { - let mut v = FlexVector::from_vec(vec![1.1, 2.2, 3.3]); + let mut v = FVector::from_vec(vec![1.1, 2.2, 3.3]); let slice: &mut [f64] = v.as_mut(); slice[1] = 9.9; assert_eq!(v.as_slice(), &[1.1, 9.9, 3.3]); @@ -1957,7 +2445,7 @@ mod tests { #[test] fn test_asmut_complex_f64() { use num::Complex; - let mut v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let mut v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let slice: &mut [Complex] = v.as_mut(); slice[0].re = 10.0; slice[1].im = 40.0; @@ -1973,7 +2461,7 @@ mod tests { #[test] fn test_borrow_i32() { use std::borrow::Borrow; - let v = FlexVector::from_vec(vec![1, 2, 3]); + let v = FVector::from_vec(vec![1, 2, 3]); let slice: &[i32] = v.borrow(); assert_eq!(slice, &[1, 2, 3]); } @@ -1981,7 +2469,7 @@ mod tests { #[test] fn test_borrow_f64() { use std::borrow::Borrow; - let v = FlexVector::from_vec(vec![1.1, 2.2, 3.3]); + let v = FVector::from_vec(vec![1.1, 2.2, 3.3]); let slice: &[f64] = v.borrow(); assert_eq!(slice, &[1.1, 2.2, 3.3]); } @@ -1990,7 +2478,7 @@ mod tests { fn test_borrow_complex_f64() { use num::Complex; use std::borrow::Borrow; - let v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let slice: &[Complex] = v.borrow(); assert_eq!(slice, &[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); } @@ -1998,7 +2486,7 @@ mod tests { #[test] fn test_borrow_mut_i32() { use std::borrow::BorrowMut; - let mut v = FlexVector::from_vec(vec![1, 2, 3]); + let mut v = FVector::from_vec(vec![1, 2, 3]); { let slice: &mut [i32] = v.borrow_mut(); slice[0] = 10; @@ -2010,7 +2498,7 @@ mod tests { #[test] fn test_borrow_mut_f64() { use std::borrow::BorrowMut; - let mut v = FlexVector::from_vec(vec![1.1, 2.2, 3.3]); + let mut v = FVector::from_vec(vec![1.1, 2.2, 3.3]); { let slice: &mut [f64] = v.borrow_mut(); slice[1] = 9.9; @@ -2022,7 +2510,7 @@ mod tests { fn test_borrow_mut_complex_f64() { use num::Complex; use std::borrow::BorrowMut; - let mut v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let mut v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); { let slice: &mut [Complex] = v.borrow_mut(); slice[0].re = 10.0; @@ -2038,14 +2526,14 @@ mod tests { // ================================ #[test] fn test_into_iter_i32() { - let v = FlexVector::from_vec(vec![1, 2, 3]); + let v = FVector::from_vec(vec![1, 2, 3]); let collected: Vec<_> = v.into_iter().collect(); assert_eq!(collected, vec![1, 2, 3]); } #[test] fn test_into_iter_f64() { - let v = FlexVector::from_vec(vec![1.1, 2.2, 3.3]); + let v = FVector::from_vec(vec![1.1, 2.2, 3.3]); let collected: Vec<_> = v.into_iter().collect(); assert_eq!(collected, vec![1.1, 2.2, 3.3]); } @@ -2053,21 +2541,21 @@ mod tests { #[test] fn test_into_iter_complex_f64() { use num::Complex; - let v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let collected: Vec<_> = v.into_iter().collect(); assert_eq!(collected, vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); } #[test] fn test_iter_ref_i32() { - let v = FlexVector::from_vec(vec![10, 20, 30]); + let v = FVector::from_vec(vec![10, 20, 30]); let collected: Vec<_> = (&v).into_iter().copied().collect(); assert_eq!(collected, vec![10, 20, 30]); } #[test] fn test_iter_ref_f64() { - let v = FlexVector::from_vec(vec![1.5, 2.5, 3.5]); + let v = FVector::from_vec(vec![1.5, 2.5, 3.5]); let collected: Vec<_> = (&v).into_iter().copied().collect(); assert_eq!(collected, vec![1.5, 2.5, 3.5]); } @@ -2075,14 +2563,14 @@ mod tests { #[test] fn test_iter_ref_complex_f64() { use num::Complex; - let v = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); + let v = FVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); let collected: Vec<_> = (&v).into_iter().cloned().collect(); assert_eq!(collected, vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); } #[test] fn test_iter_mutable_i32() { - let mut v = FlexVector::from_vec(vec![1, 2, 3]); + let mut v = FVector::from_vec(vec![1, 2, 3]); for x in &mut v { *x *= 10; } @@ -2091,7 +2579,7 @@ mod tests { #[test] fn test_iter_mutable_f64() { - let mut v = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let mut v = FVector::from_vec(vec![1.0, 2.0, 3.0]); for x in &mut v { *x += 0.5; } @@ -2101,7 +2589,7 @@ mod tests { #[test] fn test_iter_mutable_complex_f64() { use num::Complex; - let mut v = FlexVector::from_vec(vec![Complex::new(1.0, 1.0), Complex::new(2.0, 2.0)]); + let mut v = FVector::from_vec(vec![Complex::new(1.0, 1.0), Complex::new(2.0, 2.0)]); for x in &mut v { x.re *= 2.0; x.im *= 3.0; @@ -2116,18 +2604,18 @@ mod tests { // ================================ #[test] fn test_partial_eq_i32() { - let v1 = FlexVector::from_vec(vec![1, 2, 3]); - let v2 = FlexVector::from_vec(vec![1, 2, 3]); - let v3 = FlexVector::from_vec(vec![3, 2, 1]); + let v1 = FVector::from_vec(vec![1, 2, 3]); + let v2 = FVector::from_vec(vec![1, 2, 3]); + let v3 = FVector::from_vec(vec![3, 2, 1]); assert_eq!(v1, v2); assert_ne!(v1, v3); } #[test] fn test_partial_eq_f64() { - let v1 = FlexVector::from_vec(vec![1.1, 2.2, 3.3]); - let v2 = FlexVector::from_vec(vec![1.1, 2.2, 3.3]); - let v3 = FlexVector::from_vec(vec![3.3, 2.2, 1.1]); + let v1 = FVector::from_vec(vec![1.1, 2.2, 3.3]); + let v2 = FVector::from_vec(vec![1.1, 2.2, 3.3]); + let v3 = FVector::from_vec(vec![3.3, 2.2, 1.1]); assert_eq!(v1, v2); assert_ne!(v1, v3); } @@ -2135,9 +2623,9 @@ mod tests { #[test] fn test_partial_eq_complex_f64() { use num::Complex; - let v1 = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); - let v2 = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); - let v3 = FlexVector::from_vec(vec![Complex::new(4.0, 3.0), Complex::new(2.0, 1.0)]); + let v1 = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v2 = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v3 = FVector::from_vec(vec![Complex::new(4.0, 3.0), Complex::new(2.0, 1.0)]); assert_eq!(v1, v2); assert_ne!(v1, v3); } @@ -2151,28 +2639,28 @@ mod tests { #[test] fn test_partial_eq_different_lengths() { - let v1 = FlexVector::from_vec(vec![1, 2]); - let v2 = FlexVector::from_vec(vec![1, 2, 3]); + let v1 = FVector::from_vec(vec![1, 2]); + let v2 = FVector::from_vec(vec![1, 2, 3]); assert_ne!(v1, v2); } #[test] fn test_partial_eq_f64_nan() { - let v1 = FlexVector::from_vec(vec![f64::NAN, 1.0]); - let v2 = FlexVector::from_vec(vec![f64::NAN, 1.0]); + let v1 = FVector::from_vec(vec![f64::NAN, 1.0]); + let v2 = FVector::from_vec(vec![f64::NAN, 1.0]); // NaN != NaN, so these should not be equal assert_ne!(v1, v2); - let v3 = FlexVector::from_vec(vec![f64::NAN, 1.0]); - let v4 = FlexVector::from_vec(vec![f64::NAN, 2.0]); + let v3 = FVector::from_vec(vec![f64::NAN, 1.0]); + let v4 = FVector::from_vec(vec![f64::NAN, 2.0]); assert_ne!(v3, v4); } #[test] fn test_partial_eq_f64_zero_negzero() { - let v1 = FlexVector::from_vec(vec![0.0, -0.0]); - let v2 = FlexVector::from_vec(vec![0.0, -0.0]); - let v3 = FlexVector::from_vec(vec![-0.0, 0.0]); + let v1 = FVector::from_vec(vec![0.0, -0.0]); + let v2 = FVector::from_vec(vec![0.0, -0.0]); + let v3 = FVector::from_vec(vec![-0.0, 0.0]); // 0.0 == -0.0 in Rust assert_eq!(v1, v2); assert_eq!(v1, v3); @@ -2180,9 +2668,9 @@ mod tests { #[test] fn test_partial_eq_f64_infinity() { - let v1 = FlexVector::from_vec(vec![f64::INFINITY, f64::NEG_INFINITY]); - let v2 = FlexVector::from_vec(vec![f64::INFINITY, f64::NEG_INFINITY]); - let v3 = FlexVector::from_vec(vec![f64::NEG_INFINITY, f64::INFINITY]); + let v1 = FVector::from_vec(vec![f64::INFINITY, f64::NEG_INFINITY]); + let v2 = FVector::from_vec(vec![f64::INFINITY, f64::NEG_INFINITY]); + let v3 = FVector::from_vec(vec![f64::NEG_INFINITY, f64::INFINITY]); assert_eq!(v1, v2); assert_ne!(v1, v3); } @@ -2191,21 +2679,21 @@ mod tests { fn test_partial_eq_complex_nan() { use num::Complex; let nan = f64::NAN; - let v1 = FlexVector::from_vec(vec![Complex::new(nan, 1.0)]); - let v2 = FlexVector::from_vec(vec![Complex::new(nan, 1.0)]); + let v1 = FVector::from_vec(vec![Complex::new(nan, 1.0)]); + let v2 = FVector::from_vec(vec![Complex::new(nan, 1.0)]); // Complex::new(NaN, 1.0) != Complex::new(NaN, 1.0) assert_ne!(v1, v2); - let v3 = FlexVector::from_vec(vec![Complex::new(1.0, nan)]); - let v4 = FlexVector::from_vec(vec![Complex::new(1.0, nan)]); + let v3 = FVector::from_vec(vec![Complex::new(1.0, nan)]); + let v4 = FVector::from_vec(vec![Complex::new(1.0, nan)]); assert_ne!(v3, v4); } #[test] fn test_partial_eq_complex_zero_negzero() { use num::Complex; - let v1 = FlexVector::from_vec(vec![Complex::new(0.0, -0.0)]); - let v2 = FlexVector::from_vec(vec![Complex::new(-0.0, 0.0)]); + let v1 = FVector::from_vec(vec![Complex::new(0.0, -0.0)]); + let v2 = FVector::from_vec(vec![Complex::new(-0.0, 0.0)]); // 0.0 == -0.0 for both real and imaginary parts assert_eq!(v1, v2); } @@ -2213,10 +2701,10 @@ mod tests { #[test] fn test_partial_eq_complex_infinity() { use num::Complex; - let v1 = FlexVector::from_vec(vec![Complex::new(f64::INFINITY, 1.0)]); - let v2 = FlexVector::from_vec(vec![Complex::new(f64::INFINITY, 1.0)]); - let v3 = FlexVector::from_vec(vec![Complex::new(1.0, f64::INFINITY)]); - let v4 = FlexVector::from_vec(vec![Complex::new(1.0, f64::INFINITY)]); + let v1 = FVector::from_vec(vec![Complex::new(f64::INFINITY, 1.0)]); + let v2 = FVector::from_vec(vec![Complex::new(f64::INFINITY, 1.0)]); + let v3 = FVector::from_vec(vec![Complex::new(1.0, f64::INFINITY)]); + let v4 = FVector::from_vec(vec![Complex::new(1.0, f64::INFINITY)]); assert_eq!(v1, v2); assert_eq!(v3, v4); assert_ne!(v1, v3); @@ -2224,23 +2712,23 @@ mod tests { #[test] fn test_eq_trait_i32() { - let v1 = FlexVector::from_vec(vec![5, 6, 7]); - let v2 = FlexVector::from_vec(vec![5, 6, 7]); + let v1 = FVector::from_vec(vec![5, 6, 7]); + let v2 = FVector::from_vec(vec![5, 6, 7]); assert!(v1.eq(&v2)); } #[test] fn test_eq_trait_f64() { - let v1 = FlexVector::from_vec(vec![0.0, -0.0]); - let v2 = FlexVector::from_vec(vec![0.0, -0.0]); + let v1 = FVector::from_vec(vec![0.0, -0.0]); + let v2 = FVector::from_vec(vec![0.0, -0.0]); assert!(v1.eq(&v2)); } #[test] fn test_eq_trait_complex_f64() { use num::Complex; - let v1 = FlexVector::from_vec(vec![Complex::new(0.0, 1.0)]); - let v2 = FlexVector::from_vec(vec![Complex::new(0.0, 1.0)]); + let v1 = FVector::from_vec(vec![Complex::new(0.0, 1.0)]); + let v2 = FVector::from_vec(vec![Complex::new(0.0, 1.0)]); assert!(v1.eq(&v2)); } @@ -2251,9 +2739,9 @@ mod tests { // ================================ #[test] fn test_partial_ord_i32() { - let v1 = FlexVector::from_vec(vec![1, 2, 3]); - let v2 = FlexVector::from_vec(vec![1, 2, 4]); - let v3 = FlexVector::from_vec(vec![1, 2, 3]); + let v1 = FVector::from_vec(vec![1, 2, 3]); + let v2 = FVector::from_vec(vec![1, 2, 4]); + let v3 = FVector::from_vec(vec![1, 2, 3]); assert!(v1 < v2); assert!(v2 > v1); assert!(v1 <= v3); @@ -2265,9 +2753,9 @@ mod tests { #[test] fn test_ord_i32() { - let v1 = FlexVector::from_vec(vec![1, 2, 3]); - let v2 = FlexVector::from_vec(vec![1, 2, 4]); - let v3 = FlexVector::from_vec(vec![1, 2, 3]); + let v1 = FVector::from_vec(vec![1, 2, 3]); + let v2 = FVector::from_vec(vec![1, 2, 4]); + let v3 = FVector::from_vec(vec![1, 2, 3]); assert_eq!(v1.cmp(&v2), std::cmp::Ordering::Less); assert_eq!(v2.cmp(&v1), std::cmp::Ordering::Greater); assert_eq!(v1.cmp(&v3), std::cmp::Ordering::Equal); @@ -2275,9 +2763,9 @@ mod tests { #[test] fn test_partial_ord_f64() { - let v1 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); - let v2 = FlexVector::from_vec(vec![1.0, 2.0, 4.0]); - let v3 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = FVector::from_vec(vec![1.0, 2.0, 3.0]); + let v2 = FVector::from_vec(vec![1.0, 2.0, 4.0]); + let v3 = FVector::from_vec(vec![1.0, 2.0, 3.0]); assert_eq!(v1.partial_cmp(&v2), Some(std::cmp::Ordering::Less)); assert_eq!(v2.partial_cmp(&v1), Some(std::cmp::Ordering::Greater)); assert_eq!(v1.partial_cmp(&v3), Some(std::cmp::Ordering::Equal)); @@ -2285,8 +2773,8 @@ mod tests { #[test] fn test_partial_ord_f64_nan() { - let v1 = FlexVector::from_vec(vec![1.0, f64::NAN]); - let v2 = FlexVector::from_vec(vec![1.0, 2.0]); + let v1 = FVector::from_vec(vec![1.0, f64::NAN]); + let v2 = FVector::from_vec(vec![1.0, 2.0]); // Comparison with NaN yields None assert_eq!(v1.partial_cmp(&v2), None); assert_eq!(v2.partial_cmp(&v1), None); @@ -2294,10 +2782,10 @@ mod tests { #[test] fn test_partial_ord_f64_infinity() { - let v1 = FlexVector::from_vec(vec![1.0, f64::INFINITY]); - let v2 = FlexVector::from_vec(vec![1.0, f64::NEG_INFINITY]); - let v3 = FlexVector::from_vec(vec![1.0, f64::INFINITY]); - let v4 = FlexVector::from_vec(vec![1.0, 1.0]); + let v1 = FVector::from_vec(vec![1.0, f64::INFINITY]); + let v2 = FVector::from_vec(vec![1.0, f64::NEG_INFINITY]); + let v3 = FVector::from_vec(vec![1.0, f64::INFINITY]); + let v4 = FVector::from_vec(vec![1.0, 1.0]); // INFINITY > NEG_INFINITY assert_eq!(v1.partial_cmp(&v2), Some(std::cmp::Ordering::Greater)); assert_eq!(v2.partial_cmp(&v1), Some(std::cmp::Ordering::Less)); @@ -2315,9 +2803,9 @@ mod tests { // ================================ #[test] fn test_hash_i32() { - let v1 = FlexVector::from_vec(vec![1, 2, 3]); - let v2 = FlexVector::from_vec(vec![1, 2, 3]); - let v3 = FlexVector::from_vec(vec![3, 2, 1]); + let v1 = FVector::from_vec(vec![1, 2, 3]); + let v2 = FVector::from_vec(vec![1, 2, 3]); + let v3 = FVector::from_vec(vec![3, 2, 1]); let mut hasher1 = DefaultHasher::new(); v1.hash(&mut hasher1); @@ -2338,9 +2826,9 @@ mod tests { #[test] fn test_hash_complex_i32() { use num::Complex; - let v1 = FlexVector::from_vec(vec![Complex::new(1, 2), Complex::new(3, 4)]); - let v2 = FlexVector::from_vec(vec![Complex::new(1, 2), Complex::new(3, 4)]); - let v3 = FlexVector::from_vec(vec![Complex::new(4, 3), Complex::new(2, 1)]); + let v1 = FVector::from_vec(vec![Complex::new(1, 2), Complex::new(3, 4)]); + let v2 = FVector::from_vec(vec![Complex::new(1, 2), Complex::new(3, 4)]); + let v3 = FVector::from_vec(vec![Complex::new(4, 3), Complex::new(2, 1)]); let mut hasher1 = DefaultHasher::new(); v1.hash(&mut hasher1); @@ -2383,14 +2871,22 @@ mod tests { fn test_from_vec_i32() { let vec = vec![1, 2, 3]; let fv: FlexVector = FlexVector::from(vec.clone()); + let fv_col: FlexVector = vec.clone().into(); + let fv_row: FlexVector = vec.clone().into(); assert_eq!(fv.as_slice(), &vec[..]); + assert!(fv_col.is_column()); + assert!(fv_row.is_row()); } #[test] fn test_from_vec_f64() { let vec = vec![1.1, 2.2, 3.3]; let fv: FlexVector = FlexVector::from(vec.clone()); + let fv_col: FlexVector = vec.clone().into(); + let fv_row: FlexVector = vec.clone().into(); assert_eq!(fv.as_slice(), &vec[..]); + assert!(fv_col.is_column()); + assert!(fv_row.is_row()); } #[test] @@ -2398,29 +2894,165 @@ mod tests { use num::Complex; let vec = vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; let fv: FlexVector> = FlexVector::from(vec.clone()); + let fv_col: FlexVector, Column> = vec.clone().into(); + let fv_row: FlexVector, Row> = vec.clone().into(); assert_eq!(fv.as_slice(), &vec[..]); + assert!(fv_col.is_column()); + assert!(fv_row.is_row()); } #[test] fn test_from_slice_i32() { let slice: &[i32] = &[4, 5, 6]; - let fv = FlexVector::from(slice); + let fv = FVector::from(slice); + let fv_col: FlexVector = slice.into(); + let fv_row: FlexVector = slice.into(); assert_eq!(fv.as_slice(), slice); + assert!(fv_col.is_column()); + assert!(fv_row.is_row()); } #[test] fn test_from_slice_f64() { let slice: &[f64] = &[4.4, 5.5, 6.6]; - let fv = FlexVector::from(slice); + let fv = FVector::from(slice); + let fv_col: FlexVector = slice.into(); + let fv_row: FlexVector = slice.into(); assert_eq!(fv.as_slice(), slice); + assert!(fv_col.is_column()); + assert!(fv_row.is_row()); } #[test] fn test_from_slice_complex_f64() { use num::Complex; let slice: &[Complex] = &[Complex::new(7.0, 8.0), Complex::new(9.0, 10.0)]; - let fv = FlexVector::from(slice); + let fv = FVector::from(slice); + let fv_col: FlexVector, Column> = slice.into(); + let fv_row: FlexVector, Row> = slice.into(); assert_eq!(fv.as_slice(), slice); + assert!(fv_col.is_column()); + assert!(fv_row.is_row()); + } + + #[test] + fn test_from_cow_i32() { + // Borrowed + let slice: &[i32] = &[1, 2, 3]; + let cow = Cow::Borrowed(slice); + let fv: FlexVector = FlexVector::from(cow.clone()); + assert_eq!(fv.as_slice(), slice); + + // Owned + let owned_vec = vec![4, 5, 6]; + let cow: Cow<'_, [i32]> = Cow::Owned(owned_vec.clone()); + let fv: FlexVector = FlexVector::from(cow.clone()); + assert_eq!(fv.as_slice(), &owned_vec[..]); + + // From &Cow + let fv: FlexVector = FlexVector::from(&cow); + assert_eq!(fv.as_slice(), &owned_vec[..]); + } + + #[test] + fn test_from_cow_f64() { + // Borrowed + let slice: &[f64] = &[1.1, 2.2, 3.3]; + let cow = Cow::Borrowed(slice); + let fv: FlexVector = FlexVector::from(cow.clone()); + assert_eq!(fv.as_slice(), slice); + + // Owned + let owned_vec = vec![4.4, 5.5, 6.6]; + let cow: Cow<'_, [f64]> = Cow::Owned(owned_vec.clone()); + let fv: FlexVector = FlexVector::from(cow.clone()); + assert_eq!(fv.as_slice(), &owned_vec[..]); + + // From &Cow + let fv: FlexVector = FlexVector::from(&cow); + assert_eq!(fv.as_slice(), &owned_vec[..]); + } + + #[test] + fn test_from_cow_complex_f64() { + use num::Complex; + // Borrowed + let slice: &[Complex] = &[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; + let cow = Cow::Borrowed(slice); + let fv: FlexVector> = FlexVector::from(cow.clone()); + assert_eq!(fv.as_slice(), slice); + + // Owned + let owned_vec = vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]; + let cow: Cow<'_, [Complex]> = Cow::Owned(owned_vec.clone()); + let fv: FlexVector> = FlexVector::from(cow.clone()); + assert_eq!(fv.as_slice(), &owned_vec[..]); + + // From &Cow + let fv: FlexVector> = FlexVector::from(&cow); + assert_eq!(fv.as_slice(), &owned_vec[..]); + } + + #[test] + fn test_from_column_to_row() { + let col = FlexVector::::from_vec(vec![1, 2, 3]); + let row: FlexVector = col.into(); + assert_eq!(row.as_slice(), &[1, 2, 3]); + assert!(row.is_row()); + assert!(!row.is_column()); + } + + #[test] + fn test_from_row_to_column() { + let row = FlexVector::::from_vec(vec![4, 5, 6]); + let col: FlexVector = row.into(); + assert_eq!(col.as_slice(), &[4, 5, 6]); + assert!(col.is_column()); + assert!(!col.is_row()); + } + + #[test] + fn test_from_column_to_row_f64() { + let col = FlexVector::::from_vec(vec![1.1, 2.2]); + let row: FlexVector = col.into(); + assert_eq!(row.as_slice(), &[1.1, 2.2]); + assert!(row.is_row()); + assert!(!row.is_column()); + } + + #[test] + fn test_from_row_to_column_f64() { + let row = FlexVector::::from_vec(vec![3.3, 4.4]); + let col: FlexVector = row.into(); + assert_eq!(col.as_slice(), &[3.3, 4.4]); + assert!(col.is_column()); + assert!(!col.is_row()); + } + + #[test] + fn test_from_column_to_row_complex() { + use num::Complex; + let col = FlexVector::, Column>::from_vec(vec![ + Complex::new(1.0, 2.0), + Complex::new(3.0, 4.0), + ]); + let row: FlexVector, Row> = col.into(); + assert_eq!(row.as_slice(), &[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + assert!(row.is_row()); + assert!(!row.is_column()); + } + + #[test] + fn test_from_row_to_column_complex() { + use num::Complex; + let row = FlexVector::, Row>::from_vec(vec![ + Complex::new(5.0, 6.0), + Complex::new(7.0, 8.0), + ]); + let col: FlexVector, Column> = row.into(); + assert_eq!(col.as_slice(), &[Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); + assert!(col.is_column()); + assert!(!col.is_row()); } // ================================ @@ -2431,7 +3063,7 @@ mod tests { #[test] fn test_index_usize() { - let v = FlexVector::from_vec(vec![10, 20, 30, 40]); + let v = FVector::from_vec(vec![10, 20, 30, 40]); assert_eq!(v[0], 10); assert_eq!(v[1], 20); assert_eq!(v[3], 40); @@ -2440,13 +3072,13 @@ mod tests { #[test] #[should_panic] fn test_index_usize_out_of_bounds() { - let v = FlexVector::from_vec(vec![1, 2, 3]); + let v = FVector::from_vec(vec![1, 2, 3]); let _ = v[10]; } #[test] fn test_index_range() { - let v = FlexVector::from_vec(vec![1, 2, 3, 4, 5]); + let v = FVector::from_vec(vec![1, 2, 3, 4, 5]); assert_eq!(&v[1..4], &[2, 3, 4]); assert_eq!(&v[0..2], &[1, 2]); assert_eq!(&v[2..5], &[3, 4, 5]); @@ -2454,34 +3086,34 @@ mod tests { #[test] fn test_index_range_from() { - let v = FlexVector::from_vec(vec![1, 2, 3, 4, 5]); + let v = FVector::from_vec(vec![1, 2, 3, 4, 5]); assert_eq!(&v[2..], &[3, 4, 5]); assert_eq!(&v[0..], &[1, 2, 3, 4, 5]); } #[test] fn test_index_range_to() { - let v = FlexVector::from_vec(vec![1, 2, 3, 4, 5]); + let v = FVector::from_vec(vec![1, 2, 3, 4, 5]); assert_eq!(&v[..3], &[1, 2, 3]); assert_eq!(&v[..1], &[1]); } #[test] fn test_index_range_full() { - let v = FlexVector::from_vec(vec![1, 2, 3]); + let v = FVector::from_vec(vec![1, 2, 3]); assert_eq!(&v[..], &[1, 2, 3]); } #[test] fn test_index_range_inclusive() { - let v = FlexVector::from_vec(vec![1, 2, 3, 4, 5]); + let v = FVector::from_vec(vec![1, 2, 3, 4, 5]); assert_eq!(&v[1..=3], &[2, 3, 4]); assert_eq!(&v[0..=4], &[1, 2, 3, 4, 5]); } #[test] fn test_index_range_to_inclusive() { - let v = FlexVector::from_vec(vec![1, 2, 3, 4, 5]); + let v = FVector::from_vec(vec![1, 2, 3, 4, 5]); assert_eq!(&v[..=2], &[1, 2, 3]); assert_eq!(&v[..=0], &[1]); } @@ -2489,21 +3121,21 @@ mod tests { #[test] #[should_panic] fn test_index_range_out_of_bounds() { - let v = FlexVector::from_vec(vec![1, 2, 3]); + let v = FVector::from_vec(vec![1, 2, 3]); let _ = &v[2..5]; } #[test] #[should_panic] fn test_index_range_inclusive_out_of_bounds() { - let v = FlexVector::from_vec(vec![1, 2, 3]); + let v = FVector::from_vec(vec![1, 2, 3]); let _ = &v[1..=5]; } #[test] #[should_panic] fn test_index_range_to_inclusive_out_of_bounds() { - let v = FlexVector::from_vec(vec![1, 2, 3]); + let v = FVector::from_vec(vec![1, 2, 3]); let _ = &v[..=5]; } @@ -2515,7 +3147,7 @@ mod tests { #[test] fn test_index_mut_usize() { - let mut v = FlexVector::from_vec(vec![1, 2, 3]); + let mut v = FVector::from_vec(vec![1, 2, 3]); v[0] = 10; v[2] = 30; assert_eq!(v.as_slice(), &[10, 2, 30]); @@ -2524,48 +3156,48 @@ mod tests { #[test] #[should_panic] fn test_index_mut_usize_out_of_bounds() { - let mut v = FlexVector::from_vec(vec![1, 2, 3]); + let mut v = FVector::from_vec(vec![1, 2, 3]); v[10] = 99; } #[test] fn test_index_mut_range() { - let mut v = FlexVector::from_vec(vec![1, 2, 3, 4, 5]); + let mut v = FVector::from_vec(vec![1, 2, 3, 4, 5]); v[1..4].copy_from_slice(&[20, 30, 40]); assert_eq!(v.as_slice(), &[1, 20, 30, 40, 5]); } #[test] fn test_index_mut_range_from() { - let mut v = FlexVector::from_vec(vec![1, 2, 3, 4, 5]); + let mut v = FVector::from_vec(vec![1, 2, 3, 4, 5]); v[2..].copy_from_slice(&[99, 100, 101]); assert_eq!(v.as_slice(), &[1, 2, 99, 100, 101]); } #[test] fn test_index_mut_range_to() { - let mut v = FlexVector::from_vec(vec![1, 2, 3, 4, 5]); + let mut v = FVector::from_vec(vec![1, 2, 3, 4, 5]); v[..3].copy_from_slice(&[7, 8, 9]); assert_eq!(v.as_slice(), &[7, 8, 9, 4, 5]); } #[test] fn test_index_mut_range_full() { - let mut v = FlexVector::from_vec(vec![1, 2, 3]); + let mut v = FVector::from_vec(vec![1, 2, 3]); v[..].copy_from_slice(&[10, 20, 30]); assert_eq!(v.as_slice(), &[10, 20, 30]); } #[test] fn test_index_mut_range_inclusive() { - let mut v = FlexVector::from_vec(vec![1, 2, 3, 4, 5]); + let mut v = FVector::from_vec(vec![1, 2, 3, 4, 5]); v[1..=3].copy_from_slice(&[21, 31, 41]); assert_eq!(v.as_slice(), &[1, 21, 31, 41, 5]); } #[test] fn test_index_mut_range_to_inclusive() { - let mut v = FlexVector::from_vec(vec![1, 2, 3, 4, 5]); + let mut v = FVector::from_vec(vec![1, 2, 3, 4, 5]); v[..=2].copy_from_slice(&[100, 200, 300]); assert_eq!(v.as_slice(), &[100, 200, 300, 4, 5]); } @@ -2573,21 +3205,21 @@ mod tests { #[test] #[should_panic] fn test_index_mut_range_out_of_bounds() { - let mut v = FlexVector::from_vec(vec![1, 2, 3]); + let mut v = FVector::from_vec(vec![1, 2, 3]); v[2..5].copy_from_slice(&[9, 9, 9]); } #[test] #[should_panic] fn test_index_mut_range_inclusive_out_of_bounds() { - let mut v = FlexVector::from_vec(vec![1, 2, 3]); + let mut v = FVector::from_vec(vec![1, 2, 3]); v[1..=5].copy_from_slice(&[9, 9, 9, 9, 9]); } #[test] #[should_panic] fn test_index_mut_range_to_inclusive_out_of_bounds() { - let mut v = FlexVector::from_vec(vec![1, 2, 3]); + let mut v = FVector::from_vec(vec![1, 2, 3]); v[..=5].copy_from_slice(&[9, 9, 9, 9, 9, 9]); } @@ -2598,14 +3230,14 @@ mod tests { // ================================ #[test] fn test_extend_i32() { - let mut v = FlexVector::from_vec(vec![1, 2]); + let mut v = FVector::from_vec(vec![1, 2]); v.extend(vec![3, 4]); assert_eq!(v.as_slice(), &[1, 2, 3, 4]); } #[test] fn test_extend_f64() { - let mut v = FlexVector::from_vec(vec![1.1, 2.2]); + let mut v = FVector::from_vec(vec![1.1, 2.2]); v.extend(vec![3.3, 4.4]); assert_eq!(v.as_slice(), &[1.1, 2.2, 3.3, 4.4]); } @@ -2613,7 +3245,7 @@ mod tests { #[test] fn test_extend_complex_f64() { use num::Complex; - let mut v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0)]); + let mut v = FVector::from_vec(vec![Complex::new(1.0, 2.0)]); v.extend(vec![Complex::new(3.0, 4.0), Complex::new(5.0, 6.0)]); assert_eq!( v.as_slice(), @@ -2630,7 +3262,7 @@ mod tests { #[test] fn test_extend_with_empty() { - let mut v = FlexVector::from_vec(vec![1, 2]); + let mut v = FVector::from_vec(vec![1, 2]); v.extend(Vec::::new()); assert_eq!(v.as_slice(), &[1, 2]); } @@ -2643,21 +3275,21 @@ mod tests { // --- as_slice --- #[test] fn test_as_slice() { - let v = FlexVector::from_vec(vec![1, 2, 3]); + let v = FVector::from_vec(vec![1, 2, 3]); let slice = v.as_slice(); assert_eq!(slice, &[1, 2, 3]); } #[test] fn test_as_slice_f64() { - let v = FlexVector::from_vec(vec![1.1, 2.2, 3.3]); + let v = FVector::from_vec(vec![1.1, 2.2, 3.3]); let slice = v.as_slice(); assert_eq!(slice, &[1.1, 2.2, 3.3]); } #[test] fn test_as_slice_complex() { - let v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let slice = v.as_slice(); assert_eq!(slice, &[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0),]); } @@ -2665,7 +3297,7 @@ mod tests { // --- len --- #[test] fn test_len() { - let v = FlexVector::from_vec(vec![10, 20, 30, 40]); + let v = FVector::from_vec(vec![10, 20, 30, 40]); assert_eq!(v.len(), 4); let empty = FlexVector::::new(); assert_eq!(empty.len(), 0); @@ -2673,7 +3305,7 @@ mod tests { #[test] fn test_len_f64() { - let v = FlexVector::from_vec(vec![10.0, 20.0]); + let v = FVector::from_vec(vec![10.0, 20.0]); assert_eq!(v.len(), 2); let empty = FlexVector::::new(); assert_eq!(empty.len(), 0); @@ -2681,7 +3313,7 @@ mod tests { #[test] fn test_len_complex() { - let v = FlexVector::from_vec(vec![Complex::new(1.0, 0.0)]); + let v = FVector::from_vec(vec![Complex::new(1.0, 0.0)]); assert_eq!(v.len(), 1); let empty = FlexVector::>::new(); assert_eq!(empty.len(), 0); @@ -2690,7 +3322,7 @@ mod tests { // --- is_empty --- #[test] fn test_is_empty() { - let v = FlexVector::from_vec(vec![1]); + let v = FVector::from_vec(vec![1]); assert!(!v.is_empty()); let empty = FlexVector::::new(); assert!(empty.is_empty()); @@ -2698,7 +3330,7 @@ mod tests { #[test] fn test_is_empty_f64() { - let v = FlexVector::from_vec(vec![1.0]); + let v = FVector::from_vec(vec![1.0]); assert!(!v.is_empty()); let empty = FlexVector::::new(); assert!(empty.is_empty()); @@ -2706,7 +3338,7 @@ mod tests { #[test] fn test_is_empty_complex() { - let v = FlexVector::from_vec(vec![Complex::new(0.0, 1.0)]); + let v = FVector::from_vec(vec![Complex::new(0.0, 1.0)]); assert!(!v.is_empty()); let empty = FlexVector::>::new(); assert!(empty.is_empty()); @@ -2715,7 +3347,7 @@ mod tests { // --- get --- #[test] fn test_get() { - let v = FlexVector::from_vec(vec![10, 20, 30]); + let v = FVector::from_vec(vec![10, 20, 30]); assert_eq!(v.get(0), Some(&10)); assert_eq!(v.get(2), Some(&30)); assert_eq!(v.get(3), None); @@ -2723,7 +3355,7 @@ mod tests { #[test] fn test_get_f64() { - let v = FlexVector::from_vec(vec![10.5, 20.5, 30.5]); + let v = FVector::from_vec(vec![10.5, 20.5, 30.5]); assert_eq!(v.get(0), Some(&10.5)); assert_eq!(v.get(2), Some(&30.5)); assert_eq!(v.get(3), None); @@ -2731,7 +3363,7 @@ mod tests { #[test] fn test_get_complex() { - let v = FlexVector::from_vec(vec![Complex::new(1.0, 1.0), Complex::new(2.0, 2.0)]); + let v = FVector::from_vec(vec![Complex::new(1.0, 1.0), Complex::new(2.0, 2.0)]); assert_eq!(v.get(0), Some(&Complex::new(1.0, 1.0))); assert_eq!(v.get(1), Some(&Complex::new(2.0, 2.0))); assert_eq!(v.get(2), None); @@ -2740,7 +3372,7 @@ mod tests { // --- first --- #[test] fn test_first() { - let v = FlexVector::from_vec(vec![5, 6, 7]); + let v = FVector::from_vec(vec![5, 6, 7]); assert_eq!(v.first(), Some(&5)); let empty = FlexVector::::new(); assert_eq!(empty.first(), None); @@ -2748,7 +3380,7 @@ mod tests { #[test] fn test_first_f64() { - let v = FlexVector::from_vec(vec![5.5, 6.5]); + let v = FVector::from_vec(vec![5.5, 6.5]); assert_eq!(v.first(), Some(&5.5)); let empty = FlexVector::::new(); assert_eq!(empty.first(), None); @@ -2756,7 +3388,7 @@ mod tests { #[test] fn test_first_complex() { - let v = FlexVector::from_vec(vec![Complex::new(7.0, 8.0), Complex::new(9.0, 10.0)]); + let v = FVector::from_vec(vec![Complex::new(7.0, 8.0), Complex::new(9.0, 10.0)]); assert_eq!(v.first(), Some(&Complex::new(7.0, 8.0))); let empty = FlexVector::>::new(); assert_eq!(empty.first(), None); @@ -2765,7 +3397,7 @@ mod tests { // --- last --- #[test] fn test_last() { - let v = FlexVector::from_vec(vec![1, 2, 3]); + let v = FVector::from_vec(vec![1, 2, 3]); assert_eq!(v.last(), Some(&3)); let empty = FlexVector::::new(); assert_eq!(empty.last(), None); @@ -2773,7 +3405,7 @@ mod tests { #[test] fn test_last_f64() { - let v = FlexVector::from_vec(vec![1.5, 2.5, 3.5]); + let v = FVector::from_vec(vec![1.5, 2.5, 3.5]); assert_eq!(v.last(), Some(&3.5)); let empty = FlexVector::::new(); assert_eq!(empty.last(), None); @@ -2781,7 +3413,7 @@ mod tests { #[test] fn test_last_complex() { - let v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); assert_eq!(v.last(), Some(&Complex::new(3.0, 4.0))); let empty = FlexVector::>::new(); assert_eq!(empty.last(), None); @@ -2790,7 +3422,7 @@ mod tests { // --- iter --- #[test] fn test_iter() { - let v = FlexVector::from_vec(vec![1, 2, 3]); + let v = FVector::from_vec(vec![1, 2, 3]); let mut iter = v.iter(); assert_eq!(iter.next(), Some(&1)); assert_eq!(iter.next(), Some(&2)); @@ -2800,14 +3432,14 @@ mod tests { #[test] fn test_iter_f64() { - let v = FlexVector::from_vec(vec![1.1, 2.2, 3.3]); + let v = FVector::from_vec(vec![1.1, 2.2, 3.3]); let collected: Vec<_> = v.iter().copied().collect(); assert_eq!(collected, vec![1.1, 2.2, 3.3]); } #[test] fn test_iter_complex() { - let v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let collected: Vec<_> = v.iter().cloned().collect(); assert_eq!(collected, vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0),]); } @@ -2815,21 +3447,21 @@ mod tests { // --- iter_rev --- #[test] fn test_iter_rev() { - let v = FlexVector::from_vec(vec![1, 2, 3]); + let v = FVector::from_vec(vec![1, 2, 3]); let collected: Vec<_> = v.iter_rev().copied().collect(); assert_eq!(collected, vec![3, 2, 1]); } #[test] fn test_iter_rev_f64() { - let v = FlexVector::from_vec(vec![1.1, 2.2, 3.3]); + let v = FVector::from_vec(vec![1.1, 2.2, 3.3]); let collected: Vec<_> = v.iter_rev().copied().collect(); assert_eq!(collected, vec![3.3, 2.2, 1.1]); } #[test] fn test_iter_rev_complex() { - let v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let collected: Vec<_> = v.iter_rev().cloned().collect(); assert_eq!(collected, vec![Complex::new(3.0, 4.0), Complex::new(1.0, 2.0),]); } @@ -2837,21 +3469,21 @@ mod tests { // --- enumerate --- #[test] fn test_enumerate() { - let v = FlexVector::from_vec(vec![10, 20, 30]); + let v = FVector::from_vec(vec![10, 20, 30]); let pairs: Vec<_> = v.enumerate().collect(); assert_eq!(pairs, vec![(0, &10), (1, &20), (2, &30)]); } #[test] fn test_enumerate_f64() { - let v = FlexVector::from_vec(vec![1.5, 2.5]); + let v = FVector::from_vec(vec![1.5, 2.5]); let pairs: Vec<_> = v.enumerate().collect(); assert_eq!(pairs, vec![(0, &1.5), (1, &2.5)]); } #[test] fn test_enumerate_complex() { - let v = FlexVector::from_vec(vec![Complex::new(0.0, 1.0), Complex::new(2.0, 3.0)]); + let v = FVector::from_vec(vec![Complex::new(0.0, 1.0), Complex::new(2.0, 3.0)]); let pairs: Vec<_> = v.enumerate().collect(); assert_eq!(pairs, vec![(0, &Complex::new(0.0, 1.0)), (1, &Complex::new(2.0, 3.0)),]); } @@ -2859,29 +3491,66 @@ mod tests { // --- to_vec --- #[test] fn test_to_vec() { - let v = FlexVector::from_vec(vec![1, 2, 3]); + let v = FVector::from_vec(vec![1, 2, 3]); let vec_copy = v.to_vec(); assert_eq!(vec_copy, vec![1, 2, 3]); } #[test] fn test_to_vec_f64() { - let v = FlexVector::from_vec(vec![1.5, 2.5]); + let v = FVector::from_vec(vec![1.5, 2.5]); let vec_copy = v.to_vec(); assert_eq!(vec_copy, vec![1.5, 2.5]); } #[test] fn test_to_vec_complex() { - let v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let vec_copy = v.to_vec(); assert_eq!(vec_copy, vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0),]); } + // --- as_cow --- + + #[test] + fn test_as_cow_i32() { + let v = FlexVector::::from(vec![1, 2, 3]); + let cow = v.as_cow(); + assert_eq!(&*cow, &[1, 2, 3]); + // Should be borrowed, not owned + assert!(matches!(cow, std::borrow::Cow::Borrowed(_))); + } + + #[test] + fn test_as_cow_f64() { + let v = FlexVector::::from(vec![1.1, 2.2, 3.3]); + let cow = v.as_cow(); + assert_eq!(&*cow, &[1.1, 2.2, 3.3]); + assert!(matches!(cow, std::borrow::Cow::Borrowed(_))); + } + + #[test] + fn test_as_cow_complex_f64() { + use num::Complex; + let v = + FlexVector::>::from(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let cow = v.as_cow(); + assert_eq!(&*cow, &[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + assert!(matches!(cow, std::borrow::Cow::Borrowed(_))); + } + + #[test] + fn test_as_cow_empty() { + let v = FlexVector::::new(); + let cow = v.as_cow(); + assert_eq!(&*cow, &[]); + assert!(matches!(cow, std::borrow::Cow::Borrowed(_))); + } + // --- pretty --- #[test] fn test_pretty() { - let v = FlexVector::from_vec(vec![1, 2, 3]); + let v = FVector::from_vec(vec![1, 2, 3]); let pretty = v.pretty(); assert!(pretty.contains("1")); assert!(pretty.contains("2")); @@ -2891,7 +3560,7 @@ mod tests { #[test] fn test_pretty_f64() { - let v = FlexVector::from_vec(vec![1.5, 2.5]); + let v = FVector::from_vec(vec![1.5, 2.5]); let pretty = v.pretty(); assert!(pretty.contains("1.5")); assert!(pretty.contains("2.5")); @@ -2900,7 +3569,7 @@ mod tests { #[test] fn test_pretty_complex() { - let v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let pretty = v.pretty(); assert!(pretty.contains("1.0")); assert!(pretty.contains("2.0")); @@ -2912,21 +3581,21 @@ mod tests { // --- contains --- #[test] fn test_contains() { - let v = FlexVector::from_vec(vec![1, 2, 3]); + let v = FVector::from_vec(vec![1, 2, 3]); assert!(v.contains(&2)); assert!(!v.contains(&4)); } #[test] fn test_contains_f64() { - let v = FlexVector::from_vec(vec![1.1, 2.2, 3.3]); + let v = FVector::from_vec(vec![1.1, 2.2, 3.3]); assert!(v.contains(&2.2)); assert!(!v.contains(&4.4)); } #[test] fn test_contains_complex() { - let v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); assert!(v.contains(&Complex::new(1.0, 2.0))); assert!(!v.contains(&Complex::new(0.0, 0.0))); } @@ -2934,21 +3603,21 @@ mod tests { // --- starts_with --- #[test] fn test_starts_with() { - let v = FlexVector::from_vec(vec![1, 2, 3]); + let v = FVector::from_vec(vec![1, 2, 3]); assert!(v.starts_with(&[1, 2])); assert!(!v.starts_with(&[2, 3])); } #[test] fn test_starts_with_f64() { - let v = FlexVector::from_vec(vec![1.1, 2.2, 3.3]); + let v = FVector::from_vec(vec![1.1, 2.2, 3.3]); assert!(v.starts_with(&[1.1, 2.2])); assert!(!v.starts_with(&[2.2, 3.3])); } #[test] fn test_starts_with_complex() { - let v = FlexVector::from_vec(vec![ + let v = FVector::from_vec(vec![ Complex::new(1.0, 2.0), Complex::new(3.0, 4.0), Complex::new(5.0, 6.0), @@ -2960,21 +3629,21 @@ mod tests { // --- ends_with --- #[test] fn test_ends_with() { - let v = FlexVector::from_vec(vec![1, 2, 3]); + let v = FVector::from_vec(vec![1, 2, 3]); assert!(v.ends_with(&[2, 3])); assert!(!v.ends_with(&[1, 2])); } #[test] fn test_ends_with_f64() { - let v = FlexVector::from_vec(vec![1.1, 2.2, 3.3]); + let v = FVector::from_vec(vec![1.1, 2.2, 3.3]); assert!(v.ends_with(&[2.2, 3.3])); assert!(!v.ends_with(&[1.1, 2.2])); } #[test] fn test_ends_with_complex() { - let v = FlexVector::from_vec(vec![ + let v = FVector::from_vec(vec![ Complex::new(1.0, 2.0), Complex::new(3.0, 4.0), Complex::new(5.0, 6.0), @@ -2986,21 +3655,21 @@ mod tests { // --- position --- #[test] fn test_position() { - let v = FlexVector::from_vec(vec![10, 20, 30]); + let v = FVector::from_vec(vec![10, 20, 30]); assert_eq!(v.position(|&x| x == 20), Some(1)); assert_eq!(v.position(|&x| x == 99), None); } #[test] fn test_position_f64() { - let v = FlexVector::from_vec(vec![10.0, 20.0, 30.0]); + let v = FVector::from_vec(vec![10.0, 20.0, 30.0]); assert_eq!(v.position(|&x| x == 20.0), Some(1)); assert_eq!(v.position(|&x| x == 99.0), None); } #[test] fn test_position_complex() { - let v = FlexVector::from_vec(vec![ + let v = FVector::from_vec(vec![ Complex::new(1.0, 1.0), Complex::new(2.0, 2.0), Complex::new(3.0, 3.0), @@ -3012,21 +3681,21 @@ mod tests { // --- rposition --- #[test] fn test_rposition() { - let v = FlexVector::from_vec(vec![1, 2, 3, 2]); + let v = FVector::from_vec(vec![1, 2, 3, 2]); assert_eq!(v.rposition(|&x| x == 2), Some(3)); assert_eq!(v.rposition(|&x| x == 99), None); } #[test] fn test_rposition_f64() { - let v = FlexVector::from_vec(vec![1.0, 2.0, 3.0, 2.0]); + let v = FVector::from_vec(vec![1.0, 2.0, 3.0, 2.0]); assert_eq!(v.rposition(|&x| x == 2.0), Some(3)); assert_eq!(v.rposition(|&x| x == 99.0), None); } #[test] fn test_rposition_complex() { - let v = FlexVector::from_vec(vec![ + let v = FVector::from_vec(vec![ Complex::new(1.0, 1.0), Complex::new(2.0, 2.0), Complex::new(3.0, 3.0), @@ -3039,21 +3708,21 @@ mod tests { // --- windows --- #[test] fn test_windows() { - let v = FlexVector::from_vec(vec![1, 2, 3, 4]); + let v = FVector::from_vec(vec![1, 2, 3, 4]); let windows: Vec<_> = v.windows(2).collect(); assert_eq!(windows, vec![&[1, 2][..], &[2, 3][..], &[3, 4][..]]); } #[test] fn test_windows_f64() { - let v = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let v = FVector::from_vec(vec![1.0, 2.0, 3.0]); let windows: Vec<_> = v.windows(2).collect(); assert_eq!(windows, vec![&[1.0, 2.0][..], &[2.0, 3.0][..]]); } #[test] fn test_windows_complex() { - let v = FlexVector::from_vec(vec![ + let v = FVector::from_vec(vec![ Complex::new(1.0, 0.0), Complex::new(2.0, 0.0), Complex::new(3.0, 0.0), @@ -3071,21 +3740,21 @@ mod tests { // --- chunks --- #[test] fn test_chunks() { - let v = FlexVector::from_vec(vec![1, 2, 3, 4, 5]); + let v = FVector::from_vec(vec![1, 2, 3, 4, 5]); let chunks: Vec<_> = v.chunks(2).collect(); assert_eq!(chunks, vec![&[1, 2][..], &[3, 4][..], &[5][..]]); } #[test] fn test_chunks_f64() { - let v = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let v = FVector::from_vec(vec![1.0, 2.0, 3.0]); let chunks: Vec<_> = v.chunks(2).collect(); assert_eq!(chunks, vec![&[1.0, 2.0][..], &[3.0][..]]); } #[test] fn test_chunks_complex() { - let v = FlexVector::from_vec(vec![ + let v = FVector::from_vec(vec![ Complex::new(1.0, 0.0), Complex::new(2.0, 0.0), Complex::new(3.0, 0.0), @@ -3103,7 +3772,7 @@ mod tests { // --- split_at --- #[test] fn test_split_at() { - let v = FlexVector::from_vec(vec![1, 2, 3, 4]); + let v = FVector::from_vec(vec![1, 2, 3, 4]); let (left, right) = v.split_at(2); assert_eq!(left, &[1, 2]); assert_eq!(right, &[3, 4]); @@ -3111,7 +3780,7 @@ mod tests { #[test] fn test_split_at_f64() { - let v = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let v = FVector::from_vec(vec![1.0, 2.0, 3.0]); let (left, right) = v.split_at(1); assert_eq!(left, &[1.0]); assert_eq!(right, &[2.0, 3.0]); @@ -3119,7 +3788,7 @@ mod tests { #[test] fn test_split_at_complex() { - let v = FlexVector::from_vec(vec![Complex::new(1.0, 0.0), Complex::new(2.0, 0.0)]); + let v = FVector::from_vec(vec![Complex::new(1.0, 0.0), Complex::new(2.0, 0.0)]); let (left, right) = v.split_at(1); assert_eq!(left, &[Complex::new(1.0, 0.0)]); assert_eq!(right, &[Complex::new(2.0, 0.0)]); @@ -3128,21 +3797,21 @@ mod tests { // --- split --- #[test] fn test_split() { - let v = FlexVector::from_vec(vec![1, 2, 0, 3, 0, 4]); + let v = FVector::from_vec(vec![1, 2, 0, 3, 0, 4]); let splits: Vec<_> = v.split(|&x| x == 0).collect(); assert_eq!(splits, vec![&[1, 2][..], &[3][..], &[4][..]]); } #[test] fn test_split_f64() { - let v = FlexVector::from_vec(vec![1.0, 0.0, 2.0, 0.0, 3.0]); + let v = FVector::from_vec(vec![1.0, 0.0, 2.0, 0.0, 3.0]); let splits: Vec<_> = v.split(|&x| x == 0.0).collect(); assert_eq!(splits, vec![&[1.0][..], &[2.0][..], &[3.0][..]]); } #[test] fn test_split_complex() { - let v = FlexVector::from_vec(vec![ + let v = FVector::from_vec(vec![ Complex::new(1.0, 0.0), Complex::new(0.0, 0.0), Complex::new(2.0, 0.0), @@ -3154,21 +3823,21 @@ mod tests { // --- splitn --- #[test] fn test_splitn() { - let v = FlexVector::from_vec(vec![1, 0, 2, 0, 3]); + let v = FVector::from_vec(vec![1, 0, 2, 0, 3]); let splits: Vec<_> = v.splitn(2, |&x| x == 0).collect(); assert_eq!(splits, vec![&[1][..], &[2, 0, 3][..]]); } #[test] fn test_splitn_f64() { - let v = FlexVector::from_vec(vec![1.0, 0.0, 2.0, 0.0, 3.0]); + let v = FVector::from_vec(vec![1.0, 0.0, 2.0, 0.0, 3.0]); let splits: Vec<_> = v.splitn(2, |&x| x == 0.0).collect(); assert_eq!(splits, vec![&[1.0][..], &[2.0, 0.0, 3.0][..]]); } #[test] fn test_splitn_complex() { - let v = FlexVector::from_vec(vec![ + let v = FVector::from_vec(vec![ Complex::new(1.0, 0.0), Complex::new(0.0, 0.0), Complex::new(2.0, 0.0), @@ -3188,21 +3857,21 @@ mod tests { // --- rsplit --- #[test] fn test_rsplit() { - let v = FlexVector::from_vec(vec![1, 0, 2, 0, 3]); + let v = FVector::from_vec(vec![1, 0, 2, 0, 3]); let splits: Vec<_> = v.rsplit(|&x| x == 0).collect(); assert_eq!(splits, vec![&[3][..], &[2][..], &[1][..]]); } #[test] fn test_rsplit_f64() { - let v = FlexVector::from_vec(vec![1.0, 0.0, 2.0, 0.0, 3.0]); + let v = FVector::from_vec(vec![1.0, 0.0, 2.0, 0.0, 3.0]); let splits: Vec<_> = v.rsplit(|&x| x == 0.0).collect(); assert_eq!(splits, vec![&[3.0][..], &[2.0][..], &[1.0][..]]); } #[test] fn test_rsplit_complex() { - let v = FlexVector::from_vec(vec![ + let v = FVector::from_vec(vec![ Complex::new(1.0, 0.0), Complex::new(0.0, 0.0), Complex::new(2.0, 0.0), @@ -3223,21 +3892,21 @@ mod tests { // --- rsplitn --- #[test] fn test_rsplitn() { - let v = FlexVector::from_vec(vec![1, 0, 2, 0, 3]); + let v = FVector::from_vec(vec![1, 0, 2, 0, 3]); let splits: Vec<_> = v.rsplitn(2, |&x| x == 0).collect(); assert_eq!(splits, vec![&[3][..], &[1, 0, 2][..]]); } #[test] fn test_rsplitn_f64() { - let v = FlexVector::from_vec(vec![1.0, 0.0, 2.0, 0.0, 3.0]); + let v = FVector::from_vec(vec![1.0, 0.0, 2.0, 0.0, 3.0]); let splits: Vec<_> = v.rsplitn(2, |&x| x == 0.0).collect(); assert_eq!(splits, vec![&[3.0][..], &[1.0, 0.0, 2.0][..]]); } #[test] fn test_rsplitn_complex() { - let v = FlexVector::from_vec(vec![ + let v = FVector::from_vec(vec![ Complex::new(1.0, 0.0), Complex::new(0.0, 0.0), Complex::new(2.0, 0.0), @@ -3254,10 +3923,144 @@ mod tests { ); } + // -- is_row + is_column -- + + #[test] + fn test_is_row_and_is_column_i32() { + let v_col = FlexVector::::from_vec(vec![1, 2, 3]); + let v_row = FlexVector::::from_vec(vec![1, 2, 3]); + assert!(v_col.is_column()); + assert!(!v_col.is_row()); + assert!(v_row.is_row()); + assert!(!v_row.is_column()); + } + + #[test] + fn test_is_row_and_is_column_f64() { + let v_col = FlexVector::::from_vec(vec![1.0, 2.0]); + let v_row = FlexVector::::from_vec(vec![1.0, 2.0]); + assert!(v_col.is_column()); + assert!(!v_col.is_row()); + assert!(v_row.is_row()); + assert!(!v_row.is_column()); + } + + #[test] + fn test_is_row_and_is_column_complex_f64() { + use num::Complex; + let v_col = FlexVector::, Column>::from_vec(vec![Complex::new(1.0, 2.0)]); + let v_row = FlexVector::, Row>::from_vec(vec![Complex::new(1.0, 2.0)]); + assert!(v_col.is_column()); + assert!(!v_col.is_row()); + assert!(v_row.is_row()); + assert!(!v_row.is_column()); + } + + // -- as_row + as_column -- + + #[test] + fn test_as_row_and_as_column_i32() { + let v_col = FlexVector::::from_vec(vec![1, 2, 3]); + let v_row = v_col.as_row(); + assert_eq!(v_row.as_slice(), &[1, 2, 3]); + assert!(v_row.is_row()); + assert!(!v_row.is_column()); + assert!(v_col.is_column()); + + let v_col2 = v_row.as_column(); + assert_eq!(v_col2.as_slice(), &[1, 2, 3]); + assert!(v_col2.is_column()); + assert!(!v_col2.is_row()); + assert!(v_row.is_row()); + } + + #[test] + fn test_as_row_and_as_column_f64() { + let v_col = FlexVector::::from_vec(vec![1.1, 2.2]); + let v_row = v_col.as_row(); + assert_eq!(v_row.as_slice(), &[1.1, 2.2]); + assert!(v_row.is_row()); + assert!(!v_row.is_column()); + assert!(v_col.is_column()); + + let v_col2 = v_row.as_column(); + assert_eq!(v_col2.as_slice(), &[1.1, 2.2]); + assert!(v_col2.is_column()); + assert!(!v_col2.is_row()); + assert!(v_row.is_row()); + } + + #[test] + fn test_as_row_and_as_column_complex_f64() { + let v_col = FlexVector::, Column>::from_vec(vec![ + Complex::new(1.0, 2.0), + Complex::new(3.0, 4.0), + ]); + let v_row = v_col.as_row(); + assert_eq!(v_row.as_slice(), &[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + assert!(v_row.is_row()); + assert!(!v_row.is_column()); + assert!(v_col.is_column()); + + let v_col2 = v_row.as_column(); + assert_eq!(v_col2.as_slice(), &[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + assert!(v_col2.is_column()); + assert!(!v_col2.is_row()); + assert!(v_row.is_row()); + } + + // -- into_row + into_column -- + + #[test] + fn test_into_row_and_into_column_i32() { + let v_col = FlexVector::::from_vec(vec![1, 2, 3]); + let v_row = v_col.into_row(); // move occurs here + assert_eq!(v_row.as_slice(), &[1, 2, 3]); + assert!(v_row.is_row()); + assert!(!v_row.is_column()); + + let v_col2 = v_row.into_column(); // move occurs here + assert_eq!(v_col2.as_slice(), &[1, 2, 3]); + assert!(v_col2.is_column()); + assert!(!v_col2.is_row()); + } + + #[test] + fn test_into_row_and_into_column_f64() { + let v_col = FlexVector::::from_vec(vec![1.1, 2.2]); + let v_row = v_col.into_row(); // move occurs here + assert_eq!(v_row.as_slice(), &[1.1, 2.2]); + assert!(v_row.is_row()); + assert!(!v_row.is_column()); + + let v_col2 = v_row.into_column(); // move occurs here + assert_eq!(v_col2.as_slice(), &[1.1, 2.2]); + assert!(v_col2.is_column()); + assert!(!v_col2.is_row()); + } + + #[test] + fn test_into_row_and_into_column_complex_f64() { + use num::Complex; + let v_col = FlexVector::, Column>::from_vec(vec![ + Complex::new(1.0, 2.0), + Complex::new(3.0, 4.0), + ]); + let v_row = v_col.into_row(); // move occurs here + assert_eq!(v_row.as_slice(), &[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + assert!(v_row.is_row()); + assert!(!v_row.is_column()); + + let v_col2 = v_row.into_column(); // move occurs here + assert_eq!(v_col2.as_slice(), &[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + assert!(v_col2.is_column()); + assert!(!v_col2.is_row()); + } + // --- push --- #[test] fn test_push() { - let mut v = FlexVector::new(); + let mut v = FVector::new(); v.push(1); v.push(2); assert_eq!(v.as_slice(), &[1, 2]); @@ -3265,7 +4068,7 @@ mod tests { #[test] fn test_push_f64() { - let mut v = FlexVector::new(); + let mut v = FVector::new(); v.push(1.1); v.push(2.2); assert_eq!(v.as_slice(), &[1.1, 2.2]); @@ -3273,7 +4076,7 @@ mod tests { #[test] fn test_push_complex() { - let mut v = FlexVector::new(); + let mut v = FVector::new(); v.push(Complex::new(1.0, 2.0)); v.push(Complex::new(3.0, 4.0)); assert_eq!(v.as_slice(), &[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); @@ -3282,7 +4085,7 @@ mod tests { // --- pop --- #[test] fn test_pop() { - let mut v = FlexVector::from_vec(vec![1, 2]); + let mut v = FVector::from_vec(vec![1, 2]); assert_eq!(v.pop(), Some(2)); assert_eq!(v.pop(), Some(1)); assert_eq!(v.pop(), None); @@ -3290,7 +4093,7 @@ mod tests { #[test] fn test_pop_f64() { - let mut v = FlexVector::from_vec(vec![1.1, 2.2]); + let mut v = FVector::from_vec(vec![1.1, 2.2]); assert_eq!(v.pop(), Some(2.2)); assert_eq!(v.pop(), Some(1.1)); assert_eq!(v.pop(), None); @@ -3298,7 +4101,7 @@ mod tests { #[test] fn test_pop_complex() { - let mut v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let mut v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); assert_eq!(v.pop(), Some(Complex::new(3.0, 4.0))); assert_eq!(v.pop(), Some(Complex::new(1.0, 2.0))); assert_eq!(v.pop(), None); @@ -3307,21 +4110,21 @@ mod tests { // --- insert --- #[test] fn test_insert() { - let mut v = FlexVector::from_vec(vec![1, 3]); + let mut v = FVector::from_vec(vec![1, 3]); v.insert(1, 2); assert_eq!(v.as_slice(), &[1, 2, 3]); } #[test] fn test_insert_f64() { - let mut v = FlexVector::from_vec(vec![1.1, 3.3]); + let mut v = FVector::from_vec(vec![1.1, 3.3]); v.insert(1, 2.2); assert_eq!(v.as_slice(), &[1.1, 2.2, 3.3]); } #[test] fn test_insert_complex() { - let mut v = FlexVector::from_vec(vec![Complex::new(1.0, 1.0), Complex::new(3.0, 3.0)]); + let mut v = FVector::from_vec(vec![Complex::new(1.0, 1.0), Complex::new(3.0, 3.0)]); v.insert(1, Complex::new(2.0, 2.0)); assert_eq!( v.as_slice(), @@ -3332,21 +4135,21 @@ mod tests { // --- remove --- #[test] fn test_remove() { - let mut v = FlexVector::from_vec(vec![1, 2, 3]); + let mut v = FVector::from_vec(vec![1, 2, 3]); assert_eq!(v.remove(1), 2); assert_eq!(v.as_slice(), &[1, 3]); } #[test] fn test_remove_f64() { - let mut v = FlexVector::from_vec(vec![1.1, 2.2, 3.3]); + let mut v = FVector::from_vec(vec![1.1, 2.2, 3.3]); assert_eq!(v.remove(1), 2.2); assert_eq!(v.as_slice(), &[1.1, 3.3]); } #[test] fn test_remove_complex() { - let mut v = FlexVector::from_vec(vec![ + let mut v = FVector::from_vec(vec![ Complex::new(1.0, 1.0), Complex::new(2.0, 2.0), Complex::new(3.0, 3.0), @@ -3358,7 +4161,7 @@ mod tests { // --- resize --- #[test] fn test_resize() { - let mut v = FlexVector::from_vec(vec![1, 2]); + let mut v = FVector::from_vec(vec![1, 2]); v.resize(4, 0); assert_eq!(v.as_slice(), &[1, 2, 0, 0]); v.resize(2, 0); @@ -3367,7 +4170,7 @@ mod tests { #[test] fn test_resize_f64() { - let mut v = FlexVector::from_vec(vec![1.1, 2.2]); + let mut v = FVector::from_vec(vec![1.1, 2.2]); v.resize(4, 0.0); assert_eq!(v.as_slice(), &[1.1, 2.2, 0.0, 0.0]); v.resize(1, 0.0); @@ -3376,7 +4179,7 @@ mod tests { #[test] fn test_resize_complex() { - let mut v = FlexVector::from_vec(vec![Complex::new(1.0, 1.0)]); + let mut v = FVector::from_vec(vec![Complex::new(1.0, 1.0)]); v.resize(3, Complex::new(0.0, 0.0)); assert_eq!( v.as_slice(), @@ -3389,7 +4192,7 @@ mod tests { // --- clear --- #[test] fn test_clear() { - let mut v = FlexVector::from_vec(vec![1, 2, 3]); + let mut v = FVector::from_vec(vec![1, 2, 3]); assert!(!v.is_empty()); v.clear(); assert!(v.is_empty()); @@ -3397,7 +4200,7 @@ mod tests { #[test] fn test_clear_f64() { - let mut v = FlexVector::from_vec(vec![1.1, 2.2, 3.3]); + let mut v = FVector::from_vec(vec![1.1, 2.2, 3.3]); assert!(!v.is_empty()); v.clear(); assert!(v.is_empty()); @@ -3405,7 +4208,7 @@ mod tests { #[test] fn test_clear_complex() { - let mut v = FlexVector::from_vec(vec![Complex::new(1.0, 1.0), Complex::new(2.0, 2.0)]); + let mut v = FVector::from_vec(vec![Complex::new(1.0, 1.0), Complex::new(2.0, 2.0)]); assert!(!v.is_empty()); v.clear(); assert!(v.is_empty()); @@ -3414,7 +4217,7 @@ mod tests { // --- get_mut --- #[test] fn test_get_mut_single() { - let mut v = FlexVector::from_vec(vec![10, 20, 30]); + let mut v = FVector::from_vec(vec![10, 20, 30]); if let Some(x) = v.get_mut(1) { *x = 99; } @@ -3423,13 +4226,13 @@ mod tests { #[test] fn test_get_mut_out_of_bounds() { - let mut v = FlexVector::from_vec(vec![1, 2, 3]); + let mut v = FVector::from_vec(vec![1, 2, 3]); assert!(v.get_mut(10).is_none()); } #[test] fn test_get_mut_range() { - let mut v = FlexVector::from_vec(vec![1, 2, 3, 4]); + let mut v = FVector::from_vec(vec![1, 2, 3, 4]); if let Some(slice) = v.get_mut(1..3) { slice[0] = 20; slice[1] = 30; @@ -3439,7 +4242,7 @@ mod tests { #[test] fn test_get_mut_full_range() { - let mut v = FlexVector::from_vec(vec![5, 6, 7]); + let mut v = FVector::from_vec(vec![5, 6, 7]); if let Some(slice) = v.get_mut(..) { for x in slice { *x *= 2; @@ -3451,7 +4254,7 @@ mod tests { // --- iter_mut --- #[test] fn test_iter_mut_i32() { - let mut v = FlexVector::from_vec(vec![1, 2, 3]); + let mut v = FVector::from_vec(vec![1, 2, 3]); for x in v.iter_mut() { *x *= 2; } @@ -3460,7 +4263,7 @@ mod tests { #[test] fn test_iter_mut_f64() { - let mut v = FlexVector::from_vec(vec![1.5, -2.0, 0.0]); + let mut v = FVector::from_vec(vec![1.5, -2.0, 0.0]); for x in v.iter_mut() { *x += 1.0; } @@ -3470,7 +4273,7 @@ mod tests { #[test] fn test_iter_mut_complex() { use num::Complex; - let mut v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + let mut v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); for x in v.iter_mut() { x.re += 1.0; x.im *= 2.0; @@ -3480,7 +4283,7 @@ mod tests { #[test] fn test_iter_mut_empty() { - let mut v = FlexVector::::new(); + let mut v = FVector::::new(); let mut count = 0; for _ in v.iter_mut() { count += 1; @@ -3491,7 +4294,7 @@ mod tests { // --- as_mut_slice --- #[test] fn test_as_mut_slice_i32() { - let mut v = FlexVector::from_vec(vec![1, 2, 3]); + let mut v = FVector::from_vec(vec![1, 2, 3]); let slice = v.as_mut_slice(); slice[0] = 10; slice[2] = 30; @@ -3500,7 +4303,7 @@ mod tests { #[test] fn test_as_mut_slice_f64() { - let mut v = FlexVector::from_vec(vec![1.1, 2.2, 3.3]); + let mut v = FVector::from_vec(vec![1.1, 2.2, 3.3]); let slice = v.as_mut_slice(); slice[1] = 9.9; assert_eq!(v.as_slice(), &[1.1, 9.9, 3.3]); @@ -3509,7 +4312,7 @@ mod tests { #[test] fn test_as_mut_slice_complex() { use num::Complex; - let mut v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let mut v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let slice = v.as_mut_slice(); slice[0].re = 10.0; slice[1].im = 40.0; @@ -3518,7 +4321,7 @@ mod tests { #[test] fn test_as_mut_slice_empty() { - let mut v = FlexVector::::new(); + let mut v = FVector::::new(); let slice = v.as_mut_slice(); assert_eq!(slice.len(), 0); } @@ -3526,7 +4329,7 @@ mod tests { // --- map --- #[test] fn test_map_i32() { - let v = FlexVector::from_vec(vec![1, 2, 3]); + let v = FVector::from_vec(vec![1, 2, 3]); let squared = v.map(|x| x * x); assert_eq!(squared.as_slice(), &[1, 4, 9]); // original unchanged @@ -3535,7 +4338,7 @@ mod tests { #[test] fn test_map_f64() { - let v: FlexVector = FlexVector::from_vec(vec![1.5, -2.0, 0.0]); + let v: FlexVector = FVector::from_vec(vec![1.5, -2.0, 0.0]); let abs = v.map(|x| x.abs()); assert_eq!(abs.as_slice(), &[1.5, 2.0, 0.0]); } @@ -3543,7 +4346,7 @@ mod tests { #[test] fn test_map_complex() { use num::Complex; - let v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + let v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); let conj = v.map(|x| x.conj()); assert_eq!(conj.as_slice(), &[Complex::new(1.0, -2.0), Complex::new(-3.0, -4.0)]); } @@ -3557,7 +4360,7 @@ mod tests { #[test] fn test_map_with_fn_pointer() { - let v = FlexVector::from_vec(vec![1, 2, 3]); + let v = FVector::from_vec(vec![1, 2, 3]); let squared = v.map(square); assert_eq!(squared.as_slice(), &[1, 4, 9]); } @@ -3565,21 +4368,21 @@ mod tests { // --- mut_map --- #[test] fn test_mut_map_i32() { - let mut v = FlexVector::from_vec(vec![1, 2, 3]); + let mut v = FVector::from_vec(vec![1, 2, 3]); v.mut_map(|x| x * 10); assert_eq!(v.as_slice(), &[10, 20, 30]); } #[test] fn test_mut_map_f64() { - let mut v = FlexVector::from_vec(vec![1.5, -2.0, 0.0]); + let mut v = FVector::from_vec(vec![1.5, -2.0, 0.0]); v.mut_map(|x| x + 1.0); assert_eq!(v.as_slice(), &[2.5, -1.0, 1.0]); } #[test] fn test_mut_map_complex() { - let mut v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + let mut v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); v.mut_map(|x| Complex::new(x.re + 1.0, x.im * 2.0)); assert_eq!(v.as_slice(), &[Complex::new(2.0, 4.0), Complex::new(-2.0, 8.0)]); } @@ -3593,7 +4396,7 @@ mod tests { #[test] fn test_mut_map_with_fn_pointer() { - let mut v = FlexVector::from_vec(vec![1, 2, 3]); + let mut v = FVector::from_vec(vec![1, 2, 3]); v.mut_map(double); assert_eq!(v.as_slice(), &[2, 4, 6]); } @@ -3601,28 +4404,28 @@ mod tests { // --- flat_map --- #[test] fn test_flat_map_i32_basic() { - let v = FlexVector::from_vec(vec![1, 2, 3]); + let v = FVector::from_vec(vec![1, 2, 3]); let flat = v.flat_map(|x| vec![x, x * 10]); assert_eq!(flat.as_slice(), &[1, 10, 2, 20, 3, 30]); } #[test] fn test_flat_map_i32_empty_inner() { - let v = FlexVector::from_vec(vec![1, 2, 3]); + let v = FVector::from_vec(vec![1, 2, 3]); let flat = v.flat_map(|x| if x % 2 == 0 { vec![] } else { vec![x] }); assert_eq!(flat.as_slice(), &[1, 3]); } #[test] fn test_flat_map_i32_option() { - let v = FlexVector::from_vec(vec![1, 2, 3, 4]); + let v = FVector::from_vec(vec![1, 2, 3, 4]); let flat = v.flat_map(|x| if x % 2 == 0 { Some(x) } else { None }); assert_eq!(flat.as_slice(), &[2, 4]); } #[test] fn test_flat_map_f64_basic() { - let v = FlexVector::from_vec(vec![1.0, 2.0]); + let v = FVector::from_vec(vec![1.0, 2.0]); let flat = v.flat_map(|x| vec![x, x + 0.5]); assert_eq!(flat.as_slice(), &[1.0, 1.5, 2.0, 2.5]); } @@ -3636,7 +4439,7 @@ mod tests { #[test] fn test_flat_map_f64_nan_infinity() { - let v = FlexVector::from_vec(vec![f64::NAN, f64::INFINITY, 1.0]); + let v = FVector::from_vec(vec![f64::NAN, f64::INFINITY, 1.0]); let flat = v.flat_map(|x| vec![x]); assert!(flat.as_slice()[0].is_nan()); assert_eq!(flat.as_slice()[1], f64::INFINITY); @@ -3646,7 +4449,7 @@ mod tests { #[test] fn test_flat_map_complex_f64_basic() { use num::Complex; - let v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let flat = v.flat_map(|z| vec![z, z.conj()]); assert_eq!( flat.as_slice(), @@ -3662,7 +4465,7 @@ mod tests { #[test] fn test_flat_map_complex_f64_empty_inner() { use num::Complex; - let v = FlexVector::from_vec(vec![Complex::new(0.0, 0.0), Complex::new(1.0, 1.0)]); + let v = FVector::from_vec(vec![Complex::new(0.0, 0.0), Complex::new(1.0, 1.0)]); let flat = v.flat_map(|z| if z == Complex::new(0.0, 0.0) { vec![] } else { vec![z] }); assert_eq!(flat.as_slice(), &[Complex::new(1.0, 1.0)]); } @@ -3670,7 +4473,7 @@ mod tests { #[test] fn test_flat_map_complex_f64_option() { use num::Complex; - let v = FlexVector::from_vec(vec![Complex::new(1.0, 0.0), Complex::new(0.0, 0.0)]); + let v = FVector::from_vec(vec![Complex::new(1.0, 0.0), Complex::new(0.0, 0.0)]); let flat = v.flat_map(|z| if z.im == 0.0 { Some(z) } else { None }); assert_eq!(flat.as_slice(), &[Complex::new(1.0, 0.0), Complex::new(0.0, 0.0)]); } @@ -3684,7 +4487,7 @@ mod tests { #[test] fn test_flat_map_all_empty_inner() { - let v = FlexVector::from_vec(vec![1, 2, 3]); + let v = FVector::from_vec(vec![1, 2, 3]); let flat = v.flat_map(|_| Vec::::new()); assert!(flat.is_empty()); } @@ -3693,35 +4496,35 @@ mod tests { #[test] fn test_filter_i32_basic() { - let v = FlexVector::from_vec(vec![1, 2, 3, 4, 5]); + let v = FVector::from_vec(vec![1, 2, 3, 4, 5]); let filtered = v.filter(|&x| x % 2 == 0); assert_eq!(filtered.as_slice(), &[2, 4]); } #[test] fn test_filter_i32_all_false() { - let v = FlexVector::from_vec(vec![1, 3, 5]); + let v = FVector::from_vec(vec![1, 3, 5]); let filtered = v.filter(|&x| x > 10); assert!(filtered.is_empty()); } #[test] fn test_filter_i32_all_true() { - let v = FlexVector::from_vec(vec![1, 2, 3]); + let v = FVector::from_vec(vec![1, 2, 3]); let filtered = v.filter(|_| true); assert_eq!(filtered.as_slice(), &[1, 2, 3]); } #[test] fn test_filter_f64_positive() { - let v = FlexVector::from_vec(vec![-1.5, 0.0, 2.5, 3.3]); + let v = FVector::from_vec(vec![-1.5, 0.0, 2.5, 3.3]); let filtered = v.filter(|&x| x > 0.0); assert_eq!(filtered.as_slice(), &[2.5, 3.3]); } #[test] fn test_filter_f64_nan() { - let v = FlexVector::from_vec(vec![1.0, f64::NAN, 2.0]); + let v = FVector::from_vec(vec![1.0, f64::NAN, 2.0]); let filtered = v.filter(|&x| x.is_nan()); assert_eq!(filtered.len(), 1); assert!(filtered[0].is_nan()); @@ -3730,7 +4533,7 @@ mod tests { #[test] fn test_filter_complex_f64_real_positive() { use num::Complex; - let v = FlexVector::from_vec(vec![ + let v = FVector::from_vec(vec![ Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0), Complex::new(0.0, 0.0), @@ -3742,7 +4545,7 @@ mod tests { #[test] fn test_filter_complex_f64_imag_nonzero() { use num::Complex; - let v = FlexVector::from_vec(vec![ + let v = FVector::from_vec(vec![ Complex::new(1.0, 0.0), Complex::new(0.0, 2.0), Complex::new(3.0, 0.0), @@ -3753,7 +4556,7 @@ mod tests { #[test] fn test_filter_empty() { - let v: FlexVector = FlexVector::new(); + let v: FlexVector = FVector::new(); let filtered = v.filter(|&x| x > 0); assert!(filtered.is_empty()); } @@ -3762,42 +4565,42 @@ mod tests { #[test] fn test_reduce_i32_sum() { - let v = FlexVector::from_vec(vec![1, 2, 3, 4]); + let v = FVector::from_vec(vec![1, 2, 3, 4]); let sum = v.reduce(|a, b| a + b); assert_eq!(sum, Some(10)); } #[test] fn test_reduce_i32_product() { - let v = FlexVector::from_vec(vec![2, 3, 4]); + let v = FVector::from_vec(vec![2, 3, 4]); let product = v.reduce(|a, b| a * b); assert_eq!(product, Some(24)); } #[test] fn test_reduce_i32_empty() { - let v: FlexVector = FlexVector::new(); + let v: FlexVector = FVector::new(); let result = v.reduce(|a, b| a + b); assert_eq!(result, None); } #[test] fn test_reduce_f64_sum() { - let v = FlexVector::from_vec(vec![1.5, 2.5, 3.0]); + let v = FVector::from_vec(vec![1.5, 2.5, 3.0]); let sum = v.reduce(|a, b| a + b); assert_eq!(sum, Some(7.0)); } #[test] fn test_reduce_f64_product() { - let v = FlexVector::from_vec(vec![1.5, 2.0, 3.0]); + let v = FVector::from_vec(vec![1.5, 2.0, 3.0]); let product = v.reduce(|a, b| a * b); assert!((product.unwrap() - 9.0).abs() < 1e-12); } #[test] fn test_reduce_f64_empty() { - let v: FlexVector = FlexVector::new(); + let v: FlexVector = FVector::new(); let result = v.reduce(|a, b| a + b); assert_eq!(result, None); } @@ -3805,7 +4608,7 @@ mod tests { #[test] fn test_reduce_complex_f64_sum() { use num::Complex; - let v = FlexVector::from_vec(vec![ + let v = FVector::from_vec(vec![ Complex::new(1.0, 2.0), Complex::new(3.0, 4.0), Complex::new(5.0, 6.0), @@ -3817,7 +4620,7 @@ mod tests { #[test] fn test_reduce_complex_f64_product() { use num::Complex; - let v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let product = v.reduce(|a, b| a * b); assert_eq!(product, Some(Complex::new(-5.0, 10.0))); // (1+2i)*(3+4i) = -5+10i } @@ -3825,7 +4628,7 @@ mod tests { #[test] fn test_reduce_complex_f64_empty() { use num::Complex; - let v: FlexVector> = FlexVector::new(); + let v: FlexVector> = FVector::new(); let result = v.reduce(|a, b| a + b); assert_eq!(result, None); } @@ -3834,42 +4637,42 @@ mod tests { #[test] fn test_fold_i32_sum() { - let v = FlexVector::from_vec(vec![1, 2, 3, 4]); + let v = FVector::from_vec(vec![1, 2, 3, 4]); let sum = v.fold(0, |acc, x| acc + x); assert_eq!(sum, 10); } #[test] fn test_fold_i32_product() { - let v = FlexVector::from_vec(vec![2, 3, 4]); + let v = FVector::from_vec(vec![2, 3, 4]); let product = v.fold(1, |acc, x| acc * x); assert_eq!(product, 24); } #[test] fn test_fold_i32_empty() { - let v: FlexVector = FlexVector::new(); + let v: FlexVector = FVector::new(); let sum = v.fold(0, |acc, x| acc + x); assert_eq!(sum, 0); } #[test] fn test_fold_f64_sum() { - let v = FlexVector::from_vec(vec![1.5, 2.5, 3.0]); + let v = FVector::from_vec(vec![1.5, 2.5, 3.0]); let sum = v.fold(0.0, |acc, x| acc + x); assert!((sum - 7.0).abs() < 1e-12); } #[test] fn test_fold_f64_product() { - let v = FlexVector::from_vec(vec![1.5, 2.0, 3.0]); + let v = FVector::from_vec(vec![1.5, 2.0, 3.0]); let product = v.fold(1.0, |acc, x| acc * x); assert!((product - 9.0).abs() < 1e-12); } #[test] fn test_fold_f64_empty() { - let v: FlexVector = FlexVector::new(); + let v: FlexVector = FVector::new(); let sum = v.fold(0.0, |acc, x| acc + x); assert_eq!(sum, 0.0); } @@ -3877,7 +4680,7 @@ mod tests { #[test] fn test_fold_complex_f64_sum() { use num::Complex; - let v = FlexVector::from_vec(vec![ + let v = FVector::from_vec(vec![ Complex::new(1.0, 2.0), Complex::new(3.0, 4.0), Complex::new(5.0, 6.0), @@ -3889,7 +4692,7 @@ mod tests { #[test] fn test_fold_complex_f64_product() { use num::Complex; - let v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let product = v.fold(Complex::new(1.0, 0.0), |acc, x| acc * x); assert_eq!(product, Complex::new(-5.0, 10.0)); // (1+0i)*(1+2i)*(3+4i) = (1+2i)*(3+4i) = -5+10i } @@ -3897,7 +4700,7 @@ mod tests { #[test] fn test_fold_complex_f64_empty() { use num::Complex; - let v: FlexVector> = FlexVector::new(); + let v: FlexVector> = FVector::new(); let sum = v.fold(Complex::new(0.0, 0.0), |acc, x| acc + x); assert_eq!(sum, Complex::new(0.0, 0.0)); } @@ -3905,7 +4708,7 @@ mod tests { // --- zip --- #[test] fn test_zip_i32() { - let v1 = FlexVector::from_vec(vec![1, 2, 3]); + let v1 = FVector::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); let zipped = v1.zip(v2); assert_eq!(zipped.as_slice(), &[(1, 4), (2, 5), (3, 6)]); @@ -3913,8 +4716,8 @@ mod tests { #[test] fn test_zip_f64() { - let v1 = FlexVector::from_vec(vec![1.1, 2.2, 3.3]); - let v2 = FlexVector::from_vec(vec![4.4, 5.5, 6.6]); + let v1 = FVector::from_vec(vec![1.1, 2.2, 3.3]); + let v2 = FVector::from_vec(vec![4.4, 5.5, 6.6]); let zipped = v1.zip(v2); assert_eq!(zipped.as_slice(), &[(1.1, 4.4), (2.2, 5.5), (3.3, 6.6)]); } @@ -3922,8 +4725,8 @@ mod tests { #[test] fn test_zip_complex_f64() { use num::Complex; - let v1 = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); - let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); + let v1 = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v2 = FVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); let zipped = v1.zip(v2); assert_eq!( zipped.as_slice(), @@ -3936,16 +4739,16 @@ mod tests { #[test] fn test_zip_different_lengths() { - let v1 = FlexVector::from_vec(vec![1, 2]); - let v2 = FlexVector::from_vec(vec![3, 4, 5]); + let v1 = FVector::from_vec(vec![1, 2]); + let v2 = FVector::from_vec(vec![3, 4, 5]); let zipped = v1.zip(v2); assert_eq!(zipped.as_slice(), &[(1, 3), (2, 4)]); } #[test] fn test_zip_empty() { - let v1: FlexVector = FlexVector::new(); - let v2: FlexVector = FlexVector::new(); + let v1: FlexVector = FVector::new(); + let v2: FlexVector = FVector::new(); let zipped = v1.zip(v2); assert!(zipped.is_empty()); } @@ -3953,16 +4756,16 @@ mod tests { // --- zip_with --- #[test] fn test_zip_with_i32() { - let v1 = FlexVector::from_vec(vec![1, 2, 3]); - let v2 = FlexVector::from_vec(vec![4, 5, 6]); + let v1 = FVector::from_vec(vec![1, 2, 3]); + let v2 = FVector::from_vec(vec![4, 5, 6]); let summed = v1.zip_with(v2, |a, b| a + b); assert_eq!(summed.as_slice(), &[5, 7, 9]); } #[test] fn test_zip_with_f64() { - let v1 = FlexVector::from_vec(vec![1.5, 2.5, 3.5]); - let v2 = FlexVector::from_vec(vec![4.5, 5.5, 6.5]); + let v1 = FVector::from_vec(vec![1.5, 2.5, 3.5]); + let v2 = FVector::from_vec(vec![4.5, 5.5, 6.5]); let prod = v1.zip_with(v2, |a, b| a * b); assert_eq!(prod.as_slice(), &[6.75, 13.75, 22.75]); } @@ -3970,16 +4773,16 @@ mod tests { #[test] fn test_zip_with_complex_f64() { use num::Complex; - let v1 = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); - let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); + let v1 = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v2 = FVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); let sum = v1.zip_with(v2, |a, b| a + b); assert_eq!(sum.as_slice(), &[Complex::new(6.0, 8.0), Complex::new(10.0, 12.0)]); } #[test] fn test_zip_with_different_lengths() { - let v1 = FlexVector::from_vec(vec![1, 2]); - let v2 = FlexVector::from_vec(vec![10, 20, 30]); + let v1 = FVector::from_vec(vec![1, 2]); + let v2 = FVector::from_vec(vec![10, 20, 30]); let zipped = v1.zip_with(v2, |a, b| a + b); assert_eq!(zipped.as_slice(), &[11, 22]); } @@ -3996,28 +4799,28 @@ mod tests { #[test] fn test_step_by_i32_basic() { - let v = FlexVector::from_vec(vec![1, 2, 3, 4, 5, 6]); + let v = FVector::from_vec(vec![1, 2, 3, 4, 5, 6]); let stepped = v.step_by(2).unwrap(); assert_eq!(stepped.as_slice(), &[1, 3, 5]); } #[test] fn test_step_by_i32_step_one() { - let v = FlexVector::from_vec(vec![10, 20, 30]); + let v = FVector::from_vec(vec![10, 20, 30]); let stepped = v.step_by(1).unwrap(); assert_eq!(stepped.as_slice(), &[10, 20, 30]); } #[test] fn test_step_by_i32_step_equals_len() { - let v = FlexVector::from_vec(vec![7, 8, 9]); + let v = FVector::from_vec(vec![7, 8, 9]); let stepped = v.step_by(3).unwrap(); assert_eq!(stepped.as_slice(), &[7]); } #[test] fn test_step_by_i32_step_greater_than_len() { - let v = FlexVector::from_vec(vec![1, 2, 3]); + let v = FVector::from_vec(vec![1, 2, 3]); let stepped = v.step_by(5).unwrap(); assert_eq!(stepped.as_slice(), &[1]); } @@ -4031,14 +4834,14 @@ mod tests { #[test] fn test_step_by_i32_zero_step() { - let v = FlexVector::from_vec(vec![1, 2, 3]); + let v = FVector::from_vec(vec![1, 2, 3]); let result = v.step_by(0); assert!(result.is_err()); } #[test] fn test_step_by_f64_basic() { - let v = FlexVector::from_vec(vec![1.0, 2.0, 3.0, 4.0]); + let v = FVector::from_vec(vec![1.0, 2.0, 3.0, 4.0]); let stepped = v.step_by(2).unwrap(); assert_eq!(stepped.as_slice(), &[1.0, 3.0]); } @@ -4052,7 +4855,7 @@ mod tests { #[test] fn test_step_by_f64_zero_step() { - let v = FlexVector::from_vec(vec![1.1, 2.2]); + let v = FVector::from_vec(vec![1.1, 2.2]); let result = v.step_by(0); assert!(result.is_err()); } @@ -4060,7 +4863,7 @@ mod tests { #[test] fn test_step_by_complex_f64_basic() { use num::Complex; - let v = FlexVector::from_vec(vec![ + let v = FVector::from_vec(vec![ Complex::new(1.0, 2.0), Complex::new(3.0, 4.0), Complex::new(5.0, 6.0), @@ -4081,7 +4884,7 @@ mod tests { #[test] fn test_step_by_complex_f64_zero_step() { use num::Complex; - let v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0)]); + let v = FVector::from_vec(vec![Complex::new(1.0, 2.0)]); let result = v.step_by(0); assert!(result.is_err()); } @@ -4090,28 +4893,28 @@ mod tests { #[test] fn test_create_mask_i32_greater_than() { - let v = FlexVector::from_vec(vec![1, 5, 3, 7]); + let v = FVector::from_vec(vec![1, 5, 3, 7]); let mask = v.create_mask(|&x| x > 3); assert_eq!(mask.as_slice(), &[false, true, false, true]); } #[test] fn test_create_mask_i32_even() { - let v = FlexVector::from_vec(vec![2, 3, 4, 5]); + let v = FVector::from_vec(vec![2, 3, 4, 5]); let mask = v.create_mask(|&x| x % 2 == 0); assert_eq!(mask.as_slice(), &[true, false, true, false]); } #[test] fn test_create_mask_f64_positive() { - let v = FlexVector::from_vec(vec![-1.0, 0.0, 2.5, -3.3]); + let v = FVector::from_vec(vec![-1.0, 0.0, 2.5, -3.3]); let mask = v.create_mask(|&x| x > 0.0); assert_eq!(mask.as_slice(), &[false, false, true, false]); } #[test] fn test_create_mask_f64_nan() { - let v = FlexVector::from_vec(vec![1.0, f64::NAN, 2.0]); + let v = FVector::from_vec(vec![1.0, f64::NAN, 2.0]); let mask = v.create_mask(|&x| x.is_nan()); assert_eq!(mask.as_slice(), &[false, true, false]); } @@ -4119,7 +4922,7 @@ mod tests { #[test] fn test_create_mask_complex_f64_real_positive() { use num::Complex; - let v = FlexVector::from_vec(vec![ + let v = FVector::from_vec(vec![ Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0), Complex::new(0.0, 0.0), @@ -4131,7 +4934,7 @@ mod tests { #[test] fn test_create_mask_complex_f64_imag_nonzero() { use num::Complex; - let v = FlexVector::from_vec(vec![ + let v = FVector::from_vec(vec![ Complex::new(1.0, 0.0), Complex::new(0.0, 2.0), Complex::new(3.0, 0.0), @@ -4142,7 +4945,7 @@ mod tests { #[test] fn test_create_mask_empty() { - let v: FlexVector = FlexVector::new(); + let v: FlexVector = FVector::new(); let mask = v.create_mask(|&x| x > 0); assert!(mask.is_empty()); } @@ -4151,7 +4954,7 @@ mod tests { #[test] fn test_filter_by_mask_i32_basic() { - let v = FlexVector::from_vec(vec![10, 20, 30, 40]); + let v = FVector::from_vec(vec![10, 20, 30, 40]); let mask = vec![false, true, false, true]; let filtered = v.filter_by_mask(&mask).unwrap(); assert_eq!(filtered.as_slice(), &[20, 40]); @@ -4159,7 +4962,7 @@ mod tests { #[test] fn test_filter_by_mask_i32_all_true() { - let v = FlexVector::from_vec(vec![1, 2, 3]); + let v = FVector::from_vec(vec![1, 2, 3]); let mask = vec![true, true, true]; let filtered = v.filter_by_mask(&mask).unwrap(); assert_eq!(filtered.as_slice(), &[1, 2, 3]); @@ -4167,7 +4970,7 @@ mod tests { #[test] fn test_filter_by_mask_i32_all_false() { - let v = FlexVector::from_vec(vec![1, 2, 3]); + let v = FVector::from_vec(vec![1, 2, 3]); let mask = vec![false, false, false]; let filtered = v.filter_by_mask(&mask).unwrap(); assert!(filtered.is_empty()); @@ -4183,7 +4986,7 @@ mod tests { #[test] fn test_filter_by_mask_i32_mismatched_length() { - let v = FlexVector::from_vec(vec![1, 2, 3]); + let v = FVector::from_vec(vec![1, 2, 3]); let mask = vec![true, false]; let result = v.filter_by_mask(&mask); assert!(result.is_err()); @@ -4191,7 +4994,7 @@ mod tests { #[test] fn test_filter_by_mask_f64_basic() { - let v = FlexVector::from_vec(vec![1.1, 2.2, 3.3, 4.4]); + let v = FVector::from_vec(vec![1.1, 2.2, 3.3, 4.4]); let mask = vec![true, false, true, false]; let filtered = v.filter_by_mask(&mask).unwrap(); assert_eq!(filtered.as_slice(), &[1.1, 3.3]); @@ -4199,7 +5002,7 @@ mod tests { #[test] fn test_filter_by_mask_f64_all_true() { - let v = FlexVector::from_vec(vec![1.1, 2.2]); + let v = FVector::from_vec(vec![1.1, 2.2]); let mask = vec![true, true]; let filtered = v.filter_by_mask(&mask).unwrap(); assert_eq!(filtered.as_slice(), &[1.1, 2.2]); @@ -4207,7 +5010,7 @@ mod tests { #[test] fn test_filter_by_mask_f64_all_false() { - let v = FlexVector::from_vec(vec![1.1, 2.2]); + let v = FVector::from_vec(vec![1.1, 2.2]); let mask = vec![false, false]; let filtered = v.filter_by_mask(&mask).unwrap(); assert!(filtered.is_empty()); @@ -4223,7 +5026,7 @@ mod tests { #[test] fn test_filter_by_mask_f64_mismatched_length() { - let v = FlexVector::from_vec(vec![1.1, 2.2]); + let v = FVector::from_vec(vec![1.1, 2.2]); let mask = vec![true]; let result = v.filter_by_mask(&mask); assert!(result.is_err()); @@ -4232,7 +5035,7 @@ mod tests { #[test] fn test_filter_by_mask_complex_f64_basic() { use num::Complex; - let v = FlexVector::from_vec(vec![ + let v = FVector::from_vec(vec![ Complex::new(1.0, 2.0), Complex::new(3.0, 4.0), Complex::new(5.0, 6.0), @@ -4245,7 +5048,7 @@ mod tests { #[test] fn test_filter_by_mask_complex_f64_all_true() { use num::Complex; - let v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let mask = vec![true, true]; let filtered = v.filter_by_mask(&mask).unwrap(); assert_eq!(filtered.as_slice(), &[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); @@ -4254,7 +5057,7 @@ mod tests { #[test] fn test_filter_by_mask_complex_f64_all_false() { use num::Complex; - let v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let mask = vec![false, false]; let filtered = v.filter_by_mask(&mask).unwrap(); assert!(filtered.is_empty()); @@ -4272,7 +5075,7 @@ mod tests { #[test] fn test_filter_by_mask_complex_f64_mismatched_length() { use num::Complex; - let v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0)]); + let v = FVector::from_vec(vec![Complex::new(1.0, 2.0)]); let mask = vec![true, false]; let result = v.filter_by_mask(&mask); assert!(result.is_err()); @@ -4287,7 +5090,7 @@ mod tests { // -- translate -- #[test] fn test_translate_i32() { - let v1 = FlexVector::from_vec(vec![1, 2, 3]); + let v1 = FVector::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); let result = v1.translate(&v2).unwrap(); assert_eq!(result.as_slice(), &[5, 7, 9]); @@ -4295,7 +5098,7 @@ mod tests { #[test] fn test_translate_f64() { - let v1 = FlexVector::from_vec(vec![1.5, 2.5, 3.5]); + let v1 = FVector::from_vec(vec![1.5, 2.5, 3.5]); let v2 = FlexVector::from_vec(vec![0.5, 1.5, 2.5]); let result = v1.translate(&v2).unwrap(); assert_eq!(result.as_slice(), &[2.0, 4.0, 6.0]); @@ -4303,7 +5106,7 @@ mod tests { #[test] fn test_translate_complex_f64() { - let v1 = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v1 = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); let result = v1.translate(&v2).unwrap(); assert_eq!(result.as_slice(), &[Complex::new(6.0, 8.0), Complex::new(10.0, 12.0)]); @@ -4311,7 +5114,7 @@ mod tests { #[test] fn test_translate_mismatched_lengths() { - let v1 = FlexVector::from_vec(vec![1, 2]); + let v1 = FVector::from_vec(vec![1, 2]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); let result = v1.translate(&v2); assert!(result.is_err()); @@ -4320,7 +5123,7 @@ mod tests { // -- mut_translate -- #[test] fn test_mut_translate_i32() { - let mut v1 = FlexVector::from_vec(vec![1, 2, 3]); + let mut v1 = FVector::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); v1.mut_translate(&v2).unwrap(); assert_eq!(v1.as_slice(), &[5, 7, 9]); @@ -4328,7 +5131,7 @@ mod tests { #[test] fn test_mut_translate_f64() { - let mut v1 = FlexVector::from_vec(vec![1.5, 2.5, 3.5]); + let mut v1 = FVector::from_vec(vec![1.5, 2.5, 3.5]); let v2 = FlexVector::from_vec(vec![0.5, 1.5, 2.5]); v1.mut_translate(&v2).unwrap(); assert_eq!(v1.as_slice(), &[2.0, 4.0, 6.0]); @@ -4336,7 +5139,7 @@ mod tests { #[test] fn test_mut_translate_complex_f64() { - let mut v1 = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let mut v1 = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); v1.mut_translate(&v2).unwrap(); assert_eq!(v1.as_slice(), &[Complex::new(6.0, 8.0), Complex::new(10.0, 12.0)]); @@ -4344,7 +5147,7 @@ mod tests { #[test] fn test_mut_translate_mismatched_lengths() { - let mut v1 = FlexVector::from_vec(vec![1, 2]); + let mut v1 = FVector::from_vec(vec![1, 2]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); let result = v1.mut_translate(&v2); assert!(result.is_err()); @@ -4353,14 +5156,14 @@ mod tests { // -- scale -- #[test] fn test_scale_i32() { - let v = FlexVector::from_vec(vec![1, 2, 3]); + let v = FVector::from_vec(vec![1, 2, 3]); let scaled = v.scale(10); assert_eq!(scaled.as_slice(), &[10, 20, 30]); } #[test] fn test_scale_f64() { - let v = FlexVector::from_vec(vec![1.5, -2.0, 0.0]); + let v = FVector::from_vec(vec![1.5, -2.0, 0.0]); let scaled = v.scale(2.0); assert_eq!(scaled.as_slice(), &[3.0, -4.0, 0.0]); } @@ -4368,7 +5171,7 @@ mod tests { #[test] fn test_scale_complex_f64() { use num::Complex; - let v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + let v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); let scalar = Complex::new(2.0, 0.0); let scaled = v.scale(scalar); assert_eq!(scaled.as_slice(), &[Complex::new(2.0, 4.0), Complex::new(-6.0, 8.0)]); @@ -4377,21 +5180,21 @@ mod tests { // -- mut_scale -- #[test] fn test_mut_scale_i32() { - let mut v = FlexVector::from_vec(vec![1, 2, 3]); + let mut v = FVector::from_vec(vec![1, 2, 3]); v.mut_scale(10); assert_eq!(v.as_slice(), &[10, 20, 30]); } #[test] fn test_mut_scale_f64() { - let mut v = FlexVector::from_vec(vec![1.5, -2.0, 0.0]); + let mut v = FVector::from_vec(vec![1.5, -2.0, 0.0]); v.mut_scale(2.0); assert_eq!(v.as_slice(), &[3.0, -4.0, 0.0]); } #[test] fn test_mut_scale_complex_f64() { - let mut v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + let mut v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); let scalar = Complex::new(2.0, 0.0); v.mut_scale(scalar); assert_eq!(v.as_slice(), &[Complex::new(2.0, 4.0), Complex::new(-6.0, 8.0)]); @@ -4400,7 +5203,7 @@ mod tests { // -- negate -- #[test] fn test_negate_i32() { - let v = FlexVector::from_vec(vec![1, -2, 3]); + let v = FVector::from_vec(vec![1, -2, 3]); let neg = v.negate(); assert_eq!(neg.as_slice(), &[-1, 2, -3]); // original unchanged @@ -4409,7 +5212,7 @@ mod tests { #[test] fn test_negate_f64() { - let v = FlexVector::from_vec(vec![1.5, -2.5, 0.0]); + let v = FVector::from_vec(vec![1.5, -2.5, 0.0]); let neg = v.negate(); assert_eq!(neg.as_slice(), &[-1.5, 2.5, -0.0]); assert_eq!(v.as_slice(), &[1.5, -2.5, 0.0]); @@ -4418,7 +5221,7 @@ mod tests { #[test] fn test_negate_complex_f64() { use num::Complex; - let v = FlexVector::from_vec(vec![Complex::new(1.0, -2.0), Complex::new(-3.0, 4.0)]); + let v = FVector::from_vec(vec![Complex::new(1.0, -2.0), Complex::new(-3.0, 4.0)]); let neg = v.negate(); assert_eq!(neg.as_slice(), &[Complex::new(-1.0, 2.0), Complex::new(3.0, -4.0)]); assert_eq!(v.as_slice(), &[Complex::new(1.0, -2.0), Complex::new(-3.0, 4.0)]); @@ -4427,14 +5230,14 @@ mod tests { // -- mut_negate -- #[test] fn test_mut_negate_i32() { - let mut v = FlexVector::from_vec(vec![1, -2, 3]); + let mut v = FVector::from_vec(vec![1, -2, 3]); v.mut_negate(); assert_eq!(v.as_slice(), &[-1, 2, -3]); } #[test] fn test_mut_negate_f64() { - let mut v = FlexVector::from_vec(vec![1.5, -2.5, 0.0]); + let mut v = FVector::from_vec(vec![1.5, -2.5, 0.0]); v.mut_negate(); assert_eq!(v.as_slice(), &[-1.5, 2.5, -0.0]); } @@ -4442,7 +5245,7 @@ mod tests { #[test] fn test_mut_negate_complex_f64() { use num::Complex; - let mut v = FlexVector::from_vec(vec![Complex::new(1.0, -2.0), Complex::new(-3.0, 4.0)]); + let mut v = FVector::from_vec(vec![Complex::new(1.0, -2.0), Complex::new(-3.0, 4.0)]); v.mut_negate(); assert_eq!(v.as_slice(), &[Complex::new(-1.0, 2.0), Complex::new(3.0, -4.0)]); } @@ -4450,14 +5253,14 @@ mod tests { // -- mut_zero -- #[test] fn test_mut_zero_i32() { - let mut v = FlexVector::from_vec(vec![1, -2, 3]); + let mut v = FVector::from_vec(vec![1, -2, 3]); v.mut_zero(); assert_eq!(v.as_slice(), &[0, 0, 0]); } #[test] fn test_mut_zero_f64() { - let mut v = FlexVector::from_vec(vec![1.5, -2.5, 0.0]); + let mut v = FVector::from_vec(vec![1.5, -2.5, 0.0]); v.mut_zero(); assert_eq!(v.as_slice(), &[0.0, 0.0, 0.0]); } @@ -4465,7 +5268,7 @@ mod tests { #[test] fn test_mut_zero_complex_f64() { use num::Complex; - let mut v = FlexVector::from_vec(vec![Complex::new(1.0, -2.0), Complex::new(-3.0, 4.0)]); + let mut v = FVector::from_vec(vec![Complex::new(1.0, -2.0), Complex::new(-3.0, 4.0)]); v.mut_zero(); assert_eq!(v.as_slice(), &[Complex::new(0.0, 0.0), Complex::new(0.0, 0.0)]); } @@ -4473,7 +5276,7 @@ mod tests { // -- dot -- #[test] fn test_dot_i32() { - let v1 = FlexVector::from_vec(vec![1, 2, 3]); + let v1 = FVector::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); let dot = v1.dot(&v2).unwrap(); assert_eq!(dot, 1 * 4 + 2 * 5 + 3 * 6); // 32 @@ -4481,7 +5284,7 @@ mod tests { #[test] fn test_dot_f64() { - let v1 = FlexVector::from_vec(vec![1.5, 2.0, -3.0]); + let v1 = FVector::from_vec(vec![1.5, 2.0, -3.0]); let v2 = FlexVector::from_vec(vec![2.0, 0.5, 4.0]); let dot = v1.dot(&v2).unwrap(); assert!((dot - (1.5 * 2.0 + 2.0 * 0.5 + -3.0 * 4.0)).abs() < 1e-12); @@ -4489,7 +5292,7 @@ mod tests { #[test] fn test_dot_mismatched_lengths() { - let v1 = FlexVector::from_vec(vec![1, 2]); + let v1 = FVector::from_vec(vec![1, 2]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); let dot = v1.dot(&v2); assert!(dot.is_err()); @@ -4508,7 +5311,7 @@ mod tests { // -- dot_to_f64 -- #[test] fn test_dot_to_f64_i32() { - let v1 = FlexVector::from_vec(vec![1, 2, 3]); + let v1 = FVector::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); let dot = v1.dot_to_f64(&v2).unwrap(); assert!((dot - 32.0).abs() < 1e-12); @@ -4516,7 +5319,7 @@ mod tests { #[test] fn test_dot_to_f64_f64() { - let v1 = FlexVector::from_vec(vec![1.5, 2.0, -3.0]); + let v1 = FVector::from_vec(vec![1.5, 2.0, -3.0]); let v2 = FlexVector::from_vec(vec![2.0, 0.5, 4.0]); let dot = v1.dot_to_f64(&v2).unwrap(); assert!((dot - (1.5 * 2.0 + 2.0 * 0.5 + -3.0 * 4.0)).abs() < 1e-12); @@ -4524,7 +5327,7 @@ mod tests { #[test] fn test_dot_to_f64_mismatched_lengths() { - let v1 = FlexVector::from_vec(vec![1, 2]); + let v1 = FVector::from_vec(vec![1, 2]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); let dot = v1.dot(&v2); assert!(dot.is_err()); @@ -4535,7 +5338,7 @@ mod tests { // -- cross -- #[test] fn test_cross_i32() { - let v1 = FlexVector::from_vec(vec![1, 2, 3]); + let v1 = FVector::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); let cross = v1.cross(&v2).unwrap(); // [2*6 - 3*5, 3*4 - 1*6, 1*5 - 2*4] = [12-15, 12-6, 5-8] = [-3, 6, -3] @@ -4544,7 +5347,7 @@ mod tests { #[test] fn test_cross_f64() { - let v1 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = FVector::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 5.0, 6.0]); let cross = v1.cross(&v2).unwrap(); assert_eq!(cross.as_slice(), &[-3.0, 6.0, -3.0]); @@ -4556,7 +5359,7 @@ mod tests { #[test] fn test_cross_wrong_length_1() { - let v1 = FlexVector::from_vec(vec![1, 2]); + let v1 = FVector::from_vec(vec![1, 2]); let v2 = FlexVector::from_vec(vec![3, 4, 5]); let result = v1.cross(&v2); assert!(result.is_err()); @@ -4564,7 +5367,7 @@ mod tests { #[test] fn test_cross_wrong_length_2() { - let v1 = FlexVector::from_vec(vec![1, 2, 3]); + let v1 = FVector::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![3, 4]); let result = v1.cross(&v2); assert!(result.is_err()); @@ -4572,7 +5375,7 @@ mod tests { #[test] fn test_cross_wrong_length_3() { - let v1 = FlexVector::from_vec(vec![1, 2]); + let v1 = FVector::from_vec(vec![1, 2]); let v2 = FlexVector::from_vec(vec![3, 4]); let result = v1.cross(&v2); assert!(result.is_err()); @@ -4581,14 +5384,14 @@ mod tests { // -- sum -- #[test] fn test_sum_i32() { - let v = FlexVector::from_vec(vec![1, 2, 3, 4]); + let v = FVector::from_vec(vec![1, 2, 3, 4]); let s = v.sum(); assert_eq!(s, 10); } #[test] fn test_sum_f64() { - let v = FlexVector::from_vec(vec![1.5, -2.5, 3.0]); + let v = FVector::from_vec(vec![1.5, -2.5, 3.0]); let s = v.sum(); assert!((s - 2.0).abs() < 1e-12); } @@ -4596,7 +5399,7 @@ mod tests { #[test] fn test_sum_complex_f64() { use num::Complex; - let v = FlexVector::from_vec(vec![ + let v = FVector::from_vec(vec![ Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0), Complex::new(5.0, -6.0), @@ -4608,14 +5411,14 @@ mod tests { // -- product -- #[test] fn test_product_i32() { - let v = FlexVector::from_vec(vec![2, 3, 4]); + let v = FVector::from_vec(vec![2, 3, 4]); let p = v.product(); assert_eq!(p, 24); } #[test] fn test_product_f64() { - let v = FlexVector::from_vec(vec![1.5, -2.0, 3.0]); + let v = FVector::from_vec(vec![1.5, -2.0, 3.0]); let p = v.product(); assert!((p - (1.5 * -2.0 * 3.0)).abs() < 1e-12); } @@ -4623,7 +5426,7 @@ mod tests { #[test] fn test_product_complex_f64() { use num::Complex; - let v = FlexVector::from_vec(vec![ + let v = FVector::from_vec(vec![ Complex::new(1.0, 2.0), Complex::new(3.0, -1.0), Complex::new(2.0, 0.5), @@ -4636,31 +5439,31 @@ mod tests { // -- minimum -- #[test] fn test_minimum_i32() { - let v = FlexVector::from_vec(vec![3, 1, 4, 2]); + let v = FVector::from_vec(vec![3, 1, 4, 2]); assert_eq!(v.minimum(), Some(1)); } #[test] fn test_minimum_f64_basic() { - let v = FlexVector::from_vec(vec![1.5, -2.0, 3.0]); + let v = FVector::from_vec(vec![1.5, -2.0, 3.0]); assert_eq!(v.minimum(), Some(-2.0)); } #[test] fn test_minimum_f64_all_positive() { - let v = FlexVector::from_vec(vec![2.0, 4.0, 1.0, 3.0]); + let v = FVector::from_vec(vec![2.0, 4.0, 1.0, 3.0]); assert_eq!(v.minimum(), Some(1.0)); } #[test] fn test_minimum_f64_all_negative() { - let v = FlexVector::from_vec(vec![-1.0, -2.0, -3.0]); + let v = FVector::from_vec(vec![-1.0, -2.0, -3.0]); assert_eq!(v.minimum(), Some(-3.0)); } #[test] fn test_minimum_f64_single_element() { - let v = FlexVector::from_vec(vec![42.0]); + let v = FVector::from_vec(vec![42.0]); assert_eq!(v.minimum(), Some(42.0)); } @@ -4672,7 +5475,7 @@ mod tests { #[test] fn test_minimum_f64_with_nan() { - let v = FlexVector::from_vec(vec![1.0, f64::NAN, 2.0]); + let v = FVector::from_vec(vec![1.0, f64::NAN, 2.0]); // The result is not guaranteed to be meaningful if NaN is present, // but it should return Some value (could be NaN or a number). assert!(v.minimum().is_some()); @@ -4688,7 +5491,7 @@ mod tests { #[test] fn test_elementwise_min_i32() { - let v1 = FlexVector::from_vec(vec![1, 5, 3, 7]); + let v1 = FVector::from_vec(vec![1, 5, 3, 7]); let v2 = FlexVector::from_vec(vec![2, 4, 6, 0]); let min = v1.elementwise_min(&v2).unwrap(); assert_eq!(min.as_slice(), &[1, 4, 3, 0]); @@ -4696,7 +5499,7 @@ mod tests { #[test] fn test_elementwise_min_f64() { - let v1 = FlexVector::from_vec(vec![1.5, -2.0, 3.0]); + let v1 = FVector::from_vec(vec![1.5, -2.0, 3.0]); let v2 = FlexVector::from_vec(vec![2.5, -3.0, 2.0]); let min = v1.elementwise_min(&v2).unwrap(); assert_eq!(min.as_slice(), &[1.5, -3.0, 2.0]); @@ -4712,7 +5515,7 @@ mod tests { #[test] fn test_elementwise_min_mismatched_length() { - let v1 = FlexVector::from_vec(vec![1, 2]); + let v1 = FVector::from_vec(vec![1, 2]); let v2 = FlexVector::from_vec(vec![3, 4, 5]); let result = v1.elementwise_min(&v2); assert!(result.is_err()); @@ -4720,7 +5523,7 @@ mod tests { #[test] fn test_elementwise_min_f64_with_nan() { - let v1 = FlexVector::from_vec(vec![1.0, f64::NAN, 3.0]); + let v1 = FVector::from_vec(vec![1.0, f64::NAN, 3.0]); let v2 = FlexVector::from_vec(vec![2.0, 2.0, f64::NAN]); let min = v1.elementwise_min(&v2).unwrap(); assert_eq!(min[0], 1.0); // min(1.0, 2.0) = 1.0 @@ -4730,7 +5533,7 @@ mod tests { #[test] fn test_elementwise_min_f64_both_nan() { - let v1 = FlexVector::from_vec(vec![f64::NAN]); + let v1 = FVector::from_vec(vec![f64::NAN]); let v2 = FlexVector::from_vec(vec![f64::NAN]); let min = v1.elementwise_min(&v2).unwrap(); assert!(min[0].is_nan()); @@ -4739,31 +5542,31 @@ mod tests { // -- maximum -- #[test] fn test_maximum_i32() { - let v = FlexVector::from_vec(vec![3, 1, 4, 2]); + let v = FVector::from_vec(vec![3, 1, 4, 2]); assert_eq!(v.maximum(), Some(4)); } #[test] fn test_maximum_f64_basic() { - let v = FlexVector::from_vec(vec![1.5, -2.0, 3.0]); + let v = FVector::from_vec(vec![1.5, -2.0, 3.0]); assert_eq!(v.maximum(), Some(3.0)); } #[test] fn test_maximum_f64_all_positive() { - let v = FlexVector::from_vec(vec![2.0, 4.0, 1.0, 3.0]); + let v = FVector::from_vec(vec![2.0, 4.0, 1.0, 3.0]); assert_eq!(v.maximum(), Some(4.0)); } #[test] fn test_maximum_f64_all_negative() { - let v = FlexVector::from_vec(vec![-1.0, -2.0, -3.0]); + let v = FVector::from_vec(vec![-1.0, -2.0, -3.0]); assert_eq!(v.maximum(), Some(-1.0)); } #[test] fn test_maximum_f64_single_element() { - let v = FlexVector::from_vec(vec![42.0]); + let v = FVector::from_vec(vec![42.0]); assert_eq!(v.maximum(), Some(42.0)); } @@ -4775,7 +5578,7 @@ mod tests { #[test] fn test_maximum_f64_with_nan() { - let v = FlexVector::from_vec(vec![1.0, f64::NAN, 2.0]); + let v = FVector::from_vec(vec![1.0, f64::NAN, 2.0]); // The result is not guaranteed to be meaningful if NaN is present, // but it should return Some value (could be NaN or a number). assert!(v.maximum().is_some()); @@ -4785,7 +5588,7 @@ mod tests { #[test] fn test_elementwise_max_i32() { - let v1 = FlexVector::from_vec(vec![1, 5, 3, 7]); + let v1 = FVector::from_vec(vec![1, 5, 3, 7]); let v2 = FlexVector::from_vec(vec![2, 4, 6, 0]); let max = v1.elementwise_max(&v2).unwrap(); assert_eq!(max.as_slice(), &[2, 5, 6, 7]); @@ -4793,7 +5596,7 @@ mod tests { #[test] fn test_elementwise_max_f64() { - let v1 = FlexVector::from_vec(vec![1.5, -2.0, 3.0]); + let v1 = FVector::from_vec(vec![1.5, -2.0, 3.0]); let v2 = FlexVector::from_vec(vec![2.5, -3.0, 2.0]); let max = v1.elementwise_max(&v2).unwrap(); assert_eq!(max.as_slice(), &[2.5, -2.0, 3.0]); @@ -4809,7 +5612,7 @@ mod tests { #[test] fn test_elementwise_max_mismatched_length() { - let v1 = FlexVector::from_vec(vec![1, 2, 3]); + let v1 = FVector::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![4, 5]); let result = v1.elementwise_max(&v2); assert!(result.is_err()); @@ -4817,7 +5620,7 @@ mod tests { #[test] fn test_elementwise_max_f64_with_nan() { - let v1 = FlexVector::from_vec(vec![1.0, f64::NAN, 3.0]); + let v1 = FVector::from_vec(vec![1.0, f64::NAN, 3.0]); let v2 = FlexVector::from_vec(vec![2.0, 2.0, f64::NAN]); let max = v1.elementwise_max(&v2).unwrap(); assert_eq!(max[0], 2.0); // max(1.0, 2.0) = 2.0 @@ -4827,7 +5630,7 @@ mod tests { #[test] fn test_elementwise_max_f64_both_nan() { - let v1 = FlexVector::from_vec(vec![f64::NAN]); + let v1 = FVector::from_vec(vec![f64::NAN]); let v2 = FlexVector::from_vec(vec![f64::NAN]); let max = v1.elementwise_max(&v2).unwrap(); assert!(max[0].is_nan()); @@ -4837,21 +5640,21 @@ mod tests { #[test] fn test_elementwise_clamp_i32_basic() { - let v = FlexVector::from_vec(vec![-5, 0, 5, 10, 15]); + let v = FVector::from_vec(vec![-5, 0, 5, 10, 15]); let clamped = v.elementwise_clamp(0, 10); assert_eq!(clamped.as_slice(), &[0, 0, 5, 10, 10]); } #[test] fn test_elementwise_clamp_i32_all_below() { - let v = FlexVector::from_vec(vec![-3, -2, -1]); + let v = FVector::from_vec(vec![-3, -2, -1]); let clamped = v.elementwise_clamp(0, 5); assert_eq!(clamped.as_slice(), &[0, 0, 0]); } #[test] fn test_elementwise_clamp_i32_all_above() { - let v = FlexVector::from_vec(vec![11, 12, 13]); + let v = FVector::from_vec(vec![11, 12, 13]); let clamped = v.elementwise_clamp(0, 10); assert_eq!(clamped.as_slice(), &[10, 10, 10]); } @@ -4865,28 +5668,28 @@ mod tests { #[test] fn test_elementwise_clamp_f64_basic() { - let v = FlexVector::from_vec(vec![-2.5, 0.0, 3.5, 7.2, 12.0]); + let v = FVector::from_vec(vec![-2.5, 0.0, 3.5, 7.2, 12.0]); let clamped = v.elementwise_clamp(0.0, 10.0); assert_eq!(clamped.as_slice(), &[0.0, 0.0, 3.5, 7.2, 10.0]); } #[test] fn test_elementwise_clamp_f64_all_below() { - let v = FlexVector::from_vec(vec![-1.1, -2.2]); + let v = FVector::from_vec(vec![-1.1, -2.2]); let clamped = v.elementwise_clamp(0.0, 5.0); assert_eq!(clamped.as_slice(), &[0.0, 0.0]); } #[test] fn test_elementwise_clamp_f64_all_above() { - let v = FlexVector::from_vec(vec![11.1, 12.2]); + let v = FVector::from_vec(vec![11.1, 12.2]); let clamped = v.elementwise_clamp(0.0, 10.0); assert_eq!(clamped.as_slice(), &[10.0, 10.0]); } #[test] fn test_elementwise_clamp_f64_with_nan() { - let v = FlexVector::from_vec(vec![1.0, f64::NAN, 5.0]); + let v = FVector::from_vec(vec![1.0, f64::NAN, 5.0]); let clamped = v.elementwise_clamp(0.0, 4.0); assert_eq!(clamped[0], 1.0); assert!(clamped[1].is_nan()); @@ -4903,14 +5706,14 @@ mod tests { // -- l1_norm -- #[test] fn test_l1_norm_i32() { - let v = FlexVector::from_vec(vec![1, -2, 3]); + let v = FVector::from_vec(vec![1, -2, 3]); let norm = v.l1_norm(); assert_eq!(norm, 6); } #[test] fn test_l1_norm_f64() { - let v = FlexVector::from_vec(vec![1.5, -2.5, 3.0]); + let v = FVector::from_vec(vec![1.5, -2.5, 3.0]); let norm = v.l1_norm(); assert!((norm - 7.0).abs() < 1e-12); } @@ -4920,14 +5723,14 @@ mod tests { // -- linf_norm -- #[test] fn test_linf_norm_i32() { - let v = FlexVector::from_vec(vec![1, -5, 3, 2]); + let v = FVector::from_vec(vec![1, -5, 3, 2]); let norm = v.linf_norm(); assert_eq!(norm, 5); } #[test] fn test_linf_norm_f64() { - let v = FlexVector::from_vec(vec![1.5, -2.5, 3.0, -7.2]); + let v = FVector::from_vec(vec![1.5, -2.5, 3.0, -7.2]); let norm = v.linf_norm(); assert!((norm - 7.2).abs() < 1e-12); } @@ -4941,7 +5744,7 @@ mod tests { // -- normalize -- #[test] fn test_normalize_f64() { - let v = FlexVector::from_vec(vec![3.0, 4.0]); + let v = FVector::from_vec(vec![3.0, 4.0]); let normalized = v.normalize().unwrap(); // The norm is 5.0, so the normalized vector should be [0.6, 0.8] assert!((normalized.as_slice()[0] - 0.6).abs() < 1e-12); @@ -4950,14 +5753,14 @@ mod tests { #[test] fn test_normalize_f64_zero_vector() { - let v = FlexVector::from_vec(vec![0.0, 0.0]); + let v = FVector::from_vec(vec![0.0, 0.0]); let result = v.normalize(); assert!(result.is_err()); } #[test] fn test_normalize_f64_negative_values() { - let v = FlexVector::from_vec(vec![-3.0, -4.0]); + let v = FVector::from_vec(vec![-3.0, -4.0]); let normalized = v.normalize().unwrap(); // The norm is 5.0, so the normalized vector should be [-0.6, -0.8] assert!((normalized.as_slice()[0] + 0.6).abs() < 1e-12); @@ -4967,7 +5770,7 @@ mod tests { // -- mut_normalize -- #[test] fn test_mut_normalize_f64() { - let mut v = FlexVector::from_vec(vec![3.0, 4.0]); + let mut v = FVector::from_vec(vec![3.0, 4.0]); v.mut_normalize().unwrap(); // The norm is 5.0, so the normalized vector should be [0.6, 0.8] assert!((v.as_slice()[0] - 0.6).abs() < 1e-12); @@ -4976,14 +5779,14 @@ mod tests { #[test] fn test_mut_normalize_f64_zero_vector() { - let mut v = FlexVector::from_vec(vec![0.0, 0.0]); + let mut v = FVector::from_vec(vec![0.0, 0.0]); let result = v.mut_normalize(); assert!(result.is_err()); } #[test] fn test_mut_normalize_f64_negative_values() { - let mut v = FlexVector::from_vec(vec![-3.0, -4.0]); + let mut v = FVector::from_vec(vec![-3.0, -4.0]); v.mut_normalize().unwrap(); // The norm is 5.0, so the normalized vector should be [-0.6, -0.8] assert!((v.as_slice()[0] + 0.6).abs() < 1e-12); @@ -4993,7 +5796,7 @@ mod tests { // -- normalize_to -- #[test] fn test_normalize_to_f64() { - let v = FlexVector::from_vec(vec![3.0, 4.0]); + let v = FVector::from_vec(vec![3.0, 4.0]); let normalized = v.normalize_to(10.0).unwrap(); // The original norm is 5.0, so the normalized vector should be [6.0, 8.0] assert!((normalized.as_slice()[0] - 6.0).abs() < 1e-12); @@ -5002,14 +5805,14 @@ mod tests { #[test] fn test_normalize_to_f64_zero_vector() { - let v = FlexVector::from_vec(vec![0.0, 0.0]); + let v = FVector::from_vec(vec![0.0, 0.0]); let result = v.normalize_to(1.0); assert!(result.is_err()); } #[test] fn test_normalize_to_f64_negative_values() { - let v = FlexVector::from_vec(vec![-3.0, -4.0]); + let v = FVector::from_vec(vec![-3.0, -4.0]); let normalized = v.normalize_to(5.0).unwrap(); // The original norm is 5.0, so the normalized vector should be [-3.0, -4.0] assert!((normalized.as_slice()[0] + 3.0).abs() < 1e-12); @@ -5019,7 +5822,7 @@ mod tests { // -- mut_normalize_to -- #[test] fn test_mut_normalize_to_f64() { - let mut v = FlexVector::from_vec(vec![3.0, 4.0]); + let mut v = FVector::from_vec(vec![3.0, 4.0]); v.mut_normalize_to(10.0).unwrap(); // The original norm is 5.0, so the normalized vector should be [6.0, 8.0] assert!((v.as_slice()[0] - 6.0).abs() < 1e-12); @@ -5028,14 +5831,14 @@ mod tests { #[test] fn test_mut_normalize_to_f64_zero_vector() { - let mut v = FlexVector::from_vec(vec![0.0, 0.0]); + let mut v = FVector::from_vec(vec![0.0, 0.0]); let result = v.mut_normalize_to(1.0); assert!(result.is_err()); } #[test] fn test_mut_normalize_to_f64_negative_values() { - let mut v = FlexVector::from_vec(vec![-3.0, -4.0]); + let mut v = FVector::from_vec(vec![-3.0, -4.0]); v.mut_normalize_to(5.0).unwrap(); // The original norm is 5.0, so the normalized vector should be [-3.0, -4.0] assert!((v.as_slice()[0] + 3.0).abs() < 1e-12); @@ -5045,7 +5848,7 @@ mod tests { // -- lerp -- #[test] fn test_lerp_f64_weight_zero() { - let v1 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = FVector::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 5.0, 6.0]); let result = v1.lerp(&v2, 0.0).unwrap(); // Should be equal to v1 @@ -5056,7 +5859,7 @@ mod tests { #[test] fn test_lerp_f64_weight_one() { - let v1 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = FVector::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 5.0, 6.0]); let result = v1.lerp(&v2, 1.0).unwrap(); // Should be equal to v2 @@ -5067,7 +5870,7 @@ mod tests { #[test] fn test_lerp_f64_weight_half() { - let v1 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = FVector::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 5.0, 6.0]); let result = v1.lerp(&v2, 0.5).unwrap(); // Should be the midpoint @@ -5078,7 +5881,7 @@ mod tests { #[test] fn test_lerp_f64_weight_out_of_bounds() { - let v1 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = FVector::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 5.0, 6.0]); let result_low = v1.lerp(&v2, -0.1); let result_high = v1.lerp(&v2, 1.1); @@ -5089,7 +5892,7 @@ mod tests { // -- mut_lerp -- #[test] fn test_mut_lerp_f64_weight_zero() { - let mut v1 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let mut v1 = FVector::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 5.0, 6.0]); v1.mut_lerp(&v2, 0.0).unwrap(); // Should be equal to original v1 @@ -5100,7 +5903,7 @@ mod tests { #[test] fn test_mut_lerp_f64_weight_one() { - let mut v1 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let mut v1 = FVector::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 5.0, 6.0]); v1.mut_lerp(&v2, 1.0).unwrap(); // Should be equal to v2 @@ -5111,7 +5914,7 @@ mod tests { #[test] fn test_mut_lerp_f64_weight_half() { - let mut v1 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let mut v1 = FVector::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 5.0, 6.0]); v1.mut_lerp(&v2, 0.5).unwrap(); // Should be the midpoint @@ -5122,7 +5925,7 @@ mod tests { #[test] fn test_mut_lerp_f64_weight_out_of_bounds() { - let mut v1 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let mut v1 = FVector::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 5.0, 6.0]); let result_low = v1.mut_lerp(&v2, -0.1); let result_high = v1.mut_lerp(&v2, 1.1); @@ -5133,7 +5936,7 @@ mod tests { // -- midpoint -- #[test] fn test_midpoint_f64() { - let v1 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = FVector::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 5.0, 6.0]); let midpoint = v1.midpoint(&v2).unwrap(); // Should be the average of each component @@ -5144,7 +5947,7 @@ mod tests { #[test] fn test_midpoint_f64_negative_values() { - let v1 = FlexVector::from_vec(vec![-1.0, -2.0, -3.0]); + let v1 = FVector::from_vec(vec![-1.0, -2.0, -3.0]); let v2 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); let midpoint = v1.midpoint(&v2).unwrap(); // Should be [0.0, 0.0, 0.0] @@ -5155,7 +5958,7 @@ mod tests { #[test] fn test_midpoint_f64_mismatched_length() { - let v1 = FlexVector::from_vec(vec![1.0, 2.0]); + let v1 = FVector::from_vec(vec![1.0, 2.0]); let v2 = FlexVector::from_vec(vec![3.0, 4.0, 5.0]); let result = v1.midpoint(&v2); assert!(result.is_err()); @@ -5164,7 +5967,7 @@ mod tests { // -- distance -- #[test] fn test_distance_f64_basic() { - let v1 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = FVector::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 6.0, 8.0]); let dist = v1.distance(&v2).unwrap(); // sqrt((1-4)^2 + (2-6)^2 + (3-8)^2) = sqrt(9 + 16 + 25) = sqrt(50) @@ -5173,7 +5976,7 @@ mod tests { #[test] fn test_distance_f64_zero() { - let v1 = FlexVector::from_vec(vec![0.0, 0.0, 0.0]); + let v1 = FVector::from_vec(vec![0.0, 0.0, 0.0]); let v2 = FlexVector::from_vec(vec![0.0, 0.0, 0.0]); let dist = v1.distance(&v2).unwrap(); assert_eq!(dist, 0.0); @@ -5181,7 +5984,7 @@ mod tests { #[test] fn test_distance_f64_negative_values() { - let v1 = FlexVector::from_vec(vec![-1.0, -2.0, -3.0]); + let v1 = FVector::from_vec(vec![-1.0, -2.0, -3.0]); let v2 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); let dist = v1.distance(&v2).unwrap(); // sqrt(((-1)-1)^2 + ((-2)-2)^2 + ((-3)-3)^2) = sqrt(4 + 16 + 36) = sqrt(56) @@ -5190,7 +5993,7 @@ mod tests { #[test] fn test_distance_f64_mismatched_length() { - let v1 = FlexVector::from_vec(vec![1.0, 2.0]); + let v1 = FVector::from_vec(vec![1.0, 2.0]); let v2 = FlexVector::from_vec(vec![3.0, 4.0, 5.0]); let result = v1.distance(&v2); assert!(result.is_err()); @@ -5199,7 +6002,7 @@ mod tests { // -- manhattan_distance -- #[test] fn test_manhattan_distance_f64_basic() { - let v1 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = FVector::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 6.0, 8.0]); let dist = v1.manhattan_distance(&v2).unwrap(); // |1-4| + |2-6| + |3-8| = 3 + 4 + 5 = 12 @@ -5208,7 +6011,7 @@ mod tests { #[test] fn test_manhattan_distance_f64_zero() { - let v1 = FlexVector::from_vec(vec![0.0, 0.0, 0.0]); + let v1 = FVector::from_vec(vec![0.0, 0.0, 0.0]); let v2 = FlexVector::from_vec(vec![0.0, 0.0, 0.0]); let dist = v1.manhattan_distance(&v2).unwrap(); assert_eq!(dist, 0.0); @@ -5216,7 +6019,7 @@ mod tests { #[test] fn test_manhattan_distance_f64_negative_values() { - let v1 = FlexVector::from_vec(vec![-1.0, -2.0, -3.0]); + let v1 = FVector::from_vec(vec![-1.0, -2.0, -3.0]); let v2 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); let dist = v1.manhattan_distance(&v2).unwrap(); // |(-1)-1| + |(-2)-2| + |(-3)-3| = 2 + 4 + 6 = 12 @@ -5225,7 +6028,7 @@ mod tests { #[test] fn test_manhattan_distance_f64_mismatched_length() { - let v1 = FlexVector::from_vec(vec![1.0, 2.0]); + let v1 = FVector::from_vec(vec![1.0, 2.0]); let v2 = FlexVector::from_vec(vec![3.0, 4.0, 5.0]); let result = v1.manhattan_distance(&v2); assert!(result.is_err()); @@ -5234,7 +6037,7 @@ mod tests { // -- chebyshev_distance -- #[test] fn test_chebyshev_distance_f64_basic() { - let v1 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = FVector::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 6.0, 8.0]); let dist = v1.chebyshev_distance(&v2).unwrap(); // max(|1-4|, |2-6|, |3-8|) = max(3, 4, 5) = 5 @@ -5243,7 +6046,7 @@ mod tests { #[test] fn test_chebyshev_distance_f64_zero() { - let v1 = FlexVector::from_vec(vec![0.0, 0.0, 0.0]); + let v1 = FVector::from_vec(vec![0.0, 0.0, 0.0]); let v2 = FlexVector::from_vec(vec![0.0, 0.0, 0.0]); let dist = v1.chebyshev_distance(&v2).unwrap(); assert_eq!(dist, 0.0); @@ -5251,7 +6054,7 @@ mod tests { #[test] fn test_chebyshev_distance_f64_negative_values() { - let v1 = FlexVector::from_vec(vec![-1.0, -2.0, -3.0]); + let v1 = FVector::from_vec(vec![-1.0, -2.0, -3.0]); let v2 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); let dist = v1.chebyshev_distance(&v2).unwrap(); // max(|-1-1|, |-2-2|, |-3-3|) = max(2, 4, 6) = 6 @@ -5260,7 +6063,7 @@ mod tests { #[test] fn test_chebyshev_distance_f64_mismatched_length() { - let v1 = FlexVector::from_vec(vec![1.0, 2.0]); + let v1 = FVector::from_vec(vec![1.0, 2.0]); let v2 = FlexVector::from_vec(vec![3.0, 4.0, 5.0]); let result = v1.chebyshev_distance(&v2); assert!(result.is_err()); @@ -5269,7 +6072,7 @@ mod tests { // -- minkowski_distance -- #[test] fn test_minkowski_distance_f64_basic() { - let v1 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = FVector::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 6.0, 8.0]); let dist = v1.minkowski_distance(&v2, 3.0).unwrap(); // ((|1-4|^3 + |2-6|^3 + |3-8|^3))^(1/3) = (27 + 64 + 125)^(1/3) = (216)^(1/3) = 6 @@ -5278,7 +6081,7 @@ mod tests { #[test] fn test_minkowski_distance_f64_p1() { - let v1 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = FVector::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 6.0, 8.0]); let dist = v1.minkowski_distance(&v2, 1.0).unwrap(); // Should match manhattan distance: 3 + 4 + 5 = 12 @@ -5287,7 +6090,7 @@ mod tests { #[test] fn test_minkowski_distance_f64_p2() { - let v1 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = FVector::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 6.0, 8.0]); let dist = v1.minkowski_distance(&v2, 2.0).unwrap(); // Should match euclidean distance: sqrt(9 + 16 + 25) = sqrt(50) @@ -5304,7 +6107,7 @@ mod tests { #[test] fn test_minkowski_distance_f64_identical() { - let v1 = FlexVector::from_vec(vec![1.23, 4.56, 7.89]); + let v1 = FVector::from_vec(vec![1.23, 4.56, 7.89]); let v2 = FlexVector::from_vec(vec![1.23, 4.56, 7.89]); let dist = v1.minkowski_distance(&v2, 2.0).unwrap(); assert_eq!(dist, 0.0); @@ -5312,7 +6115,7 @@ mod tests { #[test] fn test_minkowski_distance_f64_partial() { - let v1 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = FVector::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 6.0]); let result = v1.minkowski_distance(&v2, 2.0); assert!(result.is_err()); @@ -5320,7 +6123,7 @@ mod tests { #[test] fn test_minkowski_distance_f64_invalid_p() { - let v1 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = FVector::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 6.0, 8.0]); let result = v1.minkowski_distance(&v2, 0.5); assert!(result.is_err()); @@ -5329,7 +6132,7 @@ mod tests { // -- norm -- #[test] fn test_norm_f64_basic() { - let v = FlexVector::from_vec(vec![3.0, 4.0]); + let v = FVector::from_vec(vec![3.0, 4.0]); let norm = v.norm(); // sqrt(3^2 + 4^2) = 5 assert!((norm - 5.0).abs() < 1e-12); @@ -5337,21 +6140,21 @@ mod tests { #[test] fn test_norm_f64_zero() { - let v = FlexVector::from_vec(vec![0.0, 0.0, 0.0]); + let v = FVector::from_vec(vec![0.0, 0.0, 0.0]); let norm = v.norm(); assert_eq!(norm, 0.0); } #[test] fn test_norm_f64_single_element() { - let v = FlexVector::from_vec(vec![7.0]); + let v = FVector::from_vec(vec![7.0]); let norm = v.norm(); assert_eq!(norm, 7.0); } #[test] fn test_norm_f64_negative_values() { - let v = FlexVector::from_vec(vec![-3.0, -4.0]); + let v = FVector::from_vec(vec![-3.0, -4.0]); let norm = v.norm(); // sqrt((-3)^2 + (-4)^2) = 5 assert!((norm - 5.0).abs() < 1e-12); @@ -5360,7 +6163,7 @@ mod tests { // -- magnitude -- #[test] fn test_magnitude_f64_basic() { - let v = FlexVector::from_vec(vec![3.0, 4.0]); + let v = FVector::from_vec(vec![3.0, 4.0]); let mag = v.magnitude(); // sqrt(3^2 + 4^2) = 5 assert!((mag - 5.0).abs() < 1e-12); @@ -5368,21 +6171,21 @@ mod tests { #[test] fn test_magnitude_f64_zero() { - let v = FlexVector::from_vec(vec![0.0, 0.0, 0.0]); + let v = FVector::from_vec(vec![0.0, 0.0, 0.0]); let mag = v.magnitude(); assert_eq!(mag, 0.0); } #[test] fn test_magnitude_f64_single_element() { - let v = FlexVector::from_vec(vec![7.0]); + let v = FVector::from_vec(vec![7.0]); let mag = v.magnitude(); assert_eq!(mag, 7.0); } #[test] fn test_magnitude_f64_negative_values() { - let v = FlexVector::from_vec(vec![-3.0, -4.0]); + let v = FVector::from_vec(vec![-3.0, -4.0]); let mag = v.magnitude(); // sqrt((-3)^2 + (-4)^2) = 5 assert!((mag - 5.0).abs() < 1e-12); @@ -5391,7 +6194,7 @@ mod tests { // -- lp_norm -- #[test] fn test_lp_norm_f64_p1() { - let v = FlexVector::from_vec(vec![1.0, -2.0, 3.0]); + let v = FVector::from_vec(vec![1.0, -2.0, 3.0]); let norm = v.lp_norm(1.0).unwrap(); // L1 norm: |1| + |−2| + |3| = 1 + 2 + 3 = 6 assert!((norm - 6.0).abs() < 1e-12); @@ -5399,7 +6202,7 @@ mod tests { #[test] fn test_lp_norm_f64_p2() { - let v = FlexVector::from_vec(vec![3.0, 4.0]); + let v = FVector::from_vec(vec![3.0, 4.0]); let norm = v.lp_norm(2.0).unwrap(); // L2 norm: sqrt(3^2 + 4^2) = 5 assert!((norm - 5.0).abs() < 1e-12); @@ -5407,7 +6210,7 @@ mod tests { #[test] fn test_lp_norm_f64_p3() { - let v = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let v = FVector::from_vec(vec![1.0, 2.0, 3.0]); let norm = v.lp_norm(3.0).unwrap(); // (|1|^3 + |2|^3 + |3|^3)^(1/3) = (1 + 8 + 27)^(1/3) = 36^(1/3) assert!((norm - 36f64.powf(1.0 / 3.0)).abs() < 1e-12); @@ -5415,14 +6218,14 @@ mod tests { #[test] fn test_lp_norm_f64_zero() { - let v = FlexVector::from_vec(vec![0.0, 0.0, 0.0]); + let v = FVector::from_vec(vec![0.0, 0.0, 0.0]); let norm = v.lp_norm(2.0).unwrap(); assert_eq!(norm, 0.0); } #[test] fn test_lp_norm_f64_invalid_p() { - let v = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let v = FVector::from_vec(vec![1.0, 2.0, 3.0]); let result = v.lp_norm(0.5); assert!(result.is_err()); } @@ -5430,7 +6233,7 @@ mod tests { // -- angle_with -- #[test] fn test_angle_with_f64_orthogonal() { - let v1 = FlexVector::from_vec(vec![1.0, 0.0]); + let v1 = FVector::from_vec(vec![1.0, 0.0]); let v2 = FlexVector::from_vec(vec![0.0, 1.0]); let angle = v1.angle_with(&v2).unwrap(); // Orthogonal vectors: angle should be pi/2 @@ -5439,7 +6242,7 @@ mod tests { #[test] fn test_angle_with_f64_parallel() { - let v1 = FlexVector::from_vec(vec![1.0, 2.0]); + let v1 = FVector::from_vec(vec![1.0, 2.0]); let v2 = FlexVector::from_vec(vec![2.0, 4.0]); let angle = v1.angle_with(&v2).unwrap(); // Parallel vectors: angle should be 0 (allow for floating-point error) @@ -5448,7 +6251,7 @@ mod tests { #[test] fn test_angle_with_f64_opposite() { - let v1 = FlexVector::from_vec(vec![1.0, 0.0]); + let v1 = FVector::from_vec(vec![1.0, 0.0]); let v2 = FlexVector::from_vec(vec![-1.0, 0.0]); let angle = v1.angle_with(&v2).unwrap(); // Opposite vectors: angle should be pi @@ -5457,7 +6260,7 @@ mod tests { #[test] fn test_angle_with_f64_identical() { - let v1 = FlexVector::from_vec(vec![3.0, 4.0]); + let v1 = FVector::from_vec(vec![3.0, 4.0]); let v2 = FlexVector::from_vec(vec![3.0, 4.0]); let angle = v1.angle_with(&v2).unwrap(); // Identical vectors: angle should be 0 @@ -5466,7 +6269,7 @@ mod tests { #[test] fn test_angle_with_f64_arbitrary() { - let v1 = FlexVector::from_vec(vec![1.0, 2.0]); + let v1 = FVector::from_vec(vec![1.0, 2.0]); let v2 = FlexVector::from_vec(vec![2.0, 1.0]); let angle = v1.angle_with(&v2).unwrap(); // Check that the angle is between 0 and pi @@ -5475,7 +6278,7 @@ mod tests { #[test] fn test_angle_with_f64_zero_vector() { - let v1 = FlexVector::from_vec(vec![0.0, 0.0]); + let v1 = FVector::from_vec(vec![0.0, 0.0]); let v2 = FlexVector::from_vec(vec![1.0, 2.0]); let result = v1.angle_with(&v2); assert!(result.is_err()); @@ -5483,7 +6286,7 @@ mod tests { #[test] fn test_angle_with_f64_mismatched_length() { - let v1 = FlexVector::from_vec(vec![1.0, 2.0]); + let v1 = FVector::from_vec(vec![1.0, 2.0]); let v2 = FlexVector::from_vec(vec![3.0, 4.0, 5.0]); let result = v1.angle_with(&v2); assert!(result.is_err()); @@ -5492,7 +6295,7 @@ mod tests { // -- project_onto -- #[test] fn test_project_onto_f64_basic() { - let v1 = FlexVector::from_vec(vec![3.0, 4.0]); + let v1 = FVector::from_vec(vec![3.0, 4.0]); let v2 = FlexVector::from_vec(vec![1.0, 0.0]); let proj = v1.project_onto(&v2).unwrap(); // Projection of [3,4] onto [1,0] is [3,0] @@ -5502,7 +6305,7 @@ mod tests { #[test] fn test_project_onto_f64_parallel() { - let v1 = FlexVector::from_vec(vec![2.0, 4.0]); + let v1 = FVector::from_vec(vec![2.0, 4.0]); let v2 = FlexVector::from_vec(vec![1.0, 2.0]); let proj = v1.project_onto(&v2).unwrap(); // v1 is parallel to v2, so projection should be v1 itself @@ -5512,7 +6315,7 @@ mod tests { #[test] fn test_project_onto_f64_orthogonal() { - let v1 = FlexVector::from_vec(vec![0.0, 1.0]); + let v1 = FVector::from_vec(vec![0.0, 1.0]); let v2 = FlexVector::from_vec(vec![1.0, 0.0]); let proj = v1.project_onto(&v2).unwrap(); // Orthogonal vectors: projection should be [0,0] @@ -5522,7 +6325,7 @@ mod tests { #[test] fn test_project_onto_f64_identical() { - let v1 = FlexVector::from_vec(vec![5.0, 5.0]); + let v1 = FVector::from_vec(vec![5.0, 5.0]); let v2 = FlexVector::from_vec(vec![5.0, 5.0]); let proj = v1.project_onto(&v2).unwrap(); // Should be v1 itself @@ -5532,7 +6335,7 @@ mod tests { #[test] fn test_project_onto_f64_zero_vector() { - let v1 = FlexVector::from_vec(vec![1.0, 2.0]); + let v1 = FVector::from_vec(vec![1.0, 2.0]); let v2 = FlexVector::from_vec(vec![0.0, 0.0]); let result = v1.project_onto(&v2); assert!(result.is_err()); @@ -5540,7 +6343,7 @@ mod tests { #[test] fn test_project_onto_f64_mismatched_length() { - let v1 = FlexVector::from_vec(vec![1.0, 2.0]); + let v1 = FVector::from_vec(vec![1.0, 2.0]); let v2 = FlexVector::from_vec(vec![3.0, 4.0, 5.0]); let result = v1.project_onto(&v2); assert!(result.is_err()); @@ -5549,7 +6352,7 @@ mod tests { // --- cosine_similarity --- #[test] fn test_cosine_similarity_f64_parallel() { - let v1 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = FVector::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![2.0, 4.0, 6.0]); let cos_sim = v1.cosine_similarity(&v2).unwrap(); assert!((cos_sim - 1.0).abs() < 1e-10); @@ -5557,7 +6360,7 @@ mod tests { #[test] fn test_cosine_similarity_f64_orthogonal() { - let v1 = FlexVector::from_vec(vec![1.0, 0.0]); + let v1 = FVector::from_vec(vec![1.0, 0.0]); let v2 = FlexVector::from_vec(vec![0.0, 1.0]); let cos_sim = v1.cosine_similarity(&v2).unwrap(); assert!((cos_sim - 0.0).abs() < 1e-10); @@ -5565,7 +6368,7 @@ mod tests { #[test] fn test_cosine_similarity_f64_opposite() { - let v1 = FlexVector::from_vec(vec![1.0, 0.0]); + let v1 = FVector::from_vec(vec![1.0, 0.0]); let v2 = FlexVector::from_vec(vec![-1.0, 0.0]); let cos_sim = v1.cosine_similarity(&v2).unwrap(); assert!((cos_sim + 1.0).abs() < 1e-10); @@ -5573,7 +6376,7 @@ mod tests { #[test] fn test_cosine_similarity_f64_zero_vector() { - let v1 = FlexVector::from_vec(vec![0.0, 0.0]); + let v1 = FVector::from_vec(vec![0.0, 0.0]); let v2 = FlexVector::from_vec(vec![1.0, 2.0]); let cos_sim = v1.cosine_similarity(&v2); assert!(cos_sim.is_err()); @@ -5588,7 +6391,7 @@ mod tests { // -- normalize -- #[test] fn test_normalize_complex_f64_basic() { - let v = FlexVector::from_vec(vec![Complex::new(3.0, 4.0)]); + let v = FVector::from_vec(vec![Complex::new(3.0, 4.0)]); let normalized = v.normalize().unwrap(); // The norm is 5.0, so the normalized vector should be [0.6 + 0.8i] assert!((normalized.as_slice()[0].re - 0.6).abs() < 1e-12); @@ -5597,7 +6400,7 @@ mod tests { #[test] fn test_normalize_complex_f64_multiple_elements() { - let v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let norm = ((1.0 * 1.0 + 2.0 * 2.0) + (3.0 * 3.0 + 4.0 * 4.0)).sqrt(); let normalized = v.normalize().unwrap(); assert!((normalized.as_slice()[0].re - 1.0 / norm).abs() < 1e-12); @@ -5608,7 +6411,7 @@ mod tests { #[test] fn test_normalize_complex_f64_zero_vector() { - let v = FlexVector::from_vec(vec![Complex::new(0.0, 0.0)]); + let v = FVector::from_vec(vec![Complex::new(0.0, 0.0)]); let result = v.normalize(); assert!(result.is_err()); } @@ -5630,28 +6433,28 @@ mod tests { #[test] fn test_neg() { - let v = FlexVector::from_vec(vec![1, -2, 3]); + let v = FVector::from_vec(vec![1, -2, 3]); let neg_v = -v; assert_eq!(neg_v.as_slice(), &[-1, 2, -3]); } #[test] fn test_neg_f64() { - let v = FlexVector::from_vec(vec![1.5, -2.5, 0.0]); + let v = FVector::from_vec(vec![1.5, -2.5, 0.0]); let neg_v = -v; assert_eq!(neg_v.as_slice(), &[-1.5, 2.5, -0.0]); } #[test] fn test_neg_complex() { - let v = FlexVector::from_vec(vec![Complex::new(1.0, -2.0), Complex::new(-3.0, 4.0)]); + let v = FVector::from_vec(vec![Complex::new(1.0, -2.0), Complex::new(-3.0, 4.0)]); let neg_v = -v; assert_eq!(neg_v.as_slice(), &[Complex::new(-1.0, 2.0), Complex::new(3.0, -4.0)]); } #[test] fn test_neg_nan() { - let v = FlexVector::from_vec(vec![f64::NAN, -f64::NAN]); + let v = FVector::from_vec(vec![f64::NAN, -f64::NAN]); let neg_v = -v; // Negating NaN is still NaN, but sign bit may flip assert!(neg_v.as_slice()[0].is_nan()); @@ -5663,14 +6466,14 @@ mod tests { #[test] fn test_neg_infinity() { - let v = FlexVector::from_vec(vec![f64::INFINITY, f64::NEG_INFINITY]); + let v = FVector::from_vec(vec![f64::INFINITY, f64::NEG_INFINITY]); let neg_v = -v; assert_eq!(neg_v.as_slice(), &[-f64::INFINITY, f64::INFINITY]); } #[test] fn test_add() { - let v1 = FlexVector::from_vec(vec![1, 2, 3]); + let v1 = FVector::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); let sum = v1 + v2; assert_eq!(sum.as_slice(), &[5, 7, 9]); @@ -5678,7 +6481,7 @@ mod tests { #[test] fn test_add_f64() { - let v1 = FlexVector::from_vec(vec![1.0, 2.5]); + let v1 = FVector::from_vec(vec![1.0, 2.5]); let v2 = FlexVector::from_vec(vec![3.0, 4.5]); let sum = v1 + v2; assert_eq!(sum.as_slice(), &[4.0, 7.0]); @@ -5686,7 +6489,7 @@ mod tests { #[test] fn test_add_complex() { - let v1 = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v1 = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); let sum = v1 + v2; assert_eq!(sum.as_slice(), &[Complex::new(6.0, 8.0), Complex::new(10.0, 12.0)]); @@ -5694,7 +6497,7 @@ mod tests { #[test] fn test_add_nan_infinity() { - let v1 = FlexVector::from_vec(vec![f64::NAN, f64::INFINITY, 1.0]); + let v1 = FVector::from_vec(vec![f64::NAN, f64::INFINITY, 1.0]); let v2 = FlexVector::from_vec(vec![1.0, 2.0, f64::INFINITY]); let sum = v1 + v2; assert!(sum.as_slice()[0].is_nan()); @@ -5705,14 +6508,14 @@ mod tests { #[test] #[should_panic(expected = "Vector length mismatch")] fn test_add_panic_on_mismatched_length() { - let v1 = FlexVector::from_vec(vec![1, 2]); + let v1 = FVector::from_vec(vec![1, 2]); let v2 = FlexVector::from_vec(vec![1, 2, 3]); let _ = v1 + v2; } #[test] fn test_sub() { - let v1 = FlexVector::from_vec(vec![10, 20, 30]); + let v1 = FVector::from_vec(vec![10, 20, 30]); let v2 = FlexVector::from_vec(vec![1, 2, 3]); let diff = v1 - v2; assert_eq!(diff.as_slice(), &[9, 18, 27]); @@ -5720,7 +6523,7 @@ mod tests { #[test] fn test_sub_f64() { - let v1 = FlexVector::from_vec(vec![5.5, 2.0]); + let v1 = FVector::from_vec(vec![5.5, 2.0]); let v2 = FlexVector::from_vec(vec![1.5, 1.0]); let diff = v1 - v2; assert_eq!(diff.as_slice(), &[4.0, 1.0]); @@ -5728,7 +6531,7 @@ mod tests { #[test] fn test_sub_complex() { - let v1 = FlexVector::from_vec(vec![Complex::new(5.0, 7.0), Complex::new(3.0, 4.0)]); + let v1 = FVector::from_vec(vec![Complex::new(5.0, 7.0), Complex::new(3.0, 4.0)]); let v2 = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(2.0, 1.0)]); let diff = v1 - v2; assert_eq!(diff.as_slice(), &[Complex::new(4.0, 5.0), Complex::new(1.0, 3.0)]); @@ -5736,7 +6539,7 @@ mod tests { #[test] fn test_sub_nan_infinity() { - let v1 = FlexVector::from_vec(vec![f64::NAN, f64::INFINITY, 5.0]); + let v1 = FVector::from_vec(vec![f64::NAN, f64::INFINITY, 5.0]); let v2 = FlexVector::from_vec(vec![2.0, f64::INFINITY, f64::NAN]); let diff = v1 - v2; assert!(diff.as_slice()[0].is_nan()); @@ -5747,14 +6550,14 @@ mod tests { #[test] #[should_panic(expected = "Vector length mismatch")] fn test_sub_panic_on_mismatched_length() { - let v1 = FlexVector::from_vec(vec![1, 2]); + let v1 = FVector::from_vec(vec![1, 2]); let v2 = FlexVector::from_vec(vec![1, 2, 3]); let _ = v1 - v2; } #[test] fn test_mul() { - let v1 = FlexVector::from_vec(vec![2, 3, 4]); + let v1 = FVector::from_vec(vec![2, 3, 4]); let v2 = FlexVector::from_vec(vec![5, 6, 7]); let prod = v1 * v2; assert_eq!(prod.as_slice(), &[10, 18, 28]); @@ -5762,7 +6565,7 @@ mod tests { #[test] fn test_mul_f64() { - let v1 = FlexVector::from_vec(vec![1.5, 2.0]); + let v1 = FVector::from_vec(vec![1.5, 2.0]); let v2 = FlexVector::from_vec(vec![2.0, 3.0]); let prod = v1 * v2; assert_eq!(prod.as_slice(), &[3.0, 6.0]); @@ -5770,7 +6573,7 @@ mod tests { #[test] fn test_mul_complex() { - let v1 = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v1 = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); let prod = v1 * v2; assert_eq!( @@ -5784,7 +6587,7 @@ mod tests { #[test] fn test_mul_nan_infinity() { - let v1 = FlexVector::from_vec(vec![f64::NAN, f64::INFINITY, 2.0]); + let v1 = FVector::from_vec(vec![f64::NAN, f64::INFINITY, 2.0]); let v2 = FlexVector::from_vec(vec![3.0, 2.0, f64::INFINITY]); let prod = v1 * v2; assert!(prod.as_slice()[0].is_nan()); @@ -5795,14 +6598,14 @@ mod tests { #[test] #[should_panic(expected = "Vector length mismatch")] fn test_mul_panic_on_mismatched_length() { - let v1 = FlexVector::from_vec(vec![1, 2]); + let v1 = FVector::from_vec(vec![1, 2]); let v2 = FlexVector::from_vec(vec![1, 2, 3]); let _ = v1 * v2; } #[test] fn test_elem_div_f32() { - let v1 = FlexVector::from_vec(vec![2.0f32, 4.0, 8.0]); + let v1 = FVector::from_vec(vec![2.0f32, 4.0, 8.0]); let v2 = FlexVector::from_vec(vec![1.0f32, 2.0, 4.0]); let result = v1 / v2; assert_eq!(result.as_slice(), &[2.0, 2.0, 2.0]); @@ -5810,7 +6613,7 @@ mod tests { #[test] fn test_elem_div_f64() { - let v1 = FlexVector::from_vec(vec![2.0f64, 4.0, 8.0]); + let v1 = FVector::from_vec(vec![2.0f64, 4.0, 8.0]); let v2 = FlexVector::from_vec(vec![1.0f64, 2.0, 4.0]); let result = v1 / v2; assert_eq!(result.as_slice(), &[2.0, 2.0, 2.0]); @@ -5818,7 +6621,7 @@ mod tests { #[test] fn test_elem_div_complex_f32() { - let v1 = FlexVector::from_vec(vec![Complex::new(2.0f32, 2.0), Complex::new(4.0, 0.0)]); + let v1 = FVector::from_vec(vec![Complex::new(2.0f32, 2.0), Complex::new(4.0, 0.0)]); let v2 = FlexVector::from_vec(vec![Complex::new(1.0f32, 1.0), Complex::new(2.0, 0.0)]); let result = v1 / v2; assert!((result[0] - Complex::new(2.0, 0.0)).norm() < 1e-6); @@ -5827,7 +6630,7 @@ mod tests { #[test] fn test_elem_div_complex_f64() { - let v1 = FlexVector::from_vec(vec![Complex::new(2.0f64, 2.0), Complex::new(4.0, 0.0)]); + let v1 = FVector::from_vec(vec![Complex::new(2.0f64, 2.0), Complex::new(4.0, 0.0)]); let v2 = FlexVector::from_vec(vec![Complex::new(1.0f64, 1.0), Complex::new(2.0, 0.0)]); let result = v1 / v2; assert!((result[0] - Complex::new(2.0, 0.0)).norm() < 1e-12); @@ -5837,14 +6640,14 @@ mod tests { #[test] #[should_panic(expected = "Vector length mismatch")] fn test_elem_div_panic_on_mismatched_length() { - let v1 = FlexVector::from_vec(vec![1.0f64, 2.0]); + let v1 = FVector::from_vec(vec![1.0f64, 2.0]); let v2 = FlexVector::from_vec(vec![1.0f64, 2.0, 3.0]); let _ = v1 / v2; } #[test] fn test_add_assign() { - let mut v1 = FlexVector::from_vec(vec![1, 2, 3]); + let mut v1 = FVector::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); v1 += v2; assert_eq!(v1.as_slice(), &[5, 7, 9]); @@ -5852,7 +6655,7 @@ mod tests { #[test] fn test_add_assign_f64() { - let mut v1 = FlexVector::from_vec(vec![1.0, 2.5]); + let mut v1 = FVector::from_vec(vec![1.0, 2.5]); let v2 = FlexVector::from_vec(vec![3.0, 4.5]); v1 += v2; assert_eq!(v1.as_slice(), &[4.0, 7.0]); @@ -5860,7 +6663,7 @@ mod tests { #[test] fn test_add_assign_complex() { - let mut v1 = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let mut v1 = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); v1 += v2; assert_eq!(v1.as_slice(), &[Complex::new(6.0, 8.0), Complex::new(10.0, 12.0)]); @@ -5869,14 +6672,14 @@ mod tests { #[test] #[should_panic(expected = "Vector length mismatch")] fn test_add_assign_panic_on_mismatched_length() { - let mut v1 = FlexVector::from_vec(vec![1, 2]); + let mut v1 = FVector::from_vec(vec![1, 2]); let v2 = FlexVector::from_vec(vec![1, 2, 3]); v1 += v2; } #[test] fn test_sub_assign() { - let mut v1 = FlexVector::from_vec(vec![10, 20, 30]); + let mut v1 = FVector::from_vec(vec![10, 20, 30]); let v2 = FlexVector::from_vec(vec![1, 2, 3]); v1 -= v2; assert_eq!(v1.as_slice(), &[9, 18, 27]); @@ -5884,7 +6687,7 @@ mod tests { #[test] fn test_sub_assign_f64() { - let mut v1 = FlexVector::from_vec(vec![5.5, 2.0]); + let mut v1 = FVector::from_vec(vec![5.5, 2.0]); let v2 = FlexVector::from_vec(vec![1.5, 1.0]); v1 -= v2; assert_eq!(v1.as_slice(), &[4.0, 1.0]); @@ -5892,7 +6695,7 @@ mod tests { #[test] fn test_sub_assign_complex() { - let mut v1 = FlexVector::from_vec(vec![Complex::new(5.0, 7.0), Complex::new(3.0, 4.0)]); + let mut v1 = FVector::from_vec(vec![Complex::new(5.0, 7.0), Complex::new(3.0, 4.0)]); let v2 = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(2.0, 1.0)]); v1 -= v2; assert_eq!(v1.as_slice(), &[Complex::new(4.0, 5.0), Complex::new(1.0, 3.0)]); @@ -5901,14 +6704,14 @@ mod tests { #[test] #[should_panic(expected = "Vector length mismatch")] fn test_sub_assign_panic_on_mismatched_length() { - let mut v1 = FlexVector::from_vec(vec![1, 2]); + let mut v1 = FVector::from_vec(vec![1, 2]); let v2 = FlexVector::from_vec(vec![1, 2, 3]); v1 -= v2; } #[test] fn test_mul_assign() { - let mut v1 = FlexVector::from_vec(vec![2, 3, 4]); + let mut v1 = FVector::from_vec(vec![2, 3, 4]); let v2 = FlexVector::from_vec(vec![5, 6, 7]); v1 *= v2; assert_eq!(v1.as_slice(), &[10, 18, 28]); @@ -5916,7 +6719,7 @@ mod tests { #[test] fn test_mul_assign_f64() { - let mut v1 = FlexVector::from_vec(vec![1.5, 2.0]); + let mut v1 = FVector::from_vec(vec![1.5, 2.0]); let v2 = FlexVector::from_vec(vec![2.0, 3.0]); v1 *= v2; assert_eq!(v1.as_slice(), &[3.0, 6.0]); @@ -5924,7 +6727,7 @@ mod tests { #[test] fn test_mul_assign_complex() { - let mut v1 = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let mut v1 = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); v1 *= v2; assert_eq!(v1.as_slice(), &[Complex::new(-7.0, 16.0), Complex::new(-11.0, 52.0)]); @@ -5933,14 +6736,14 @@ mod tests { #[test] #[should_panic(expected = "Vector length mismatch")] fn test_mul_assign_panic_on_mismatched_length() { - let mut v1 = FlexVector::from_vec(vec![1, 2]); + let mut v1 = FVector::from_vec(vec![1, 2]); let v2 = FlexVector::from_vec(vec![1, 2, 3]); v1 *= v2; } #[test] fn test_elem_div_assign_f32() { - let mut v1 = FlexVector::from_vec(vec![2.0f32, 4.0, 8.0]); + let mut v1 = FVector::from_vec(vec![2.0f32, 4.0, 8.0]); let v2 = FlexVector::from_vec(vec![1.0f32, 2.0, 4.0]); v1 /= v2; assert_eq!(v1.as_slice(), &[2.0, 2.0, 2.0]); @@ -5948,7 +6751,7 @@ mod tests { #[test] fn test_elem_div_assign_f64() { - let mut v1 = FlexVector::from_vec(vec![2.0f64, 4.0, 8.0]); + let mut v1 = FVector::from_vec(vec![2.0f64, 4.0, 8.0]); let v2 = FlexVector::from_vec(vec![1.0f64, 2.0, 4.0]); v1 /= v2; assert_eq!(v1.as_slice(), &[2.0, 2.0, 2.0]); @@ -5956,7 +6759,7 @@ mod tests { #[test] fn test_elem_div_assign_complex_f32() { - let mut v1 = FlexVector::from_vec(vec![Complex::new(2.0f32, 2.0), Complex::new(4.0, 0.0)]); + let mut v1 = FVector::from_vec(vec![Complex::new(2.0f32, 2.0), Complex::new(4.0, 0.0)]); let v2 = FlexVector::from_vec(vec![Complex::new(1.0f32, 1.0), Complex::new(2.0, 0.0)]); v1 /= v2; assert!((v1[0] - Complex::new(2.0, 0.0)).norm() < 1e-6); @@ -5965,7 +6768,7 @@ mod tests { #[test] fn test_elem_div_assign_complex_f64() { - let mut v1 = FlexVector::from_vec(vec![Complex::new(2.0f64, 2.0), Complex::new(4.0, 0.0)]); + let mut v1 = FVector::from_vec(vec![Complex::new(2.0f64, 2.0), Complex::new(4.0, 0.0)]); let v2 = FlexVector::from_vec(vec![Complex::new(1.0f64, 1.0), Complex::new(2.0, 0.0)]); v1 /= v2; assert!((v1[0] - Complex::new(2.0, 0.0)).norm() < 1e-12); @@ -5975,21 +6778,21 @@ mod tests { #[test] #[should_panic(expected = "Vector length mismatch")] fn test_elem_div_assign_panic_on_mismatched_length() { - let mut v1 = FlexVector::from_vec(vec![1.0f64, 2.0]); + let mut v1 = FVector::from_vec(vec![1.0f64, 2.0]); let v2 = FlexVector::from_vec(vec![1.0f64, 2.0, 3.0]); v1 /= v2; } #[test] fn test_scalar_mul() { - let v = FlexVector::from_vec(vec![2, -3, 4]); + let v = FVector::from_vec(vec![2, -3, 4]); let prod = v.clone() * 3; assert_eq!(prod.as_slice(), &[6, -9, 12]); } #[test] fn test_scalar_mul_f64() { - let v = FlexVector::from_vec(vec![1.5, -2.0, 0.0]); + let v = FVector::from_vec(vec![1.5, -2.0, 0.0]); let prod = v.clone() * 2.0; assert_eq!(prod.as_slice(), &[3.0, -4.0, 0.0]); } @@ -5997,7 +6800,7 @@ mod tests { #[test] fn test_scalar_mul_complex() { use num::Complex; - let v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + let v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); let scalar = Complex::new(2.0, 0.0); let prod = v.clone() * scalar; assert_eq!(prod.as_slice(), &[Complex::new(2.0, 4.0), Complex::new(-6.0, 8.0)]); @@ -6005,14 +6808,14 @@ mod tests { #[test] fn test_scalar_mul_assign() { - let mut v = FlexVector::from_vec(vec![2, -3, 4]); + let mut v = FVector::from_vec(vec![2, -3, 4]); v *= 3; assert_eq!(v.as_slice(), &[6, -9, 12]); } #[test] fn test_scalar_mul_assign_f64() { - let mut v = FlexVector::from_vec(vec![1.5, -2.0, 0.0]); + let mut v = FVector::from_vec(vec![1.5, -2.0, 0.0]); v *= 2.0; assert_eq!(v.as_slice(), &[3.0, -4.0, 0.0]); } @@ -6020,7 +6823,7 @@ mod tests { #[test] fn test_scalar_mul_assign_complex() { use num::Complex; - let mut v = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + let mut v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); let scalar = Complex::new(2.0, 0.0); v *= scalar; assert_eq!(v.as_slice(), &[Complex::new(2.0, 4.0), Complex::new(-6.0, 8.0)]); @@ -6072,7 +6875,7 @@ mod tests { fn test_scalar_div_assign_complex_by_complex() { use num::Complex; let mut v: FlexVector> = - FlexVector::from_vec(vec![Complex::new(2.0, 4.0), Complex::new(-6.0, 8.0)]); + FVector::from_vec(vec![Complex::new(2.0, 4.0), Complex::new(-6.0, 8.0)]); let divisor = Complex::new(2.0, 0.0); v /= divisor; assert_eq!(v.as_slice(), &[Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); @@ -6080,7 +6883,7 @@ mod tests { #[test] fn test_scalar_div_nan_infinity() { - let v = FlexVector::from_vec(vec![f64::NAN, f64::INFINITY, 4.0]); + let v = FVector::from_vec(vec![f64::NAN, f64::INFINITY, 4.0]); let result: FlexVector = v.clone() / 2.0; assert!(result.as_slice()[0].is_nan()); assert_eq!(result.as_slice()[1], f64::INFINITY); @@ -6089,7 +6892,7 @@ mod tests { #[test] fn test_scalar_div_assign_nan_infinity() { - let mut v: FlexVector = FlexVector::from_vec(vec![f64::NAN, f64::INFINITY, 4.0]); + let mut v: FlexVector = FVector::from_vec(vec![f64::NAN, f64::INFINITY, 4.0]); v /= 2.0; assert!(v.as_slice()[0].is_nan()); assert_eq!(v.as_slice()[1], f64::INFINITY); @@ -6100,7 +6903,7 @@ mod tests { #[test] fn test_add_overflow_i32() { - let v1 = FlexVector::from_vec(vec![i32::MAX]); + let v1 = FVector::from_vec(vec![i32::MAX]); let v2 = FlexVector::from_vec(vec![1]); if cfg!(debug_assertions) { // In debug mode, should panic on overflow @@ -6117,7 +6920,7 @@ mod tests { #[test] fn test_mul_overflow_i32() { - let v1 = FlexVector::from_vec(vec![i32::MAX]); + let v1 = FVector::from_vec(vec![i32::MAX]); let v2 = FlexVector::from_vec(vec![2]); if cfg!(debug_assertions) { // In debug mode, should panic on overflow @@ -6134,7 +6937,7 @@ mod tests { #[test] fn test_divide_by_zero_f64() { - let v: FlexVector = FlexVector::from_vec(vec![1.0, -2.0, 0.0]); + let v: FlexVector = FVector::from_vec(vec![1.0, -2.0, 0.0]); let result = v.clone() / 0.0; assert_eq!(result.as_slice()[0], f64::INFINITY); assert_eq!(result.as_slice()[1], f64::NEG_INFINITY); @@ -6143,7 +6946,7 @@ mod tests { #[test] fn test_neg_zero_f64() { - let v = FlexVector::from_vec(vec![0.0, -0.0]); + let v = FVector::from_vec(vec![0.0, -0.0]); let neg_v = -v; assert_eq!(neg_v.as_slice()[0], -0.0); assert_eq!(neg_v.as_slice()[1], 0.0); @@ -6152,7 +6955,7 @@ mod tests { #[test] fn test_complex_div_by_zero() { use num::Complex; - let v: FlexVector> = FlexVector::from_vec(vec![Complex::new(1.0, 1.0)]); + let v: FlexVector> = FVector::from_vec(vec![Complex::new(1.0, 1.0)]); let result = v.clone() / Complex::new(0.0, 0.0); assert!(result.as_slice()[0].re.is_nan()); assert!(result.as_slice()[0].im.is_nan()); diff --git a/src/types/mod.rs b/src/types/mod.rs index 2a0e52f..a7897df 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -1,6 +1,7 @@ //! Library types. pub mod flexvector; +pub mod orientation; pub mod traits; mod utils; pub mod vector; diff --git a/src/types/orientation.rs b/src/types/orientation.rs new file mode 100644 index 0000000..c1a7da1 --- /dev/null +++ b/src/types/orientation.rs @@ -0,0 +1,32 @@ +//! Orientation marker types for vectors. +//! +//! These zero-sized types are used as type parameters to distinguish between row and column vectors +//! at compile time for both `Vector` and `FlexVector`. + +use crate::types::traits::VectorOrientationName; + +/// Marker type for row vectors. +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct Row; + +/// Marker type for column vectors. +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct Column; + +impl VectorOrientationName for Row { + fn orientation_name() -> &'static str { + "Row" + } +} + +impl VectorOrientationName for Column { + fn orientation_name() -> &'static str { + "Column" + } +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] +pub enum VectorOrientation { + Row, + Column, +} diff --git a/src/types/traits.rs b/src/types/traits.rs index 48c86d6..33f10f9 100644 --- a/src/types/traits.rs +++ b/src/types/traits.rs @@ -1,9 +1,12 @@ //! Traits. +use crate::types::orientation::VectorOrientation; use num::Complex; +use std::borrow::Cow; use crate::errors::VectorError; +/// ... pub trait VectorBase { // --- Core accessors --- /// ... @@ -72,6 +75,15 @@ pub trait VectorBase { self.as_slice().to_vec() } + /// Returns a Cow<[T]> of the vector's data. + #[inline] + fn as_cow(&self) -> Cow<'_, [T]> + where + T: Clone, + { + Cow::Borrowed(self.as_slice()) + } + /// ... #[inline] fn pretty(&self) -> String @@ -190,6 +202,16 @@ pub trait VectorBase { } } +/// A trait for types that can be transposed between row and column orientation. +pub trait Transposable { + /// The type returned by transposing. + type Transposed; + + /// Returns a new value with the opposite orientation. + fn transpose(self) -> Self::Transposed; +} + +/// ... pub trait VectorOps: VectorBase { type Output; @@ -353,6 +375,7 @@ pub trait VectorOps: VectorBase { } } +/// ... pub trait VectorOpsFloat: VectorBase { type Output; @@ -465,6 +488,7 @@ pub trait VectorOpsFloat: VectorBase { T: num::Float + Clone + std::iter::Sum + std::ops::Div; } +/// ... pub trait VectorOpsComplex: VectorBase> { type Output; @@ -597,3 +621,21 @@ pub trait VectorOpsComplex: VectorBase> { N: num::Float + Clone + std::iter::Sum + std::ops::Neg, Complex: std::ops::Div>; } + +/// ... +pub trait VectorHasOrientation { + /// ... + fn orientation(&self) -> VectorOrientation; +} + +// ================================ +// +// pub(crate) trait impls +// +// ================================ + +/// Helper trait for orientation name. +pub(crate) trait VectorOrientationName { + /// Returns orientation name. + fn orientation_name() -> &'static str; +} From 3bd1f39c096026be399b1d39a1211dbb13135ff1 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Thu, 22 May 2025 00:30:22 -0400 Subject: [PATCH 43/82] stub source documentation to quiet clippy lints --- src/macros.rs | 9 +++++++++ src/types/flexvector.rs | 4 ++++ src/types/orientation.rs | 3 +++ src/types/traits.rs | 3 +++ 4 files changed, 19 insertions(+) diff --git a/src/macros.rs b/src/macros.rs index 2ae7751..7064f82 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -215,6 +215,7 @@ macro_rules! try_fv_iter { }; } +/// ... #[macro_export] macro_rules! impl_vector_unary_op { ($VectorType:ident, $trait:ident, $method:ident, $op:tt) => { @@ -232,6 +233,7 @@ macro_rules! impl_vector_unary_op { }; } +/// ... #[macro_export] macro_rules! impl_vector_binop { // With length check (for FlexVector) @@ -271,6 +273,7 @@ macro_rules! impl_vector_binop { }; } +/// ... #[macro_export] macro_rules! impl_vector_binop_div { // With length check (for FlexVector) @@ -365,6 +368,7 @@ macro_rules! impl_vector_binop_div { }; } +/// ... #[macro_export] macro_rules! impl_vector_binop_assign { // With length check (for FlexVector) @@ -398,6 +402,7 @@ macro_rules! impl_vector_binop_assign { }; } +/// ... #[macro_export] macro_rules! impl_vector_binop_div_assign { // With length check (for FlexVector) @@ -484,6 +489,7 @@ macro_rules! impl_vector_binop_div_assign { }; } +/// ... #[macro_export] macro_rules! impl_vector_scalar_op { ($VectorType:ident, $trait:ident, $method:ident, $op:tt) => { @@ -501,6 +507,7 @@ macro_rules! impl_vector_scalar_op { }; } +/// ... #[macro_export] macro_rules! impl_vector_scalar_op_assign { ($VectorType:ident, $trait:ident, $method:ident, $op:tt) => { @@ -518,6 +525,7 @@ macro_rules! impl_vector_scalar_op_assign { }; } +/// ... #[macro_export] macro_rules! impl_vector_scalar_div_op { ($VectorType:ident) => { @@ -578,6 +586,7 @@ macro_rules! impl_vector_scalar_div_op { }; } +/// ... #[macro_export] macro_rules! impl_vector_scalar_div_op_assign { ($VectorType:ident) => { diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index 6009d7b..502642b 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -38,12 +38,16 @@ use num::{Complex, Zero}; /// This type is analogous to `Vector` but supports dynamic sizing. #[derive(Clone)] pub struct FlexVector { + /// ... pub components: Vec, _orientation: PhantomData, } +/// ... pub type FVector = FlexVector; +/// ... pub type ColFVector = FlexVector; +/// ... pub type RowFVector = FlexVector; // ================================ diff --git a/src/types/orientation.rs b/src/types/orientation.rs index c1a7da1..fac0b51 100644 --- a/src/types/orientation.rs +++ b/src/types/orientation.rs @@ -25,8 +25,11 @@ impl VectorOrientationName for Column { } } +/// ... #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] pub enum VectorOrientation { + /// ... Row, + /// ... Column, } diff --git a/src/types/traits.rs b/src/types/traits.rs index 33f10f9..4e894f9 100644 --- a/src/types/traits.rs +++ b/src/types/traits.rs @@ -213,6 +213,7 @@ pub trait Transposable { /// ... pub trait VectorOps: VectorBase { + /// ... type Output; /// ... @@ -377,6 +378,7 @@ pub trait VectorOps: VectorBase { /// ... pub trait VectorOpsFloat: VectorBase { + /// ... type Output; /// ... @@ -490,6 +492,7 @@ pub trait VectorOpsFloat: VectorBase { /// ... pub trait VectorOpsComplex: VectorBase> { + /// ... type Output; /// ... From 88b1c923ed7122850d5665a1217326a9db352e08 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Thu, 22 May 2025 00:32:48 -0400 Subject: [PATCH 44/82] fix clippy lint errors --- src/errors.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/errors.rs b/src/errors.rs index 8473393..62f5a4e 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -29,10 +29,10 @@ impl std::fmt::Display for VectorError { write!(f, "VectorError::EmptyVectorError: {s}") } VectorError::MismatchedLengthError(s) => { - write!(f, "VectorError::MismatchedLengthError: {}", s) + write!(f, "VectorError::MismatchedLengthError: {s}") } VectorError::OutOfRangeError(s) => { - write!(f, "VectorError::OutOfRangeError: {}", s) + write!(f, "VectorError::OutOfRangeError: {s}") } VectorError::TryFromVecError(s) => { write!(f, "VectorError::TryFromVecError: {s}") @@ -44,7 +44,7 @@ impl std::fmt::Display for VectorError { write!(f, "VectorError::ValueError: {s}") } VectorError::ZeroVectorError(s) => { - write!(f, "VectorError::ZeroVectorError: {}", s) + write!(f, "VectorError::ZeroVectorError: {s}") } } } From 43db914a6a40a1ab97f171094e8a8b127e5c3dee Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Thu, 22 May 2025 00:58:38 -0400 Subject: [PATCH 45/82] Add FlexVector From> and From> impl --- src/types/flexvector.rs | 67 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index 502642b..115716f 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -436,6 +436,22 @@ where } } +impl From> for FlexVector { + fn from(b: Box<[T]>) -> Self { + FlexVector::from_vec(b.into_vec()) + } +} + +#[cfg(target_has_atomic = "ptr")] +impl From> for FlexVector +where + T: Clone, +{ + fn from(a: std::sync::Arc<[T]>) -> Self { + FlexVector::from_slice(&a) + } +} + impl From> for FlexVector { #[inline] fn from(v: FlexVector) -> Self { @@ -2997,6 +3013,57 @@ mod tests { assert_eq!(fv.as_slice(), &owned_vec[..]); } + #[test] + fn test_from_box_slice_i32() { + let boxed: Box<[i32]> = vec![1, 2, 3].into_boxed_slice(); + let fv: FlexVector = FlexVector::from(boxed); + assert_eq!(fv.as_slice(), &[1, 2, 3]); + } + + #[test] + fn test_from_box_slice_f64() { + let boxed: Box<[f64]> = vec![1.1, 2.2, 3.3].into_boxed_slice(); + let fv: FlexVector = FlexVector::from(boxed); + assert_eq!(fv.as_slice(), &[1.1, 2.2, 3.3]); + } + + #[test] + fn test_from_box_slice_complex_f64() { + use num::Complex; + let boxed: Box<[Complex]> = + vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)].into_boxed_slice(); + let fv: FlexVector> = FlexVector::from(boxed); + assert_eq!(fv.as_slice(), &[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + } + + #[cfg(target_has_atomic = "ptr")] + #[test] + fn test_from_arc_slice_i32() { + use std::sync::Arc; + let arc: Arc<[i32]> = Arc::from(vec![4, 5, 6].into_boxed_slice()); + let fv: FlexVector = FlexVector::from(arc); + assert_eq!(fv.as_slice(), &[4, 5, 6]); + } + + #[cfg(target_has_atomic = "ptr")] + #[test] + fn test_from_arc_slice_f64() { + use std::sync::Arc; + let arc: Arc<[f64]> = Arc::from(vec![7.7, 8.8].into_boxed_slice()); + let fv: FlexVector = FlexVector::from(arc); + assert_eq!(fv.as_slice(), &[7.7, 8.8]); + } + + #[cfg(target_has_atomic = "ptr")] + #[test] + fn test_from_arc_slice_complex_f64() { + use num::Complex; + use std::sync::Arc; + let arc: Arc<[Complex]> = Arc::from(vec![Complex::new(9.0, 10.0)].into_boxed_slice()); + let fv: FlexVector> = FlexVector::from(arc); + assert_eq!(fv.as_slice(), &[Complex::new(9.0, 10.0)]); + } + #[test] fn test_from_column_to_row() { let col = FlexVector::::from_vec(vec![1, 2, 3]); From f21b9da4a7b1e382ef7319fd7617c064dae2323c Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Thu, 22 May 2025 14:44:35 -0400 Subject: [PATCH 46/82] Add more FlexVector collection interop trait / method support --- src/types/flexvector.rs | 325 +++++++++++++++++++++++++++++++++++++++- src/types/traits.rs | 28 ++++ 2 files changed, 351 insertions(+), 2 deletions(-) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index 115716f..78c7475 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -393,7 +393,10 @@ where // From trait impl // // ================================ -impl From> for FlexVector { +impl From> for FlexVector +where + T: Clone, +{ #[inline] fn from(vec: Vec) -> Self { FlexVector { components: vec, _orientation: PhantomData } @@ -409,6 +412,26 @@ where } } +impl From<&mut [T]> for FlexVector +where + T: Clone, +{ + #[inline] + fn from(slice: &mut [T]) -> Self { + FlexVector::from_slice(slice) + } +} + +impl From<[T; N]> for FlexVector +where + T: Clone, +{ + #[inline] + fn from(arr: [T; N]) -> Self { + FlexVector::from_vec(Vec::from(arr)) + } +} + impl From> for FlexVector where T: Clone, @@ -436,7 +459,21 @@ where } } -impl From> for FlexVector { +impl From> for FlexVector +where + T: Clone, +{ + #[inline] + fn from(deque: std::collections::VecDeque) -> Self { + FlexVector::from_vec(deque.into()) + } +} + +impl From> for FlexVector +where + T: Clone, +{ + #[inline] fn from(b: Box<[T]>) -> Self { FlexVector::from_vec(b.into_vec()) } @@ -447,11 +484,23 @@ impl From> for FlexVector where T: Clone, { + #[inline] fn from(a: std::sync::Arc<[T]>) -> Self { FlexVector::from_slice(&a) } } +impl From> for FlexVector +where + T: Clone, +{ + #[inline] + fn from(rc: std::rc::Rc<[T]>) -> Self { + FlexVector::from_slice(&rc) + } +} + +// From trait impl for Column <==> Row FlexVector conversions impl From> for FlexVector { #[inline] fn from(v: FlexVector) -> Self { @@ -1406,6 +1455,31 @@ impl FlexVector { Ok(FlexVector { components, _orientation: PhantomData }) } + /// Consumes the FlexVector and returns a Vec. + #[inline] + pub fn into_vec(self) -> Vec { + self.components + } + + /// Consumes the FlexVector and returns a Box. + #[inline] + pub fn into_boxed_slice(self) -> Box<[T]> { + self.components.into_boxed_slice() + } + + /// Consumes the FlexVector and returns an Arc. + #[cfg(target_has_atomic = "ptr")] + #[inline] + pub fn into_arc_slice(self) -> std::sync::Arc<[T]> { + std::sync::Arc::from(self.components) + } + + /// Consumes the FlexVector and returns a Rc. + #[inline] + pub fn into_rc_slice(self) -> std::rc::Rc<[T]> { + std::rc::Rc::from(self.components) + } + // ================================ // // Private methods @@ -2955,6 +3029,80 @@ mod tests { assert!(fv_row.is_row()); } + #[test] + fn test_from_mut_slice_i32() { + let mut data = [10, 20, 30]; + let fv: FlexVector = FlexVector::from(&mut data[..]); + assert_eq!(fv.as_slice(), &[10, 20, 30]); + // Changing the original slice does not affect the FlexVector + data[0] = 99; + assert_eq!(fv.as_slice(), &[10, 20, 30]); + } + + #[test] + fn test_from_mut_slice_f64() { + let mut data = [1.1, 2.2, 3.3]; + let fv: FlexVector = FlexVector::from(&mut data[..]); + assert_eq!(fv.as_slice(), &[1.1, 2.2, 3.3]); + data[2] = 9.9; + assert_eq!(fv.as_slice(), &[1.1, 2.2, 3.3]); + } + + #[test] + fn test_from_mut_slice_complex_f64() { + use num::Complex; + let mut data = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; + let fv: FlexVector> = FlexVector::from(&mut data[..]); + assert_eq!(fv.as_slice(), &[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + data[0] = Complex::new(9.0, 9.0); + assert_eq!(fv.as_slice(), &[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + } + + #[test] + fn test_from_array_i32() { + let arr = [1, 2, 3]; + let fv: FlexVector = FlexVector::from(arr); + assert_eq!(fv.as_slice(), &[1, 2, 3]); + let fv_col: FlexVector = arr.into(); + assert_eq!(fv_col.as_slice(), &[1, 2, 3]); + let fv_row: FlexVector = arr.into(); + assert_eq!(fv_row.as_slice(), &[1, 2, 3]); + } + + #[test] + fn test_from_array_f64() { + let arr = [1.1, 2.2, 3.3]; + let fv: FlexVector = FlexVector::from(arr); + assert_eq!(fv.as_slice(), &[1.1, 2.2, 3.3]); + let fv_col: FlexVector = arr.into(); + assert_eq!(fv_col.as_slice(), &[1.1, 2.2, 3.3]); + let fv_row: FlexVector = arr.into(); + assert_eq!(fv_row.as_slice(), &[1.1, 2.2, 3.3]); + } + + #[test] + fn test_from_array_complex_f64() { + use num::Complex; + let arr = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; + let fv: FlexVector> = FlexVector::from(arr); + assert_eq!(fv.as_slice(), &[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let fv_col: FlexVector, Column> = arr.into(); + assert_eq!(fv_col.as_slice(), &[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let fv_row: FlexVector, Row> = arr.into(); + assert_eq!(fv_row.as_slice(), &[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + } + + #[test] + fn test_from_empty_array() { + let arr: [i32; 0] = []; + let fv: FlexVector = FlexVector::from(arr); + assert!(fv.is_empty()); + let fv_col: FlexVector = arr.into(); + assert!(fv_col.is_empty()); + let fv_row: FlexVector = arr.into(); + assert!(fv_row.is_empty()); + } + #[test] fn test_from_cow_i32() { // Borrowed @@ -3013,6 +3161,62 @@ mod tests { assert_eq!(fv.as_slice(), &owned_vec[..]); } + #[test] + fn test_from_vecdeque_i32() { + use std::collections::VecDeque; + let mut deque = VecDeque::new(); + deque.push_back(1); + deque.push_back(2); + deque.push_back(3); + let fv: FlexVector = FlexVector::from(deque.clone()); + assert_eq!(fv.as_slice(), &[1, 2, 3]); + let fv_col: FlexVector = deque.clone().into(); + assert_eq!(fv_col.as_slice(), &[1, 2, 3]); + let fv_row: FlexVector = deque.into(); + assert_eq!(fv_row.as_slice(), &[1, 2, 3]); + } + + #[test] + fn test_from_vecdeque_f64() { + use std::collections::VecDeque; + let mut deque = VecDeque::new(); + deque.push_back(1.1); + deque.push_back(2.2); + deque.push_back(3.3); + let fv: FlexVector = FlexVector::from(deque.clone()); + assert_eq!(fv.as_slice(), &[1.1, 2.2, 3.3]); + let fv_col: FlexVector = deque.clone().into(); + assert_eq!(fv_col.as_slice(), &[1.1, 2.2, 3.3]); + let fv_row: FlexVector = deque.into(); + assert_eq!(fv_row.as_slice(), &[1.1, 2.2, 3.3]); + } + + #[test] + fn test_from_vecdeque_complex_f64() { + use std::collections::VecDeque; + let mut deque = VecDeque::new(); + deque.push_back(Complex::new(1.0, 2.0)); + deque.push_back(Complex::new(3.0, 4.0)); + let fv: FlexVector> = FlexVector::from(deque.clone()); + assert_eq!(fv.as_slice(), &[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let fv_col: FlexVector, Column> = deque.clone().into(); + assert_eq!(fv_col.as_slice(), &[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let fv_row: FlexVector, Row> = deque.into(); + assert_eq!(fv_row.as_slice(), &[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + } + + #[test] + fn test_from_vecdeque_empty() { + use std::collections::VecDeque; + let deque: VecDeque = VecDeque::new(); + let fv: FlexVector = FlexVector::from(deque.clone()); + assert!(fv.is_empty()); + let fv_col: FlexVector = deque.clone().into(); + assert!(fv_col.is_empty()); + let fv_row: FlexVector = deque.into(); + assert!(fv_row.is_empty()); + } + #[test] fn test_from_box_slice_i32() { let boxed: Box<[i32]> = vec![1, 2, 3].into_boxed_slice(); @@ -3064,6 +3268,56 @@ mod tests { assert_eq!(fv.as_slice(), &[Complex::new(9.0, 10.0)]); } + #[test] + fn test_from_rc_slice_i32() { + use std::rc::Rc; + let rc: Rc<[i32]> = Rc::from(vec![1, 2, 3].into_boxed_slice()); + let fv: FlexVector = FlexVector::from(rc.clone()); + assert_eq!(fv.as_slice(), &[1, 2, 3]); + let fv_col: FlexVector = rc.clone().into(); + assert_eq!(fv_col.as_slice(), &[1, 2, 3]); + let fv_row: FlexVector = rc.into(); + assert_eq!(fv_row.as_slice(), &[1, 2, 3]); + } + + #[test] + fn test_from_rc_slice_f64() { + use std::rc::Rc; + let rc: Rc<[f64]> = Rc::from(vec![1.1, 2.2, 3.3].into_boxed_slice()); + let fv: FlexVector = FlexVector::from(rc.clone()); + assert_eq!(fv.as_slice(), &[1.1, 2.2, 3.3]); + let fv_col: FlexVector = rc.clone().into(); + assert_eq!(fv_col.as_slice(), &[1.1, 2.2, 3.3]); + let fv_row: FlexVector = rc.into(); + assert_eq!(fv_row.as_slice(), &[1.1, 2.2, 3.3]); + } + + #[test] + fn test_from_rc_slice_complex_f64() { + use num::Complex; + use std::rc::Rc; + let rc: Rc<[Complex]> = + Rc::from(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)].into_boxed_slice()); + let fv: FlexVector> = FlexVector::from(rc.clone()); + assert_eq!(fv.as_slice(), &[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let fv_col: FlexVector, Column> = rc.clone().into(); + assert_eq!(fv_col.as_slice(), &[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let fv_row: FlexVector, Row> = rc.into(); + assert_eq!(fv_row.as_slice(), &[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + } + + #[test] + fn test_from_rc_slice_empty() { + use std::rc::Rc; + let rc: Rc<[i32]> = Rc::from(Vec::::new().into_boxed_slice()); + let fv: FlexVector = FlexVector::from(rc.clone()); + assert!(fv.is_empty()); + let fv_col: FlexVector = rc.clone().into(); + assert!(fv_col.is_empty()); + let fv_row: FlexVector = rc.into(); + assert!(fv_row.is_empty()); + } + #[test] fn test_from_column_to_row() { let col = FlexVector::::from_vec(vec![1, 2, 3]); @@ -3581,6 +3835,34 @@ mod tests { assert_eq!(vec_copy, vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0),]); } + // -- to_boxed_slice -- + + #[test] + fn test_to_boxed_slice() { + let fv: FlexVector = FlexVector::from_vec(vec![1, 2, 3]); + let boxed = fv.to_boxed_slice(); + assert_eq!(&*boxed, &[1, 2, 3]); + } + + // -- to_arc_slice -- + + #[cfg(target_has_atomic = "ptr")] + #[test] + fn test_to_arc_slice() { + let fv: FlexVector = FlexVector::from_vec(vec![4.4, 5.5, 6.6]); + let arc = fv.to_arc_slice(); + assert_eq!(&*arc, &[4.4, 5.5, 6.6]); + } + + // -- to_rc_slice -- + + #[test] + fn test_to_rc_slice() { + let fv: FlexVector = FlexVector::from_vec(vec![7, 8, 9]); + let rc = fv.to_rc_slice(); + assert_eq!(&*rc, &[7, 8, 9]); + } + // --- as_cow --- #[test] @@ -5152,6 +5434,45 @@ mod tests { assert!(result.is_err()); } + // -- into_vec -- + + #[test] + fn test_into_vec() { + let fv: FlexVector = FlexVector::from_vec(vec![1, 2, 3]); + let vec = fv.into_vec(); + assert_eq!(vec, vec![1, 2, 3]); + } + + // -- into_boxed_slice -- + + #[test] + fn test_into_boxed_slice() { + let fv: FlexVector = FlexVector::from_vec(vec![1.1, 2.2, 3.3]); + let boxed = fv.into_boxed_slice(); + assert_eq!(&*boxed, &[1.1, 2.2, 3.3]); + } + + // -- into_arc_slice -- + + #[cfg(target_has_atomic = "ptr")] + #[test] + fn test_into_arc_slice() { + use std::sync::Arc; + let fv: FlexVector = FlexVector::from_vec(vec![4, 5, 6]); + let arc: Arc<[i32]> = fv.into_arc_slice(); + assert_eq!(&*arc, &[4, 5, 6]); + } + + // -- into_rc_slice -- + + #[test] + fn test_into_rc_slice() { + use std::rc::Rc; + let fv: FlexVector = FlexVector::from_vec(vec![7, 8, 9]); + let rc: Rc<[i32]> = fv.into_rc_slice(); + assert_eq!(&*rc, &[7, 8, 9]); + } + // ================================ // // VectorOps trait tests diff --git a/src/types/traits.rs b/src/types/traits.rs index 4e894f9..a000f41 100644 --- a/src/types/traits.rs +++ b/src/types/traits.rs @@ -75,6 +75,34 @@ pub trait VectorBase { self.as_slice().to_vec() } + /// Returns a boxed slice containing a clone of the vector's data. + #[inline] + fn to_boxed_slice(&self) -> Box<[T]> + where + T: Clone, + { + self.as_slice().to_vec().into_boxed_slice() + } + + /// Returns an Arc slice containing a clone of the vector's data. + #[cfg(target_has_atomic = "ptr")] + #[inline] + fn to_arc_slice(&self) -> std::sync::Arc<[T]> + where + T: Clone, + { + std::sync::Arc::from(self.as_slice().to_vec()) + } + + /// Returns an Rc slice containing a clone of the vector's data. + #[inline] + fn to_rc_slice(&self) -> std::rc::Rc<[T]> + where + T: Clone, + { + std::rc::Rc::from(self.as_slice().to_vec()) + } + /// Returns a Cow<[T]> of the vector's data. #[inline] fn as_cow(&self) -> Cow<'_, [T]> From 1a5a0635684fa9c002e154dcb94df2873eabe819 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Mon, 26 May 2025 15:37:32 -0400 Subject: [PATCH 47/82] add LTO=true to release builds --- Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 944de97..9604567 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,3 +34,6 @@ harness = false [package.metadata.docs.rs] # Whether to pass `--all-features` to Cargo (default: false) all-features = true + +[profile.release] +lto = true From 794dfb055894d54550ee5ac7225da80934b2287a Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Mon, 26 May 2025 15:37:57 -0400 Subject: [PATCH 48/82] add VectorSlice and VectorSliceMut types --- src/lib.rs | 1 + src/types/flexvector.rs | 16 +- src/types/mod.rs | 1 + src/types/vectorslice.rs | 573 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 590 insertions(+), 1 deletion(-) create mode 100644 src/types/vectorslice.rs diff --git a/src/lib.rs b/src/lib.rs index 219b2a7..2466db5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -914,6 +914,7 @@ pub mod prelude { pub use crate::types::flexvector::FlexVector; pub use crate::types::orientation::{Column, Row, VectorOrientation}; pub use crate::types::vector::Vector; + pub use crate::types::vectorslice::{VectorSlice, VectorSliceMut}; // Macros pub use crate::{fv, fv_from, fv_iter, try_fv_iter, try_vector, vector}; } diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index 78c7475..9309231 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -16,7 +16,8 @@ use crate::{ types::orientation::Column, types::orientation::Row, types::orientation::VectorOrientation, types::traits::Transposable, types::traits::VectorBase, types::traits::VectorHasOrientation, types::traits::VectorOps, types::traits::VectorOpsComplex, types::traits::VectorOpsFloat, - types::traits::VectorOrientationName, + types::traits::VectorOrientationName, types::vectorslice::VectorSlice, + types::vectorslice::VectorSliceMut, }; use crate::types::utils::{ @@ -402,6 +403,7 @@ where FlexVector { components: vec, _orientation: PhantomData } } } + impl From<&[T]> for FlexVector where T: Clone, @@ -1480,6 +1482,18 @@ impl FlexVector { std::rc::Rc::from(self.components) } + /// Create a VectorSlice from a FlexVector. + #[inline] + pub fn as_vslice(&self, range: std::ops::Range) -> VectorSlice<'_, T, O> { + VectorSlice::new(&self.components[range]) + } + + /// Create a mutable VectorSliceMut from a FlexVector. + #[inline] + pub fn as_mut_vslice(&mut self, range: std::ops::Range) -> VectorSliceMut<'_, T, O> { + VectorSliceMut::new(&mut self.components[range]) + } + // ================================ // // Private methods diff --git a/src/types/mod.rs b/src/types/mod.rs index a7897df..d716dbe 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -5,6 +5,7 @@ pub mod orientation; pub mod traits; mod utils; pub mod vector; +pub mod vectorslice; pub use flexvector::FlexVector; pub use vector::Vector; diff --git a/src/types/vectorslice.rs b/src/types/vectorslice.rs new file mode 100644 index 0000000..8b96f6a --- /dev/null +++ b/src/types/vectorslice.rs @@ -0,0 +1,573 @@ +//! VectorSlice types. + +use crate::types::orientation::Column; +use std::marker::PhantomData; + +// ///////////////////////////////// +// ================================ +// +// VectorSlice type +// +// ================================ +// ///////////////////////////////// + +/// ... +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct VectorSlice<'a, T, O = Column> { + /// ... + pub slice: &'a [T], + _orientation: PhantomData, +} + +// ================================ +// +// Constructors +// +// ================================ + +impl<'a, T, O> VectorSlice<'a, T, O> { + /// ... + #[inline] + pub fn new(slice: &'a [T]) -> Self { + VectorSlice { slice, _orientation: PhantomData } + } + + /// ... + #[inline] + pub fn from_range(parent: &'a [T], range: std::ops::Range) -> Self { + Self::new(&parent[range]) + } +} + +// ================================ +// +// Traits +// +// ================================ + +impl<'a, T, O> std::ops::Deref for VectorSlice<'a, T, O> { + type Target = [T]; + fn deref(&self) -> &Self::Target { + self.slice + } +} + +impl<'a, T, O> AsRef<[T]> for VectorSlice<'a, T, O> { + fn as_ref(&self) -> &[T] { + self.slice + } +} + +impl<'a, T, O> IntoIterator for VectorSlice<'a, T, O> { + type Item = &'a T; + type IntoIter = std::slice::Iter<'a, T>; + + fn into_iter(self) -> Self::IntoIter { + self.slice.iter() + } +} + +impl<'a, T, O> IntoIterator for &'a VectorSlice<'a, T, O> { + type Item = &'a T; + type IntoIter = std::slice::Iter<'a, T>; + + fn into_iter(self) -> Self::IntoIter { + self.slice.iter() + } +} + +// ///////////////////////////////// +// ================================ +// +// VectorSliceMut type +// +// ================================ +// ///////////////////////////////// + +/// ... +#[derive(Debug)] +pub struct VectorSliceMut<'a, T, O = Column> { + /// ... + pub slice: &'a mut [T], + _orientation: PhantomData, +} + +// ================================ +// +// Constructors +// +// ================================ + +impl<'a, T, O> VectorSliceMut<'a, T, O> { + /// ... + #[inline] + pub fn new(slice: &'a mut [T]) -> Self { + VectorSliceMut { slice, _orientation: PhantomData } + } + + /// Creates a mutable VectorSliceMut from a parent mutable slice and a range. + #[inline] + pub fn from_range(parent: &'a mut [T], range: std::ops::Range) -> Self { + Self::new(&mut parent[range]) + } +} + +// ================================ +// +// Traits +// +// ================================ + +impl<'a, T, O> std::ops::Deref for VectorSliceMut<'a, T, O> { + type Target = [T]; + fn deref(&self) -> &Self::Target { + self.slice + } +} + +impl<'a, T, O> std::ops::DerefMut for VectorSliceMut<'a, T, O> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.slice + } +} + +impl<'a, T, O> AsMut<[T]> for VectorSliceMut<'a, T, O> { + fn as_mut(&mut self) -> &mut [T] { + self.slice + } +} + +impl<'a, T, O> IntoIterator for VectorSliceMut<'a, T, O> { + type Item = &'a mut T; + type IntoIter = std::slice::IterMut<'a, T>; + + fn into_iter(self) -> Self::IntoIter { + self.slice.into_iter() + } +} + +impl<'a, T, O> IntoIterator for &'a mut VectorSliceMut<'a, T, O> { + type Item = &'a mut T; + type IntoIter = std::slice::IterMut<'a, T>; + + fn into_iter(self) -> Self::IntoIter { + self.slice.iter_mut() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::prelude::{FlexVector, VectorBase}; + use crate::types::orientation::{Column, Row}; + use num::Complex; + + // ///////////////////////////////// + // ================================ + // + // VectorSlice type + // + // ================================ + // ///////////////////////////////// + + // -- new -- + #[test] + fn test_vector_slice_new() { + let data = [1, 2, 3, 4, 5]; + let vslice: VectorSlice<'_, i32, Column> = VectorSlice::new(&data); + assert_eq!(vslice.slice, &[1, 2, 3, 4, 5]); + } + + #[test] + fn test_vector_slice_new_complex() { + let data = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0), Complex::new(5.0, 6.0)]; + let vslice: VectorSlice<'_, Complex, Column> = VectorSlice::new(&data); + assert_eq!( + vslice.slice, + &[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0), Complex::new(5.0, 6.0),] + ); + } + + // -- from_range -- + #[test] + fn test_vector_slice_from_range_middle() { + let data = [10, 20, 30, 40, 50]; + let vslice: VectorSlice<'_, i32, Row> = VectorSlice::from_range(&data, 1..4); + assert_eq!(vslice.slice, &[20, 30, 40]); + } + + #[test] + fn test_vector_slice_from_range_full() { + let data = [7, 8, 9]; + let vslice: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&data, 0..3); + assert_eq!(vslice.slice, &[7, 8, 9]); + } + + #[test] + fn test_vector_slice_from_range_empty() { + let data = [1, 2, 3]; + let vslice: VectorSlice<'_, i32, Row> = VectorSlice::from_range(&data, 1..1); + assert_eq!(vslice.slice, &[]); + } + + #[test] + fn test_vector_slice_from_range_middle_complex() { + let data = [ + Complex::new(10.0, 1.0), + Complex::new(20.0, 2.0), + Complex::new(30.0, 3.0), + Complex::new(40.0, 4.0), + Complex::new(50.0, 5.0), + ]; + let vslice: VectorSlice<'_, Complex, Row> = VectorSlice::from_range(&data, 1..4); + assert_eq!( + vslice.slice, + &[Complex::new(20.0, 2.0), Complex::new(30.0, 3.0), Complex::new(40.0, 4.0),] + ); + } + + #[test] + fn test_vector_slice_from_range_full_complex() { + let data = [Complex::new(7.0, 0.0), Complex::new(8.0, 1.0), Complex::new(9.0, 2.0)]; + let vslice: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&data, 0..3); + assert_eq!( + vslice.slice, + &[Complex::new(7.0, 0.0), Complex::new(8.0, 1.0), Complex::new(9.0, 2.0),] + ); + } + + #[test] + fn test_vector_slice_from_range_empty_complex() { + let data = [Complex::new(1.0, 1.0), Complex::new(2.0, 2.0), Complex::new(3.0, 3.0)]; + let vslice: VectorSlice<'_, Complex, Row> = VectorSlice::from_range(&data, 1..1); + assert_eq!(vslice.slice, &[]); + } + + // -- Deref trait -- + #[test] + fn test_vector_slice_deref_access() { + let data = [1, 2, 3, 4, 5]; + let vslice: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&data, 1..4); + // Deref allows direct indexing + assert_eq!(vslice[0], 2); + assert_eq!(vslice[1], 3); + assert_eq!(vslice[2], 4); + // Deref allows using slice methods + assert_eq!(vslice.len(), 3); + assert!(vslice.contains(&3)); + assert_eq!(vslice.iter().sum::(), 9); + } + + #[test] + fn test_vector_slice_deref_complex() { + let data = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0), Complex::new(5.0, 6.0)]; + let vslice: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&data, 0..2); + // Deref allows direct indexing + assert_eq!(vslice[0], Complex::new(1.0, 2.0)); + assert_eq!(vslice[1], Complex::new(3.0, 4.0)); + // Deref allows using slice methods + assert_eq!(vslice.len(), 2); + assert!(vslice.contains(&Complex::new(3.0, 4.0))); + } + + // -- AsRef trait -- + + #[test] + fn test_vector_slice_as_ref_basic() { + let data = [1, 2, 3, 4, 5]; + let vslice: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&data, 2..5); + let slice_ref: &[i32] = vslice.as_ref(); + assert_eq!(slice_ref, &[3, 4, 5]); + } + + #[test] + fn test_vector_slice_as_ref_complex() { + let data = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0), Complex::new(5.0, 6.0)]; + let vslice: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&data, 1..3); + let slice_ref: &[Complex] = vslice.as_ref(); + assert_eq!(slice_ref, &[Complex::new(3.0, 4.0), Complex::new(5.0, 6.0)]); + } + + #[test] + fn test_vector_slice_as_ref_with_std_function() { + let data = [10, 20, 30, 40]; + let vslice: VectorSlice<'_, i32, Row> = VectorSlice::from_range(&data, 1..4); + // Use a standard library function that takes AsRef<[i32]> + let sum: i32 = vslice.as_ref().iter().sum(); + assert_eq!(sum, 20 + 30 + 40); + } + + // -- IntoIterator trait for VectorSlice -- + #[test] + fn test_vector_slice_into_iter_basic() { + let data = [1, 2, 3, 4]; + let vslice: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&data, 1..4); + let collected: Vec = vslice.into_iter().copied().collect(); + assert_eq!(collected, vec![2, 3, 4]); + } + + #[test] + fn test_vector_slice_into_iter_ref() { + let data = [10, 20, 30]; + let vslice: VectorSlice<'_, i32, Row> = VectorSlice::from_range(&data, 0..2); + let mut sum = 0; + for val in &vslice { + sum += *val; + } + assert_eq!(sum, 10 + 20); + } + + #[test] + fn test_vector_slice_into_iter_complex() { + let data = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0), Complex::new(5.0, 6.0)]; + let vslice: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&data, 0..3); + let collected: Vec> = vslice.into_iter().cloned().collect(); + assert_eq!( + collected, + vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0), Complex::new(5.0, 6.0)] + ); + } + + // ///////////////////////////////// + // ================================ + // + // VectorSliceMut type + // + // ================================ + // ///////////////////////////////// + + // -- new -- + #[test] + fn test_vector_slice_mut_new() { + let mut data = [1, 2, 3, 4, 5]; + let vslice: VectorSliceMut<'_, i32, Column> = VectorSliceMut::new(&mut data); + assert_eq!(vslice.slice, &mut [1, 2, 3, 4, 5]); + // Mutate through the slice + vslice.slice[0] = 10; + assert_eq!(vslice.slice[0], 10); + assert_eq!(data[0], 10); + } + + #[test] + fn test_vector_slice_mut_new_complex() { + let mut data = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0), Complex::new(5.0, 6.0)]; + let vslice: VectorSliceMut<'_, Complex, Column> = VectorSliceMut::new(&mut data); + assert_eq!( + vslice.slice, + &mut [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0), Complex::new(5.0, 6.0),] + ); + // Mutate through the slice + vslice.slice[0] = Complex::new(7.0, 8.0); + assert_eq!(vslice.slice[0], Complex::new(7.0, 8.0)); + } + + // -- from_range -- + #[test] + fn test_vector_slice_mut_from_range_middle() { + let mut data = [10, 20, 30, 40, 50]; + { + let vslice: VectorSliceMut<'_, i32, Row> = VectorSliceMut::from_range(&mut data, 1..4); + assert_eq!(vslice.slice, &mut [20, 30, 40]); + vslice.slice[1] = 99; + } + assert_eq!(data, [10, 20, 99, 40, 50]); + } + + #[test] + fn test_vector_slice_mut_from_range_full() { + let mut data = [7, 8, 9]; + { + let vslice: VectorSliceMut<'_, i32, Column> = + VectorSliceMut::from_range(&mut data, 0..3); + assert_eq!(vslice.slice, &mut [7, 8, 9]); + vslice.slice[2] = 42; + } + assert_eq!(data, [7, 8, 42]); + } + + #[test] + fn test_vector_slice_mut_from_range_empty() { + let mut data = [1, 2, 3]; + let vslice: VectorSliceMut<'_, i32, Row> = VectorSliceMut::from_range(&mut data, 1..1); + assert_eq!(vslice.slice, &mut []); + } + + #[test] + fn test_vector_slice_mut_from_range_middle_complex() { + let mut data = [ + Complex::new(10.0, 1.0), + Complex::new(20.0, 2.0), + Complex::new(30.0, 3.0), + Complex::new(40.0, 4.0), + Complex::new(50.0, 5.0), + ]; + { + let vslice: VectorSliceMut<'_, Complex, Row> = + VectorSliceMut::from_range(&mut data, 1..4); + assert_eq!( + vslice.slice, + &mut [Complex::new(20.0, 2.0), Complex::new(30.0, 3.0), Complex::new(40.0, 4.0),] + ); + vslice.slice[2] = Complex::new(99.0, 99.0); + } + assert_eq!( + data, + [ + Complex::new(10.0, 1.0), + Complex::new(20.0, 2.0), + Complex::new(30.0, 3.0), + Complex::new(99.0, 99.0), + Complex::new(50.0, 5.0), + ] + ); + } + + #[test] + fn test_vector_slice_mut_from_range_full_complex() { + let mut data = [Complex::new(7.0, 0.0), Complex::new(8.0, 1.0), Complex::new(9.0, 2.0)]; + { + let vslice: VectorSliceMut<'_, Complex, Column> = + VectorSliceMut::from_range(&mut data, 0..3); + assert_eq!( + vslice.slice, + &mut [Complex::new(7.0, 0.0), Complex::new(8.0, 1.0), Complex::new(9.0, 2.0),] + ); + vslice.slice[1] = Complex::new(42.0, 24.0); + } + assert_eq!( + data, + [Complex::new(7.0, 0.0), Complex::new(42.0, 24.0), Complex::new(9.0, 2.0),] + ); + } + + #[test] + fn test_vector_slice_mut_from_range_empty_complex() { + let mut data = [Complex::new(1.0, 1.0), Complex::new(2.0, 2.0), Complex::new(3.0, 3.0)]; + let vslice: VectorSliceMut<'_, Complex, Row> = + VectorSliceMut::from_range(&mut data, 1..1); + assert_eq!(vslice.slice, &mut []); + } + + // -- Deref trait for VectorSliceMut -- + #[test] + fn test_vector_slice_mut_deref_access() { + let mut data = [1, 2, 3, 4, 5]; + let vslice: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut data, 1..4); + // Deref allows direct indexing + assert_eq!(vslice[0], 2); + assert_eq!(vslice[1], 3); + assert_eq!(vslice[2], 4); + // Deref allows using slice methods + assert_eq!(vslice.len(), 3); + assert!(vslice.contains(&3)); + assert_eq!(vslice.iter().sum::(), 9); + } + + #[test] + fn test_vector_slice_mut_deref_complex() { + let mut data = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0), Complex::new(5.0, 6.0)]; + let mut vslice: VectorSliceMut<'_, Complex, Column> = + VectorSliceMut::from_range(&mut data, 0..2); + // Deref allows direct indexing + assert_eq!(vslice[0], Complex::new(1.0, 2.0)); + assert_eq!(vslice[1], Complex::new(3.0, 4.0)); + // DerefMut allows mutation + vslice[1] = Complex::new(7.0, 8.0); + assert_eq!(vslice[1], Complex::new(7.0, 8.0)); + } + + // -- DerefMut trait for VectorSliceMut -- + #[test] + fn test_vector_slice_mut_deref_mut_access() { + let mut fv: FlexVector = FlexVector::from([10, 20, 30, 40, 50]); + { + let mut vslice: VectorSliceMut<'_, i32, Column> = + VectorSliceMut::from_range(&mut fv, 1..4); + // DerefMut allows mutation through indexing + vslice[0] = 100; + vslice[2] = 400; + // DerefMut allows using mutable slice methods + vslice.reverse(); + } + // After mutation, check the underlying data + assert_eq!(fv.as_slice(), &[10, 400, 30, 100, 50]); + } + + // -- AsMut trait for VectorSliceMut -- + #[test] + fn test_vector_slice_mut_as_mut_basic() { + let mut data = [1, 2, 3, 4, 5]; + let mut vslice: VectorSliceMut<'_, i32, Column> = + VectorSliceMut::from_range(&mut data, 2..5); + let slice_mut: &mut [i32] = vslice.as_mut(); + slice_mut[0] = 10; + slice_mut[2] = 50; + assert_eq!(slice_mut, &[10, 4, 50]); + // Changes are reflected in the original data + assert_eq!(data, [1, 2, 10, 4, 50]); + } + + #[test] + fn test_vector_slice_mut_as_mut_complex() { + let mut data = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0), Complex::new(5.0, 6.0)]; + let mut vslice: VectorSliceMut<'_, Complex, Column> = + VectorSliceMut::from_range(&mut data, 1..3); + let slice_mut: &mut [Complex] = vslice.as_mut(); + slice_mut[1] = Complex::new(9.0, 9.0); + assert_eq!(slice_mut, &[Complex::new(3.0, 4.0), Complex::new(9.0, 9.0)]); + // Changes are reflected in the original data + assert_eq!(data, [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0), Complex::new(9.0, 9.0)]); + } + + #[test] + fn test_vector_slice_mut_as_mut_with_std_function() { + let mut data = [10, 20, 30, 40]; + let mut vslice: VectorSliceMut<'_, i32, Row> = VectorSliceMut::from_range(&mut data, 1..4); + // Use a standard library function that takes AsMut<[i32]> + vslice.as_mut().reverse(); + assert_eq!(vslice.as_mut(), &[40, 30, 20]); + // Changes are reflected in the original data + assert_eq!(data, [10, 40, 30, 20]); + } + + // -- IntoIterator trait for VectorSliceMut -- + #[test] + fn test_vector_slice_mut_into_iter_basic() { + let mut data = [1, 2, 3, 4]; + let vslice: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut data, 1..4); + let collected: Vec = vslice + .into_iter() + .map(|x| { + *x += 10; + *x + }) + .collect(); + assert_eq!(collected, vec![12, 13, 14]); + // The original data is also updated + assert_eq!(data, [1, 12, 13, 14]); + } + + #[test] + fn test_vector_slice_mut_into_iter_ref() { + let mut data = [10, 20, 30]; + let mut vslice: VectorSliceMut<'_, i32, Row> = VectorSliceMut::from_range(&mut data, 0..2); + { + for x in &mut vslice { + *x *= 2; + } + } + // The original data is updated + assert_eq!(data, [20, 40, 30]); + } + + #[test] + fn test_vector_slice_mut_into_iter_complex() { + let mut data = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0), Complex::new(5.0, 6.0)]; + let vslice: VectorSliceMut<'_, Complex, Column> = + VectorSliceMut::from_range(&mut data, 0..3); + for x in vslice.into_iter() { + x.im += 1.0; + } + assert_eq!(data, [Complex::new(1.0, 3.0), Complex::new(3.0, 5.0), Complex::new(5.0, 7.0)]); + } +} From da3606e948727805230a8fd6e858a74897e73d60 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Mon, 26 May 2025 15:50:30 -0400 Subject: [PATCH 49/82] refactor "components" struct fields to "elements" --- src/macros.rs | 126 ++++++++++----------- src/types/flexvector.rs | 235 +++++++++++++++++++-------------------- src/types/vectorslice.rs | 74 ++++++------ 3 files changed, 215 insertions(+), 220 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index 7064f82..efccd1d 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -226,8 +226,8 @@ macro_rules! impl_vector_unary_op { type Output = Self; #[inline] fn $method(self) -> Self { - let components = self.components.into_iter().map(|a| $op a).collect(); - Self { components, _orientation: PhantomData } + let elements = self.elements.into_iter().map(|a| $op a).collect(); + Self { elements, _orientation: PhantomData } } } }; @@ -246,11 +246,11 @@ macro_rules! impl_vector_binop { #[inline] fn $method(self, rhs: Self) -> Self { assert_eq!(self.len(), rhs.len(), "Vector length mismatch"); - let components = self.components.into_iter() - .zip(rhs.components) + let elements = self.elements.into_iter() + .zip(rhs.elements) .map(|(a, b)| a $op b) .collect(); - Self { components, _orientation: PhantomData } + Self { elements, _orientation: PhantomData } } } }; @@ -263,11 +263,11 @@ macro_rules! impl_vector_binop { type Output = Self; #[inline] fn $method(self, rhs: Self) -> Self { - let components = self.components.into_iter() - .zip(rhs.components) + let elements = self.elements.into_iter() + .zip(rhs.elements) .map(|(a, b)| a $op b) .collect(); - Self { components, _orientation: PhantomData } + Self { elements, _orientation: PhantomData } } } }; @@ -284,9 +284,9 @@ macro_rules! impl_vector_binop_div { #[inline] fn div(self, rhs: Self) -> Self { assert_eq!(self.len(), rhs.len(), "Vector length mismatch"); - let components = - self.components.into_iter().zip(rhs.components).map(|(a, b)| a / b).collect(); - Self { components, _orientation: PhantomData } + let elements = + self.elements.into_iter().zip(rhs.elements).map(|(a, b)| a / b).collect(); + Self { elements, _orientation: PhantomData } } } // f64 @@ -295,9 +295,9 @@ macro_rules! impl_vector_binop_div { #[inline] fn div(self, rhs: Self) -> Self { assert_eq!(self.len(), rhs.len(), "Vector length mismatch"); - let components = - self.components.into_iter().zip(rhs.components).map(|(a, b)| a / b).collect(); - Self { components, _orientation: PhantomData } + let elements = + self.elements.into_iter().zip(rhs.elements).map(|(a, b)| a / b).collect(); + Self { elements, _orientation: PhantomData } } } // Complex @@ -306,9 +306,9 @@ macro_rules! impl_vector_binop_div { #[inline] fn div(self, rhs: Self) -> Self { assert_eq!(self.len(), rhs.len(), "Vector length mismatch"); - let components = - self.components.into_iter().zip(rhs.components).map(|(a, b)| a / b).collect(); - Self { components, _orientation: PhantomData } + let elements = + self.elements.into_iter().zip(rhs.elements).map(|(a, b)| a / b).collect(); + Self { elements, _orientation: PhantomData } } } // Complex @@ -317,9 +317,9 @@ macro_rules! impl_vector_binop_div { #[inline] fn div(self, rhs: Self) -> Self { assert_eq!(self.len(), rhs.len(), "Vector length mismatch"); - let components = - self.components.into_iter().zip(rhs.components).map(|(a, b)| a / b).collect(); - Self { components, _orientation: PhantomData } + let elements = + self.elements.into_iter().zip(rhs.elements).map(|(a, b)| a / b).collect(); + Self { elements, _orientation: PhantomData } } } }; @@ -330,9 +330,9 @@ macro_rules! impl_vector_binop_div { type Output = Self; #[inline] fn div(self, rhs: Self) -> Self { - let components = - self.components.into_iter().zip(rhs.components).map(|(a, b)| a / b).collect(); - Self { components, _orientation: PhantomData } + let elements = + self.elements.into_iter().zip(rhs.elements).map(|(a, b)| a / b).collect(); + Self { elements, _orientation: PhantomData } } } // f64 @@ -340,9 +340,9 @@ macro_rules! impl_vector_binop_div { type Output = Self; #[inline] fn div(self, rhs: Self) -> Self { - let components = - self.components.into_iter().zip(rhs.components).map(|(a, b)| a / b).collect(); - Self { components, _orientation: PhantomData } + let elements = + self.elements.into_iter().zip(rhs.elements).map(|(a, b)| a / b).collect(); + Self { elements, _orientation: PhantomData } } } // Complex @@ -350,9 +350,9 @@ macro_rules! impl_vector_binop_div { type Output = Self; #[inline] fn div(self, rhs: Self) -> Self { - let components = - self.components.into_iter().zip(rhs.components).map(|(a, b)| a / b).collect(); - Self { components, _orientation: PhantomData } + let elements = + self.elements.into_iter().zip(rhs.elements).map(|(a, b)| a / b).collect(); + Self { elements, _orientation: PhantomData } } } // Complex @@ -360,9 +360,9 @@ macro_rules! impl_vector_binop_div { type Output = Self; #[inline] fn div(self, rhs: Self) -> Self { - let components = - self.components.into_iter().zip(rhs.components).map(|(a, b)| a / b).collect(); - Self { components, _orientation: PhantomData } + let elements = + self.elements.into_iter().zip(rhs.elements).map(|(a, b)| a / b).collect(); + Self { elements, _orientation: PhantomData } } } }; @@ -380,7 +380,7 @@ macro_rules! impl_vector_binop_assign { #[inline] fn $method(&mut self, rhs: Self) { assert_eq!(self.len(), rhs.len(), "Vector length mismatch"); - for (a, b) in self.components.iter_mut().zip(rhs.components) { + for (a, b) in self.elements.iter_mut().zip(rhs.elements) { *a = a.clone() $op b; } } @@ -394,7 +394,7 @@ macro_rules! impl_vector_binop_assign { { #[inline] fn $method(&mut self, rhs: Self) { - for (a, b) in self.components.iter_mut().zip(rhs.components) { + for (a, b) in self.elements.iter_mut().zip(rhs.elements) { *a = a.clone() $op b; } } @@ -412,7 +412,7 @@ macro_rules! impl_vector_binop_div_assign { #[inline] fn div_assign(&mut self, rhs: Self) { assert_eq!(self.len(), rhs.len(), "Vector length mismatch"); - for (a, b) in self.components.iter_mut().zip(rhs.components) { + for (a, b) in self.elements.iter_mut().zip(rhs.elements) { *a /= b; } } @@ -422,7 +422,7 @@ macro_rules! impl_vector_binop_div_assign { #[inline] fn div_assign(&mut self, rhs: Self) { assert_eq!(self.len(), rhs.len(), "Vector length mismatch"); - for (a, b) in self.components.iter_mut().zip(rhs.components) { + for (a, b) in self.elements.iter_mut().zip(rhs.elements) { *a /= b; } } @@ -432,7 +432,7 @@ macro_rules! impl_vector_binop_div_assign { #[inline] fn div_assign(&mut self, rhs: Self) { assert_eq!(self.len(), rhs.len(), "Vector length mismatch"); - for (a, b) in self.components.iter_mut().zip(rhs.components) { + for (a, b) in self.elements.iter_mut().zip(rhs.elements) { *a /= b; } } @@ -442,7 +442,7 @@ macro_rules! impl_vector_binop_div_assign { #[inline] fn div_assign(&mut self, rhs: Self) { assert_eq!(self.len(), rhs.len(), "Vector length mismatch"); - for (a, b) in self.components.iter_mut().zip(rhs.components) { + for (a, b) in self.elements.iter_mut().zip(rhs.elements) { *a /= b; } } @@ -454,7 +454,7 @@ macro_rules! impl_vector_binop_div_assign { impl std::ops::DivAssign for $VectorType { #[inline] fn div_assign(&mut self, rhs: Self) { - for (a, b) in self.components.iter_mut().zip(rhs.components) { + for (a, b) in self.elements.iter_mut().zip(rhs.elements) { *a /= b; } } @@ -463,7 +463,7 @@ macro_rules! impl_vector_binop_div_assign { impl std::ops::DivAssign for $VectorType { #[inline] fn div_assign(&mut self, rhs: Self) { - for (a, b) in self.components.iter_mut().zip(rhs.components) { + for (a, b) in self.elements.iter_mut().zip(rhs.elements) { *a /= b; } } @@ -472,7 +472,7 @@ macro_rules! impl_vector_binop_div_assign { impl std::ops::DivAssign for $VectorType, O> { #[inline] fn div_assign(&mut self, rhs: Self) { - for (a, b) in self.components.iter_mut().zip(rhs.components) { + for (a, b) in self.elements.iter_mut().zip(rhs.elements) { *a /= b; } } @@ -481,7 +481,7 @@ macro_rules! impl_vector_binop_div_assign { impl std::ops::DivAssign for $VectorType, O> { #[inline] fn div_assign(&mut self, rhs: Self) { - for (a, b) in self.components.iter_mut().zip(rhs.components) { + for (a, b) in self.elements.iter_mut().zip(rhs.elements) { *a /= b; } } @@ -500,8 +500,8 @@ macro_rules! impl_vector_scalar_op { type Output = Self; #[inline] fn $method(self, rhs: T) -> Self { - let components = self.components.into_iter().map(|a| a $op rhs.clone()).collect(); - Self { components, _orientation: PhantomData } + let elements = self.elements.into_iter().map(|a| a $op rhs.clone()).collect(); + Self { elements, _orientation: PhantomData } } } }; @@ -517,7 +517,7 @@ macro_rules! impl_vector_scalar_op_assign { { #[inline] fn $method(&mut self, rhs: T) { - for a in &mut self.components { + for a in &mut self.elements { *a = a.clone() $op rhs.clone(); } } @@ -534,8 +534,8 @@ macro_rules! impl_vector_scalar_div_op { type Output = Self; #[inline] fn div(self, rhs: f32) -> Self { - let components = self.components.into_iter().map(|a| a / rhs).collect(); - Self { components, _orientation: PhantomData } + let elements = self.elements.into_iter().map(|a| a / rhs).collect(); + Self { elements, _orientation: PhantomData } } } // For f64 @@ -543,8 +543,8 @@ macro_rules! impl_vector_scalar_div_op { type Output = Self; #[inline] fn div(self, rhs: f64) -> Self { - let components = self.components.into_iter().map(|a| a / rhs).collect(); - Self { components, _orientation: PhantomData } + let elements = self.elements.into_iter().map(|a| a / rhs).collect(); + Self { elements, _orientation: PhantomData } } } // For Complex / f32 @@ -552,8 +552,8 @@ macro_rules! impl_vector_scalar_div_op { type Output = Self; #[inline] fn div(self, rhs: f32) -> Self { - let components = self.components.into_iter().map(|a| a / rhs).collect(); - Self { components, _orientation: PhantomData } + let elements = self.elements.into_iter().map(|a| a / rhs).collect(); + Self { elements, _orientation: PhantomData } } } // For Complex / f64 @@ -561,8 +561,8 @@ macro_rules! impl_vector_scalar_div_op { type Output = Self; #[inline] fn div(self, rhs: f64) -> Self { - let components = self.components.into_iter().map(|a| a / rhs).collect(); - Self { components, _orientation: PhantomData } + let elements = self.elements.into_iter().map(|a| a / rhs).collect(); + Self { elements, _orientation: PhantomData } } } // For Complex / Complex @@ -570,8 +570,8 @@ macro_rules! impl_vector_scalar_div_op { type Output = Self; #[inline] fn div(self, rhs: num::Complex) -> Self { - let components = self.components.into_iter().map(|a| a / rhs).collect(); - Self { components, _orientation: PhantomData } + let elements = self.elements.into_iter().map(|a| a / rhs).collect(); + Self { elements, _orientation: PhantomData } } } // For Complex / Complex @@ -579,8 +579,8 @@ macro_rules! impl_vector_scalar_div_op { type Output = Self; #[inline] fn div(self, rhs: num::Complex) -> Self { - let components = self.components.into_iter().map(|a| a / rhs).collect(); - Self { components, _orientation: PhantomData } + let elements = self.elements.into_iter().map(|a| a / rhs).collect(); + Self { elements, _orientation: PhantomData } } } }; @@ -594,7 +594,7 @@ macro_rules! impl_vector_scalar_div_op_assign { impl std::ops::DivAssign for $VectorType { #[inline] fn div_assign(&mut self, rhs: f32) { - for a in &mut self.components { + for a in &mut self.elements { *a = *a / rhs; } } @@ -603,7 +603,7 @@ macro_rules! impl_vector_scalar_div_op_assign { impl std::ops::DivAssign for $VectorType { #[inline] fn div_assign(&mut self, rhs: f64) { - for a in &mut self.components { + for a in &mut self.elements { *a = *a / rhs; } } @@ -612,7 +612,7 @@ macro_rules! impl_vector_scalar_div_op_assign { impl std::ops::DivAssign for $VectorType, O> { #[inline] fn div_assign(&mut self, rhs: f32) { - for a in &mut self.components { + for a in &mut self.elements { *a = *a / rhs; } } @@ -621,7 +621,7 @@ macro_rules! impl_vector_scalar_div_op_assign { impl std::ops::DivAssign for $VectorType, O> { #[inline] fn div_assign(&mut self, rhs: f64) { - for a in &mut self.components { + for a in &mut self.elements { *a = *a / rhs; } } @@ -630,7 +630,7 @@ macro_rules! impl_vector_scalar_div_op_assign { impl std::ops::DivAssign> for $VectorType, O> { #[inline] fn div_assign(&mut self, rhs: num::Complex) { - for a in &mut self.components { + for a in &mut self.elements { *a = *a / rhs; } } @@ -639,7 +639,7 @@ macro_rules! impl_vector_scalar_div_op_assign { impl std::ops::DivAssign> for $VectorType, O> { #[inline] fn div_assign(&mut self, rhs: num::Complex) { - for a in &mut self.components { + for a in &mut self.elements { *a = *a / rhs; } } diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index 9309231..0d1f3ed 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -40,7 +40,7 @@ use num::{Complex, Zero}; #[derive(Clone)] pub struct FlexVector { /// ... - pub components: Vec, + pub elements: Vec, _orientation: PhantomData, } @@ -60,13 +60,13 @@ impl FlexVector { /// Creates a new, empty FlexVector. #[inline] pub fn new() -> Self { - Self { components: Vec::new(), _orientation: PhantomData } + Self { elements: Vec::new(), _orientation: PhantomData } } /// Creates a new FlexVector with a pre-allocated capacity. #[inline] pub fn with_capacity(capacity: usize) -> Self { - Self { components: Vec::with_capacity(capacity), _orientation: PhantomData } + Self { elements: Vec::with_capacity(capacity), _orientation: PhantomData } } /// Returns a new FlexVector of the given length, filled with zeros. @@ -75,7 +75,7 @@ impl FlexVector { where T: num::Zero + Clone, { - Self { components: vec![T::zero(); len], _orientation: PhantomData } + Self { elements: vec![T::zero(); len], _orientation: PhantomData } } /// Returns a new FlexVector of the given length, filled with ones. @@ -84,7 +84,7 @@ impl FlexVector { where T: num::One + Clone, { - Self { components: vec![T::one(); len], _orientation: PhantomData } + Self { elements: vec![T::one(); len], _orientation: PhantomData } } /// Returns a new FlexVector of the given length, filled with the given value. @@ -93,7 +93,7 @@ impl FlexVector { where T: Clone, { - Self { components: vec![value; len], _orientation: PhantomData } + Self { elements: vec![value; len], _orientation: PhantomData } } /// Creates a new FlexVector from a slice. @@ -102,13 +102,13 @@ impl FlexVector { where T: Clone, { - Self { components: slice.to_vec(), _orientation: PhantomData } + Self { elements: slice.to_vec(), _orientation: PhantomData } } /// Creates a FlexVector from a Vec. #[inline] pub fn from_vec(vec: Vec) -> Self { - Self { components: vec, _orientation: PhantomData } + Self { elements: vec, _orientation: PhantomData } } /// Creates a FlexVector from a Cow. @@ -126,8 +126,8 @@ impl FlexVector { where I: IntoIterator>, { - let components: Result, E> = iter.into_iter().collect(); - components.map(|vec| FlexVector { components: vec, _orientation: PhantomData }) + let elements: Result, E> = iter.into_iter().collect(); + elements.map(|vec| FlexVector { elements: vec, _orientation: PhantomData }) } /// Creates a new [`FlexVector`] by calling the provided function or closure for each index. @@ -144,8 +144,8 @@ impl FlexVector { where F: FnMut(usize) -> T, { - let components = (0..len).map(f).collect(); - FlexVector { components, _orientation: PhantomData } + let elements = (0..len).map(f).collect(); + FlexVector { elements, _orientation: PhantomData } } /// Fallibly creates a new [`FlexVector`] by calling the provided function or closure for each index. @@ -164,11 +164,11 @@ impl FlexVector { where F: FnMut(usize) -> Result, { - let mut components = Vec::with_capacity(len); + let mut elements = Vec::with_capacity(len); for i in 0..len { - components.push(f(i)?); + elements.push(f(i)?); } - Ok(FlexVector { components, _orientation: PhantomData }) + Ok(FlexVector { elements, _orientation: PhantomData }) } /// Returns a new FlexVector by repeating the pattern until length `len` is reached. @@ -183,8 +183,8 @@ impl FlexVector { "pattern must not be empty if len > 0".to_string(), )); } - let components = pattern.iter().cloned().cycle().take(len).collect(); - Ok(FlexVector { components, _orientation: PhantomData }) + let elements = pattern.iter().cloned().cycle().take(len).collect(); + Ok(FlexVector { elements, _orientation: PhantomData }) } } @@ -211,7 +211,7 @@ where O: VectorOrientationName + 'static, { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{} FlexVector {:?}", O::orientation_name(), self.components) + write!(f, "{} FlexVector {:?}", O::orientation_name(), self.elements) } } @@ -228,7 +228,7 @@ where fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("FlexVector") .field("orientation", &O::orientation_name()) - .field("components", &self.components) + .field("elements", &self.elements) .finish() } } @@ -241,7 +241,7 @@ where impl FromIterator for FlexVector { #[inline] fn from_iter>(iter: I) -> Self { - FlexVector { components: iter.into_iter().collect(), _orientation: PhantomData } + FlexVector { elements: iter.into_iter().collect(), _orientation: PhantomData } } } @@ -254,14 +254,14 @@ impl Deref for FlexVector { type Target = [T]; #[inline] fn deref(&self) -> &Self::Target { - &self.components + &self.elements } } impl DerefMut for FlexVector { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.components + &mut self.elements } } @@ -273,13 +273,13 @@ impl DerefMut for FlexVector { impl AsRef<[T]> for FlexVector { #[inline] fn as_ref(&self) -> &[T] { - &self.components + &self.elements } } impl AsMut<[T]> for FlexVector { #[inline] fn as_mut(&mut self) -> &mut [T] { - &mut self.components + &mut self.elements } } @@ -291,13 +291,13 @@ impl AsMut<[T]> for FlexVector { impl Borrow<[T]> for FlexVector { #[inline] fn borrow(&self) -> &[T] { - &self.components + &self.elements } } impl BorrowMut<[T]> for FlexVector { #[inline] fn borrow_mut(&mut self) -> &mut [T] { - &mut self.components + &mut self.elements } } @@ -312,7 +312,7 @@ impl IntoIterator for FlexVector { #[inline] fn into_iter(self) -> Self::IntoIter { - self.components.into_iter() + self.elements.into_iter() } } impl<'a, T, O> IntoIterator for &'a FlexVector { @@ -321,7 +321,7 @@ impl<'a, T, O> IntoIterator for &'a FlexVector { #[inline] fn into_iter(self) -> Self::IntoIter { - self.components.iter() + self.elements.iter() } } impl<'a, T, O> IntoIterator for &'a mut FlexVector { @@ -330,7 +330,7 @@ impl<'a, T, O> IntoIterator for &'a mut FlexVector { #[inline] fn into_iter(self) -> Self::IntoIter { - self.components.iter_mut() + self.elements.iter_mut() } } @@ -345,7 +345,7 @@ where { #[inline] fn eq(&self, other: &Self) -> bool { - self.components == other.components + self.elements == other.elements } } impl Eq for FlexVector where T: Eq {} @@ -361,7 +361,7 @@ where { #[inline] fn partial_cmp(&self, other: &Self) -> Option { - self.components.partial_cmp(&other.components) + self.elements.partial_cmp(&other.elements) } } impl Ord for FlexVector @@ -370,7 +370,7 @@ where { #[inline] fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.components.cmp(&other.components) + self.elements.cmp(&other.elements) } } @@ -385,7 +385,7 @@ where { #[inline] fn hash(&self, state: &mut H) { - self.components.hash(state) + self.elements.hash(state) } } @@ -400,7 +400,7 @@ where { #[inline] fn from(vec: Vec) -> Self { - FlexVector { components: vec, _orientation: PhantomData } + FlexVector { elements: vec, _orientation: PhantomData } } } @@ -410,7 +410,7 @@ where { #[inline] fn from(slice: &[T]) -> Self { - FlexVector { components: slice.to_vec(), _orientation: PhantomData } + FlexVector { elements: slice.to_vec(), _orientation: PhantomData } } } @@ -506,13 +506,13 @@ where impl From> for FlexVector { #[inline] fn from(v: FlexVector) -> Self { - FlexVector { components: v.components, _orientation: PhantomData } + FlexVector { elements: v.elements, _orientation: PhantomData } } } impl From> for FlexVector { #[inline] fn from(v: FlexVector) -> Self { - FlexVector { components: v.components, _orientation: PhantomData } + FlexVector { elements: v.elements, _orientation: PhantomData } } } @@ -526,7 +526,7 @@ impl Index for FlexVector { #[inline] fn index(&self, idx: usize) -> &Self::Output { - &self.components[idx] + &self.elements[idx] } } @@ -535,7 +535,7 @@ impl Index> for FlexVector { #[inline] fn index(&self, range: Range) -> &Self::Output { - &self.components[range] + &self.elements[range] } } @@ -544,7 +544,7 @@ impl Index> for FlexVector { #[inline] fn index(&self, range: RangeFrom) -> &Self::Output { - &self.components[range] + &self.elements[range] } } @@ -553,7 +553,7 @@ impl Index> for FlexVector { #[inline] fn index(&self, range: RangeTo) -> &Self::Output { - &self.components[range] + &self.elements[range] } } @@ -562,7 +562,7 @@ impl Index for FlexVector { #[inline] fn index(&self, range: RangeFull) -> &Self::Output { - &self.components[range] + &self.elements[range] } } @@ -571,7 +571,7 @@ impl Index> for FlexVector { #[inline] fn index(&self, range: RangeInclusive) -> &Self::Output { - &self.components[range] + &self.elements[range] } } @@ -580,7 +580,7 @@ impl Index> for FlexVector { #[inline] fn index(&self, range: RangeToInclusive) -> &Self::Output { - &self.components[range] + &self.elements[range] } } @@ -592,49 +592,49 @@ impl Index> for FlexVector { impl IndexMut for FlexVector { #[inline] fn index_mut(&mut self, idx: usize) -> &mut Self::Output { - &mut self.components[idx] + &mut self.elements[idx] } } impl IndexMut> for FlexVector { #[inline] fn index_mut(&mut self, range: Range) -> &mut Self::Output { - &mut self.components[range] + &mut self.elements[range] } } impl IndexMut> for FlexVector { #[inline] fn index_mut(&mut self, range: RangeFrom) -> &mut Self::Output { - &mut self.components[range] + &mut self.elements[range] } } impl IndexMut> for FlexVector { #[inline] fn index_mut(&mut self, range: RangeTo) -> &mut Self::Output { - &mut self.components[range] + &mut self.elements[range] } } impl IndexMut for FlexVector { #[inline] fn index_mut(&mut self, range: RangeFull) -> &mut Self::Output { - &mut self.components[range] + &mut self.elements[range] } } impl IndexMut> for FlexVector { #[inline] fn index_mut(&mut self, range: RangeInclusive) -> &mut Self::Output { - &mut self.components[range] + &mut self.elements[range] } } impl IndexMut> for FlexVector { #[inline] fn index_mut(&mut self, range: RangeToInclusive) -> &mut Self::Output { - &mut self.components[range] + &mut self.elements[range] } } @@ -646,7 +646,7 @@ impl IndexMut> for FlexVector { impl Extend for FlexVector { #[inline] fn extend>(&mut self, iter: I) { - self.components.extend(iter) + self.elements.extend(iter) } } @@ -656,16 +656,16 @@ impl Extend for FlexVector { // // ================================ impl VectorBase for FlexVector { - /// Returns an immutable slice of the FlexVector's components. + /// Returns an immutable slice of the FlexVector's elements. #[inline] fn as_slice(&self) -> &[T] { - &self.components + &self.elements } - /// Returns a mutable slice of the FlexVector's components. + /// Returns a mutable slice of the FlexVector's elements. #[inline] fn as_mut_slice(&mut self) -> &mut [T] { - &mut self.components[..] + &mut self.elements[..] } } @@ -680,7 +680,7 @@ impl Transposable for FlexVector { #[inline] fn transpose(self) -> Self::Transposed { - FlexVector { components: self.components, _orientation: PhantomData } + FlexVector { elements: self.elements, _orientation: PhantomData } } } @@ -689,7 +689,7 @@ impl Transposable for FlexVector { #[inline] fn transpose(self) -> Self::Transposed { - FlexVector { components: self.components, _orientation: PhantomData } + FlexVector { elements: self.elements, _orientation: PhantomData } } } @@ -806,13 +806,13 @@ where T: PartialOrd + Clone, { self.check_same_length_and_raise(other)?; - let components = self + let elements = self .as_slice() .iter() .zip(other.as_slice()) .map(|(a, b)| if a < b { a.clone() } else { b.clone() }) .collect(); - Ok(FlexVector { components, _orientation: PhantomData }) + Ok(FlexVector { elements, _orientation: PhantomData }) } /// Element-wise max @@ -822,13 +822,13 @@ where T: PartialOrd + Clone, { self.check_same_length_and_raise(other)?; - let components = self + let elements = self .as_slice() .iter() .zip(other.as_slice()) .map(|(a, b)| if a > b { a.clone() } else { b.clone() }) .collect(); - Ok(FlexVector { components, _orientation: PhantomData }) + Ok(FlexVector { elements, _orientation: PhantomData }) } } @@ -1244,7 +1244,7 @@ impl FlexVector { where T: Clone, { - FlexVector { components: self.components.clone(), _orientation: PhantomData } + FlexVector { elements: self.elements.clone(), _orientation: PhantomData } } /// ... @@ -1253,43 +1253,43 @@ impl FlexVector { where T: Clone, { - FlexVector { components: self.components.clone(), _orientation: PhantomData } + FlexVector { elements: self.elements.clone(), _orientation: PhantomData } } /// Consumes self and returns a Row-oriented FlexVector. #[inline] pub fn into_row(self) -> FlexVector { - FlexVector { components: self.components, _orientation: std::marker::PhantomData } + FlexVector { elements: self.elements, _orientation: std::marker::PhantomData } } /// Consumes self and returns a Column-oriented FlexVector. #[inline] pub fn into_column(self) -> FlexVector { - FlexVector { components: self.components, _orientation: std::marker::PhantomData } + FlexVector { elements: self.elements, _orientation: std::marker::PhantomData } } /// Adds an element to the end of the vector. #[inline] pub fn push(&mut self, value: T) { - self.components.push(value); + self.elements.push(value); } /// Removes the last element and returns it, or None if empty. #[inline] pub fn pop(&mut self) -> Option { - self.components.pop() + self.elements.pop() } /// Inserts an element at position index, shifting all elements after it. #[inline] pub fn insert(&mut self, index: usize, value: T) { - self.components.insert(index, value); + self.elements.insert(index, value); } /// Removes and returns the element at position index. #[inline] pub fn remove(&mut self, index: usize) -> T { - self.components.remove(index) + self.elements.remove(index) } /// Resizes the vector in-place. @@ -1298,13 +1298,13 @@ impl FlexVector { where T: Clone, { - self.components.resize(new_len, value); + self.elements.resize(new_len, value); } /// Clears the vector, removing all values. #[inline] pub fn clear(&mut self) { - self.components.clear(); + self.elements.clear(); } /// Returns a mutable reference to a FlexVector index value or range, @@ -1314,13 +1314,13 @@ impl FlexVector { where I: std::slice::SliceIndex<[T]>, { - self.components.get_mut(index) + self.elements.get_mut(index) } /// Returns an iterator over mutable references to the elements. #[inline] pub fn iter_mut(&mut self) -> impl Iterator { - self.components.iter_mut() + self.elements.iter_mut() } /// Returns a new FlexVector with each element mapped to a new value using the provided closure or function. @@ -1330,8 +1330,8 @@ impl FlexVector { F: FnMut(T) -> U, T: Clone, { - let components = self.components.iter().cloned().map(&mut f).collect(); - FlexVector { components, _orientation: PhantomData } + let elements = self.elements.iter().cloned().map(&mut f).collect(); + FlexVector { elements, _orientation: PhantomData } } /// Applies a closure or function to each element, modifying them in place. @@ -1341,7 +1341,7 @@ impl FlexVector { F: FnMut(T) -> T, T: Clone, { - for x in self.components.iter_mut() { + for x in self.elements.iter_mut() { *x = f(x.clone()); } } @@ -1353,8 +1353,8 @@ impl FlexVector { F: FnMut(T) -> I, I: IntoIterator, { - let components = self.components.into_iter().flat_map(&mut f).collect(); - FlexVector { components, _orientation: PhantomData } + let elements = self.elements.into_iter().flat_map(&mut f).collect(); + FlexVector { elements, _orientation: PhantomData } } /// Returns a new FlexVector containing only the elements that satisfy the predicate. @@ -1364,8 +1364,8 @@ impl FlexVector { F: FnMut(&T) -> bool, T: Clone, { - let components = self.components.iter().filter(|&x| predicate(x)).cloned().collect(); - FlexVector { components, _orientation: PhantomData } + let elements = self.elements.iter().filter(|&x| predicate(x)).cloned().collect(); + FlexVector { elements, _orientation: PhantomData } } /// Reduces the elements to a single value using the provided closure, or returns None if empty. @@ -1375,7 +1375,7 @@ impl FlexVector { F: FnMut(T, T) -> T, T: Clone, { - let mut iter = self.components.iter().cloned(); + let mut iter = self.elements.iter().cloned(); let first = iter.next()?; Some(iter.fold(first, &mut f)) } @@ -1386,15 +1386,15 @@ impl FlexVector { where F: FnMut(B, &T) -> B, { - self.components.iter().fold(init, &mut f) + self.elements.iter().fold(init, &mut f) } /// Zips two FlexVectors into a FlexVector of pairs. #[inline] pub fn zip(self, other: FlexVector) -> FlexVector<(T, U), O> { let len = self.len().min(other.len()); - let components = self.components.into_iter().zip(other.components).take(len).collect(); - FlexVector { components, _orientation: PhantomData } + let elements = self.elements.into_iter().zip(other.elements).take(len).collect(); + FlexVector { elements, _orientation: PhantomData } } /// Zips two FlexVectors with a function, producing a FlexVector of the function's output. @@ -1404,14 +1404,9 @@ impl FlexVector { F: FnMut(T, U) -> R, { let len = self.len().min(other.len()); - let components = self - .components - .into_iter() - .zip(other.components) - .map(|(a, b)| f(a, b)) - .take(len) - .collect(); - FlexVector { components, _orientation: PhantomData } + let elements = + self.elements.into_iter().zip(other.elements).map(|(a, b)| f(a, b)).take(len).collect(); + FlexVector { elements, _orientation: PhantomData } } /// Returns a new FlexVector containing every `step`th element, starting from the first. @@ -1424,8 +1419,8 @@ impl FlexVector { if step == 0 { return Err(VectorError::ValueError("step must be non-zero".to_string())); } - let components = self.components.iter().step_by(step).cloned().collect(); - Ok(FlexVector { components, _orientation: PhantomData }) + let elements = self.elements.iter().step_by(step).cloned().collect(); + Ok(FlexVector { elements, _orientation: PhantomData }) } /// Returns a boolean vector mask where each element is the result of applying the predicate to the corresponding element. @@ -1434,7 +1429,7 @@ impl FlexVector { where F: FnMut(&T) -> bool, { - self.components.iter().map(predicate).collect() + self.elements.iter().map(predicate).collect() } /// Returns a new FlexVector containing elements where the corresponding mask value is true. @@ -1448,50 +1443,50 @@ impl FlexVector { "Mask length must match vector length".to_string(), )); } - let components = self - .components + let elements = self + .elements .iter() .zip(mask) .filter_map(|(x, &m)| if m { Some(x.clone()) } else { None }) .collect(); - Ok(FlexVector { components, _orientation: PhantomData }) + Ok(FlexVector { elements, _orientation: PhantomData }) } /// Consumes the FlexVector and returns a Vec. #[inline] pub fn into_vec(self) -> Vec { - self.components + self.elements } /// Consumes the FlexVector and returns a Box. #[inline] pub fn into_boxed_slice(self) -> Box<[T]> { - self.components.into_boxed_slice() + self.elements.into_boxed_slice() } /// Consumes the FlexVector and returns an Arc. #[cfg(target_has_atomic = "ptr")] #[inline] pub fn into_arc_slice(self) -> std::sync::Arc<[T]> { - std::sync::Arc::from(self.components) + std::sync::Arc::from(self.elements) } /// Consumes the FlexVector and returns a Rc. #[inline] pub fn into_rc_slice(self) -> std::rc::Rc<[T]> { - std::rc::Rc::from(self.components) + std::rc::Rc::from(self.elements) } /// Create a VectorSlice from a FlexVector. #[inline] pub fn as_vslice(&self, range: std::ops::Range) -> VectorSlice<'_, T, O> { - VectorSlice::new(&self.components[range]) + VectorSlice::new(&self.elements[range]) } /// Create a mutable VectorSliceMut from a FlexVector. #[inline] pub fn as_mut_vslice(&mut self, range: std::ops::Range) -> VectorSliceMut<'_, T, O> { - VectorSliceMut::new(&mut self.components[range]) + VectorSliceMut::new(&mut self.elements[range]) } // ================================ @@ -1519,7 +1514,7 @@ where /// Returns a FlexVector of owned values by cloning each referenced element. #[inline] pub fn cloned(&self) -> FlexVector { - FlexVector::from_vec(self.components.iter().map(|&x| x.clone()).collect()) + FlexVector::from_vec(self.elements.iter().map(|&x| x.clone()).collect()) } } @@ -1530,8 +1525,8 @@ where /// Flattens a FlexVector of iterables into a single FlexVector by concatenating all elements. #[inline] pub fn flatten(self) -> FlexVector { - let components = self.components.into_iter().flatten().collect(); - FlexVector { components, _orientation: PhantomData } + let elements = self.elements.into_iter().flatten().collect(); + FlexVector { elements, _orientation: PhantomData } } } @@ -1543,8 +1538,8 @@ where /// Flattens a FlexVector of iterables into a single FlexVector by concatenating all elements as cloned elements. #[inline] pub fn flatten_cloned(self) -> FlexVector { - let components = self.components.into_iter().flat_map(|v| v.into_iter().cloned()).collect(); - FlexVector { components, _orientation: PhantomData } + let elements = self.elements.into_iter().flat_map(|v| v.into_iter().cloned()).collect(); + FlexVector { elements, _orientation: PhantomData } } } @@ -1627,14 +1622,14 @@ mod tests { fn test_with_capacity() { let v = FlexVector::::with_capacity(10); assert_eq!(v.len(), 0); - assert!(v.components.capacity() >= 10); + assert!(v.elements.capacity() >= 10); } #[test] fn test_with_capacity_large() { let v = FlexVector::::with_capacity(1000); assert_eq!(v.len(), 0); - assert!(v.components.capacity() >= 1000); + assert!(v.elements.capacity() >= 1000); } #[test] @@ -2271,14 +2266,14 @@ mod tests { assert!(debug_col.contains("FlexVector")); assert!(debug_col.contains("orientation")); assert!(debug_col.contains("Column")); - assert!(debug_col.contains("components")); + assert!(debug_col.contains("elements")); assert!(debug_col.contains("1")); assert!(debug_col.contains("2")); assert!(debug_col.contains("3")); assert!(debug_row.contains("FlexVector")); assert!(debug_row.contains("orientation")); assert!(debug_row.contains("Row")); - assert!(debug_row.contains("components")); + assert!(debug_row.contains("elements")); assert!(debug_row.contains("4")); assert!(debug_row.contains("5")); assert!(debug_row.contains("6")); @@ -2293,13 +2288,13 @@ mod tests { assert!(debug_col.contains("FlexVector")); assert!(debug_col.contains("orientation")); assert!(debug_col.contains("Column")); - assert!(debug_col.contains("components")); + assert!(debug_col.contains("elements")); assert!(debug_col.contains("1.1")); assert!(debug_col.contains("2.2")); assert!(debug_row.contains("FlexVector")); assert!(debug_row.contains("orientation")); assert!(debug_row.contains("Row")); - assert!(debug_row.contains("components")); + assert!(debug_row.contains("elements")); assert!(debug_row.contains("3.3")); assert!(debug_row.contains("4.4")); } @@ -2319,13 +2314,13 @@ mod tests { assert!(debug_col.contains("FlexVector")); assert!(debug_col.contains("orientation")); assert!(debug_col.contains("Column")); - assert!(debug_col.contains("components")); + assert!(debug_col.contains("elements")); assert!(debug_col.contains("1.0")); assert!(debug_col.contains("2.0")); assert!(debug_row.contains("FlexVector")); assert!(debug_row.contains("orientation")); assert!(debug_row.contains("Row")); - assert!(debug_row.contains("components")); + assert!(debug_row.contains("elements")); assert!(debug_row.contains("5.0")); assert!(debug_row.contains("6.0")); } @@ -2337,7 +2332,7 @@ mod tests { assert!(debug.contains("FlexVector")); assert!(debug.contains("orientation")); assert!(debug.contains("Column")); // Default orientation is Column - assert!(debug.contains("components")); + assert!(debug.contains("elements")); assert!(debug.contains("[]")); } @@ -6345,7 +6340,7 @@ mod tests { let v1 = FVector::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 5.0, 6.0]); let midpoint = v1.midpoint(&v2).unwrap(); - // Should be the average of each component + // Should be the average of each element assert!((midpoint.as_slice()[0] - 2.5).abs() < 1e-12); assert!((midpoint.as_slice()[1] - 3.5).abs() < 1e-12); assert!((midpoint.as_slice()[2] - 4.5).abs() < 1e-12); diff --git a/src/types/vectorslice.rs b/src/types/vectorslice.rs index 8b96f6a..cdfaeb0 100644 --- a/src/types/vectorslice.rs +++ b/src/types/vectorslice.rs @@ -15,7 +15,7 @@ use std::marker::PhantomData; #[derive(Clone, Debug, PartialEq, Eq)] pub struct VectorSlice<'a, T, O = Column> { /// ... - pub slice: &'a [T], + pub elements: &'a [T], _orientation: PhantomData, } @@ -29,7 +29,7 @@ impl<'a, T, O> VectorSlice<'a, T, O> { /// ... #[inline] pub fn new(slice: &'a [T]) -> Self { - VectorSlice { slice, _orientation: PhantomData } + VectorSlice { elements: slice, _orientation: PhantomData } } /// ... @@ -48,13 +48,13 @@ impl<'a, T, O> VectorSlice<'a, T, O> { impl<'a, T, O> std::ops::Deref for VectorSlice<'a, T, O> { type Target = [T]; fn deref(&self) -> &Self::Target { - self.slice + self.elements } } impl<'a, T, O> AsRef<[T]> for VectorSlice<'a, T, O> { fn as_ref(&self) -> &[T] { - self.slice + self.elements } } @@ -63,7 +63,7 @@ impl<'a, T, O> IntoIterator for VectorSlice<'a, T, O> { type IntoIter = std::slice::Iter<'a, T>; fn into_iter(self) -> Self::IntoIter { - self.slice.iter() + self.elements.iter() } } @@ -72,7 +72,7 @@ impl<'a, T, O> IntoIterator for &'a VectorSlice<'a, T, O> { type IntoIter = std::slice::Iter<'a, T>; fn into_iter(self) -> Self::IntoIter { - self.slice.iter() + self.elements.iter() } } @@ -88,7 +88,7 @@ impl<'a, T, O> IntoIterator for &'a VectorSlice<'a, T, O> { #[derive(Debug)] pub struct VectorSliceMut<'a, T, O = Column> { /// ... - pub slice: &'a mut [T], + pub elements: &'a mut [T], _orientation: PhantomData, } @@ -102,7 +102,7 @@ impl<'a, T, O> VectorSliceMut<'a, T, O> { /// ... #[inline] pub fn new(slice: &'a mut [T]) -> Self { - VectorSliceMut { slice, _orientation: PhantomData } + VectorSliceMut { elements: slice, _orientation: PhantomData } } /// Creates a mutable VectorSliceMut from a parent mutable slice and a range. @@ -121,19 +121,19 @@ impl<'a, T, O> VectorSliceMut<'a, T, O> { impl<'a, T, O> std::ops::Deref for VectorSliceMut<'a, T, O> { type Target = [T]; fn deref(&self) -> &Self::Target { - self.slice + self.elements } } impl<'a, T, O> std::ops::DerefMut for VectorSliceMut<'a, T, O> { fn deref_mut(&mut self) -> &mut Self::Target { - self.slice + self.elements } } impl<'a, T, O> AsMut<[T]> for VectorSliceMut<'a, T, O> { fn as_mut(&mut self) -> &mut [T] { - self.slice + self.elements } } @@ -142,7 +142,7 @@ impl<'a, T, O> IntoIterator for VectorSliceMut<'a, T, O> { type IntoIter = std::slice::IterMut<'a, T>; fn into_iter(self) -> Self::IntoIter { - self.slice.into_iter() + self.elements.into_iter() } } @@ -151,7 +151,7 @@ impl<'a, T, O> IntoIterator for &'a mut VectorSliceMut<'a, T, O> { type IntoIter = std::slice::IterMut<'a, T>; fn into_iter(self) -> Self::IntoIter { - self.slice.iter_mut() + self.elements.iter_mut() } } @@ -175,7 +175,7 @@ mod tests { fn test_vector_slice_new() { let data = [1, 2, 3, 4, 5]; let vslice: VectorSlice<'_, i32, Column> = VectorSlice::new(&data); - assert_eq!(vslice.slice, &[1, 2, 3, 4, 5]); + assert_eq!(vslice.elements, &[1, 2, 3, 4, 5]); } #[test] @@ -183,7 +183,7 @@ mod tests { let data = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0), Complex::new(5.0, 6.0)]; let vslice: VectorSlice<'_, Complex, Column> = VectorSlice::new(&data); assert_eq!( - vslice.slice, + vslice.elements, &[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0), Complex::new(5.0, 6.0),] ); } @@ -193,21 +193,21 @@ mod tests { fn test_vector_slice_from_range_middle() { let data = [10, 20, 30, 40, 50]; let vslice: VectorSlice<'_, i32, Row> = VectorSlice::from_range(&data, 1..4); - assert_eq!(vslice.slice, &[20, 30, 40]); + assert_eq!(vslice.elements, &[20, 30, 40]); } #[test] fn test_vector_slice_from_range_full() { let data = [7, 8, 9]; let vslice: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&data, 0..3); - assert_eq!(vslice.slice, &[7, 8, 9]); + assert_eq!(vslice.elements, &[7, 8, 9]); } #[test] fn test_vector_slice_from_range_empty() { let data = [1, 2, 3]; let vslice: VectorSlice<'_, i32, Row> = VectorSlice::from_range(&data, 1..1); - assert_eq!(vslice.slice, &[]); + assert_eq!(vslice.elements, &[]); } #[test] @@ -221,7 +221,7 @@ mod tests { ]; let vslice: VectorSlice<'_, Complex, Row> = VectorSlice::from_range(&data, 1..4); assert_eq!( - vslice.slice, + vslice.elements, &[Complex::new(20.0, 2.0), Complex::new(30.0, 3.0), Complex::new(40.0, 4.0),] ); } @@ -231,7 +231,7 @@ mod tests { let data = [Complex::new(7.0, 0.0), Complex::new(8.0, 1.0), Complex::new(9.0, 2.0)]; let vslice: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&data, 0..3); assert_eq!( - vslice.slice, + vslice.elements, &[Complex::new(7.0, 0.0), Complex::new(8.0, 1.0), Complex::new(9.0, 2.0),] ); } @@ -240,7 +240,7 @@ mod tests { fn test_vector_slice_from_range_empty_complex() { let data = [Complex::new(1.0, 1.0), Complex::new(2.0, 2.0), Complex::new(3.0, 3.0)]; let vslice: VectorSlice<'_, Complex, Row> = VectorSlice::from_range(&data, 1..1); - assert_eq!(vslice.slice, &[]); + assert_eq!(vslice.elements, &[]); } // -- Deref trait -- @@ -341,10 +341,10 @@ mod tests { fn test_vector_slice_mut_new() { let mut data = [1, 2, 3, 4, 5]; let vslice: VectorSliceMut<'_, i32, Column> = VectorSliceMut::new(&mut data); - assert_eq!(vslice.slice, &mut [1, 2, 3, 4, 5]); + assert_eq!(vslice.elements, &mut [1, 2, 3, 4, 5]); // Mutate through the slice - vslice.slice[0] = 10; - assert_eq!(vslice.slice[0], 10); + vslice.elements[0] = 10; + assert_eq!(vslice.elements[0], 10); assert_eq!(data[0], 10); } @@ -353,12 +353,12 @@ mod tests { let mut data = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0), Complex::new(5.0, 6.0)]; let vslice: VectorSliceMut<'_, Complex, Column> = VectorSliceMut::new(&mut data); assert_eq!( - vslice.slice, + vslice.elements, &mut [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0), Complex::new(5.0, 6.0),] ); // Mutate through the slice - vslice.slice[0] = Complex::new(7.0, 8.0); - assert_eq!(vslice.slice[0], Complex::new(7.0, 8.0)); + vslice.elements[0] = Complex::new(7.0, 8.0); + assert_eq!(vslice.elements[0], Complex::new(7.0, 8.0)); } // -- from_range -- @@ -367,8 +367,8 @@ mod tests { let mut data = [10, 20, 30, 40, 50]; { let vslice: VectorSliceMut<'_, i32, Row> = VectorSliceMut::from_range(&mut data, 1..4); - assert_eq!(vslice.slice, &mut [20, 30, 40]); - vslice.slice[1] = 99; + assert_eq!(vslice.elements, &mut [20, 30, 40]); + vslice.elements[1] = 99; } assert_eq!(data, [10, 20, 99, 40, 50]); } @@ -379,8 +379,8 @@ mod tests { { let vslice: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut data, 0..3); - assert_eq!(vslice.slice, &mut [7, 8, 9]); - vslice.slice[2] = 42; + assert_eq!(vslice.elements, &mut [7, 8, 9]); + vslice.elements[2] = 42; } assert_eq!(data, [7, 8, 42]); } @@ -389,7 +389,7 @@ mod tests { fn test_vector_slice_mut_from_range_empty() { let mut data = [1, 2, 3]; let vslice: VectorSliceMut<'_, i32, Row> = VectorSliceMut::from_range(&mut data, 1..1); - assert_eq!(vslice.slice, &mut []); + assert_eq!(vslice.elements, &mut []); } #[test] @@ -405,10 +405,10 @@ mod tests { let vslice: VectorSliceMut<'_, Complex, Row> = VectorSliceMut::from_range(&mut data, 1..4); assert_eq!( - vslice.slice, + vslice.elements, &mut [Complex::new(20.0, 2.0), Complex::new(30.0, 3.0), Complex::new(40.0, 4.0),] ); - vslice.slice[2] = Complex::new(99.0, 99.0); + vslice.elements[2] = Complex::new(99.0, 99.0); } assert_eq!( data, @@ -429,10 +429,10 @@ mod tests { let vslice: VectorSliceMut<'_, Complex, Column> = VectorSliceMut::from_range(&mut data, 0..3); assert_eq!( - vslice.slice, + vslice.elements, &mut [Complex::new(7.0, 0.0), Complex::new(8.0, 1.0), Complex::new(9.0, 2.0),] ); - vslice.slice[1] = Complex::new(42.0, 24.0); + vslice.elements[1] = Complex::new(42.0, 24.0); } assert_eq!( data, @@ -445,7 +445,7 @@ mod tests { let mut data = [Complex::new(1.0, 1.0), Complex::new(2.0, 2.0), Complex::new(3.0, 3.0)]; let vslice: VectorSliceMut<'_, Complex, Row> = VectorSliceMut::from_range(&mut data, 1..1); - assert_eq!(vslice.slice, &mut []); + assert_eq!(vslice.elements, &mut []); } // -- Deref trait for VectorSliceMut -- From ed7582c7d5b3ce6ce06e8bf7d643d1a9a7798a07 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Mon, 26 May 2025 15:57:11 -0400 Subject: [PATCH 50/82] fix clippy lint into_iter => iter_mut --- src/types/vectorslice.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/types/vectorslice.rs b/src/types/vectorslice.rs index cdfaeb0..6543c2b 100644 --- a/src/types/vectorslice.rs +++ b/src/types/vectorslice.rs @@ -142,7 +142,7 @@ impl<'a, T, O> IntoIterator for VectorSliceMut<'a, T, O> { type IntoIter = std::slice::IterMut<'a, T>; fn into_iter(self) -> Self::IntoIter { - self.elements.into_iter() + self.elements.iter_mut() } } From 41dda9466ad0fe1adc68f59901251e9f9f3e1149 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Mon, 26 May 2025 16:17:06 -0400 Subject: [PATCH 51/82] Add Debug and Display impl for VectorSlice and VectorSliceMut --- src/types/vectorslice.rs | 154 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 150 insertions(+), 4 deletions(-) diff --git a/src/types/vectorslice.rs b/src/types/vectorslice.rs index 6543c2b..9813c19 100644 --- a/src/types/vectorslice.rs +++ b/src/types/vectorslice.rs @@ -1,6 +1,8 @@ //! VectorSlice types. use crate::types::orientation::Column; +use crate::types::traits::VectorOrientationName; +use std::fmt; use std::marker::PhantomData; // ///////////////////////////////// @@ -12,7 +14,7 @@ use std::marker::PhantomData; // ///////////////////////////////// /// ... -#[derive(Clone, Debug, PartialEq, Eq)] +#[derive(Clone, PartialEq, Eq)] pub struct VectorSlice<'a, T, O = Column> { /// ... pub elements: &'a [T], @@ -76,6 +78,29 @@ impl<'a, T, O> IntoIterator for &'a VectorSlice<'a, T, O> { } } +impl<'a, T, O> fmt::Display for VectorSlice<'a, T, O> +where + T: fmt::Debug, + O: VectorOrientationName + 'static, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{} VectorSlice {:?}", O::orientation_name(), self.elements) + } +} + +impl<'a, T, O> std::fmt::Debug for VectorSlice<'a, T, O> +where + T: std::fmt::Debug, + O: VectorOrientationName + 'static, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("VectorSlice") + .field("orientation", &O::orientation_name()) + .field("elements", &self.elements) + .finish() + } +} + // ///////////////////////////////// // ================================ // @@ -85,7 +110,6 @@ impl<'a, T, O> IntoIterator for &'a VectorSlice<'a, T, O> { // ///////////////////////////////// /// ... -#[derive(Debug)] pub struct VectorSliceMut<'a, T, O = Column> { /// ... pub elements: &'a mut [T], @@ -155,6 +179,29 @@ impl<'a, T, O> IntoIterator for &'a mut VectorSliceMut<'a, T, O> { } } +impl<'a, T, O> fmt::Display for VectorSliceMut<'a, T, O> +where + T: fmt::Debug, + O: VectorOrientationName + 'static, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{} VectorSliceMut {:?}", O::orientation_name(), self.elements) + } +} + +impl<'a, T, O> std::fmt::Debug for VectorSliceMut<'a, T, O> +where + T: std::fmt::Debug, + O: VectorOrientationName + 'static, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("VectorSliceMut") + .field("orientation", &O::orientation_name()) + .field("elements", &self.elements) + .finish() + } +} + #[cfg(test)] mod tests { use super::*; @@ -243,7 +290,7 @@ mod tests { assert_eq!(vslice.elements, &[]); } - // -- Deref trait -- + // -- Deref trait for VectorSlice -- #[test] fn test_vector_slice_deref_access() { let data = [1, 2, 3, 4, 5]; @@ -270,7 +317,7 @@ mod tests { assert!(vslice.contains(&Complex::new(3.0, 4.0))); } - // -- AsRef trait -- + // -- AsRef trait for VectorSlice -- #[test] fn test_vector_slice_as_ref_basic() { @@ -328,6 +375,55 @@ mod tests { ); } + // -- Display trait for VectorSlice -- + + #[test] + fn test_vector_slice_display() { + let data = [1, 2, 3]; + let vslice: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&data, 0..3); + let display = format!("{}", vslice); + // The exact string depends on your orientation name implementation + assert!(display.contains("Column VectorSlice")); + assert!(display.contains("[1, 2, 3]")); + } + + #[test] + fn test_vector_slice_display_complex() { + let data = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; + let vslice: VectorSlice<'_, Complex, Row> = VectorSlice::from_range(&data, 0..2); + let display = format!("{}", vslice); + assert!(display.contains("Row VectorSlice")); + assert!(display.contains("Complex { re: 1.0, im: 2.0 }")); + assert!(display.contains("Complex { re: 3.0, im: 4.0 }")); + } + + // -- Debug trait for VectorSlice -- + + #[test] + fn test_vector_slice_debug() { + let data = [1, 2, 3]; + let vslice: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&data, 0..3); + let debug = format!("{:?}", vslice); + assert!(debug.contains("VectorSlice")); + assert!(debug.contains("orientation")); + assert!(debug.contains("elements")); + assert!(debug.contains("1")); + assert!(debug.contains("2")); + assert!(debug.contains("3")); + } + + #[test] + fn test_vector_slice_debug_complex() { + let data = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; + let vslice: VectorSlice<'_, Complex, Row> = VectorSlice::from_range(&data, 0..2); + let debug = format!("{:?}", vslice); + assert!(debug.contains("VectorSlice")); + assert!(debug.contains("orientation")); + assert!(debug.contains("elements")); + assert!(debug.contains("Complex { re: 1.0, im: 2.0 }")); + assert!(debug.contains("Complex { re: 3.0, im: 4.0 }")); + } + // ///////////////////////////////// // ================================ // @@ -570,4 +666,54 @@ mod tests { } assert_eq!(data, [Complex::new(1.0, 3.0), Complex::new(3.0, 5.0), Complex::new(5.0, 7.0)]); } + + // -- Display trait for VectorSliceMut -- + + #[test] + fn test_vector_slice_mut_display() { + let mut data = [10, 20, 30]; + let vslice: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut data, 0..3); + let display = format!("{}", vslice); + assert!(display.contains("Column VectorSliceMut")); + assert!(display.contains("[10, 20, 30]")); + } + + #[test] + fn test_vector_slice_mut_display_complex() { + let mut data = [Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]; + let vslice: VectorSliceMut<'_, Complex, Row> = + VectorSliceMut::from_range(&mut data, 0..2); + let display = format!("{}", vslice); + assert!(display.contains("Row VectorSliceMut")); + assert!(display.contains("Complex { re: 5.0, im: 6.0 }")); + assert!(display.contains("Complex { re: 7.0, im: 8.0 }")); + } + + // -- Debug trait for VectorSliceMut -- + + #[test] + fn test_vector_slice_mut_debug() { + let mut data = [10, 20, 30]; + let vslice: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut data, 0..3); + let debug = format!("{:?}", vslice); + assert!(debug.contains("VectorSliceMut")); + assert!(debug.contains("orientation")); + assert!(debug.contains("elements")); + assert!(debug.contains("10")); + assert!(debug.contains("20")); + assert!(debug.contains("30")); + } + + #[test] + fn test_vector_slice_mut_debug_complex() { + let mut data = [Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]; + let vslice: VectorSliceMut<'_, Complex, Row> = + VectorSliceMut::from_range(&mut data, 0..2); + let debug = format!("{:?}", vslice); + assert!(debug.contains("VectorSliceMut")); + assert!(debug.contains("orientation")); + assert!(debug.contains("elements")); + assert!(debug.contains("Complex { re: 5.0, im: 6.0 }")); + assert!(debug.contains("Complex { re: 7.0, im: 8.0 }")); + } } From faa10d3d3596333f98bcd48ebbff8b4d6aec9c2c Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Mon, 26 May 2025 17:20:00 -0400 Subject: [PATCH 52/82] Add Hash, Ord, PartialOrd trait impl on VectorSlice type --- src/types/vectorslice.rs | 116 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/src/types/vectorslice.rs b/src/types/vectorslice.rs index 9813c19..0dd5585 100644 --- a/src/types/vectorslice.rs +++ b/src/types/vectorslice.rs @@ -101,6 +101,35 @@ where } } +impl<'a, T, O> std::hash::Hash for VectorSlice<'a, T, O> +where + T: std::hash::Hash, +{ + fn hash(&self, state: &mut H) { + self.elements.hash(state); + } +} + +impl<'a, T, O> Ord for VectorSlice<'a, T, O> +where + T: Ord, + O: Eq, +{ + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.elements.cmp(other.elements) + } +} + +impl<'a, T, O> PartialOrd for VectorSlice<'a, T, O> +where + T: PartialOrd, + O: PartialEq, +{ + fn partial_cmp(&self, other: &Self) -> Option { + self.elements.partial_cmp(other.elements) + } +} + // ///////////////////////////////// // ================================ // @@ -208,6 +237,8 @@ mod tests { use crate::prelude::{FlexVector, VectorBase}; use crate::types::orientation::{Column, Row}; use num::Complex; + use std::collections::hash_map::DefaultHasher; + use std::hash::{Hash, Hasher}; // ///////////////////////////////// // ================================ @@ -424,6 +455,91 @@ mod tests { assert!(debug.contains("Complex { re: 3.0, im: 4.0 }")); } + // -- Hash trait for VectorSlice -- + + #[test] + fn test_vector_slice_hash_basic() { + let data = [1, 2, 3, 4]; + let vslice1: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&data, 1..4); + let vslice2: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&data, 1..4); + + let mut hasher1 = DefaultHasher::new(); + vslice1.hash(&mut hasher1); + let hash1 = hasher1.finish(); + + let mut hasher2 = DefaultHasher::new(); + vslice2.hash(&mut hasher2); + let hash2 = hasher2.finish(); + + assert_eq!(hash1, hash2); + } + + #[test] + fn test_vector_slice_hash_different() { + let data = [1, 2, 3, 4]; + let vslice1: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&data, 0..3); + let vslice2: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&data, 1..4); + + let mut hasher1 = DefaultHasher::new(); + vslice1.hash(&mut hasher1); + let hash1 = hasher1.finish(); + + let mut hasher2 = DefaultHasher::new(); + vslice2.hash(&mut hasher2); + let hash2 = hasher2.finish(); + + assert_ne!(hash1, hash2); + } + + #[test] + fn test_vector_slice_hash_complex() { + let data = [Complex::new(1, 2), Complex::new(3, 4), Complex::new(5, 6)]; + let vslice1: VectorSlice<'_, Complex, Row> = VectorSlice::from_range(&data, 0..2); + let vslice2: VectorSlice<'_, Complex, Row> = VectorSlice::from_range(&data, 0..2); + + let mut hasher1 = DefaultHasher::new(); + vslice1.hash(&mut hasher1); + let hash1 = hasher1.finish(); + + let mut hasher2 = DefaultHasher::new(); + vslice2.hash(&mut hasher2); + let hash2 = hasher2.finish(); + + assert_eq!(hash1, hash2); + } + + // -- Ord / PartialOrd traits for VectorSlice -- + + #[test] + fn test_vector_slice_ord_basic() { + let data = [1, 2, 3, 4, 5]; + let vslice1: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&data, 1..4); // [2, 3, 4] + let vslice2: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&data, 2..5); // [3, 4, 5] + assert!(vslice1 < vslice2); + assert!(vslice2 > vslice1); + assert_eq!(vslice1, VectorSlice::from_range(&data, 1..4)); + } + + #[test] + fn test_vector_slice_ord_equal() { + let data = [10, 20, 30]; + let vslice1: VectorSlice<'_, i32, Row> = VectorSlice::from_range(&data, 0..3); + let vslice2: VectorSlice<'_, i32, Row> = VectorSlice::from_range(&data, 0..3); + assert_eq!(vslice1, vslice2); + assert!(vslice1 <= vslice2); + assert!(vslice1 >= vslice2); + } + + #[test] + fn test_vector_slice_partial_ord_f64() { + let data = [1.0, 2.0, 3.0, 4.0]; + let vslice1: VectorSlice<'_, f64, Column> = VectorSlice::from_range(&data, 0..3); // [1.0, 2.0, 3.0] + let vslice2: VectorSlice<'_, f64, Column> = VectorSlice::from_range(&data, 1..4); // [2.0, 3.0, 4.0] + assert!(vslice1 < vslice2); + assert!(vslice2 > vslice1); + assert_eq!(vslice1.partial_cmp(&vslice1), Some(std::cmp::Ordering::Equal)); + } + // ///////////////////////////////// // ================================ // From fcdec594c49fee6213aa313aaf394f66c9955c05 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Mon, 26 May 2025 19:45:11 -0400 Subject: [PATCH 53/82] refactor VectorBase and VectorOps* traits to immutable and mutable trait types --- src/lib.rs | 3 +- src/types/flexvector.rs | 223 +++++++++++++++++++++++----------------- src/types/traits.rs | 177 +++++++++++++++++-------------- 3 files changed, 230 insertions(+), 173 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 2466db5..f96f7b7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -908,7 +908,8 @@ pub use types::vector::Vector; pub mod prelude { // Traits pub use crate::types::traits::{ - Transposable, VectorBase, VectorHasOrientation, VectorOps, VectorOpsComplex, VectorOpsFloat, + Transposable, VectorBase, VectorBaseMut, VectorHasOrientation, VectorOps, VectorOpsComplex, + VectorOpsComplexMut, VectorOpsFloat, VectorOpsFloatMut, VectorOpsMut, }; // Types pub use crate::types::flexvector::FlexVector; diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index 0d1f3ed..ea589a3 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -14,8 +14,10 @@ use crate::{ impl_vector_binop_div_assign, impl_vector_scalar_div_op, impl_vector_scalar_div_op_assign, impl_vector_scalar_op, impl_vector_scalar_op_assign, impl_vector_unary_op, types::orientation::Column, types::orientation::Row, types::orientation::VectorOrientation, - types::traits::Transposable, types::traits::VectorBase, types::traits::VectorHasOrientation, - types::traits::VectorOps, types::traits::VectorOpsComplex, types::traits::VectorOpsFloat, + types::traits::Transposable, types::traits::VectorBase, types::traits::VectorBaseMut, + types::traits::VectorHasOrientation, types::traits::VectorOps, types::traits::VectorOpsComplex, + types::traits::VectorOpsComplexMut, types::traits::VectorOpsFloat, + types::traits::VectorOpsFloatMut, types::traits::VectorOpsMut, types::traits::VectorOrientationName, types::vectorslice::VectorSlice, types::vectorslice::VectorSliceMut, }; @@ -652,7 +654,7 @@ impl Extend for FlexVector { // ================================ // -// VectorBase trait impl +// VectorBase/VectorBaseMut trait impl // // ================================ impl VectorBase for FlexVector { @@ -661,7 +663,9 @@ impl VectorBase for FlexVector { fn as_slice(&self) -> &[T] { &self.elements } +} +impl VectorBaseMut for FlexVector { /// Returns a mutable slice of the FlexVector's elements. #[inline] fn as_mut_slice(&mut self) -> &mut [T] { @@ -735,16 +739,6 @@ where Ok(out) } - #[inline] - fn mut_translate(&mut self, other: &Self) -> Result<(), VectorError> - where - T: num::Num + Copy, - { - self.check_same_length_and_raise(other)?; - mut_translate_impl(self.as_mut_slice(), other.as_slice()); - Ok(()) - } - #[inline] fn scale(&self, scalar: T) -> Self::Output where @@ -834,30 +828,43 @@ where // ================================ // -// VectorOpsFloat trait impl +// VectorOpsMut trait impl // // ================================ -impl VectorOpsFloat for FlexVector +impl VectorOpsMut for FlexVector where - T: num::Float + Clone + std::iter::Sum, + T: Clone, { type Output = Self; #[inline] - fn normalize(&self) -> Result + fn mut_translate(&mut self, other: &Self) -> Result<(), VectorError> where - T: Copy + PartialEq + std::ops::Div + num::Zero, + T: num::Num + Copy, { - normalize_impl(self.as_slice(), self.norm()) + self.check_same_length_and_raise(other)?; + mut_translate_impl(self.as_mut_slice(), other.as_slice()); + Ok(()) } +} + +// ================================ +// +// VectorOpsFloat trait impl +// +// ================================ +impl VectorOpsFloat for FlexVector +where + T: num::Float + Clone + std::iter::Sum, +{ + type Output = Self; #[inline] - fn mut_normalize(&mut self) -> Result<(), VectorError> + fn normalize(&self) -> Result where T: Copy + PartialEq + std::ops::Div + num::Zero, { - let norm = self.norm(); - mut_normalize_impl(self.as_mut_slice(), norm) + normalize_impl(self.as_slice(), self.norm()) } /// Returns a new vector with the same direction and the given magnitude. @@ -874,19 +881,6 @@ where normalize_to_impl(self.as_slice(), self.norm(), magnitude) } - #[inline] - fn mut_normalize_to(&mut self, magnitude: T) -> Result<(), VectorError> - where - T: Copy - + PartialEq - + std::ops::Div - + std::ops::Mul - + num::Zero, - { - let n = self.norm(); - mut_normalize_to_impl(self.as_mut_slice(), n, magnitude) - } - #[inline] fn lerp(&self, end: &Self, weight: T) -> Result where @@ -901,19 +895,6 @@ where Ok(out) } - #[inline] - fn mut_lerp(&mut self, end: &Self, weight: T) -> Result<(), VectorError> - where - T: num::Float + Copy + PartialOrd, - { - self.check_same_length_and_raise(end)?; - if weight < T::zero() || weight > T::one() { - return Err(VectorError::OutOfRangeError("weight must be in [0, 1]".to_string())); - } - mut_lerp_impl(self.as_mut_slice(), end.as_slice(), weight); - Ok(()) - } - #[inline] fn midpoint(&self, other: &Self) -> Result where @@ -1014,6 +995,53 @@ where } } +// ================================ +// +// VectorOpsFloatMut trait impl +// +// ================================ +impl VectorOpsFloatMut for FlexVector +where + T: num::Float + Clone + std::iter::Sum, +{ + type Output = Self; + + #[inline] + fn mut_normalize(&mut self) -> Result<(), VectorError> + where + T: Copy + PartialEq + std::ops::Div + num::Zero, + { + let norm = self.norm(); + mut_normalize_impl(self.as_mut_slice(), norm) + } + + #[inline] + fn mut_normalize_to(&mut self, magnitude: T) -> Result<(), VectorError> + where + T: Copy + + PartialEq + + std::ops::Div + + std::ops::Mul + + num::Zero, + { + let n = self.norm(); + mut_normalize_to_impl(self.as_mut_slice(), n, magnitude) + } + + #[inline] + fn mut_lerp(&mut self, end: &Self, weight: T) -> Result<(), VectorError> + where + T: num::Float + Copy + PartialOrd, + { + self.check_same_length_and_raise(end)?; + if weight < T::zero() || weight > T::one() { + return Err(VectorError::OutOfRangeError("weight must be in [0, 1]".to_string())); + } + mut_lerp_impl(self.as_mut_slice(), end.as_slice(), weight); + Ok(()) + } +} + // ================================ // // VectorOpsComplex trait impl @@ -1035,15 +1063,6 @@ where normalize_impl(self.as_slice(), Complex::new(self.norm(), N::zero())) } - #[inline] - fn mut_normalize(&mut self) -> Result<(), VectorError> - where - Complex: Copy + PartialEq + std::ops::Div, Output = Complex> + num::Zero, - { - let norm = self.norm(); - mut_normalize_impl(self.as_mut_slice(), Complex::new(norm, N::zero())) - } - #[inline] fn normalize_to(&self, magnitude: N) -> Result where @@ -1061,23 +1080,6 @@ where ) } - #[inline] - fn mut_normalize_to(&mut self, magnitude: N) -> Result<(), VectorError> - where - Complex: Copy - + PartialEq - + std::ops::Div, Output = Complex> - + std::ops::Mul, Output = Complex> - + num::Zero, - { - let n = self.norm(); - mut_normalize_to_impl( - self.as_mut_slice(), - Complex::new(n, N::zero()), - Complex::new(magnitude, N::zero()), - ) - } - #[inline] fn dot(&self, other: &Self) -> Result, VectorError> where @@ -1107,25 +1109,6 @@ where Ok(out) } - #[inline] - fn mut_lerp(&mut self, end: &Self, weight: N) -> Result<(), VectorError> - where - N: num::Float + Copy + PartialOrd, - Complex: Copy - + std::ops::Add> - + std::ops::Mul> - + std::ops::Sub> - + num::One, - { - self.check_same_length_and_raise(end)?; - if weight < N::zero() || weight > N::one() { - return Err(VectorError::OutOfRangeError("weight must be in [0, 1]".to_string())); - } - let w = Complex::new(weight, N::zero()); - mut_lerp_impl(self.as_mut_slice(), end.as_slice(), w); - Ok(()) - } - #[inline] fn midpoint(&self, end: &Self) -> Result where @@ -1214,11 +1197,65 @@ where } } +// TODO: add tests +impl VectorOpsComplexMut for FlexVector, O> +where + N: num::Float + Clone + std::iter::Sum, +{ + type Output = Self; + + #[inline] + fn mut_normalize(&mut self) -> Result<(), VectorError> + where + Complex: Copy + PartialEq + std::ops::Div, Output = Complex> + num::Zero, + { + let norm = self.norm(); + mut_normalize_impl(self.as_mut_slice(), Complex::new(norm, N::zero())) + } + + #[inline] + fn mut_normalize_to(&mut self, magnitude: N) -> Result<(), VectorError> + where + Complex: Copy + + PartialEq + + std::ops::Div, Output = Complex> + + std::ops::Mul, Output = Complex> + + num::Zero, + { + let n = self.norm(); + mut_normalize_to_impl( + self.as_mut_slice(), + Complex::new(n, N::zero()), + Complex::new(magnitude, N::zero()), + ) + } + + #[inline] + fn mut_lerp(&mut self, end: &Self, weight: N) -> Result<(), VectorError> + where + N: num::Float + Copy + PartialOrd, + Complex: Copy + + std::ops::Add> + + std::ops::Mul> + + std::ops::Sub> + + num::One, + { + self.check_same_length_and_raise(end)?; + if weight < N::zero() || weight > N::one() { + return Err(VectorError::OutOfRangeError("weight must be in [0, 1]".to_string())); + } + let w = Complex::new(weight, N::zero()); + mut_lerp_impl(self.as_mut_slice(), end.as_slice(), w); + Ok(()) + } +} + // ================================ // // Methods // // ================================ + impl FlexVector { /// ... #[inline] diff --git a/src/types/traits.rs b/src/types/traits.rs index a000f41..97bca6e 100644 --- a/src/types/traits.rs +++ b/src/types/traits.rs @@ -12,9 +12,6 @@ pub trait VectorBase { /// ... fn as_slice(&self) -> &[T]; - /// ... - fn as_mut_slice(&mut self) -> &mut [T]; - /// ... #[inline] fn len(&self) -> usize { @@ -230,6 +227,13 @@ pub trait VectorBase { } } +/// ... +pub trait VectorBaseMut { + // --- Core accessors --- + /// ... + fn as_mut_slice(&mut self) -> &mut [T]; +} + /// A trait for types that can be transposed between row and column orientation. pub trait Transposable { /// The type returned by transposing. @@ -249,56 +253,18 @@ pub trait VectorOps: VectorBase { where T: num::Num + Copy; - /// ... - fn mut_translate(&mut self, other: &Self) -> Result<(), VectorError> - where - T: num::Num + Copy; - /// Returns a new vector scaled by the given scalar. fn scale(&self, scalar: T) -> Self::Output where T: num::Num + Copy, Self::Output: std::iter::FromIterator; - /// Scales the vector in place by the given scalar. - #[inline] - fn mut_scale(&mut self, scalar: T) - where - T: num::Num + Copy, - { - for a in self.as_mut_slice().iter_mut() { - *a = *a * scalar; - } - } - /// Returns a new vector with all elements negated. fn negate(&self) -> Self::Output where T: std::ops::Neg + Clone, Self::Output: std::iter::FromIterator; - /// Negates all elements in place. - #[inline] - fn mut_negate(&mut self) - where - T: std::ops::Neg + Clone, - { - for a in self.as_mut_slice().iter_mut() { - *a = -a.clone(); - } - } - - /// Sets all elements to zero in place. - #[inline] - fn mut_zero(&mut self) - where - T: num::Zero + Clone, - { - for a in self.as_mut_slice().iter_mut() { - *a = T::zero(); - } - } - /// ... fn dot(&self, other: &Self) -> Result where @@ -404,6 +370,49 @@ pub trait VectorOps: VectorBase { } } +/// ... +pub trait VectorOpsMut: VectorBaseMut { + /// ... + type Output; + + /// ... + fn mut_translate(&mut self, other: &Self) -> Result<(), VectorError> + where + T: num::Num + Copy; + + /// Scales the vector in place by the given scalar. + #[inline] + fn mut_scale(&mut self, scalar: T) + where + T: num::Num + Copy, + { + for a in self.as_mut_slice().iter_mut() { + *a = *a * scalar; + } + } + + /// Negates all elements in place. + #[inline] + fn mut_negate(&mut self) + where + T: std::ops::Neg + Clone, + { + for a in self.as_mut_slice().iter_mut() { + *a = -a.clone(); + } + } + + /// Sets all elements to zero in place. + #[inline] + fn mut_zero(&mut self) + where + T: num::Zero, + { + for a in self.as_mut_slice().iter_mut() { + *a = T::zero(); + } + } +} /// ... pub trait VectorOpsFloat: VectorBase { /// ... @@ -415,36 +424,17 @@ pub trait VectorOpsFloat: VectorBase { T: Copy + PartialEq + std::ops::Div, Self::Output: std::iter::FromIterator; - /// ... - fn mut_normalize(&mut self) -> Result<(), VectorError> - where - T: Copy + PartialEq + std::ops::Div; - /// Returns a new vector with the same direction and the given magnitude. fn normalize_to(&self, magnitude: T) -> Result where T: Copy + PartialEq + std::ops::Div + std::ops::Mul, Self::Output: std::iter::FromIterator; - /// ... - fn mut_normalize_to(&mut self, magnitude: T) -> Result<(), VectorError> - where - T: Copy - + PartialEq - + std::ops::Div - + std::ops::Mul - + num::Zero; - /// Linear interpolation between self and end by weight in [0, 1]. fn lerp(&self, end: &Self, weight: T) -> Result where T: num::Float + Clone + PartialOrd; - /// In-place linear interpolation between self and end by weight in [0, 1]. - fn mut_lerp(&mut self, end: &Self, weight: T) -> Result<(), VectorError> - where - T: num::Float + Copy + PartialOrd; - /// Midpoint fn midpoint(&self, other: &Self) -> Result where @@ -518,6 +508,30 @@ pub trait VectorOpsFloat: VectorBase { T: num::Float + Clone + std::iter::Sum + std::ops::Div; } +/// ... +pub trait VectorOpsFloatMut: VectorBaseMut { + /// ... + type Output; + + /// ... + fn mut_normalize(&mut self) -> Result<(), VectorError> + where + T: Copy + PartialEq + std::ops::Div; + + /// ... + fn mut_normalize_to(&mut self, magnitude: T) -> Result<(), VectorError> + where + T: Copy + + PartialEq + + std::ops::Div + + std::ops::Mul + + num::Zero; + + /// In-place linear interpolation between self and end by weight in [0, 1]. + fn mut_lerp(&mut self, end: &Self, weight: T) -> Result<(), VectorError> + where + T: num::Float + Copy + PartialOrd; +} /// ... pub trait VectorOpsComplex: VectorBase> { /// ... @@ -529,11 +543,6 @@ pub trait VectorOpsComplex: VectorBase> { Complex: Copy + PartialEq + std::ops::Div, Output = Complex>, Self::Output: std::iter::FromIterator>; - /// ... - fn mut_normalize(&mut self) -> Result<(), VectorError> - where - Complex: Copy + PartialEq + std::ops::Div, Output = Complex>; - /// Returns a new vector with the same direction and the given magnitude (real). fn normalize_to(&self, magnitude: N) -> Result where @@ -543,15 +552,6 @@ pub trait VectorOpsComplex: VectorBase> { + std::ops::Mul, Output = Complex>, Self::Output: std::iter::FromIterator>; - /// Scales the complex vector in place to the given (real) magnitude. - fn mut_normalize_to(&mut self, magnitude: N) -> Result<(), VectorError> - where - Complex: Copy - + PartialEq - + std::ops::Div, Output = Complex> - + std::ops::Mul, Output = Complex> - + num::Zero; - /// Hermitian dot product: for all complex types fn dot(&self, other: &Self) -> Result, VectorError> where @@ -562,11 +562,6 @@ pub trait VectorOpsComplex: VectorBase> { where N: num::Float + Clone + PartialOrd; - /// In-place linear interpolation between self and end by real weight in [0, 1]. - fn mut_lerp(&mut self, end: &Self, weight: N) -> Result<(), VectorError> - where - N: num::Float + Copy + PartialOrd; - /// Midpoint fn midpoint(&self, end: &Self) -> Result where @@ -653,6 +648,30 @@ pub trait VectorOpsComplex: VectorBase> { Complex: std::ops::Div>; } +/// ... +pub trait VectorOpsComplexMut: VectorBaseMut> { + /// ... + type Output; + + /// ... + fn mut_normalize(&mut self) -> Result<(), VectorError> + where + Complex: Copy + PartialEq + std::ops::Div, Output = Complex>; + + /// Scales the complex vector in place to the given (real) magnitude. + fn mut_normalize_to(&mut self, magnitude: N) -> Result<(), VectorError> + where + Complex: Copy + + PartialEq + + std::ops::Div, Output = Complex> + + std::ops::Mul, Output = Complex> + + num::Zero; + + /// In-place linear interpolation between self and end by real weight in [0, 1]. + fn mut_lerp(&mut self, end: &Self, weight: N) -> Result<(), VectorError> + where + N: num::Float + Copy + PartialOrd; +} /// ... pub trait VectorHasOrientation { /// ... From eb7a7f4246b0d3255fecf7738f8e5c23489894ba Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Mon, 26 May 2025 20:24:42 -0400 Subject: [PATCH 54/82] fix the Hermitian dot product utility impl --- src/types/utils.rs | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/src/types/utils.rs b/src/types/utils.rs index 9198f68..4e6bc0d 100644 --- a/src/types/utils.rs +++ b/src/types/utils.rs @@ -39,7 +39,7 @@ pub(crate) fn hermitian_dot_impl(a: &[Complex], b: &[Complex]) -> Compl where N: num::Num + Copy + std::iter::Sum + std::ops::Neg, { - a.iter().zip(b.iter()).map(|(x, y)| *x * y.conj()).sum() + a.iter().zip(b.iter()).map(|(x, y)| x.conj() * *y).sum() } #[inline] @@ -381,21 +381,21 @@ mod tests { fn test_hermitian_dot_impl_basic() { let a = [Complex::new(1.0_f64, 2.0), Complex::new(3.0, 4.0)]; let b = [Complex::new(5.0, -1.0), Complex::new(-2.0, 2.0)]; - // Hermitian dot: sum_i a_i * conj(b_i) - // conj(b_0) = 5.0 + 1.0i, conj(b_1) = -2.0 - 2.0i - // a_0 * conj(b_0) = (1+2i)*(5+1i) = 1*5 + 1*1i + 2i*5 + 2i*1i = 5 + 1i + 10i + 2i^2 = 5 + 11i - 2 = 3 + 11i - // a_1 * conj(b_1) = (3+4i)*(-2-2i) = 3*-2 + 3*-2i + 4i*-2 + 4i*-2i = -6 -6i -8i -8i^2 = -6 -14i +8 = 2 -14i - // sum = (3+11i) + (2-14i) = 5 -3i - let result = super::hermitian_dot_impl(&a, &b); - assert!((result.re - 5.0).abs() < 1e-12); - assert!((result.im + 3.0).abs() < 1e-12); + // Hermitian dot: sum_i conj(a_i) * b_i + // conj(a_0) = 1.0 - 2.0i, conj(a_1) = 3.0 - 4.0i + // conj(a_0) * b_0 = (1-2i)*(5-1i) = 1*5 + 1*-1i -2i*5 -2i*-1i = 5 -1i -10i +2i^2 = 5 -11i +2*(-1) = 5 -11i -2 = 3 -11i + // conj(a_1) * b_1 = (3-4i)*(-2+2i) = 3*-2 + 3*2i -4i*-2 -4i*2i = -6 +6i +8i -8i^2 = -6 +14i +8 = 2 +14i + // sum = (3-11i) + (2+14i) = 5 + 3i + let result = hermitian_dot_impl(&a, &b); + assert!((result.re - 5.0).abs() < 1e-12, "real part: got {}, expected 5.0", result.re); + assert!((result.im - 3.0).abs() < 1e-12, "imag part: got {}, expected 3.0", result.im); } #[test] fn test_hermitian_dot_impl_empty() { let a: [Complex; 0] = []; let b: [Complex; 0] = []; - let result = super::hermitian_dot_impl(&a, &b); + let result = hermitian_dot_impl(&a, &b); assert_eq!(result, Complex::new(0.0, 0.0)); } @@ -403,19 +403,18 @@ mod tests { fn test_hermitian_dot_impl_partial() { let a = [Complex::new(1.0_f64, 2.0), Complex::new(3.0, 4.0)]; let b = [Complex::new(5.0, -1.0)]; - // Only the first element is used - let result = super::hermitian_dot_impl(&a, &b); - // a_0 * conj(b_0) = (1+2i)*(5+1i) = 3 + 11i (see above) - assert!((result.re - 3.0).abs() < 1e-12); - assert!((result.im - 11.0).abs() < 1e-12); + // Only the first element is used: conj(a_0) * b_0 = (1-2i)*(5-1i) = 3 - 11i + let result = hermitian_dot_impl(&a, &b); + assert!((result.re - 3.0).abs() < 1e-12, "real part: got {}, expected 3.0", result.re); + assert!((result.im + 11.0).abs() < 1e-12, "imag part: got {}, expected -11.0", result.im); } #[test] fn test_hermitian_dot_impl_identical() { let a = [Complex::new(2.0_f64, -3.0), Complex::new(-1.0, 4.0)]; let b = [Complex::new(2.0, -3.0), Complex::new(-1.0, 4.0)]; - // Hermitian dot with self: sum_i a_i * conj(a_i) = sum_i |a_i|^2 (real, >= 0) - let result = super::hermitian_dot_impl(&a, &b); + // Hermitian dot with self: sum_i conj(a_i) * a_i = sum_i |a_i|^2 (real, >= 0) + let result = hermitian_dot_impl(&a, &b); let expected = Complex::new((2.0 * 2.0 + (-3.0) * (-3.0)) + ((-1.0) * (-1.0) + 4.0 * 4.0), 0.0); assert!((result.re - expected.re).abs() < 1e-12); From eabfdd2f603903dfff84523d25d4814721e844ae Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Mon, 26 May 2025 23:30:14 -0400 Subject: [PATCH 55/82] complete unit tests on the FlexVector type --- src/types/flexvector.rs | 767 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 764 insertions(+), 3 deletions(-) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index ea589a3..bf7fac4 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -1047,7 +1047,7 @@ where // VectorOpsComplex trait impl // // ================================ -// TODO: add tests + impl VectorOpsComplex for FlexVector, O> where N: num::Float + Clone + std::iter::Sum, @@ -1197,7 +1197,6 @@ where } } -// TODO: add tests impl VectorOpsComplexMut for FlexVector, O> where N: num::Float + Clone + std::iter::Sum, @@ -5519,6 +5518,77 @@ mod tests { assert_eq!(&*rc, &[7, 8, 9]); } + // -- as_vslice -- + + #[test] + fn test_as_vslice_basic() { + let v: FlexVector = FlexVector::from_vec(vec![10, 20, 30, 40, 50]); + let vslice = v.as_vslice(1..4); + assert_eq!(vslice.as_slice(), &[20, 30, 40]); + } + + #[test] + fn test_as_vslice_full_range() { + let v: FlexVector = FlexVector::from_vec(vec![1, 2, 3]); + let vslice = v.as_vslice(0..v.len()); + assert_eq!(vslice.as_slice(), &[1, 2, 3]); + } + + #[test] + fn test_as_vslice_empty_range() { + let v: FlexVector = FlexVector::from_vec(vec![1, 2, 3]); + let vslice = v.as_vslice(1..1); + assert_eq!(vslice.as_slice(), &[]); + } + + #[test] + #[should_panic] + fn test_as_vslice_out_of_bounds() { + let v: FlexVector = FlexVector::from_vec(vec![1, 2, 3]); + // This should panic because the range is out of bounds + let _ = v.as_vslice(2..5); + } + + // -- as_mut_vslice -- + + #[test] + fn test_as_mut_vslice_basic() { + let mut v: FlexVector = FlexVector::from_vec(vec![10, 20, 30, 40, 50]); + { + let mut vslice = v.as_mut_vslice(1..4); + vslice.as_mut_slice()[0] = 21; + vslice.as_mut_slice()[2] = 41; + } + assert_eq!(v.as_slice(), &[10, 21, 30, 41, 50]); + } + + #[test] + fn test_as_mut_vslice_full_range() { + let mut v: FlexVector = FlexVector::from_vec(vec![1, 2, 3]); + { + let mut vslice = v.as_mut_vslice(0..v.len()); + for x in vslice.as_mut_slice() { + *x *= 2; + } + } + assert_eq!(v.as_slice(), &[2, 4, 6]); + } + + #[test] + fn test_as_mut_vslice_empty_range() { + let mut v: FlexVector = FlexVector::from_vec(vec![1, 2, 3]); + let mut vslice = v.as_mut_vslice(1..1); + assert_eq!(vslice.as_mut_slice(), &mut []); + } + + #[test] + #[should_panic] + fn test_as_mut_vslice_out_of_bounds() { + let mut v: FlexVector = FlexVector::from_vec(vec![1, 2, 3]); + // This should panic because the range is out of bounds + let _ = v.as_mut_vslice(2..5); + } + // ================================ // // VectorOps trait tests @@ -6861,7 +6931,698 @@ mod tests { assert!(normalized.is_err()); } - //TODO: continue tests for all methods in VectorOpsComplex trait + // -- normalize_to -- + + #[test] + fn test_normalize_to_complex_f64_basic() { + use num::Complex; + let v: FlexVector, Column> = + FlexVector::from_vec(vec![Complex::new(3.0, 4.0)]); + let normalized = v.normalize_to(10.0).unwrap(); + // The original norm is 5.0, so the normalized vector should be [6.0 + 8.0i] + assert!((normalized.as_slice()[0].re - 6.0).abs() < 1e-12); + assert!((normalized.as_slice()[0].im - 8.0).abs() < 1e-12); + } + + #[test] + fn test_normalize_to_complex_f64_multiple_elements() { + use num::Complex; + let v: FlexVector, Column> = + FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let norm = ((1.0 * 1.0 + 2.0 * 2.0) + (3.0 * 3.0 + 4.0 * 4.0)).sqrt(); + let normalized = v.normalize_to(2.0).unwrap(); + assert!((normalized.as_slice()[0].re - 1.0 / norm * 2.0).abs() < 1e-12); + assert!((normalized.as_slice()[0].im - 2.0 / norm * 2.0).abs() < 1e-12); + assert!((normalized.as_slice()[1].re - 3.0 / norm * 2.0).abs() < 1e-12); + assert!((normalized.as_slice()[1].im - 4.0 / norm * 2.0).abs() < 1e-12); + } + + #[test] + fn test_normalize_to_complex_f64_zero_vector() { + use num::Complex; + let v: FlexVector, Row> = FlexVector::from_vec(vec![Complex::new(0.0, 0.0)]); + let result = v.normalize_to(1.0); + assert!(result.is_err()); + } + + #[test] + fn test_normalize_to_complex_f64_empty() { + use num::Complex; + let v: FlexVector> = FlexVector::new(); + let normalized = v.normalize_to(1.0); + assert!(normalized.is_err()); + } + + // -- dot -- + + #[test] + fn test_dot_complex_f64_basic() { + let v1: FlexVector, Column> = + FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); + // Hermitian dot product: conj(v1[0])*v2[0] + conj(v1[1])*v2[1] + let expected = + v1.as_slice()[0].conj() * v2.as_slice()[0] + v1.as_slice()[1].conj() * v2.as_slice()[1]; + let dot = VectorOpsComplex::dot(&v1, &v2).unwrap(); + assert!((dot - expected).norm() < 1e-12); + } + + #[test] + fn test_dot_complex_f64_negative_values() { + let v1: FlexVector, Row> = + FlexVector::from_vec(vec![Complex::new(-1.0, -2.0), Complex::new(-3.0, -4.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(2.0, 1.0), Complex::new(4.0, 3.0)]); + let expected = + v1.as_slice()[0].conj() * v2.as_slice()[0] + v1.as_slice()[1].conj() * v2.as_slice()[1]; + let dot = VectorOpsComplex::dot(&v1, &v2).unwrap(); + assert!((dot - expected).norm() < 1e-12); + } + + #[test] + fn test_dot_complex_f64_zero_vector() { + let v1: FlexVector, Column> = + FlexVector::from_vec(vec![Complex::new(0.0, 0.0), Complex::new(0.0, 0.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let dot = VectorOpsComplex::dot(&v1, &v2).unwrap(); + assert!((dot - Complex::new(0.0, 0.0)).norm() < 1e-12); + } + + #[test] + fn test_dot_complex_f64_empty() { + let v1: FlexVector> = FlexVector::new(); + let v2: FlexVector> = FlexVector::new(); + let dot = VectorOpsComplex::dot(&v1, &v2).unwrap(); + assert!((dot - Complex::new(0.0, 0.0)).norm() < 1e-12); + } + + #[test] + fn test_dot_complex_f64_mismatched_length() { + let v1: FlexVector, Column> = + FlexVector::from_vec(vec![Complex::new(1.0, 2.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(3.0, 4.0), Complex::new(5.0, 6.0)]); + let result = VectorOpsComplex::dot(&v1, &v2); + assert!(result.is_err()); + } + + // -- lerp -- + + #[test] + fn test_lerp_complex_f64_weight_zero() { + let v1: FlexVector, Column> = + FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); + let result = v1.lerp(&v2, 0.0).unwrap(); + // Should be equal to v1 + assert!((result.as_slice()[0] - Complex::new(1.0, 2.0)).norm() < 1e-12); + assert!((result.as_slice()[1] - Complex::new(3.0, 4.0)).norm() < 1e-12); + } + + #[test] + fn test_lerp_complex_f64_weight_one() { + let v1: FlexVector, Row> = + FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); + let result = v1.lerp(&v2, 1.0).unwrap(); + // Should be equal to v2 + assert!((result.as_slice()[0] - Complex::new(5.0, 6.0)).norm() < 1e-12); + assert!((result.as_slice()[1] - Complex::new(7.0, 8.0)).norm() < 1e-12); + } + + #[test] + fn test_lerp_complex_f64_weight_half() { + let v1: FlexVector, Column> = + FlexVector::from_vec(vec![Complex::new(2.0, 4.0), Complex::new(6.0, 8.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(4.0, 6.0), Complex::new(8.0, 10.0)]); + let result = v1.lerp(&v2, 0.5).unwrap(); + // Should be the midpoint + assert!((result.as_slice()[0] - Complex::new(3.0, 5.0)).norm() < 1e-12); + assert!((result.as_slice()[1] - Complex::new(7.0, 9.0)).norm() < 1e-12); + } + + #[test] + fn test_lerp_complex_f64_weight_out_of_bounds() { + let v1: FlexVector, Column> = + FlexVector::from_vec(vec![Complex::new(1.0, 2.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(3.0, 4.0)]); + let result_low = v1.lerp(&v2, -0.1); + let result_high = v1.lerp(&v2, 1.1); + assert!(result_low.is_err()); + assert!(result_high.is_err()); + } + + #[test] + fn test_lerp_complex_f64_mismatched_length() { + let v1: FlexVector, Column> = + FlexVector::from_vec(vec![Complex::new(1.0, 2.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(3.0, 4.0), Complex::new(5.0, 6.0)]); + let result = v1.lerp(&v2, 0.5); + assert!(result.is_err()); + } + + // -- midpoint -- + + #[test] + fn test_midpoint_complex_f64_basic() { + let v1: FlexVector, Column> = + FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); + let midpoint = v1.midpoint(&v2).unwrap(); + assert!((midpoint.as_slice()[0] - Complex::new(3.0, 4.0)).norm() < 1e-12); + assert!((midpoint.as_slice()[1] - Complex::new(5.0, 6.0)).norm() < 1e-12); + } + + #[test] + fn test_midpoint_complex_f64_negative_values() { + let v1: FlexVector, Row> = + FlexVector::from_vec(vec![Complex::new(-1.0, -2.0), Complex::new(-3.0, -4.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let midpoint = v1.midpoint(&v2).unwrap(); + assert!((midpoint.as_slice()[0] - Complex::new(0.0, 0.0)).norm() < 1e-12); + assert!((midpoint.as_slice()[1] - Complex::new(0.0, 0.0)).norm() < 1e-12); + } + + #[test] + fn test_midpoint_complex_f64_identical() { + let v1: FlexVector, Column> = + FlexVector::from_vec(vec![Complex::new(2.0, 3.0), Complex::new(4.0, 5.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(2.0, 3.0), Complex::new(4.0, 5.0)]); + let midpoint = v1.midpoint(&v2).unwrap(); + assert!((midpoint.as_slice()[0] - Complex::new(2.0, 3.0)).norm() < 1e-12); + assert!((midpoint.as_slice()[1] - Complex::new(4.0, 5.0)).norm() < 1e-12); + } + + #[test] + fn test_midpoint_complex_f64_mismatched_length() { + let v1: FlexVector, Row> = FlexVector::from_vec(vec![Complex::new(1.0, 2.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(3.0, 4.0), Complex::new(5.0, 6.0)]); + let result = v1.midpoint(&v2); + assert!(result.is_err()); + } + + // -- distance -- + + #[test] + fn test_distance_complex_f64_basic() { + let v1: FlexVector, Column> = + FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); + // Euclidean distance: sqrt(sum_i |v1[i] - v2[i]|^2) + let d0 = (v1.as_slice()[0] - v2.as_slice()[0]).norm_sqr(); + let d1 = (v1.as_slice()[1] - v2.as_slice()[1]).norm_sqr(); + let expected = (d0 + d1).sqrt(); + let dist = v1.distance(&v2).unwrap(); + assert!((dist - expected).abs() < 1e-12); + } + + #[test] + fn test_distance_complex_f64_zero() { + let v1: FlexVector, Row> = + FlexVector::from_vec(vec![Complex::new(0.0, 0.0), Complex::new(0.0, 0.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(0.0, 0.0), Complex::new(0.0, 0.0)]); + let dist = v1.distance(&v2).unwrap(); + assert_eq!(dist, 0.0); + } + + #[test] + fn test_distance_complex_f64_identical() { + let v1: FlexVector, Column> = + FlexVector::from_vec(vec![Complex::new(2.0, 3.0), Complex::new(4.0, 5.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(2.0, 3.0), Complex::new(4.0, 5.0)]); + let dist = v1.distance(&v2).unwrap(); + assert_eq!(dist, 0.0); + } + + #[test] + fn test_distance_complex_f64_negative_values() { + let v1: FlexVector, Row> = + FlexVector::from_vec(vec![Complex::new(-1.0, -2.0), Complex::new(-3.0, -4.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let d0 = (v1.as_slice()[0] - v2.as_slice()[0]).norm_sqr(); + let d1 = (v1.as_slice()[1] - v2.as_slice()[1]).norm_sqr(); + let expected = (d0 + d1).sqrt(); + let dist = v1.distance(&v2).unwrap(); + assert!((dist - expected).abs() < 1e-12); + } + + #[test] + fn test_distance_complex_f64_mismatched_length() { + let v1: FlexVector, Column> = + FlexVector::from_vec(vec![Complex::new(1.0, 2.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(3.0, 4.0), Complex::new(5.0, 6.0)]); + let result = v1.distance(&v2); + assert!(result.is_err()); + } + + // -- manhattan_distance -- + + #[test] + fn test_manhattan_distance_complex_f64_basic() { + let v1: FlexVector, Column> = + FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); + // Manhattan distance: sum_i |v1[i] - v2[i]| + let d0 = (v1.as_slice()[0] - v2.as_slice()[0]).norm(); + let d1 = (v1.as_slice()[1] - v2.as_slice()[1]).norm(); + let expected = d0 + d1; + let dist = v1.manhattan_distance(&v2).unwrap(); + assert!((dist - expected).abs() < 1e-12); + } + + #[test] + fn test_manhattan_distance_complex_f64_zero() { + let v1: FlexVector, Row> = + FlexVector::from_vec(vec![Complex::new(0.0, 0.0), Complex::new(0.0, 0.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(0.0, 0.0), Complex::new(0.0, 0.0)]); + let dist = v1.manhattan_distance(&v2).unwrap(); + assert_eq!(dist, 0.0); + } + + #[test] + fn test_manhattan_distance_complex_f64_identical() { + let v1: FlexVector, Column> = + FlexVector::from_vec(vec![Complex::new(2.0, 3.0), Complex::new(4.0, 5.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(2.0, 3.0), Complex::new(4.0, 5.0)]); + let dist = v1.manhattan_distance(&v2).unwrap(); + assert_eq!(dist, 0.0); + } + + #[test] + fn test_manhattan_distance_complex_f64_negative_values() { + let v1: FlexVector, Column> = + FlexVector::from_vec(vec![Complex::new(-1.0, -2.0), Complex::new(-3.0, -4.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let d0 = (v1.as_slice()[0] - v2.as_slice()[0]).norm(); + let d1 = (v1.as_slice()[1] - v2.as_slice()[1]).norm(); + let expected = d0 + d1; + let dist = v1.manhattan_distance(&v2).unwrap(); + assert!((dist - expected).abs() < 1e-12); + } + + #[test] + fn test_manhattan_distance_complex_f64_mismatched_length() { + let v1: FlexVector, Column> = + FlexVector::from_vec(vec![Complex::new(1.0, 2.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(3.0, 4.0), Complex::new(5.0, 6.0)]); + let result = v1.manhattan_distance(&v2); + assert!(result.is_err()); + } + + // -- chebyshev_distance -- + + #[test] + fn test_chebyshev_distance_complex_f64_basic() { + let v1: FlexVector, Column> = + FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); + // Chebyshev distance: max_i |v1[i] - v2[i]| + let d0 = (v1.as_slice()[0] - v2.as_slice()[0]).norm(); + let d1 = (v1.as_slice()[1] - v2.as_slice()[1]).norm(); + let expected = d0.max(d1); + let dist = v1.chebyshev_distance(&v2).unwrap(); + assert!((dist - expected).abs() < 1e-12); + } + + #[test] + fn test_chebyshev_distance_complex_f64_zero() { + let v1: FlexVector, Row> = + FlexVector::from_vec(vec![Complex::new(0.0, 0.0), Complex::new(0.0, 0.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(0.0, 0.0), Complex::new(0.0, 0.0)]); + let dist = v1.chebyshev_distance(&v2).unwrap(); + assert_eq!(dist, 0.0); + } + + #[test] + fn test_chebyshev_distance_complex_f64_identical() { + let v1: FlexVector, Column> = + FlexVector::from_vec(vec![Complex::new(2.0, 3.0), Complex::new(4.0, 5.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(2.0, 3.0), Complex::new(4.0, 5.0)]); + let dist = v1.chebyshev_distance(&v2).unwrap(); + assert_eq!(dist, 0.0); + } + + #[test] + fn test_chebyshev_distance_complex_f64_negative_values() { + let v1: FlexVector, Row> = + FlexVector::from_vec(vec![Complex::new(-1.0, -2.0), Complex::new(-3.0, -4.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let d0 = (v1.as_slice()[0] - v2.as_slice()[0]).norm(); + let d1 = (v1.as_slice()[1] - v2.as_slice()[1]).norm(); + let expected = d0.max(d1); + let dist = v1.chebyshev_distance(&v2).unwrap(); + assert!((dist - expected).abs() < 1e-12); + } + + #[test] + fn test_chebyshev_distance_complex_f64_mismatched_length() { + let v1: FlexVector, Column> = + FlexVector::from_vec(vec![Complex::new(1.0, 2.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(3.0, 4.0), Complex::new(5.0, 6.0)]); + let result = v1.chebyshev_distance(&v2); + assert!(result.is_err()); + } + + // -- minkowski_distance -- + + #[test] + fn test_minkowski_distance_complex_f64_basic() { + let v1: FlexVector, Column> = + FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); + // For p = 3.0: ((|v1[0]-v2[0]|^3 + |v1[1]-v2[1]|^3))^(1/3) + let p = 3.0; + let d0 = (v1.as_slice()[0] - v2.as_slice()[0]).norm().powf(p); + let d1 = (v1.as_slice()[1] - v2.as_slice()[1]).norm().powf(p); + let expected = (d0 + d1).powf(1.0 / p); + let dist = v1.minkowski_distance(&v2, p).unwrap(); + assert!((dist - expected).abs() < 1e-12); + } + + #[test] + fn test_minkowski_distance_complex_f64_p1() { + let v1: FlexVector, Row> = + FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); + // Should match manhattan distance + let p = 1.0; + let d0 = (v1.as_slice()[0] - v2.as_slice()[0]).norm(); + let d1 = (v1.as_slice()[1] - v2.as_slice()[1]).norm(); + let expected = d0 + d1; + let dist = v1.minkowski_distance(&v2, p).unwrap(); + assert!((dist - expected).abs() < 1e-12); + } + + #[test] + fn test_minkowski_distance_complex_f64_p2() { + let v1: FlexVector, Column> = + FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); + // Should match euclidean distance + let p = 2.0; + let d0 = (v1.as_slice()[0] - v2.as_slice()[0]).norm_sqr(); + let d1 = (v1.as_slice()[1] - v2.as_slice()[1]).norm_sqr(); + let expected = (d0 + d1).sqrt(); + let dist = v1.minkowski_distance(&v2, p).unwrap(); + assert!((dist - expected).abs() < 1e-12); + } + + #[test] + fn test_minkowski_distance_complex_f64_empty() { + let v1: FlexVector> = FlexVector::new(); + let v2: FlexVector> = FlexVector::new(); + let dist = v1.minkowski_distance(&v2, 2.0).unwrap(); + assert_eq!(dist, 0.0); + } + + #[test] + fn test_minkowski_distance_complex_f64_identical() { + let v1: FlexVector, Column> = + FlexVector::from_vec(vec![Complex::new(1.23, 4.56), Complex::new(7.89, 0.12)]); + let v2 = FlexVector::from_vec(vec![Complex::new(1.23, 4.56), Complex::new(7.89, 0.12)]); + let dist = v1.minkowski_distance(&v2, 2.0).unwrap(); + assert_eq!(dist, 0.0); + } + + #[test] + fn test_minkowski_distance_complex_f64_partial() { + let v1: FlexVector, Row> = + FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0)]); + let result = v1.minkowski_distance(&v2, 2.0); + assert!(result.is_err()); + } + + #[test] + fn test_minkowski_distance_complex_f64_invalid_p() { + let v1: FlexVector, Column> = + FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); + let result = v1.minkowski_distance(&v2, 0.5); + assert!(result.is_err()); + } + + // -- project_onto -- + + #[test] + fn test_project_onto_complex_f64_basic() { + let v1: FlexVector, Column> = + FlexVector::from_vec(vec![Complex::new(3.0, 4.0), Complex::new(0.0, 0.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(1.0, 0.0), Complex::new(0.0, 0.0)]); + // Project v1 onto v2: should be [3.0 - 4.0i, 0.0] + let proj = v1.project_onto(&v2).unwrap(); + assert!((proj.as_slice()[0] - Complex::new(3.0, -4.0)).norm() < 1e-12); + assert!((proj.as_slice()[1] - Complex::new(0.0, 0.0)).norm() < 1e-12); + } + + #[test] + fn test_project_onto_complex_f64_parallel() { + let v1: FlexVector, Row> = + FlexVector::from_vec(vec![Complex::new(2.0, 2.0), Complex::new(4.0, 4.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(1.0, 1.0), Complex::new(2.0, 2.0)]); + // v1 is parallel to v2, so projection should be v1 itself + let proj = v1.project_onto(&v2).unwrap(); + assert!((proj.as_slice()[0] - Complex::new(2.0, 2.0)).norm() < 1e-12); + assert!((proj.as_slice()[1] - Complex::new(4.0, 4.0)).norm() < 1e-12); + } + + #[test] + fn test_project_onto_complex_f64_orthogonal() { + let v1: FlexVector, Column> = + FlexVector::from_vec(vec![Complex::new(0.0, 1.0), Complex::new(0.0, 0.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(1.0, 0.0), Complex::new(0.0, 0.0)]); + // Hermitian projection: should be [0.0 - 1.0i, 0.0] + let proj = v1.project_onto(&v2).unwrap(); + assert!((proj.as_slice()[0] - Complex::new(0.0, -1.0)).norm() < 1e-12); + assert!((proj.as_slice()[1] - Complex::new(0.0, 0.0)).norm() < 1e-12); + } + + #[test] + fn test_project_onto_complex_f64_identical() { + let v1: FlexVector, Row> = + FlexVector::from_vec(vec![Complex::new(5.0, 5.0), Complex::new(5.0, 5.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 5.0), Complex::new(5.0, 5.0)]); + let proj = v1.project_onto(&v2).unwrap(); + assert!((proj.as_slice()[0] - Complex::new(5.0, 5.0)).norm() < 1e-12); + assert!((proj.as_slice()[1] - Complex::new(5.0, 5.0)).norm() < 1e-12); + } + + #[test] + fn test_project_onto_complex_f64_zero_vector() { + let v1: FlexVector, Column> = + FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(0.0, 0.0), Complex::new(0.0, 0.0)]); + let result = v1.project_onto(&v2); + assert!(result.is_err()); + } + + #[test] + fn test_project_onto_complex_f64_mismatched_length() { + let v1: FlexVector, Row> = FlexVector::from_vec(vec![Complex::new(1.0, 2.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(3.0, 4.0), Complex::new(5.0, 6.0)]); + let result = v1.project_onto(&v2); + assert!(result.is_err()); + } + + // -- cosine_similarity -- + + #[test] + fn test_cosine_similarity_complex_f64_parallel() { + let v1: FlexVector, Column> = + FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(2.0, 4.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(2.0, 4.0), Complex::new(4.0, 8.0)]); + // v2 is a scalar multiple of v1, so cosine similarity should be 1+0i + let cos_sim = v1.cosine_similarity(&v2).unwrap(); + assert!((cos_sim - Complex::new(1.0, 0.0)).norm() < 1e-12); + } + + #[test] + fn test_cosine_similarity_complex_f64_orthogonal() { + let v1: FlexVector, Column> = + FlexVector::from_vec(vec![Complex::new(1.0, 0.0), Complex::new(0.0, 0.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(0.0, 0.0), Complex::new(1.0, 0.0)]); + let cos_sim = v1.cosine_similarity(&v2).unwrap(); + assert!((cos_sim - Complex::new(0.0, 0.0)).norm() < 1e-12); + } + + #[test] + fn test_cosine_similarity_complex_f64_opposite() { + let v1: FlexVector, Column> = + FlexVector::from_vec(vec![Complex::new(1.0, 0.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(-1.0, 0.0)]); + // Opposite vectors: cosine similarity should be -1+0i + let cos_sim = v1.cosine_similarity(&v2).unwrap(); + assert!((cos_sim + Complex::new(1.0, 0.0)).norm() < 1e-12); + } + + #[test] + fn test_cosine_similarity_complex_f64_identical() { + let v1: FlexVector, Column> = + FlexVector::from_vec(vec![Complex::new(3.0, 4.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(3.0, 4.0)]); + // Identical vectors: cosine similarity should be 1+0i + let cos_sim = v1.cosine_similarity(&v2).unwrap(); + assert!((cos_sim - Complex::new(1.0, 0.0)).norm() < 1e-12); + } + + #[test] + fn test_cosine_similarity_complex_f64_arbitrary() { + let v1: FlexVector, Column> = + FlexVector::from_vec(vec![Complex::new(1.0, 2.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(2.0, 1.0)]); + let cos_sim = v1.cosine_similarity(&v2).unwrap(); + // Should be a complex number with norm <= 1 + assert!(cos_sim.norm() <= 1.0 + 1e-12); + } + + #[test] + fn test_cosine_similarity_complex_f64_zero_vector() { + let v1: FlexVector, Column> = + FlexVector::from_vec(vec![Complex::new(0.0, 0.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(1.0, 2.0)]); + let result = v1.cosine_similarity(&v2); + assert!(result.is_err()); + } + + #[test] + fn test_cosine_similarity_complex_f64_mismatched_length() { + let v1: FlexVector, Column> = + FlexVector::from_vec(vec![Complex::new(1.0, 2.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(3.0, 4.0), Complex::new(5.0, 6.0)]); + let result = v1.cosine_similarity(&v2); + assert!(result.is_err()); + } + + // -- mut_normalize -- + + #[test] + fn test_mut_normalize_complex_f64_basic() { + let mut v: FlexVector, Column> = + FlexVector::from_vec(vec![Complex::new(3.0, 4.0)]); + v.mut_normalize().unwrap(); + // The norm is 5.0, so the normalized vector should be [0.6 + 0.8i] + assert!((v.as_slice()[0].re - 0.6).abs() < 1e-12); + assert!((v.as_slice()[0].im - 0.8).abs() < 1e-12); + } + + #[test] + fn test_mut_normalize_complex_f64_multiple_elements() { + let mut v: FlexVector, Row> = + FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let norm = ((1.0 * 1.0 + 2.0 * 2.0) + (3.0 * 3.0 + 4.0 * 4.0)).sqrt(); + v.mut_normalize().unwrap(); + assert!((v.as_slice()[0].re - 1.0 / norm).abs() < 1e-12); + assert!((v.as_slice()[0].im - 2.0 / norm).abs() < 1e-12); + assert!((v.as_slice()[1].re - 3.0 / norm).abs() < 1e-12); + assert!((v.as_slice()[1].im - 4.0 / norm).abs() < 1e-12); + } + + #[test] + fn test_mut_normalize_complex_f64_zero_vector() { + let mut v: FlexVector, Column> = + FlexVector::from_vec(vec![Complex::new(0.0, 0.0)]); + let result = v.mut_normalize(); + assert!(result.is_err()); + } + + #[test] + fn test_mut_normalize_complex_f64_empty() { + let mut v: FlexVector> = FlexVector::new(); + let result = v.mut_normalize(); + assert!(result.is_err()); + } + + // -- mut_normalize_to -- + + #[test] + fn test_mut_normalize_to_complex_f64_basic() { + let mut v: FlexVector, Column> = + FlexVector::from_vec(vec![Complex::new(3.0, 4.0)]); + v.mut_normalize_to(10.0).unwrap(); + // The original norm is 5.0, so the normalized vector should be [6.0 + 8.0i] + assert!((v.as_slice()[0].re - 6.0).abs() < 1e-12); + assert!((v.as_slice()[0].im - 8.0).abs() < 1e-12); + } + + #[test] + fn test_mut_normalize_to_complex_f64_multiple_elements() { + let mut v: FlexVector, Row> = + FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let norm = ((1.0 * 1.0 + 2.0 * 2.0) + (3.0 * 3.0 + 4.0 * 4.0)).sqrt(); + v.mut_normalize_to(2.0).unwrap(); + assert!((v.as_slice()[0].re - 1.0 / norm * 2.0).abs() < 1e-12); + assert!((v.as_slice()[0].im - 2.0 / norm * 2.0).abs() < 1e-12); + assert!((v.as_slice()[1].re - 3.0 / norm * 2.0).abs() < 1e-12); + assert!((v.as_slice()[1].im - 4.0 / norm * 2.0).abs() < 1e-12); + } + + #[test] + fn test_mut_normalize_to_complex_f64_zero_vector() { + let mut v: FlexVector, Column> = + FlexVector::from_vec(vec![Complex::new(0.0, 0.0)]); + let result = v.mut_normalize_to(1.0); + assert!(result.is_err()); + } + + #[test] + fn test_mut_normalize_to_complex_f64_empty() { + let mut v: FlexVector> = FlexVector::new(); + let result = v.mut_normalize_to(1.0); + assert!(result.is_err()); + } + + // -- mut_lerp -- + + #[test] + fn test_mut_lerp_complex_f64_weight_zero() { + let mut v1: FlexVector, Column> = + FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); + v1.mut_lerp(&v2, 0.0).unwrap(); + // Should be equal to original v1 + assert!((v1.as_slice()[0] - Complex::new(1.0, 2.0)).norm() < 1e-12); + assert!((v1.as_slice()[1] - Complex::new(3.0, 4.0)).norm() < 1e-12); + } + + #[test] + fn test_mut_lerp_complex_f64_weight_one() { + let mut v1: FlexVector, Row> = + FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); + v1.mut_lerp(&v2, 1.0).unwrap(); + // Should be equal to v2 + assert!((v1.as_slice()[0] - Complex::new(5.0, 6.0)).norm() < 1e-12); + assert!((v1.as_slice()[1] - Complex::new(7.0, 8.0)).norm() < 1e-12); + } + + #[test] + fn test_mut_lerp_complex_f64_weight_half() { + let mut v1: FlexVector, Column> = + FlexVector::from_vec(vec![Complex::new(2.0, 4.0), Complex::new(6.0, 8.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(4.0, 6.0), Complex::new(8.0, 10.0)]); + v1.mut_lerp(&v2, 0.5).unwrap(); + // Should be the midpoint + assert!((v1.as_slice()[0] - Complex::new(3.0, 5.0)).norm() < 1e-12); + assert!((v1.as_slice()[1] - Complex::new(7.0, 9.0)).norm() < 1e-12); + } + + #[test] + fn test_mut_lerp_complex_f64_weight_out_of_bounds() { + let mut v1: FlexVector, Row> = + FlexVector::from_vec(vec![Complex::new(1.0, 2.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(3.0, 4.0)]); + let result_low = v1.mut_lerp(&v2, -0.1); + let result_high = v1.mut_lerp(&v2, 1.1); + assert!(result_low.is_err()); + assert!(result_high.is_err()); + } + + #[test] + fn test_mut_lerp_complex_f64_mismatched_length() { + let mut v1: FlexVector, Column> = + FlexVector::from_vec(vec![Complex::new(1.0, 2.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(3.0, 4.0), Complex::new(5.0, 6.0)]); + let result = v1.mut_lerp(&v2, 0.5); + assert!(result.is_err()); + } // ================================ // From 35eaf84ded29510a34fac4f0cc4e9f55ab89e37d Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Tue, 27 May 2025 08:44:48 -0400 Subject: [PATCH 56/82] Add VectorBase impl for VectorSlice, VectorBase + VectorBaseMut for VectorSliceMut --- src/types/vectorslice.rs | 191 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 190 insertions(+), 1 deletion(-) diff --git a/src/types/vectorslice.rs b/src/types/vectorslice.rs index 0dd5585..adf233b 100644 --- a/src/types/vectorslice.rs +++ b/src/types/vectorslice.rs @@ -1,7 +1,7 @@ //! VectorSlice types. use crate::types::orientation::Column; -use crate::types::traits::VectorOrientationName; +use crate::types::traits::{VectorBase, VectorBaseMut, VectorOrientationName}; use std::fmt; use std::marker::PhantomData; @@ -130,6 +130,19 @@ where } } +// ================================ +// +// Crate trait impls +// +// ================================ + +impl<'a, T, O> VectorBase for VectorSlice<'a, T, O> { + #[inline] + fn as_slice(&self) -> &[T] { + self.elements + } +} + // ///////////////////////////////// // ================================ // @@ -231,6 +244,26 @@ where } } +// ================================ +// +// Crate trait impls +// +// ================================ + +impl<'a, T, O> VectorBase for VectorSliceMut<'a, T, O> { + #[inline] + fn as_slice(&self) -> &[T] { + self.elements + } +} + +impl<'a, T, O> VectorBaseMut for VectorSliceMut<'a, T, O> { + #[inline] + fn as_mut_slice(&mut self) -> &mut [T] { + self.elements + } +} + #[cfg(test)] mod tests { use super::*; @@ -540,6 +573,70 @@ mod tests { assert_eq!(vslice1.partial_cmp(&vslice1), Some(std::cmp::Ordering::Equal)); } + // -- VectorBase trait for VectorSlice -- + + #[test] + fn test_vector_slice_as_slice_basic() { + let data = [1, 2, 3, 4]; + let vslice: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&data, 1..3); + assert_eq!(vslice.as_slice(), &[2, 3]); + } + + #[test] + fn test_vector_slice_as_slice_full_range() { + let data = [10, 20, 30]; + let vslice: VectorSlice<'_, i32, Row> = VectorSlice::from_range(&data, 0..data.len()); + assert_eq!(vslice.as_slice(), &[10, 20, 30]); + } + + #[test] + fn test_vector_slice_as_slice_empty() { + let data = [1, 2, 3]; + let vslice: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&data, 1..1); + assert_eq!(vslice.as_slice(), &[]); + } + + #[test] + fn test_vector_slice_as_slice_complex() { + let data = [num::Complex::new(1.0, 2.0), num::Complex::new(3.0, 4.0)]; + let vslice: VectorSlice<'_, num::Complex, Column> = + VectorSlice::from_range(&data, 0..2); + assert_eq!(vslice.as_slice(), &data); + } + + #[test] + fn test_vector_slice_len_and_is_empty() { + let data = [1, 2, 3]; + let vslice: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&data, 0..2); + assert_eq!(vslice.len(), 2); + assert!(!vslice.is_empty()); + let empty: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&data, 1..1); + assert_eq!(empty.len(), 0); + assert!(empty.is_empty()); + } + + #[test] + fn test_vector_slice_get_and_first_last() { + let data = [1, 2, 3]; + let vslice: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&data, 0..3); + assert_eq!(vslice.get(1), Some(&2)); + assert_eq!(vslice.first(), Some(&1)); + assert_eq!(vslice.last(), Some(&3)); + let empty: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&data, 1..1); + assert_eq!(empty.get(1), None); + assert_eq!(empty.first(), None); + assert_eq!(empty.last(), None); + } + + #[test] + fn test_vector_slice_iter_and_to_vec() { + let data = [1, 2, 3]; + let vslice: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&data, 0..3); + let collected: Vec = vslice.iter().copied().collect(); + assert_eq!(collected, vec![1, 2, 3]); + assert_eq!(vslice.to_vec(), vec![1, 2, 3]); + } + // ///////////////////////////////// // ================================ // @@ -832,4 +929,96 @@ mod tests { assert!(debug.contains("Complex { re: 5.0, im: 6.0 }")); assert!(debug.contains("Complex { re: 7.0, im: 8.0 }")); } + + // -- VectorBase trait for VectorSliceMut -- + + #[test] + fn test_vector_slice_mut_as_slice_basic() { + let mut data = [1, 2, 3, 4]; + let vslice: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut data, 1..3); + assert_eq!(vslice.as_slice(), &[2, 3]); + } + + #[test] + fn test_vector_slice_mut_as_slice_full_range() { + let mut data = [10, 20, 30]; + let length = data.len(); + let vslice: VectorSliceMut<'_, i32, Row> = VectorSliceMut::from_range(&mut data, 0..length); + assert_eq!(vslice.as_slice(), &[10, 20, 30]); + } + + #[test] + fn test_vector_slice_mut_as_slice_empty() { + let mut data = [1, 2, 3]; + let vslice: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut data, 1..1); + assert_eq!(vslice.as_slice(), &[]); + } + + #[test] + fn test_vector_slice_mut_as_slice_complex() { + let mut data = [num::Complex::new(1.0, 2.0), num::Complex::new(3.0, 4.0)]; + let vslice: VectorSliceMut<'_, num::Complex, Column> = + VectorSliceMut::from_range(&mut data, 0..2); + assert_eq!(vslice.as_slice(), &[num::Complex::new(1.0, 2.0), num::Complex::new(3.0, 4.0)]); + } + + #[test] + fn test_vector_slice_mut_len_and_is_empty() { + let mut data = [1, 2, 3]; + let vslice: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut data, 0..2); + assert_eq!(vslice.len(), 2); + assert!(!vslice.is_empty()); + let empty: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut data, 1..1); + assert_eq!(empty.len(), 0); + assert!(empty.is_empty()); + } + + // -- VectorBaseMut trait for VectorSliceMut -- + + #[test] + fn test_vector_slice_mut_as_mut_slice_basic() { + let mut data = [1, 2, 3, 4]; + let mut vslice: VectorSliceMut<'_, i32, Column> = + VectorSliceMut::from_range(&mut data, 1..3); + assert_eq!(vslice.as_mut_slice(), &mut [2, 3]); + // Mutate through as_mut_slice + vslice.as_mut_slice()[0] = 20; + vslice.as_mut_slice()[1] = 30; + assert_eq!(vslice.as_mut_slice(), &mut [20, 30]); + // Changes are reflected in the original data + assert_eq!(data, [1, 20, 30, 4]); + } + + #[test] + fn test_vector_slice_mut_as_mut_slice_full_range() { + let mut data = [10, 20, 30]; + let length = data.len(); + let mut vslice: VectorSliceMut<'_, i32, Row> = + VectorSliceMut::from_range(&mut data, 0..length); + assert_eq!(vslice.as_mut_slice(), &mut [10, 20, 30]); + vslice.as_mut_slice()[2] = 99; + assert_eq!(data, [10, 20, 99]); + } + + #[test] + fn test_vector_slice_mut_as_mut_slice_empty() { + let mut data = [1, 2, 3]; + let mut vslice: VectorSliceMut<'_, i32, Column> = + VectorSliceMut::from_range(&mut data, 1..1); + assert_eq!(vslice.as_mut_slice(), &mut []); + } + + #[test] + fn test_vector_slice_mut_as_mut_slice_complex() { + let mut data = [num::Complex::new(1.0, 2.0), num::Complex::new(3.0, 4.0)]; + let mut vslice: VectorSliceMut<'_, num::Complex, Column> = + VectorSliceMut::from_range(&mut data, 0..2); + assert_eq!( + vslice.as_mut_slice(), + &mut [num::Complex::new(1.0, 2.0), num::Complex::new(3.0, 4.0)] + ); + // Mutate through as_mut_slice + vslice.as_mut_slice()[1] = num::Complex::new(9.0, 9.0); + assert_eq!(data, [num::Complex::new(1.0, 2.0), num::Complex::new(9.0, 9.0)]); + } } From 251242a84c2aee130033dbe54cfa1a0600bcac82 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Tue, 27 May 2025 21:51:26 -0400 Subject: [PATCH 57/82] refactor scale and negate VectorOps method impl to the trait definition these do not need to be defined at each struct impl --- src/types/flexvector.rs | 18 ------------------ src/types/traits.rs | 14 +++++++++++--- 2 files changed, 11 insertions(+), 21 deletions(-) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index bf7fac4..89152f4 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -739,24 +739,6 @@ where Ok(out) } - #[inline] - fn scale(&self, scalar: T) -> Self::Output - where - T: num::Num + Clone, - Self::Output: std::iter::FromIterator, - { - self.as_slice().iter().map(|a| a.clone() * scalar.clone()).collect() - } - - #[inline] - fn negate(&self) -> Self::Output - where - T: std::ops::Neg + Clone, - Self::Output: std::iter::FromIterator, - { - self.as_slice().iter().map(|a| -(a.clone())).collect() - } - #[inline] fn dot(&self, other: &Self) -> Result where diff --git a/src/types/traits.rs b/src/types/traits.rs index 97bca6e..7509c60 100644 --- a/src/types/traits.rs +++ b/src/types/traits.rs @@ -254,16 +254,24 @@ pub trait VectorOps: VectorBase { T: num::Num + Copy; /// Returns a new vector scaled by the given scalar. + #[inline] fn scale(&self, scalar: T) -> Self::Output where - T: num::Num + Copy, - Self::Output: std::iter::FromIterator; + T: num::Num + Clone, + Self::Output: std::iter::FromIterator, + { + self.as_slice().iter().map(|a| a.clone() * scalar.clone()).collect() + } /// Returns a new vector with all elements negated. + #[inline] fn negate(&self) -> Self::Output where T: std::ops::Neg + Clone, - Self::Output: std::iter::FromIterator; + Self::Output: std::iter::FromIterator, + { + self.as_slice().iter().map(|a| -(a.clone())).collect() + } /// ... fn dot(&self, other: &Self) -> Result From db83452b1a7ec334432146870895968ac30af015 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Tue, 27 May 2025 22:44:13 -0400 Subject: [PATCH 58/82] Add VectorOps impl for VectorSlice, VectorOps + VectorOpsMut impl for VectorSliceMut --- src/types/vectorslice.rs | 539 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 538 insertions(+), 1 deletion(-) diff --git a/src/types/vectorslice.rs b/src/types/vectorslice.rs index adf233b..80a43fc 100644 --- a/src/types/vectorslice.rs +++ b/src/types/vectorslice.rs @@ -1,7 +1,15 @@ //! VectorSlice types. +use crate::errors::VectorError; +use crate::types::flexvector::FlexVector; use crate::types::orientation::Column; -use crate::types::traits::{VectorBase, VectorBaseMut, VectorOrientationName}; +use crate::types::traits::{ + VectorBase, VectorBaseMut, VectorOps, VectorOpsMut, VectorOrientationName, +}; +use crate::types::utils::{ + cross_impl, dot_impl, dot_to_f64_impl, mut_translate_impl, translate_impl, +}; + use std::fmt; use std::marker::PhantomData; @@ -143,6 +151,114 @@ impl<'a, T, O> VectorBase for VectorSlice<'a, T, O> { } } +impl<'a, T, O> VectorOps for VectorSlice<'a, T, O> +where + T: Clone, +{ + type Output = FlexVector; + + #[inline] + fn translate(&self, other: &Self) -> Result + where + T: num::Num + Copy, + { + self.check_same_length_and_raise(other)?; + let mut out = FlexVector::zero(self.len()); + translate_impl(self.as_slice(), other.as_slice(), out.as_mut_slice()); + Ok(out) + } + + #[inline] + fn dot(&self, other: &Self) -> Result + where + T: num::Num + Copy + std::iter::Sum, + { + self.check_same_length_and_raise(other)?; + Ok(dot_impl(self.as_slice(), other.as_slice())) + } + + #[inline] + fn dot_to_f64(&self, other: &Self) -> Result + where + T: num::ToPrimitive, + { + self.check_same_length_and_raise(other)?; + Ok(dot_to_f64_impl(self.as_slice(), other.as_slice())) + } + + #[inline] + fn cross(&self, other: &Self) -> Result + where + T: num::Num + Copy, + Self::Output: std::iter::FromIterator, + { + if self.len() != 3 || other.len() != 3 { + return Err(VectorError::OutOfRangeError( + "Cross product is only defined for 3D vectors".to_string(), + )); + } + let a = self.as_slice(); + let b = other.as_slice(); + let result = cross_impl(a, b); + Ok(result.into_iter().collect()) + } + + #[inline] + fn elementwise_min(&self, other: &Self) -> Result + where + T: PartialOrd + Clone, + { + self.check_same_length_and_raise(other)?; + let elements: Vec = self + .as_slice() + .iter() + .zip(other.as_slice()) + .map(|(a, b)| if a < b { a.clone() } else { b.clone() }) + .collect(); + Ok(FlexVector::from_vec(elements)) + } + + #[inline] + fn elementwise_max(&self, other: &Self) -> Result + where + T: PartialOrd + Clone, + { + self.check_same_length_and_raise(other)?; + let elements: Vec = self + .as_slice() + .iter() + .zip(other.as_slice()) + .map(|(a, b)| if a > b { a.clone() } else { b.clone() }) + .collect(); + Ok(FlexVector::from_vec(elements)) + } +} + +// ================================ +// +// Methods +// +// ================================ + +impl<'a, T, O> VectorSlice<'a, T, O> { + // ================================ + // + // Private methods + // + // ================================ + + /// Returns Ok(()) if self and other have the same length (i.e. vector dimensionality), + /// otherwise returns a VectorError. + #[inline] + fn check_same_length_and_raise(&self, other: &Self) -> Result<(), VectorError> { + if self.len() != other.len() { + Err(VectorError::MismatchedLengthError("Vectors must have the same length".to_string())) + } else { + Ok(()) + } + } +} + // ///////////////////////////////// // ================================ // @@ -264,6 +380,131 @@ impl<'a, T, O> VectorBaseMut for VectorSliceMut<'a, T, O> { } } +impl<'a, T, O> VectorOps for VectorSliceMut<'a, T, O> +where + T: Clone, +{ + type Output = FlexVector; + + #[inline] + fn translate(&self, other: &Self) -> Result + where + T: num::Num + Copy, + { + self.check_same_length_and_raise(other)?; + let mut out = FlexVector::zero(self.len()); + translate_impl(self.as_slice(), other.as_slice(), out.as_mut_slice()); + Ok(out) + } + + #[inline] + fn dot(&self, other: &Self) -> Result + where + T: num::Num + Copy + std::iter::Sum, + { + self.check_same_length_and_raise(other)?; + Ok(dot_impl(self.as_slice(), other.as_slice())) + } + + #[inline] + fn dot_to_f64(&self, other: &Self) -> Result + where + T: num::ToPrimitive, + { + self.check_same_length_and_raise(other)?; + Ok(dot_to_f64_impl(self.as_slice(), other.as_slice())) + } + + #[inline] + fn cross(&self, other: &Self) -> Result + where + T: num::Num + Copy, + Self::Output: std::iter::FromIterator, + { + if self.len() != 3 || other.len() != 3 { + return Err(VectorError::OutOfRangeError( + "Cross product is only defined for 3D vectors".to_string(), + )); + } + let a = self.as_slice(); + let b = other.as_slice(); + let result = cross_impl(a, b); + Ok(result.into_iter().collect()) + } + + #[inline] + fn elementwise_min(&self, other: &Self) -> Result + where + T: PartialOrd + Clone, + { + self.check_same_length_and_raise(other)?; + let elements: Vec = self + .as_slice() + .iter() + .zip(other.as_slice()) + .map(|(a, b)| if a < b { a.clone() } else { b.clone() }) + .collect(); + Ok(FlexVector::from_vec(elements)) + } + + #[inline] + fn elementwise_max(&self, other: &Self) -> Result + where + T: PartialOrd + Clone, + { + self.check_same_length_and_raise(other)?; + let elements: Vec = self + .as_slice() + .iter() + .zip(other.as_slice()) + .map(|(a, b)| if a > b { a.clone() } else { b.clone() }) + .collect(); + Ok(FlexVector::from_vec(elements)) + } +} + +impl<'a, T, O> VectorOpsMut for VectorSliceMut<'a, T, O> +where + T: Clone, +{ + type Output = Self; + + #[inline] + fn mut_translate(&mut self, other: &Self) -> Result<(), VectorError> + where + T: num::Num + Copy, + { + self.check_same_length_and_raise(other)?; + mut_translate_impl(self.as_mut_slice(), other.as_slice()); + Ok(()) + } +} + +// ================================ +// +// Methods +// +// ================================ + +impl<'a, T, O> VectorSliceMut<'a, T, O> { + // ================================ + // + // Private methods + // + // ================================ + + /// Returns Ok(()) if self and other have the same length (i.e. vector dimensionality), + /// otherwise returns a VectorError. + #[inline] + fn check_same_length_and_raise(&self, other: &Self) -> Result<(), VectorError> { + if self.len() != other.len() { + Err(VectorError::MismatchedLengthError("Vectors must have the same length".to_string())) + } else { + Ok(()) + } + } +} + #[cfg(test)] mod tests { use super::*; @@ -637,6 +878,128 @@ mod tests { assert_eq!(vslice.to_vec(), vec![1, 2, 3]); } + // -- VectorOps trait for VectorSlice -- + + #[test] + fn test_vector_slice_translate() { + let a = [1, 2, 3]; + let b = [4, 5, 6]; + let vslice_a: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&a, 0..3); + let vslice_b = VectorSlice::from_range(&b, 0..3); + let result = vslice_a.translate(&vslice_b).unwrap(); + assert_eq!(result.as_slice(), &[5, 7, 9]); + } + + #[test] + fn test_vector_slice_translate_mismatched_length() { + let a = [1, 2, 3]; + let b = [4, 5]; + let vslice_a: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&a, 0..3); + let vslice_b = VectorSlice::from_range(&b, 0..2); + let result = vslice_a.translate(&vslice_b); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_dot() { + let a = [1, 2, 3]; + let b = [4, 5, 6]; + let vslice_a: VectorSlice<'_, i32, Row> = VectorSlice::from_range(&a, 0..3); + let vslice_b = VectorSlice::from_range(&b, 0..3); + let result = vslice_a.dot(&vslice_b).unwrap(); + assert_eq!(result, 1 * 4 + 2 * 5 + 3 * 6); + } + + #[test] + fn test_vector_slice_dot_mismatched_length() { + let a = [1, 2, 3]; + let b = [4, 5]; + let vslice_a: VectorSlice<'_, i32, Row> = VectorSlice::from_range(&a, 0..3); + let vslice_b = VectorSlice::from_range(&b, 0..2); + let result = vslice_a.dot(&vslice_b); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_dot_to_f64() { + let a = [1, 2, 3]; + let b = [4, 5, 6]; + let vslice_a: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&a, 0..3); + let vslice_b = VectorSlice::from_range(&b, 0..3); + let result = vslice_a.dot_to_f64(&vslice_b).unwrap(); + assert_eq!(result, 1.0 * 4.0 + 2.0 * 5.0 + 3.0 * 6.0); + } + + #[test] + fn test_vector_slice_dot_to_f64_mismatched_length() { + let a = [1, 2, 3]; + let b = [4, 5]; + let vslice_a: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&a, 0..3); + let vslice_b = VectorSlice::from_range(&b, 0..2); + let result = vslice_a.dot_to_f64(&vslice_b); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_cross() { + let a = [1, 2, 3]; + let b = [4, 5, 6]; + let vslice_a: VectorSlice<'_, i32, Row> = VectorSlice::from_range(&a, 0..3); + let vslice_b = VectorSlice::from_range(&b, 0..3); + let result = vslice_a.cross(&vslice_b).unwrap(); + assert_eq!(result.as_slice(), &[-3, 6, -3]); + } + + #[test] + fn test_vector_slice_cross_incorrect_length() { + let a = [1, 2, 3, 4]; + let b = [4, 5, 6, 7]; + let vslice_a: VectorSlice<'_, i32, Row> = VectorSlice::from_range(&a, 0..4); + let vslice_b = VectorSlice::from_range(&b, 0..3); + let result = vslice_a.cross(&vslice_b); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_elementwise_min() { + let a = [1, 5, 3]; + let b = [4, 2, 6]; + let vslice_a: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&a, 0..3); + let vslice_b = VectorSlice::from_range(&b, 0..3); + let result = vslice_a.elementwise_min(&vslice_b).unwrap(); + assert_eq!(result.as_slice(), &[1, 2, 3]); + } + + #[test] + fn test_vector_slice_elementwise_min_mismatched_length() { + let a = [1, 5, 3]; + let b = [4, 2]; + let vslice_a: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&a, 0..3); + let vslice_b = VectorSlice::from_range(&b, 0..2); + let result = vslice_a.elementwise_min(&vslice_b); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_elementwise_max() { + let a = [1, 5, 3]; + let b = [4, 2, 6]; + let vslice_a: VectorSlice<'_, i32, Row> = VectorSlice::from_range(&a, 0..3); + let vslice_b = VectorSlice::from_range(&b, 0..3); + let result = vslice_a.elementwise_max(&vslice_b).unwrap(); + assert_eq!(result.as_slice(), &[4, 5, 6]); + } + + #[test] + fn test_vector_slice_elementwise_max_mismatched_length() { + let a = [1, 5, 3]; + let b = [4, 2]; + let vslice_a: VectorSlice<'_, i32, Row> = VectorSlice::from_range(&a, 0..3); + let vslice_b = VectorSlice::from_range(&b, 0..2); + let result = vslice_a.elementwise_max(&vslice_b); + assert!(result.is_err()); + } + // ///////////////////////////////// // ================================ // @@ -1021,4 +1384,178 @@ mod tests { vslice.as_mut_slice()[1] = num::Complex::new(9.0, 9.0); assert_eq!(data, [num::Complex::new(1.0, 2.0), num::Complex::new(9.0, 9.0)]); } + + // -- VectorOps trait for VectorSliceMut -- + + #[test] + fn test_vector_slice_mut_translate() { + let mut a = [1, 2, 3]; + let mut b = [4, 5, 6]; + let vslice_a: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut a, 0..3); + let vslice_b: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut b, 0..3); + let result = vslice_a.translate(&vslice_b).unwrap(); + assert_eq!(result.as_slice(), &[5, 7, 9]); + } + + #[test] + fn test_vector_slice_mut_translate_mismatched_length() { + let mut a = [1, 2, 3]; + let mut b = [4, 5]; + let vslice_a: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut a, 0..3); + let vslice_b: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut b, 0..2); + let result = vslice_a.translate(&vslice_b); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_mut_dot() { + let mut a = [1, 2, 3]; + let mut b = [4, 5, 6]; + let vslice_a: VectorSliceMut<'_, i32, Row> = VectorSliceMut::from_range(&mut a, 0..3); + let vslice_b: VectorSliceMut<'_, i32, Row> = VectorSliceMut::from_range(&mut b, 0..3); + let result = vslice_a.dot(&vslice_b).unwrap(); + assert_eq!(result, 1 * 4 + 2 * 5 + 3 * 6); + } + + #[test] + fn test_vector_slice_mut_dot_mismatched_length() { + let mut a = [1, 2, 3]; + let mut b = [4, 5]; + let vslice_a: VectorSliceMut<'_, i32, Row> = VectorSliceMut::from_range(&mut a, 0..3); + let vslice_b: VectorSliceMut<'_, i32, Row> = VectorSliceMut::from_range(&mut b, 0..2); + let result = vslice_a.dot(&vslice_b); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_mut_dot_to_f64() { + let mut a = [1, 2, 3]; + let mut b = [4, 5, 6]; + let vslice_a: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut a, 0..3); + let vslice_b: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut b, 0..3); + let result = vslice_a.dot_to_f64(&vslice_b).unwrap(); + assert_eq!(result, 1.0 * 4.0 + 2.0 * 5.0 + 3.0 * 6.0); + } + + #[test] + fn test_vector_slice_mut_dot_to_f64_mismatched_length() { + let mut a = [1, 2, 3]; + let mut b = [4, 5]; + let vslice_a: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut a, 0..3); + let vslice_b: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut b, 0..2); + let result = vslice_a.dot_to_f64(&vslice_b); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_mut_cross() { + let mut a = [1, 2, 3]; + let mut b = [4, 5, 6]; + let vslice_a: VectorSliceMut<'_, i32, Row> = VectorSliceMut::from_range(&mut a, 0..3); + let vslice_b: VectorSliceMut<'_, i32, Row> = VectorSliceMut::from_range(&mut b, 0..3); + let result = vslice_a.cross(&vslice_b).unwrap(); + assert_eq!(result.as_slice(), &[-3, 6, -3]); + } + + #[test] + fn test_vector_slice_mut_cross_incorrect_length() { + let mut a = [1, 2, 3, 4]; + let mut b = [4, 5, 6, 7]; + let vslice_a: VectorSliceMut<'_, i32, Row> = VectorSliceMut::from_range(&mut a, 0..4); + let vslice_b: VectorSliceMut<'_, i32, Row> = VectorSliceMut::from_range(&mut b, 0..3); + let result = vslice_a.cross(&vslice_b); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_mut_elementwise_min() { + let mut a = [1, 5, 3]; + let mut b = [4, 2, 6]; + let vslice_a: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut a, 0..3); + let vslice_b: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut b, 0..3); + let result = vslice_a.elementwise_min(&vslice_b).unwrap(); + assert_eq!(result.as_slice(), &[1, 2, 3]); + } + + #[test] + fn test_vector_slice_mut_elementwise_min_mismatched_length() { + let mut a = [1, 5, 3]; + let mut b = [4, 2]; + let vslice_a: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut a, 0..3); + let vslice_b: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut b, 0..2); + let result = vslice_a.elementwise_min(&vslice_b); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_mut_elementwise_max() { + let mut a = [1, 5, 3]; + let mut b = [4, 2, 6]; + let vslice_a: VectorSliceMut<'_, i32, Row> = VectorSliceMut::from_range(&mut a, 0..3); + let vslice_b: VectorSliceMut<'_, i32, Row> = VectorSliceMut::from_range(&mut b, 0..3); + let result = vslice_a.elementwise_max(&vslice_b).unwrap(); + assert_eq!(result.as_slice(), &[4, 5, 6]); + } + + #[test] + fn test_vector_slice_mut_elementwise_max_mismatched_length() { + let mut a = [1, 5, 3]; + let mut b = [4, 2]; + let vslice_a: VectorSliceMut<'_, i32, Row> = VectorSliceMut::from_range(&mut a, 0..3); + let vslice_b: VectorSliceMut<'_, i32, Row> = VectorSliceMut::from_range(&mut b, 0..2); + let result = vslice_a.elementwise_max(&vslice_b); + assert!(result.is_err()); + } + + // -- VectorOpsMut trait for VectorSliceMut -- + + #[test] + fn test_vector_slice_mut_mut_translate() { + let mut a = [1, 2, 3]; + let mut b = [4, 5, 6]; + let mut vslice_a: VectorSliceMut<'_, i32, Column> = + VectorSliceMut::from_range(&mut a, 0..3); + let vslice_b: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut b, 0..3); + vslice_a.mut_translate(&vslice_b).unwrap(); + assert_eq!(vslice_a.as_slice(), &[5, 7, 9]); + assert_eq!(a, [5, 7, 9]); + } + + #[test] + fn test_vector_slice_mut_mut_translate_mismatched_length() { + let mut a = [1, 2, 3]; + let mut b = [4, 5]; + let mut vslice_a: VectorSliceMut<'_, i32, Column> = + VectorSliceMut::from_range(&mut a, 0..3); + let vslice_b: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut b, 0..2); + let result = vslice_a.mut_translate(&vslice_b); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_mut_mut_scale() { + let mut a = [1, 2, 3]; + let mut vslice: VectorSliceMut<'_, i32, Row> = VectorSliceMut::from_range(&mut a, 0..3); + vslice.mut_scale(10); + assert_eq!(vslice.as_slice(), &[10, 20, 30]); + assert_eq!(a, [10, 20, 30]); + } + + #[test] + fn test_vector_slice_mut_mut_negate() { + let mut a = [1, -2, 3]; + let mut vslice: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut a, 0..3); + vslice.mut_negate(); + assert_eq!(vslice.as_slice(), &[-1, 2, -3]); + assert_eq!(a, [-1, 2, -3]); + } + + #[test] + fn test_vector_slice_mut_mut_zero() { + let mut a = [1, -2, 3]; + let mut vslice: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut a, 0..3); + vslice.mut_zero(); + assert_eq!(vslice.as_slice(), [0, 0, 0]); + assert_eq!(a, [0, 0, 0]); + } } From a13707633c9aaacb9b4ced6ca7fccdf261777273 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Tue, 27 May 2025 23:01:13 -0400 Subject: [PATCH 59/82] improve performance of mut_translate_impl reduces to single pass over data --- src/types/utils.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/types/utils.rs b/src/types/utils.rs index 4e6bc0d..1df3714 100644 --- a/src/types/utils.rs +++ b/src/types/utils.rs @@ -13,9 +13,9 @@ pub(crate) fn mut_translate_impl(out: &mut [T], b: &[T]) { #[inline] pub(crate) fn translate_impl(a: &[T], b: &[T], out: &mut [T]) { - // Copy a into out, then add b in-place - out.copy_from_slice(a); - mut_translate_impl(out, b); + for ((out_elem, &a_elem), &b_elem) in out.iter_mut().zip(a).zip(b) { + *out_elem = a_elem + b_elem; + } } #[inline] @@ -313,7 +313,7 @@ mod tests { let mut out = [0; 3]; translate_impl(&a, &b, &mut out); // Only the first two elements are updated - assert_eq!(out, [11, 22, 3]); + assert_eq!(out, [11, 22, 0]); } // -- dot_impl -- From 69d8f180e18a29b930e139ad9004e0adc1fd63bf Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Wed, 28 May 2025 18:45:05 -0400 Subject: [PATCH 60/82] Add VectorOps preallocated buffer functions: translate_into, cross_into, elementwise_min_into, elementwise_max_into, and elementwise_clamp_into includes FlexVector, VectorSlice, VectorSliceMut impl --- src/types/flexvector.rs | 317 ++++++++++++++++++++-- src/types/traits.rs | 43 +++ src/types/utils.rs | 272 +++++++++++++++++++ src/types/vectorslice.rs | 564 +++++++++++++++++++++++++++++++++++++-- 4 files changed, 1148 insertions(+), 48 deletions(-) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index 89152f4..6d578b6 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -24,11 +24,13 @@ use crate::{ use crate::types::utils::{ angle_with_impl, chebyshev_distance_complex_impl, chebyshev_distance_impl, - cosine_similarity_complex_impl, cosine_similarity_impl, cross_impl, distance_complex_impl, - distance_impl, dot_impl, dot_to_f64_impl, hermitian_dot_impl, lerp_impl, - manhattan_distance_complex_impl, manhattan_distance_impl, minkowski_distance_complex_impl, - minkowski_distance_impl, mut_lerp_impl, mut_normalize_impl, mut_normalize_to_impl, - mut_translate_impl, normalize_impl, normalize_to_impl, project_onto_impl, translate_impl, + cosine_similarity_complex_impl, cosine_similarity_impl, cross_impl, cross_into_impl, + distance_complex_impl, distance_impl, dot_impl, dot_to_f64_impl, elementwise_max_impl, + elementwise_max_into_impl, elementwise_min_impl, elementwise_min_into_impl, hermitian_dot_impl, + lerp_impl, manhattan_distance_complex_impl, manhattan_distance_impl, + minkowski_distance_complex_impl, minkowski_distance_impl, mut_lerp_impl, mut_normalize_impl, + mut_normalize_to_impl, mut_translate_impl, normalize_impl, normalize_to_impl, + project_onto_impl, translate_impl, }; use crate::errors::VectorError; @@ -739,6 +741,21 @@ where Ok(out) } + #[inline] + fn translate_into(&self, other: &Self, out: &mut [T]) -> Result<(), VectorError> + where + T: num::Num + Copy, + { + self.check_same_length_and_raise(other)?; + if out.len() != self.len() { + return Err(VectorError::MismatchedLengthError( + "Output buffer has wrong length".to_string(), + )); + } + translate_impl(self, other, out); + Ok(()) + } + #[inline] fn dot(&self, other: &Self) -> Result where @@ -775,6 +792,23 @@ where Ok(result.into_iter().collect()) } + /// ... + #[inline] + fn cross_into(&self, other: &Self, out: &mut [T]) -> Result<(), VectorError> + where + T: num::Num + Copy, + { + if self.len() != 3 || other.len() != 3 || out.len() != 3 { + return Err(VectorError::OutOfRangeError( + "Cross product is only defined for 3D vectors".to_string(), + )); + } + let a = self.as_slice(); + let b = other.as_slice(); + cross_into_impl(a, b, out); + Ok(()) + } + /// Element-wise min #[inline] fn elementwise_min(&self, other: &Self) -> Result @@ -782,13 +816,21 @@ where T: PartialOrd + Clone, { self.check_same_length_and_raise(other)?; - let elements = self - .as_slice() - .iter() - .zip(other.as_slice()) - .map(|(a, b)| if a < b { a.clone() } else { b.clone() }) - .collect(); - Ok(FlexVector { elements, _orientation: PhantomData }) + Ok(FlexVector::from_vec(elementwise_min_impl(self.as_slice(), other.as_slice()))) + } + + #[inline] + fn elementwise_min_into(&self, other: &Self, out: &mut [T]) -> Result<(), VectorError> + where + T: PartialOrd + Clone, + { + if self.len() != other.len() || out.len() != self.len() { + return Err(VectorError::MismatchedLengthError( + "Vectors and output buffer must have the same length".to_string(), + )); + } + elementwise_min_into_impl(self.as_slice(), other.as_slice(), out); + Ok(()) } /// Element-wise max @@ -798,13 +840,21 @@ where T: PartialOrd + Clone, { self.check_same_length_and_raise(other)?; - let elements = self - .as_slice() - .iter() - .zip(other.as_slice()) - .map(|(a, b)| if a > b { a.clone() } else { b.clone() }) - .collect(); - Ok(FlexVector { elements, _orientation: PhantomData }) + Ok(FlexVector::from_vec(elementwise_max_impl(self.as_slice(), other.as_slice()))) + } + + #[inline] + fn elementwise_max_into(&self, other: &Self, out: &mut [T]) -> Result<(), VectorError> + where + T: PartialOrd + Clone, + { + if self.len() != other.len() || out.len() != self.len() { + return Err(VectorError::MismatchedLengthError( + "Vectors and output buffer must have the same length".to_string(), + )); + } + elementwise_max_into_impl(self.as_slice(), other.as_slice(), out); + Ok(()) } } @@ -5610,6 +5660,48 @@ mod tests { assert!(result.is_err()); } + // -- translate_into -- + + #[test] + fn test_translate_into_i32() { + let v1 = FVector::from_vec(vec![1, 2, 3]); + let v2 = FlexVector::from_vec(vec![4, 5, 6]); + let mut out = [0; 3]; + v1.translate_into(&v2, &mut out).unwrap(); + assert_eq!(out, [5, 7, 9]); + assert_eq!(v1.as_slice(), &[1, 2, 3]); + } + + #[test] + fn test_translate_into_f64() { + let v1 = FVector::from_vec(vec![1.5, 2.5, 3.5]); + let v2 = FlexVector::from_vec(vec![0.5, 1.5, 2.5]); + let mut out = [0.0; 3]; + v1.translate_into(&v2, &mut out).unwrap(); + assert_eq!(out, [2.0, 4.0, 6.0]); + assert_eq!(v1.as_slice(), &[1.5, 2.5, 3.5]); + } + + #[test] + fn test_translate_into_complex_f64() { + use num::Complex; + let v1 = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); + let mut out = [Complex::new(0.0, 0.0); 2]; + v1.translate_into(&v2, &mut out).unwrap(); + assert_eq!(out, [Complex::new(6.0, 8.0), Complex::new(10.0, 12.0)]); + assert_eq!(v1.as_slice(), &[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + } + + #[test] + fn test_translate_into_mismatched_length() { + let v1 = FVector::from_vec(vec![1, 2, 3]); + let v2 = FlexVector::from_vec(vec![4, 5, 6]); + let mut out = [0; 2]; + let result = v1.translate_into(&v2, &mut out); + assert!(result.is_err()); + } + // -- mut_translate -- #[test] fn test_mut_translate_i32() { @@ -5871,6 +5963,47 @@ mod tests { assert!(result.is_err()); } + // -- cross_into -- + + #[test] + fn test_cross_into_i32() { + let v1 = FVector::from_vec(vec![1, 2, 3]); + let v2 = FlexVector::from_vec(vec![4, 5, 6]); + let mut out = [0; 3]; + v1.cross_into(&v2, &mut out).unwrap(); + assert_eq!(out, [-3, 6, -3]); + } + + #[test] + fn test_cross_into_f64() { + let v1 = FVector::from_vec(vec![1.0, 2.0, 3.0]); + let v2 = FlexVector::from_vec(vec![4.0, 5.0, 6.0]); + let mut out = [0.0; 3]; + v1.cross_into(&v2, &mut out).unwrap(); + assert_eq!(out, [-3.0, 6.0, -3.0]); + } + + #[test] + fn test_cross_into_wrong_length() { + let v1 = FVector::from_vec(vec![1, 2]); + let v2 = FlexVector::from_vec(vec![3, 4, 5]); + let mut out = [0; 3]; + let result = v1.cross_into(&v2, &mut out); + assert!(result.is_err()); + + let v1 = FVector::from_vec(vec![1, 2, 3]); + let v2 = FlexVector::from_vec(vec![3, 4]); + let mut out = [0; 3]; + let result = v1.cross_into(&v2, &mut out); + assert!(result.is_err()); + + let v1 = FVector::from_vec(vec![1, 2, 3]); + let v2 = FlexVector::from_vec(vec![3, 4, 5]); + let mut out = [0; 2]; + let result = v1.cross_into(&v2, &mut out); + assert!(result.is_err()); + } + // -- sum -- #[test] fn test_sum_i32() { @@ -6029,6 +6162,41 @@ mod tests { assert!(min[0].is_nan()); } + // -- elementwise_min_into -- + + #[test] + fn test_elementwise_min_into_i32() { + let v1 = FVector::from_vec(vec![1, 5, 3, 7]); + let v2 = FlexVector::from_vec(vec![2, 4, 6, 0]); + let mut out = [0; 4]; + v1.elementwise_min_into(&v2, &mut out).unwrap(); + assert_eq!(out, [1, 4, 3, 0]); + } + + #[test] + fn test_elementwise_min_into_f64() { + let v1 = FVector::from_vec(vec![1.5, -2.0, 3.0]); + let v2 = FlexVector::from_vec(vec![2.5, -3.0, 2.0]); + let mut out = [0.0; 3]; + v1.elementwise_min_into(&v2, &mut out).unwrap(); + assert_eq!(out, [1.5, -3.0, 2.0]); + } + + #[test] + fn test_elementwise_min_into_mismatched_length() { + let v1 = FVector::from_vec(vec![1, 2, 3]); + let v2 = FlexVector::from_vec(vec![4, 5]); + let mut out = [0; 3]; + let result = v1.elementwise_min_into(&v2, &mut out); + assert!(result.is_err()); + + let v1 = FVector::from_vec(vec![1, 2, 3]); + let v2 = FlexVector::from_vec(vec![4, 5, 6]); + let mut out = [0; 2]; + let result = v1.elementwise_min_into(&v2, &mut out); + assert!(result.is_err()); + } + // -- maximum -- #[test] fn test_maximum_i32() { @@ -6126,6 +6294,41 @@ mod tests { assert!(max[0].is_nan()); } + // -- elementwise_max_into -- + + #[test] + fn test_elementwise_max_into_i32() { + let v1 = FVector::from_vec(vec![1, 5, 3, 7]); + let v2 = FlexVector::from_vec(vec![2, 4, 6, 0]); + let mut out = [0; 4]; + v1.elementwise_max_into(&v2, &mut out).unwrap(); + assert_eq!(out, [2, 5, 6, 7]); + } + + #[test] + fn test_elementwise_max_into_f64() { + let v1 = FVector::from_vec(vec![1.5, -2.0, 3.0]); + let v2 = FlexVector::from_vec(vec![2.5, -3.0, 2.0]); + let mut out = [0.0; 3]; + v1.elementwise_max_into(&v2, &mut out).unwrap(); + assert_eq!(out, [2.5, -2.0, 3.0]); + } + + #[test] + fn test_elementwise_max_into_mismatched_length() { + let v1 = FVector::from_vec(vec![1, 2, 3]); + let v2 = FlexVector::from_vec(vec![4, 5]); + let mut out = [0; 3]; + let result = v1.elementwise_max_into(&v2, &mut out); + assert!(result.is_err()); + + let v1 = FVector::from_vec(vec![1, 2, 3]); + let v2 = FlexVector::from_vec(vec![4, 5, 6]); + let mut out = [0; 2]; + let result = v1.elementwise_max_into(&v2, &mut out); + assert!(result.is_err()); + } + // -- elementwise_clamp -- #[test] @@ -6193,6 +6396,82 @@ mod tests { assert!(clamped.is_empty()); } + // -- elementwise_clamp_into -- + + #[test] + fn test_elementwise_clamp_into_i32_basic() { + let v = FVector::from_vec(vec![-5, 0, 5, 10, 15]); + let mut out = [0; 5]; + v.elementwise_clamp_into(0, 10, &mut out).unwrap(); + assert_eq!(out, [0, 0, 5, 10, 10]); + } + + #[test] + fn test_elementwise_clamp_into_i32_all_below() { + let v = FVector::from_vec(vec![-3, -2, -1]); + let mut out = [0; 3]; + v.elementwise_clamp_into(0, 5, &mut out).unwrap(); + assert_eq!(out, [0, 0, 0]); + } + + #[test] + fn test_elementwise_clamp_into_i32_all_above() { + let v = FVector::from_vec(vec![11, 12, 13]); + let mut out = [0; 3]; + v.elementwise_clamp_into(0, 10, &mut out).unwrap(); + assert_eq!(out, [10, 10, 10]); + } + + #[test] + fn test_elementwise_clamp_into_i32_empty() { + let v: FlexVector = FlexVector::new(); + let mut out = []; + v.elementwise_clamp_into(0, 10, &mut out).unwrap(); + assert!(out.is_empty()); + } + + #[test] + fn test_elementwise_clamp_into_f64_basic() { + let v = FVector::from_vec(vec![-2.5, 0.0, 3.5, 7.2, 12.0]); + let mut out = [0.0; 5]; + v.elementwise_clamp_into(0.0, 10.0, &mut out).unwrap(); + assert_eq!(out, [0.0, 0.0, 3.5, 7.2, 10.0]); + } + + #[test] + fn test_elementwise_clamp_into_f64_all_below() { + let v = FVector::from_vec(vec![-1.1, -2.2]); + let mut out = [0.0; 2]; + v.elementwise_clamp_into(0.0, 5.0, &mut out).unwrap(); + assert_eq!(out, [0.0, 0.0]); + } + + #[test] + fn test_elementwise_clamp_into_f64_all_above() { + let v = FVector::from_vec(vec![11.1, 12.2]); + let mut out = [0.0; 2]; + v.elementwise_clamp_into(0.0, 10.0, &mut out).unwrap(); + assert_eq!(out, [10.0, 10.0]); + } + + #[test] + fn test_elementwise_clamp_into_f64_with_nan() { + let v = FVector::from_vec(vec![1.0, f64::NAN, 5.0]); + let mut out = [0.0; 3]; + v.elementwise_clamp_into(0.0, 4.0, &mut out).unwrap(); + assert_eq!(out[0], 1.0); + assert!(out[1].is_nan()); + assert_eq!(out[2], 4.0); + } + + #[test] + fn test_elementwise_clamp_into_f64_empty() { + let v: FlexVector = FlexVector::new(); + let mut out = []; + v.elementwise_clamp_into(0.0, 10.0, &mut out).unwrap(); + assert!(out.is_empty()); + } + // -- l1_norm -- #[test] fn test_l1_norm_i32() { diff --git a/src/types/traits.rs b/src/types/traits.rs index 7509c60..b2dc26f 100644 --- a/src/types/traits.rs +++ b/src/types/traits.rs @@ -253,6 +253,11 @@ pub trait VectorOps: VectorBase { where T: num::Num + Copy; + /// ... + fn translate_into(&self, other: &Self, out: &mut [T]) -> Result<(), VectorError> + where + T: num::Num + Copy; + /// Returns a new vector scaled by the given scalar. #[inline] fn scale(&self, scalar: T) -> Self::Output @@ -289,6 +294,11 @@ pub trait VectorOps: VectorBase { T: num::Num + Copy, Self::Output: std::iter::FromIterator; + /// ... + fn cross_into(&self, other: &Self, out: &mut [T]) -> Result<(), VectorError> + where + T: num::Num + Copy; + /// ... #[inline] fn sum(&self) -> T @@ -321,6 +331,11 @@ pub trait VectorOps: VectorBase { where T: PartialOrd + Clone; + /// ... + fn elementwise_min_into(&self, other: &Self, out: &mut [T]) -> Result<(), VectorError> + where + T: PartialOrd + Clone; + /// ... #[inline] fn maximum(&self) -> Option @@ -335,6 +350,11 @@ pub trait VectorOps: VectorBase { where T: PartialOrd + Clone; + /// ... + fn elementwise_max_into(&self, other: &Self, out: &mut [T]) -> Result<(), VectorError> + where + T: PartialOrd + Clone; + /// Returns a new vector where each element is clamped to the [min, max] range. #[inline] fn elementwise_clamp(&self, min: T, max: T) -> Self::Output @@ -356,6 +376,29 @@ pub trait VectorOps: VectorBase { .collect() } + /// ... + #[inline] + fn elementwise_clamp_into(&self, min: T, max: T, out: &mut [T]) -> Result<(), VectorError> + where + T: PartialOrd + Clone, + { + if out.len() != self.len() { + return Err(VectorError::MismatchedLengthError( + "Output buffer has the wrong length".to_string(), + )); + } + for (out_elem, x) in out.iter_mut().zip(self.as_slice()) { + *out_elem = if *x < min { + min.clone() + } else if *x > max { + max.clone() + } else { + x.clone() + }; + } + Ok(()) + } + /// L1 norm (sum of absolute values). #[inline] fn l1_norm(&self) -> T diff --git a/src/types/utils.rs b/src/types/utils.rs index 1df3714..12b9b6e 100644 --- a/src/types/utils.rs +++ b/src/types/utils.rs @@ -50,6 +50,52 @@ where [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]] } +/// Writes the cross product of two 3D vectors into the provided output buffer. +/// Assumes all slices are of length 3. +#[inline] +pub(crate) fn cross_into_impl(a: &[T], b: &[T], out: &mut [T]) { + out[0] = a[1] * b[2] - a[2] * b[1]; + out[1] = a[2] * b[0] - a[0] * b[2]; + out[2] = a[0] * b[1] - a[1] * b[0]; +} + +/// Returns a Vec containing the elementwise minimum of two slices. +/// Assumes all slices are the same length. +pub(crate) fn elementwise_min_impl(a: &[T], b: &[T]) -> Vec { + a.iter() + .zip(b.iter()) + .map(|(a_elem, b_elem)| if a_elem < b_elem { a_elem.clone() } else { b_elem.clone() }) + .collect() +} + +/// Writes the elementwise minimum of two slices into the provided output buffer. +/// Assumes all slices are the same length. +#[inline] +pub(crate) fn elementwise_min_into_impl(a: &[T], b: &[T], out: &mut [T]) { + for ((out_elem, a_elem), b_elem) in out.iter_mut().zip(a.iter()).zip(b.iter()) { + *out_elem = if a_elem < b_elem { a_elem.clone() } else { b_elem.clone() }; + } +} + +/// Returns a Vec containing the elementwise maximum of two slices. +/// Assumes all slices are the same length. +#[inline] +pub(crate) fn elementwise_max_impl(a: &[T], b: &[T]) -> Vec { + a.iter() + .zip(b.iter()) + .map(|(a_elem, b_elem)| if a_elem > b_elem { a_elem.clone() } else { b_elem.clone() }) + .collect() +} + +/// Writes the elementwise maximum of two slices into the provided output buffer. +/// Assumes all slices are the same length. +#[inline] +pub(crate) fn elementwise_max_into_impl(a: &[T], b: &[T], out: &mut [T]) { + for ((out_elem, a_elem), b_elem) in out.iter_mut().zip(a.iter()).zip(b.iter()) { + *out_elem = if a_elem > b_elem { a_elem.clone() } else { b_elem.clone() }; + } +} + #[inline] pub(crate) fn lerp_impl(a: &[T], b: &[T], weight: T, out: &mut [T]) where @@ -455,7 +501,233 @@ mod tests { assert_eq!(cross, [0, 0, 1]); } + // -- cross_into_impl -- + + #[test] + fn test_cross_into_impl_basic() { + let a = [1, 2, 3]; + let b = [4, 5, 6]; + let mut out = [0; 3]; + cross_into_impl(&a, &b, &mut out); + assert_eq!(out, [-3, 6, -3]); + } + + #[test] + fn test_cross_into_impl_zero_vector() { + let a = [0, 0, 0]; + let b = [1, 2, 3]; + let mut out = [0; 3]; + cross_into_impl(&a, &b, &mut out); + assert_eq!(out, [0, 0, 0]); + } + + #[test] + fn test_cross_into_impl_parallel_vectors() { + let a = [1, 2, 3]; + let b = [2, 4, 6]; + let mut out = [0; 3]; + cross_into_impl(&a, &b, &mut out); + assert_eq!(out, [0, 0, 0]); + } + + #[test] + fn test_cross_into_impl_orthogonal_vectors() { + let a = [1, 0, 0]; + let b = [0, 1, 0]; + let mut out = [0; 3]; + cross_into_impl(&a, &b, &mut out); + assert_eq!(out, [0, 0, 1]); + } + + #[test] + fn test_cross_into_impl_negative_values() { + let a = [-1, -2, -3]; + let b = [4, 5, 6]; + let mut out = [0; 3]; + cross_into_impl(&a, &b, &mut out); + assert_eq!(out, [3, -6, 3]); + } + + // -- elementwise_min_impl -- + + #[test] + fn test_elementwise_min_impl_basic() { + let a = [1, 5, 3]; + let b = [4, 2, 6]; + let result = elementwise_min_impl(&a, &b); + assert_eq!(result, vec![1, 2, 3]); + } + + #[test] + fn test_elementwise_min_impl_equal() { + let a = [2, 2, 2]; + let b = [2, 2, 2]; + let result = elementwise_min_impl(&a, &b); + assert_eq!(result, vec![2, 2, 2]); + } + + #[test] + fn test_elementwise_min_impl_negative_values() { + let a = [-1, -5, 3]; + let b = [4, -2, -6]; + let result = elementwise_min_impl(&a, &b); + assert_eq!(result, vec![-1, -5, -6]); + } + + #[test] + fn test_elementwise_min_impl_empty() { + let a: [i32; 0] = []; + let b: [i32; 0] = []; + let result = elementwise_min_impl(&a, &b); + assert_eq!(result, Vec::::new()); + } + + #[test] + fn test_elementwise_min_impl_f64() { + let a = [1.5, -2.0, 3.0]; + let b = [2.5, -3.0, 2.0]; + let result = elementwise_min_impl(&a, &b); + assert_eq!(result, vec![1.5, -3.0, 2.0]); + } + + // -- elementwise_min_into_impl -- + + #[test] + fn test_elementwise_min_into_impl_basic() { + let a = [1, 5, 3]; + let b = [4, 2, 6]; + let mut out = [0; 3]; + elementwise_min_into_impl(&a, &b, &mut out); + assert_eq!(out, [1, 2, 3]); + } + + #[test] + fn test_elementwise_min_into_impl_equal() { + let a = [2, 2, 2]; + let b = [2, 2, 2]; + let mut out = [0; 3]; + elementwise_min_into_impl(&a, &b, &mut out); + assert_eq!(out, [2, 2, 2]); + } + + #[test] + fn test_elementwise_min_into_impl_negative_values() { + let a = [-1, -5, 3]; + let b = [4, -2, -6]; + let mut out = [0; 3]; + elementwise_min_into_impl(&a, &b, &mut out); + assert_eq!(out, [-1, -5, -6]); + } + + #[test] + fn test_elementwise_min_into_impl_empty() { + let a: [i32; 0] = []; + let b: [i32; 0] = []; + let mut out: [i32; 0] = []; + elementwise_min_into_impl(&a, &b, &mut out); + assert_eq!(out, []); + } + + #[test] + fn test_elementwise_min_into_impl_f64() { + let a = [1.5, -2.0, 3.0]; + let b = [2.5, -3.0, 2.0]; + let mut out = [0.0; 3]; + elementwise_min_into_impl(&a, &b, &mut out); + assert_eq!(out, [1.5, -3.0, 2.0]); + } + + // -- elementwise_max_impl -- + + #[test] + fn test_elementwise_max_impl_basic() { + let a = [1, 5, 3]; + let b = [4, 2, 6]; + let result = elementwise_max_impl(&a, &b); + assert_eq!(result, vec![4, 5, 6]); + } + + #[test] + fn test_elementwise_max_impl_equal() { + let a = [2, 2, 2]; + let b = [2, 2, 2]; + let result = elementwise_max_impl(&a, &b); + assert_eq!(result, vec![2, 2, 2]); + } + + #[test] + fn test_elementwise_max_impl_negative_values() { + let a = [-1, -5, 3]; + let b = [4, -2, -6]; + let result = elementwise_max_impl(&a, &b); + assert_eq!(result, vec![4, -2, 3]); + } + + #[test] + fn test_elementwise_max_impl_empty() { + let a: [i32; 0] = []; + let b: [i32; 0] = []; + let result = elementwise_max_impl(&a, &b); + assert_eq!(result, Vec::::new()); + } + + #[test] + fn test_elementwise_max_impl_f64() { + let a = [1.5, -2.0, 3.0]; + let b = [2.5, -3.0, 2.0]; + let result = elementwise_max_impl(&a, &b); + assert_eq!(result, vec![2.5, -2.0, 3.0]); + } + + // -- elementwise_max_into_impl -- + + #[test] + fn test_elementwise_max_into_impl_basic() { + let a = [1, 5, 3]; + let b = [4, 2, 6]; + let mut out = [0; 3]; + elementwise_max_into_impl(&a, &b, &mut out); + assert_eq!(out, [4, 5, 6]); + } + + #[test] + fn test_elementwise_max_into_impl_equal() { + let a = [2, 2, 2]; + let b = [2, 2, 2]; + let mut out = [0; 3]; + elementwise_max_into_impl(&a, &b, &mut out); + assert_eq!(out, [2, 2, 2]); + } + + #[test] + fn test_elementwise_max_into_impl_negative_values() { + let a = [-1, -5, 3]; + let b = [4, -2, -6]; + let mut out = [0; 3]; + elementwise_max_into_impl(&a, &b, &mut out); + assert_eq!(out, [4, -2, 3]); + } + + #[test] + fn test_elementwise_max_into_impl_empty() { + let a: [i32; 0] = []; + let b: [i32; 0] = []; + let mut out: [i32; 0] = []; + elementwise_max_into_impl(&a, &b, &mut out); + assert_eq!(out, []); + } + + #[test] + fn test_elementwise_max_into_impl_f64() { + let a = [1.5, -2.0, 3.0]; + let b = [2.5, -3.0, 2.0]; + let mut out = [0.0; 3]; + elementwise_max_into_impl(&a, &b, &mut out); + assert_eq!(out, [2.5, -2.0, 3.0]); + } + // -- lerp_impl -- + #[test] fn test_lerp_impl_basic() { let a = [1.0f32, 2.0, 3.0]; diff --git a/src/types/vectorslice.rs b/src/types/vectorslice.rs index 80a43fc..a69c3c2 100644 --- a/src/types/vectorslice.rs +++ b/src/types/vectorslice.rs @@ -7,7 +7,9 @@ use crate::types::traits::{ VectorBase, VectorBaseMut, VectorOps, VectorOpsMut, VectorOrientationName, }; use crate::types::utils::{ - cross_impl, dot_impl, dot_to_f64_impl, mut_translate_impl, translate_impl, + cross_impl, cross_into_impl, dot_impl, dot_to_f64_impl, elementwise_max_impl, + elementwise_max_into_impl, elementwise_min_impl, elementwise_min_into_impl, mut_translate_impl, + translate_impl, }; use std::fmt; @@ -168,6 +170,21 @@ where Ok(out) } + #[inline] + fn translate_into(&self, other: &Self, out: &mut [T]) -> Result<(), VectorError> + where + T: num::Num + Copy, + { + self.check_same_length_and_raise(other)?; + if out.len() != self.len() { + return Err(VectorError::MismatchedLengthError( + "Output buffer has wrong length".to_string(), + )); + } + translate_impl(self, other, out); + Ok(()) + } + #[inline] fn dot(&self, other: &Self) -> Result where @@ -203,19 +220,43 @@ where Ok(result.into_iter().collect()) } + #[inline] + fn cross_into(&self, other: &Self, out: &mut [T]) -> Result<(), VectorError> + where + T: num::Num + Copy, + { + if self.len() != 3 || other.len() != 3 || out.len() != 3 { + return Err(VectorError::OutOfRangeError( + "Cross product is only defined for 3D vectors".to_string(), + )); + } + let a = self.as_slice(); + let b = other.as_slice(); + cross_into_impl(a, b, out); + Ok(()) + } + #[inline] fn elementwise_min(&self, other: &Self) -> Result where T: PartialOrd + Clone, { self.check_same_length_and_raise(other)?; - let elements: Vec = self - .as_slice() - .iter() - .zip(other.as_slice()) - .map(|(a, b)| if a < b { a.clone() } else { b.clone() }) - .collect(); - Ok(FlexVector::from_vec(elements)) + Ok(FlexVector::from_vec(elementwise_min_impl(self.as_slice(), other.as_slice()))) + } + + #[inline] + fn elementwise_min_into(&self, other: &Self, out: &mut [T]) -> Result<(), VectorError> + where + T: PartialOrd + Clone, + { + if self.len() != other.len() || out.len() != self.len() { + return Err(VectorError::MismatchedLengthError( + "Vectors must have the same length".to_string(), + )); + } + elementwise_min_into_impl(self.as_slice(), other.as_slice(), out); + Ok(()) } #[inline] @@ -224,13 +265,21 @@ where T: PartialOrd + Clone, { self.check_same_length_and_raise(other)?; - let elements: Vec = self - .as_slice() - .iter() - .zip(other.as_slice()) - .map(|(a, b)| if a > b { a.clone() } else { b.clone() }) - .collect(); - Ok(FlexVector::from_vec(elements)) + Ok(FlexVector::from_vec(elementwise_max_impl(self.as_slice(), other.as_slice()))) + } + + #[inline] + fn elementwise_max_into(&self, other: &Self, out: &mut [T]) -> Result<(), VectorError> + where + T: PartialOrd + Clone, + { + if self.len() != other.len() || out.len() != self.len() { + return Err(VectorError::MismatchedLengthError( + "Vectors must have the same length".to_string(), + )); + } + elementwise_max_into_impl(self.as_slice(), other.as_slice(), out); + Ok(()) } } @@ -397,6 +446,21 @@ where Ok(out) } + #[inline] + fn translate_into(&self, other: &Self, out: &mut [T]) -> Result<(), VectorError> + where + T: num::Num + Copy, + { + self.check_same_length_and_raise(other)?; + if out.len() != self.len() { + return Err(VectorError::MismatchedLengthError( + "Output buffer has wrong length".to_string(), + )); + } + translate_impl(self, other, out); + Ok(()) + } + #[inline] fn dot(&self, other: &Self) -> Result where @@ -432,19 +496,43 @@ where Ok(result.into_iter().collect()) } + #[inline] + fn cross_into(&self, other: &Self, out: &mut [T]) -> Result<(), VectorError> + where + T: num::Num + Copy, + { + if self.len() != 3 || other.len() != 3 || out.len() != 3 { + return Err(VectorError::OutOfRangeError( + "Cross product is only defined for 3D vectors".to_string(), + )); + } + let a = self.as_slice(); + let b = other.as_slice(); + cross_into_impl(a, b, out); + Ok(()) + } + #[inline] fn elementwise_min(&self, other: &Self) -> Result where T: PartialOrd + Clone, { self.check_same_length_and_raise(other)?; - let elements: Vec = self - .as_slice() - .iter() - .zip(other.as_slice()) - .map(|(a, b)| if a < b { a.clone() } else { b.clone() }) - .collect(); - Ok(FlexVector::from_vec(elements)) + Ok(FlexVector::from_vec(elementwise_min_impl(self.as_slice(), other.as_slice()))) + } + + #[inline] + fn elementwise_min_into(&self, other: &Self, out: &mut [T]) -> Result<(), VectorError> + where + T: PartialOrd + Clone, + { + if self.len() != other.len() || out.len() != self.len() { + return Err(VectorError::MismatchedLengthError( + "Vectors must have the same length".to_string(), + )); + } + elementwise_min_into_impl(self.as_slice(), other.as_slice(), out); + Ok(()) } #[inline] @@ -453,13 +541,21 @@ where T: PartialOrd + Clone, { self.check_same_length_and_raise(other)?; - let elements: Vec = self - .as_slice() - .iter() - .zip(other.as_slice()) - .map(|(a, b)| if a > b { a.clone() } else { b.clone() }) - .collect(); - Ok(FlexVector::from_vec(elements)) + Ok(FlexVector::from_vec(elementwise_max_impl(self.as_slice(), other.as_slice()))) + } + + #[inline] + fn elementwise_max_into(&self, other: &Self, out: &mut [T]) -> Result<(), VectorError> + where + T: PartialOrd + Clone, + { + if self.len() != other.len() || out.len() != self.len() { + return Err(VectorError::MismatchedLengthError( + "Vectors must have the same length".to_string(), + )); + } + elementwise_max_into_impl(self.as_slice(), other.as_slice(), out); + Ok(()) } } @@ -880,6 +976,8 @@ mod tests { // -- VectorOps trait for VectorSlice -- + // -- translate -- + #[test] fn test_vector_slice_translate() { let a = [1, 2, 3]; @@ -900,6 +998,51 @@ mod tests { assert!(result.is_err()); } + // -- translate_into -- + + #[test] + fn test_vector_slice_translate_into_basic() { + let a = [1, 2, 3]; + let b = [4, 5, 6]; + let vslice_a: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&a, 0..3); + let vslice_b = VectorSlice::from_range(&b, 0..3); + let mut out = [0; 3]; + vslice_a.translate_into(&vslice_b, &mut out).unwrap(); + assert_eq!(out, [5, 7, 9]); + } + + #[test] + fn test_vector_slice_translate_into_empty() { + let a: [i32; 0] = []; + let b: [i32; 0] = []; + let vslice_a: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&a, 0..0); + let vslice_b = VectorSlice::from_range(&b, 0..0); + let mut out: [i32; 0] = []; + vslice_a.translate_into(&vslice_b, &mut out).unwrap(); + assert_eq!(out, []); + } + + #[test] + fn test_vector_slice_translate_into_mismatched_length() { + let a = [1, 2, 3]; + let b = [4, 5]; + let vslice_a: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&a, 0..3); + let vslice_b = VectorSlice::from_range(&b, 0..2); + let mut out = [0; 3]; + let result = vslice_a.translate_into(&vslice_b, &mut out); + assert!(result.is_err()); + + let a = [1, 2, 3]; + let b = [4, 5, 6]; + let vslice_a: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&a, 0..3); + let vslice_b = VectorSlice::from_range(&b, 0..3); + let mut out = [0; 2]; + let result = vslice_a.translate_into(&vslice_b, &mut out); + assert!(result.is_err()); + } + + // -- dot -- + #[test] fn test_vector_slice_dot() { let a = [1, 2, 3]; @@ -920,6 +1063,8 @@ mod tests { assert!(result.is_err()); } + // -- dot_to_f64 -- + #[test] fn test_vector_slice_dot_to_f64() { let a = [1, 2, 3]; @@ -940,6 +1085,8 @@ mod tests { assert!(result.is_err()); } + // -- cross -- + #[test] fn test_vector_slice_cross() { let a = [1, 2, 3]; @@ -960,6 +1107,51 @@ mod tests { assert!(result.is_err()); } + // -- cross_into -- + + #[test] + fn test_vector_slice_cross_into_basic() { + let a = [1, 2, 3]; + let b = [4, 5, 6]; + let vslice_a: VectorSlice<'_, i32, Row> = VectorSlice::from_range(&a, 0..3); + let vslice_b = VectorSlice::from_range(&b, 0..3); + let mut out = [0; 3]; + vslice_a.cross_into(&vslice_b, &mut out).unwrap(); + assert_eq!(out, [-3, 6, -3]); + } + + #[test] + fn test_vector_slice_cross_into_incorrect_length() { + let a = [1, 2, 3, 4]; + let b = [4, 5, 6, 7]; + let vslice_a: VectorSlice<'_, i32, Row> = VectorSlice::from_range(&a, 0..4); + let vslice_b = VectorSlice::from_range(&b, 0..3); + let mut out = [0; 3]; + let result = vslice_a.cross_into(&vslice_b, &mut out); + assert!(result.is_err()); + + let a = [1, 2, 3]; + let b = [4, 5, 6]; + let vslice_a: VectorSlice<'_, i32, Row> = VectorSlice::from_range(&a, 0..3); + let vslice_b = VectorSlice::from_range(&b, 0..3); + let mut out = [0; 2]; + let result = vslice_a.cross_into(&vslice_b, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_cross_into_empty() { + let a: [i32; 0] = []; + let b: [i32; 0] = []; + let vslice_a: VectorSlice<'_, i32, Row> = VectorSlice::from_range(&a, 0..0); + let vslice_b = VectorSlice::from_range(&b, 0..0); + let mut out: [i32; 0] = []; + let result = vslice_a.cross_into(&vslice_b, &mut out); + assert!(result.is_err()); + } + + // -- elementwise_min -- + #[test] fn test_vector_slice_elementwise_min() { let a = [1, 5, 3]; @@ -980,6 +1172,62 @@ mod tests { assert!(result.is_err()); } + // -- elementwise_min_into -- + + #[test] + fn test_vector_slice_elementwise_min_into_basic() { + let a = [1, 5, 3]; + let b = [4, 2, 6]; + let vslice_a: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&a, 0..3); + let vslice_b = VectorSlice::from_range(&b, 0..3); + let mut out = [0; 3]; + vslice_a.elementwise_min_into(&vslice_b, &mut out).unwrap(); + assert_eq!(out, [1, 2, 3]); + } + + #[test] + fn test_vector_slice_elementwise_min_into_equal() { + let a = [2, 2, 2]; + let b = [2, 2, 2]; + let vslice_a: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&a, 0..3); + let vslice_b = VectorSlice::from_range(&b, 0..3); + let mut out = [0; 3]; + vslice_a.elementwise_min_into(&vslice_b, &mut out).unwrap(); + assert_eq!(out, [2, 2, 2]); + } + + #[test] + fn test_vector_slice_elementwise_min_into_mismatched_length() { + let a = [1, 5, 3]; + let b = [4, 2]; + let vslice_a: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&a, 0..3); + let vslice_b = VectorSlice::from_range(&b, 0..2); + let mut out = [0; 3]; + let result = vslice_a.elementwise_min_into(&vslice_b, &mut out); + assert!(result.is_err()); + + let a = [1, 5, 3]; + let b = [4, 2, 6]; + let vslice_a: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&a, 0..3); + let vslice_b = VectorSlice::from_range(&b, 0..3); + let mut out = [0; 2]; + let result = vslice_a.elementwise_min_into(&vslice_b, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_elementwise_min_into_empty() { + let a: [i32; 0] = []; + let b: [i32; 0] = []; + let vslice_a: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&a, 0..0); + let vslice_b = VectorSlice::from_range(&b, 0..0); + let mut out: [i32; 0] = []; + vslice_a.elementwise_min_into(&vslice_b, &mut out).unwrap(); + assert_eq!(out, []); + } + + // -- elementwise_max -- + #[test] fn test_vector_slice_elementwise_max() { let a = [1, 5, 3]; @@ -1000,6 +1248,60 @@ mod tests { assert!(result.is_err()); } + // -- elementwise_max_into -- + + #[test] + fn test_vector_slice_elementwise_max_into_basic() { + let a = [1, 5, 3]; + let b = [4, 2, 6]; + let vslice_a: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&a, 0..3); + let vslice_b = VectorSlice::from_range(&b, 0..3); + let mut out = [0; 3]; + vslice_a.elementwise_max_into(&vslice_b, &mut out).unwrap(); + assert_eq!(out, [4, 5, 6]); + } + + #[test] + fn test_vector_slice_elementwise_max_into_equal() { + let a = [2, 2, 2]; + let b = [2, 2, 2]; + let vslice_a: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&a, 0..3); + let vslice_b = VectorSlice::from_range(&b, 0..3); + let mut out = [0; 3]; + vslice_a.elementwise_max_into(&vslice_b, &mut out).unwrap(); + assert_eq!(out, [2, 2, 2]); + } + + #[test] + fn test_vector_slice_elementwise_max_into_mismatched_length() { + let a = [1, 5, 3]; + let b = [4, 2]; + let vslice_a: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&a, 0..3); + let vslice_b = VectorSlice::from_range(&b, 0..2); + let mut out = [0; 3]; + let result = vslice_a.elementwise_max_into(&vslice_b, &mut out); + assert!(result.is_err()); + + let a = [1, 5, 3]; + let b = [4, 2, 6]; + let vslice_a: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&a, 0..3); + let vslice_b = VectorSlice::from_range(&b, 0..3); + let mut out = [0; 2]; + let result = vslice_a.elementwise_max_into(&vslice_b, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_elementwise_max_into_empty() { + let a: [i32; 0] = []; + let b: [i32; 0] = []; + let vslice_a: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&a, 0..0); + let vslice_b = VectorSlice::from_range(&b, 0..0); + let mut out: [i32; 0] = []; + vslice_a.elementwise_max_into(&vslice_b, &mut out).unwrap(); + assert_eq!(out, []); + } + // ///////////////////////////////// // ================================ // @@ -1387,6 +1689,8 @@ mod tests { // -- VectorOps trait for VectorSliceMut -- + // -- translate -- + #[test] fn test_vector_slice_mut_translate() { let mut a = [1, 2, 3]; @@ -1407,6 +1711,51 @@ mod tests { assert!(result.is_err()); } + // -- translate_into -- + + #[test] + fn test_vector_slice_mut_translate_into_basic() { + let mut a = [1, 2, 3]; + let mut b = [4, 5, 6]; + let vslice_a: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut a, 0..3); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..3); + let mut out = [0; 3]; + vslice_a.translate_into(&vslice_b, &mut out).unwrap(); + assert_eq!(out, [5, 7, 9]); + } + + #[test] + fn test_vector_slice_mut_translate_into_empty() { + let mut a: [i32; 0] = []; + let mut b: [i32; 0] = []; + let vslice_a: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut a, 0..0); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..0); + let mut out: [i32; 0] = []; + vslice_a.translate_into(&vslice_b, &mut out).unwrap(); + assert_eq!(out, []); + } + + #[test] + fn test_vector_slice_mut_translate_into_mismatched_length() { + let mut a = [1, 2, 3]; + let mut b = [4, 5]; + let vslice_a: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut a, 0..3); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + let mut out = [0; 3]; + let result = vslice_a.translate_into(&vslice_b, &mut out); + assert!(result.is_err()); + + let mut a = [1, 2, 3]; + let mut b = [4, 5, 6]; + let vslice_a: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut a, 0..3); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..3); + let mut out = [0; 2]; + let result = vslice_a.translate_into(&vslice_b, &mut out); + assert!(result.is_err()); + } + + // -- dot -- + #[test] fn test_vector_slice_mut_dot() { let mut a = [1, 2, 3]; @@ -1447,6 +1796,8 @@ mod tests { assert!(result.is_err()); } + // -- cross -- + #[test] fn test_vector_slice_mut_cross() { let mut a = [1, 2, 3]; @@ -1467,6 +1818,51 @@ mod tests { assert!(result.is_err()); } + // -- cross_into -- + + #[test] + fn test_vector_slice_mut_cross_into_basic() { + let mut a = [1, 2, 3]; + let mut b = [4, 5, 6]; + let vslice_a: VectorSliceMut<'_, i32, Row> = VectorSliceMut::from_range(&mut a, 0..3); + let vslice_b: VectorSliceMut<'_, i32, Row> = VectorSliceMut::from_range(&mut b, 0..3); + let mut out = [0; 3]; + vslice_a.cross_into(&vslice_b, &mut out).unwrap(); + assert_eq!(out, [-3, 6, -3]); + } + + #[test] + fn test_vector_slice_mut_cross_into_incorrect_length() { + let mut a = [1, 2, 3, 4]; + let mut b = [4, 5, 6, 7]; + let vslice_a: VectorSliceMut<'_, i32, Row> = VectorSliceMut::from_range(&mut a, 0..4); + let vslice_b: VectorSliceMut<'_, i32, Row> = VectorSliceMut::from_range(&mut b, 0..3); + let mut out = [0; 3]; + let result = vslice_a.cross_into(&vslice_b, &mut out); + assert!(result.is_err()); + + let mut a = [1, 2, 3]; + let mut b = [4, 5, 6]; + let vslice_a: VectorSliceMut<'_, i32, Row> = VectorSliceMut::from_range(&mut a, 0..3); + let vslice_b: VectorSliceMut<'_, i32, Row> = VectorSliceMut::from_range(&mut b, 0..3); + let mut out = [0; 2]; + let result = vslice_a.cross_into(&vslice_b, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_mut_cross_into_empty() { + let mut a: [i32; 0] = []; + let mut b: [i32; 0] = []; + let vslice_a: VectorSliceMut<'_, i32, Row> = VectorSliceMut::from_range(&mut a, 0..0); + let vslice_b: VectorSliceMut<'_, i32, Row> = VectorSliceMut::from_range(&mut b, 0..0); + let mut out: [i32; 0] = []; + let result = vslice_a.cross_into(&vslice_b, &mut out); + assert!(result.is_err()); + } + + // -- elementwise_min -- + #[test] fn test_vector_slice_mut_elementwise_min() { let mut a = [1, 5, 3]; @@ -1487,6 +1883,62 @@ mod tests { assert!(result.is_err()); } + // -- elementwise_min_into -- + + #[test] + fn test_vector_slice_mut_elementwise_min_into_basic() { + let mut a = [1, 5, 3]; + let mut b = [4, 2, 6]; + let vslice_a: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut a, 0..3); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..3); + let mut out = [0; 3]; + vslice_a.elementwise_min_into(&vslice_b, &mut out).unwrap(); + assert_eq!(out, [1, 2, 3]); + } + + #[test] + fn test_vector_slice_mut_elementwise_min_into_equal() { + let mut a = [2, 2, 2]; + let mut b = [2, 2, 2]; + let vslice_a: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut a, 0..3); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..3); + let mut out = [0; 3]; + vslice_a.elementwise_min_into(&vslice_b, &mut out).unwrap(); + assert_eq!(out, [2, 2, 2]); + } + + #[test] + fn test_vector_slice_mut_elementwise_min_into_mismatched_length() { + let mut a = [1, 5, 3]; + let mut b = [4, 2]; + let vslice_a: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut a, 0..3); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + let mut out = [0; 3]; + let result = vslice_a.elementwise_min_into(&vslice_b, &mut out); + assert!(result.is_err()); + + let mut a = [1, 5, 3]; + let mut b = [4, 2, 6]; + let vslice_a: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut a, 0..3); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..3); + let mut out = [0; 2]; + let result = vslice_a.elementwise_min_into(&vslice_b, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_mut_elementwise_min_into_empty() { + let mut a: [i32; 0] = []; + let mut b: [i32; 0] = []; + let vslice_a: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut a, 0..0); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..0); + let mut out: [i32; 0] = []; + vslice_a.elementwise_min_into(&vslice_b, &mut out).unwrap(); + assert_eq!(out, []); + } + + // -- elementwise_max -- + #[test] fn test_vector_slice_mut_elementwise_max() { let mut a = [1, 5, 3]; @@ -1507,6 +1959,60 @@ mod tests { assert!(result.is_err()); } + // -- elementwise_max_into -- + + #[test] + fn test_vector_slice_mut_elementwise_max_into_basic() { + let mut a = [1, 5, 3]; + let mut b = [4, 2, 6]; + let vslice_a: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut a, 0..3); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..3); + let mut out = [0; 3]; + vslice_a.elementwise_max_into(&vslice_b, &mut out).unwrap(); + assert_eq!(out, [4, 5, 6]); + } + + #[test] + fn test_vector_slice_mut_elementwise_max_into_equal() { + let mut a = [2, 2, 2]; + let mut b = [2, 2, 2]; + let vslice_a: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut a, 0..3); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..3); + let mut out = [0; 3]; + vslice_a.elementwise_max_into(&vslice_b, &mut out).unwrap(); + assert_eq!(out, [2, 2, 2]); + } + + #[test] + fn test_vector_slice_mut_elementwise_max_into_mismatched_length() { + let mut a = [1, 5, 3]; + let mut b = [4, 2]; + let vslice_a: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut a, 0..3); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + let mut out = [0; 3]; + let result = vslice_a.elementwise_max_into(&vslice_b, &mut out); + assert!(result.is_err()); + + let mut a = [1, 5, 3]; + let mut b = [4, 2, 6]; + let vslice_a: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut a, 0..3); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..3); + let mut out = [0; 2]; + let result = vslice_a.elementwise_max_into(&vslice_b, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_mut_elementwise_max_into_empty() { + let mut a: [i32; 0] = []; + let mut b: [i32; 0] = []; + let vslice_a: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut a, 0..0); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..0); + let mut out: [i32; 0] = []; + vslice_a.elementwise_max_into(&vslice_b, &mut out).unwrap(); + assert_eq!(out, []); + } + // -- VectorOpsMut trait for VectorSliceMut -- #[test] From cac27be2cd080a15f1103956af15b0e893223324 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Wed, 28 May 2025 20:37:55 -0400 Subject: [PATCH 61/82] update trait bounds on normalize and lerp --- src/types/flexvector.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index 6d578b6..1361fe0 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -895,6 +895,7 @@ where fn normalize(&self) -> Result where T: Copy + PartialEq + std::ops::Div + num::Zero, + Self::Output: std::iter::FromIterator, { normalize_impl(self.as_slice(), self.norm()) } @@ -916,7 +917,7 @@ where #[inline] fn lerp(&self, end: &Self, weight: T) -> Result where - T: num::Float + Copy, + T: num::Float + Clone, { self.check_same_length_and_raise(end)?; if weight < T::zero() || weight > T::one() { From 863d3fbb90b6f99a729d53ed3a931ad49f997b5b Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Wed, 28 May 2025 20:38:20 -0400 Subject: [PATCH 62/82] impl VectorOpsFloat on VectorSlice and VectorSliceMut --- src/types/vectorslice.rs | 565 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 561 insertions(+), 4 deletions(-) diff --git a/src/types/vectorslice.rs b/src/types/vectorslice.rs index a69c3c2..3a0f6ea 100644 --- a/src/types/vectorslice.rs +++ b/src/types/vectorslice.rs @@ -4,12 +4,15 @@ use crate::errors::VectorError; use crate::types::flexvector::FlexVector; use crate::types::orientation::Column; use crate::types::traits::{ - VectorBase, VectorBaseMut, VectorOps, VectorOpsMut, VectorOrientationName, + VectorBase, VectorBaseMut, VectorOps, VectorOpsFloat, VectorOpsFloatMut, VectorOpsMut, + VectorOrientationName, }; use crate::types::utils::{ - cross_impl, cross_into_impl, dot_impl, dot_to_f64_impl, elementwise_max_impl, - elementwise_max_into_impl, elementwise_min_impl, elementwise_min_into_impl, mut_translate_impl, - translate_impl, + angle_with_impl, chebyshev_distance_impl, cosine_similarity_impl, cross_impl, cross_into_impl, + distance_impl, dot_impl, dot_to_f64_impl, elementwise_max_impl, elementwise_max_into_impl, + elementwise_min_impl, elementwise_min_into_impl, lerp_impl, manhattan_distance_impl, + minkowski_distance_impl, mut_translate_impl, normalize_impl, normalize_to_impl, + project_onto_impl, translate_impl, }; use std::fmt; @@ -283,6 +286,148 @@ where } } +impl<'a, T, O> VectorOpsFloat for VectorSlice<'a, T, O> +where + T: num::Float + Clone + std::iter::Sum, +{ + type Output = FlexVector; + + #[inline] + fn normalize(&self) -> Result + where + T: Copy + PartialEq + std::ops::Div + num::Zero, + Self::Output: std::iter::FromIterator, + { + normalize_impl(self.as_slice(), self.norm()) + } + + #[inline] + fn normalize_to(&self, magnitude: T) -> Result + where + T: Copy + + PartialEq + + std::ops::Div + + std::ops::Mul + + num::Zero, + Self::Output: std::iter::FromIterator, + { + normalize_to_impl(self.as_slice(), self.norm(), magnitude) + } + + #[inline] + fn lerp(&self, end: &Self, weight: T) -> Result + where + T: num::Float + Clone, + { + self.check_same_length_and_raise(end)?; + if weight < T::zero() || weight > T::one() { + return Err(VectorError::OutOfRangeError("weight must be in [0, 1]".to_string())); + } + let mut out = FlexVector::zero(self.len()); + lerp_impl(self.as_slice(), end.as_slice(), weight, out.as_mut_slice()); + Ok(out) + } + + #[inline] + fn midpoint(&self, other: &Self) -> Result + where + T: num::Float + Clone, + { + self.check_same_length_and_raise(other)?; + let mut out = FlexVector::zero(self.len()); + lerp_impl(self.as_slice(), other.as_slice(), T::from(0.5).unwrap(), out.as_mut_slice()); + Ok(out) + } + + #[inline] + fn distance(&self, other: &Self) -> Result + where + T: num::Float + Clone + std::iter::Sum, + { + self.check_same_length_and_raise(other)?; + Ok(distance_impl(self.as_slice(), other.as_slice())) + } + + #[inline] + fn manhattan_distance(&self, other: &Self) -> Result + where + T: num::Float + Clone + std::iter::Sum, + { + self.check_same_length_and_raise(other)?; + Ok(manhattan_distance_impl(self.as_slice(), other.as_slice())) + } + + #[inline] + fn chebyshev_distance(&self, other: &Self) -> Result + where + T: num::Float + Clone + PartialOrd, + { + self.check_same_length_and_raise(other)?; + Ok(chebyshev_distance_impl(self.as_slice(), other.as_slice())) + } + + #[inline] + fn minkowski_distance(&self, other: &Self, p: T) -> Result + where + T: num::Float + Clone + std::iter::Sum, + { + self.check_same_length_and_raise(other)?; + if p < T::one() { + return Err(VectorError::OutOfRangeError("p must be >= 1".to_string())); + } + Ok(minkowski_distance_impl(self.as_slice(), other.as_slice(), p)) + } + + #[inline] + fn angle_with(&self, other: &Self) -> Result + where + T: num::Float + Clone + std::iter::Sum, + { + self.check_same_length_and_raise(other)?; + let norm_self = self.norm(); + let norm_other = other.norm(); + if norm_self == T::zero() || norm_other == T::zero() { + return Err(VectorError::ZeroVectorError( + "Cannot compute angle with zero vector".to_string(), + )); + } + Ok(angle_with_impl(self.as_slice(), other.as_slice(), norm_self, norm_other)) + } + + #[inline] + fn project_onto(&self, other: &Self) -> Result + where + T: num::Float + Clone + std::iter::Sum, + Self::Output: std::iter::FromIterator, + { + self.check_same_length_and_raise(other)?; + let denom = dot_impl(other.as_slice(), other.as_slice()); + if denom == T::zero() { + return Err(VectorError::ZeroVectorError( + "Cannot project onto zero vector".to_string(), + )); + } + let scalar = dot_impl(self.as_slice(), other.as_slice()) / denom; + Ok(project_onto_impl(other.as_slice(), scalar)) + } + + #[inline] + fn cosine_similarity(&self, other: &Self) -> Result + where + T: num::Float + Clone + std::iter::Sum + std::ops::Div, + { + self.check_same_length_and_raise(other)?; + let norm_self = self.norm(); + let norm_other = other.norm(); + if norm_self == T::zero() || norm_other == T::zero() { + return Err(VectorError::ZeroVectorError( + "Cannot compute cosine similarity with zero vector".to_string(), + )); + } + Ok(cosine_similarity_impl(self.as_slice(), other.as_slice(), norm_self, norm_other)) + } +} + // ================================ // // Methods @@ -576,6 +721,148 @@ where } } +impl<'a, T, O> VectorOpsFloat for VectorSliceMut<'a, T, O> +where + T: num::Float + Clone + std::iter::Sum, +{ + type Output = FlexVector; + + #[inline] + fn normalize(&self) -> Result + where + T: Copy + PartialEq + std::ops::Div + num::Zero, + Self::Output: std::iter::FromIterator, + { + normalize_impl(self.as_slice(), self.norm()) + } + + #[inline] + fn normalize_to(&self, magnitude: T) -> Result + where + T: Copy + + PartialEq + + std::ops::Div + + std::ops::Mul + + num::Zero, + Self::Output: std::iter::FromIterator, + { + normalize_to_impl(self.as_slice(), self.norm(), magnitude) + } + + #[inline] + fn lerp(&self, end: &Self, weight: T) -> Result + where + T: num::Float + Clone, + { + self.check_same_length_and_raise(end)?; + if weight < T::zero() || weight > T::one() { + return Err(VectorError::OutOfRangeError("weight must be in [0, 1]".to_string())); + } + let mut out = FlexVector::zero(self.len()); + lerp_impl(self.as_slice(), end.as_slice(), weight, out.as_mut_slice()); + Ok(out) + } + + #[inline] + fn midpoint(&self, other: &Self) -> Result + where + T: num::Float + Clone, + { + self.check_same_length_and_raise(other)?; + let mut out = FlexVector::zero(self.len()); + lerp_impl(self.as_slice(), other.as_slice(), T::from(0.5).unwrap(), out.as_mut_slice()); + Ok(out) + } + + #[inline] + fn distance(&self, other: &Self) -> Result + where + T: num::Float + Clone + std::iter::Sum, + { + self.check_same_length_and_raise(other)?; + Ok(distance_impl(self.as_slice(), other.as_slice())) + } + + #[inline] + fn manhattan_distance(&self, other: &Self) -> Result + where + T: num::Float + Clone + std::iter::Sum, + { + self.check_same_length_and_raise(other)?; + Ok(manhattan_distance_impl(self.as_slice(), other.as_slice())) + } + + #[inline] + fn chebyshev_distance(&self, other: &Self) -> Result + where + T: num::Float + Clone + PartialOrd, + { + self.check_same_length_and_raise(other)?; + Ok(chebyshev_distance_impl(self.as_slice(), other.as_slice())) + } + + #[inline] + fn minkowski_distance(&self, other: &Self, p: T) -> Result + where + T: num::Float + Clone + std::iter::Sum, + { + self.check_same_length_and_raise(other)?; + if p < T::one() { + return Err(VectorError::OutOfRangeError("p must be >= 1".to_string())); + } + Ok(minkowski_distance_impl(self.as_slice(), other.as_slice(), p)) + } + + #[inline] + fn angle_with(&self, other: &Self) -> Result + where + T: num::Float + Clone + std::iter::Sum, + { + self.check_same_length_and_raise(other)?; + let norm_self = self.norm(); + let norm_other = other.norm(); + if norm_self == T::zero() || norm_other == T::zero() { + return Err(VectorError::ZeroVectorError( + "Cannot compute angle with zero vector".to_string(), + )); + } + Ok(angle_with_impl(self.as_slice(), other.as_slice(), norm_self, norm_other)) + } + + #[inline] + fn project_onto(&self, other: &Self) -> Result + where + T: num::Float + Clone + std::iter::Sum, + Self::Output: std::iter::FromIterator, + { + self.check_same_length_and_raise(other)?; + let denom = dot_impl(other.as_slice(), other.as_slice()); + if denom == T::zero() { + return Err(VectorError::ZeroVectorError( + "Cannot project onto zero vector".to_string(), + )); + } + let scalar = dot_impl(self.as_slice(), other.as_slice()) / denom; + Ok(project_onto_impl(other.as_slice(), scalar)) + } + + #[inline] + fn cosine_similarity(&self, other: &Self) -> Result + where + T: num::Float + Clone + std::iter::Sum + std::ops::Div, + { + self.check_same_length_and_raise(other)?; + let norm_self = self.norm(); + let norm_other = other.norm(); + if norm_self == T::zero() || norm_other == T::zero() { + return Err(VectorError::ZeroVectorError( + "Cannot compute cosine similarity with zero vector".to_string(), + )); + } + Ok(cosine_similarity_impl(self.as_slice(), other.as_slice(), norm_self, norm_other)) + } +} + // ================================ // // Methods @@ -1302,6 +1589,141 @@ mod tests { assert_eq!(out, []); } + // -- VectorOpsFloat trait for VectorSlice -- + + #[test] + fn test_vector_slice_normalize() { + let a = [3.0, 4.0]; + let vslice: VectorSlice<'_, f64, Column> = VectorSlice::from_range(&a, 0..2); + let result = vslice.normalize().unwrap(); + let expected = [0.6, 0.8]; + for (x, y) in result.as_slice().iter().zip(expected.iter()) { + assert!((x - y).abs() < 1e-8); + } + } + + #[test] + fn test_vector_slice_normalize_zero_vector() { + let a = [0.0, 0.0]; + let vslice: VectorSlice<'_, f64, Column> = VectorSlice::from_range(&a, 0..2); + let result = vslice.normalize(); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_normalize_to() { + let a = [3.0, 4.0]; + let vslice: VectorSlice<'_, f64, Column> = VectorSlice::from_range(&a, 0..2); + let result = vslice.normalize_to(10.0).unwrap(); + let expected = [6.0, 8.0]; + for (x, y) in result.as_slice().iter().zip(expected.iter()) { + assert!((x - y).abs() < 1e-8); + } + } + + #[test] + fn test_vector_slice_lerp() { + let a = [1.0, 2.0, 3.0]; + let b = [4.0, 5.0, 6.0]; + let vslice_a: VectorSlice<'_, f64, Column> = VectorSlice::from_range(&a, 0..3); + let vslice_b = VectorSlice::from_range(&b, 0..3); + let result = vslice_a.lerp(&vslice_b, 0.5).unwrap(); + assert_eq!(result.as_slice(), &[2.5, 3.5, 4.5]); + } + + #[test] + fn test_vector_slice_lerp_weight_out_of_bounds() { + let a = [1.0, 2.0]; + let b = [3.0, 4.0]; + let vslice_a: VectorSlice<'_, f64, Column> = VectorSlice::from_range(&a, 0..2); + let vslice_b = VectorSlice::from_range(&b, 0..2); + assert!(vslice_a.lerp(&vslice_b, -0.1).is_err()); + assert!(vslice_a.lerp(&vslice_b, 1.1).is_err()); + } + + #[test] + fn test_vector_slice_midpoint() { + let a = [1.0, 2.0, 3.0]; + let b = [4.0, 5.0, 6.0]; + let vslice_a: VectorSlice<'_, f64, Row> = VectorSlice::from_range(&a, 0..3); + let vslice_b = VectorSlice::from_range(&b, 0..3); + let result = vslice_a.midpoint(&vslice_b).unwrap(); + assert_eq!(result.as_slice(), &[2.5, 3.5, 4.5]); + } + + #[test] + fn test_vector_slice_distance() { + let a = [1.0, 2.0, 3.0]; + let b = [4.0, 6.0, 3.0]; + let vslice_a: VectorSlice<'_, f64, Row> = VectorSlice::from_range(&a, 0..3); + let vslice_b = VectorSlice::from_range(&b, 0..3); + let result = vslice_a.distance(&vslice_b).unwrap(); + assert!((result - 5.0).abs() < 1e-8); + } + + #[test] + fn test_vector_slice_manhattan_distance() { + let a = [1.0, 2.0, 3.0]; + let b = [4.0, 6.0, 3.0]; + let vslice_a: VectorSlice<'_, f64, Row> = VectorSlice::from_range(&a, 0..3); + let vslice_b = VectorSlice::from_range(&b, 0..3); + let result = vslice_a.manhattan_distance(&vslice_b).unwrap(); + assert!((result - 7.0).abs() < 1e-8); + } + + #[test] + fn test_vector_slice_chebyshev_distance() { + let a = [1.0, 2.0, 3.0]; + let b = [4.0, 6.0, 3.0]; + let vslice_a: VectorSlice<'_, f64, Row> = VectorSlice::from_range(&a, 0..3); + let vslice_b = VectorSlice::from_range(&b, 0..3); + let result = vslice_a.chebyshev_distance(&vslice_b).unwrap(); + assert!((result - 4.0).abs() < 1e-8); + } + + #[test] + fn test_vector_slice_minkowski_distance() { + let a = [1.0, 2.0, 3.0]; + let b = [4.0, 6.0, 3.0]; + let vslice_a: VectorSlice<'_, f64, Row> = VectorSlice::from_range(&a, 0..3); + let vslice_b = VectorSlice::from_range(&b, 0..3); + let result = vslice_a.minkowski_distance(&vslice_b, 3.0).unwrap(); + assert!((result - 4.497941445275415).abs() < 1e-8); + } + + #[test] + fn test_vector_slice_angle_with() { + let a = [1.0, 0.0]; + let b = [0.0, 1.0]; + let vslice_a: VectorSlice<'_, f64, Row> = VectorSlice::from_range(&a, 0..2); + let vslice_b = VectorSlice::from_range(&b, 0..2); + let result = vslice_a.angle_with(&vslice_b).unwrap(); + assert!((result - std::f64::consts::FRAC_PI_2).abs() < 1e-8); + } + + #[test] + fn test_vector_slice_project_onto() { + let a = [3.0, 4.0]; + let b = [6.0, 8.0]; + let vslice_a: VectorSlice<'_, f64, Column> = VectorSlice::from_range(&a, 0..2); + let vslice_b = VectorSlice::from_range(&b, 0..2); + let result = vslice_a.project_onto(&vslice_b).unwrap(); + let expected = [3.0, 4.0]; + for (x, y) in result.as_slice().iter().zip(expected.iter()) { + assert!((x - y).abs() < 1e-8); + } + } + + #[test] + fn test_vector_slice_cosine_similarity() { + let a = [1.0, 0.0]; + let b = [0.0, 1.0]; + let vslice_a: VectorSlice<'_, f64, Row> = VectorSlice::from_range(&a, 0..2); + let vslice_b = VectorSlice::from_range(&b, 0..2); + let result = vslice_a.cosine_similarity(&vslice_b).unwrap(); + assert!((result - 0.0).abs() < 1e-8); + } + // ///////////////////////////////// // ================================ // @@ -2064,4 +2486,139 @@ mod tests { assert_eq!(vslice.as_slice(), [0, 0, 0]); assert_eq!(a, [0, 0, 0]); } + + // -- VectorOpsFloat trait for VectorSliceMut -- + + #[test] + fn test_vector_slice_mut_normalize() { + let mut a = [3.0, 4.0]; + let vslice: VectorSliceMut<'_, f64, Column> = VectorSliceMut::from_range(&mut a, 0..2); + let result = vslice.normalize().unwrap(); + let expected = [0.6, 0.8]; + for (x, y) in result.as_slice().iter().zip(expected.iter()) { + assert!((x - y).abs() < 1e-8); + } + } + + #[test] + fn test_vector_slice_mut_normalize_zero_vector() { + let mut a = [0.0, 0.0]; + let vslice: VectorSliceMut<'_, f64, Column> = VectorSliceMut::from_range(&mut a, 0..2); + let result = vslice.normalize(); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_mut_normalize_to() { + let mut a = [3.0, 4.0]; + let vslice: VectorSliceMut<'_, f64, Column> = VectorSliceMut::from_range(&mut a, 0..2); + let result = vslice.normalize_to(10.0).unwrap(); + let expected = [6.0, 8.0]; + for (x, y) in result.as_slice().iter().zip(expected.iter()) { + assert!((x - y).abs() < 1e-8); + } + } + + #[test] + fn test_vector_slice_mut_lerp() { + let mut a = [1.0, 2.0, 3.0]; + let mut b = [4.0, 5.0, 6.0]; + let vslice_a: VectorSliceMut<'_, f64, Column> = VectorSliceMut::from_range(&mut a, 0..3); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..3); + let result = vslice_a.lerp(&vslice_b, 0.5).unwrap(); + assert_eq!(result.as_slice(), &[2.5, 3.5, 4.5]); + } + + #[test] + fn test_vector_slice_mut_lerp_weight_out_of_bounds() { + let mut a = [1.0, 2.0]; + let mut b = [3.0, 4.0]; + let vslice_a: VectorSliceMut<'_, f64, Column> = VectorSliceMut::from_range(&mut a, 0..2); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + assert!(vslice_a.lerp(&vslice_b, -0.1).is_err()); + assert!(vslice_a.lerp(&vslice_b, 1.1).is_err()); + } + + #[test] + fn test_vector_slice_mut_midpoint() { + let mut a = [1.0, 2.0, 3.0]; + let mut b = [4.0, 5.0, 6.0]; + let vslice_a: VectorSliceMut<'_, f64, Row> = VectorSliceMut::from_range(&mut a, 0..3); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..3); + let result = vslice_a.midpoint(&vslice_b).unwrap(); + assert_eq!(result.as_slice(), &[2.5, 3.5, 4.5]); + } + + #[test] + fn test_vector_slice_mut_distance() { + let mut a = [1.0, 2.0, 3.0]; + let mut b = [4.0, 6.0, 3.0]; + let vslice_a: VectorSliceMut<'_, f64, Row> = VectorSliceMut::from_range(&mut a, 0..3); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..3); + let result = vslice_a.distance(&vslice_b).unwrap(); + assert!((result - 5.0).abs() < 1e-8); + } + + #[test] + fn test_vector_slice_mut_manhattan_distance() { + let mut a = [1.0, 2.0, 3.0]; + let mut b = [4.0, 6.0, 3.0]; + let vslice_a: VectorSliceMut<'_, f64, Row> = VectorSliceMut::from_range(&mut a, 0..3); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..3); + let result = vslice_a.manhattan_distance(&vslice_b).unwrap(); + assert!((result - 7.0).abs() < 1e-8); + } + + #[test] + fn test_vector_slice_mut_chebyshev_distance() { + let mut a = [1.0, 2.0, 3.0]; + let mut b = [4.0, 6.0, 3.0]; + let vslice_a: VectorSliceMut<'_, f64, Row> = VectorSliceMut::from_range(&mut a, 0..3); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..3); + let result = vslice_a.chebyshev_distance(&vslice_b).unwrap(); + assert!((result - 4.0).abs() < 1e-8); + } + + #[test] + fn test_vector_slice_mut_minkowski_distance() { + let mut a = [1.0, 2.0, 3.0]; + let mut b = [4.0, 6.0, 3.0]; + let vslice_a: VectorSliceMut<'_, f64, Row> = VectorSliceMut::from_range(&mut a, 0..3); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..3); + let result = vslice_a.minkowski_distance(&vslice_b, 3.0).unwrap(); + assert!((result - 4.497941445275415).abs() < 1e-8); + } + + #[test] + fn test_vector_slice_mut_angle_with() { + let mut a = [1.0, 0.0]; + let mut b = [0.0, 1.0]; + let vslice_a: VectorSliceMut<'_, f64, Row> = VectorSliceMut::from_range(&mut a, 0..2); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + let result = vslice_a.angle_with(&vslice_b).unwrap(); + assert!((result - std::f64::consts::FRAC_PI_2).abs() < 1e-8); + } + + #[test] + fn test_vector_slice_mut_project_onto() { + let mut a = [3.0, 4.0]; + let mut b = [6.0, 8.0]; + let vslice_a: VectorSliceMut<'_, f64, Column> = VectorSliceMut::from_range(&mut a, 0..2); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + let result = vslice_a.project_onto(&vslice_b).unwrap(); + let expected = [3.0, 4.0]; + for (x, y) in result.as_slice().iter().zip(expected.iter()) { + assert!((x - y).abs() < 1e-8); + } + } + + #[test] + fn test_vector_slice_mut_cosine_similarity() { + let mut a = [1.0, 0.0]; + let mut b = [0.0, 1.0]; + let vslice_a: VectorSliceMut<'_, f64, Row> = VectorSliceMut::from_range(&mut a, 0..2); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + let result = vslice_a.cosine_similarity(&vslice_b).unwrap(); + assert!((result - 0.0).abs() < 1e-8); + } } From 83cd33e76ba7c4e39c45e0449936fd17f8143ca6 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Wed, 28 May 2025 21:27:45 -0400 Subject: [PATCH 63/82] impl VectorOpsFloatMut on VectorSliceMut --- src/types/vectorslice.rs | 100 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 98 insertions(+), 2 deletions(-) diff --git a/src/types/vectorslice.rs b/src/types/vectorslice.rs index 3a0f6ea..dfe9252 100644 --- a/src/types/vectorslice.rs +++ b/src/types/vectorslice.rs @@ -11,8 +11,8 @@ use crate::types::utils::{ angle_with_impl, chebyshev_distance_impl, cosine_similarity_impl, cross_impl, cross_into_impl, distance_impl, dot_impl, dot_to_f64_impl, elementwise_max_impl, elementwise_max_into_impl, elementwise_min_impl, elementwise_min_into_impl, lerp_impl, manhattan_distance_impl, - minkowski_distance_impl, mut_translate_impl, normalize_impl, normalize_to_impl, - project_onto_impl, translate_impl, + minkowski_distance_impl, mut_lerp_impl, mut_normalize_impl, mut_normalize_to_impl, + mut_translate_impl, normalize_impl, normalize_to_impl, project_onto_impl, translate_impl, }; use std::fmt; @@ -863,6 +863,48 @@ where } } +impl<'a, T, O> VectorOpsFloatMut for VectorSliceMut<'a, T, O> +where + T: num::Float + Clone + std::iter::Sum, +{ + type Output = Self; + + #[inline] + fn mut_normalize(&mut self) -> Result<(), VectorError> + where + T: Copy + PartialEq + std::ops::Div + num::Zero, + { + let norm = self.norm(); + mut_normalize_impl(self.as_mut_slice(), norm) + } + + #[inline] + fn mut_normalize_to(&mut self, magnitude: T) -> Result<(), VectorError> + where + T: Copy + + PartialEq + + std::ops::Div + + std::ops::Mul + + num::Zero, + { + let n = self.norm(); + mut_normalize_to_impl(self.as_mut_slice(), n, magnitude) + } + + #[inline] + fn mut_lerp(&mut self, end: &Self, weight: T) -> Result<(), VectorError> + where + T: num::Float + Copy + PartialOrd, + { + self.check_same_length_and_raise(end)?; + if weight < T::zero() || weight > T::one() { + return Err(VectorError::OutOfRangeError("weight must be in [0, 1]".to_string())); + } + mut_lerp_impl(self.as_mut_slice(), end.as_slice(), weight); + Ok(()) + } +} + // ================================ // // Methods @@ -2621,4 +2663,58 @@ mod tests { let result = vslice_a.cosine_similarity(&vslice_b).unwrap(); assert!((result - 0.0).abs() < 1e-8); } + + // -- VectorOpsFloatMut trait for VectorSliceMut -- + + #[test] + fn test_vector_slice_mut_mut_normalize() { + let mut a = [3.0, 4.0]; + let mut vslice: VectorSliceMut<'_, f64, Column> = VectorSliceMut::from_range(&mut a, 0..2); + vslice.mut_normalize().unwrap(); + let expected = [0.6, 0.8]; + for (x, y) in vslice.as_slice().iter().zip(expected.iter()) { + assert!((x - y).abs() < 1e-8); + } + } + + #[test] + fn test_vector_slice_mut_mut_normalize_zero_vector() { + let mut a = [0.0, 0.0]; + let mut vslice: VectorSliceMut<'_, f64, Column> = VectorSliceMut::from_range(&mut a, 0..2); + let result = vslice.mut_normalize(); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_mut_mut_normalize_to() { + let mut a = [3.0, 4.0]; + let mut vslice: VectorSliceMut<'_, f64, Column> = VectorSliceMut::from_range(&mut a, 0..2); + vslice.mut_normalize_to(10.0).unwrap(); + let expected = [6.0, 8.0]; + for (x, y) in vslice.as_slice().iter().zip(expected.iter()) { + assert!((x - y).abs() < 1e-8); + } + } + + #[test] + fn test_vector_slice_mut_mut_lerp() { + let mut a = [1.0, 2.0, 3.0]; + let mut b = [4.0, 5.0, 6.0]; + let mut vslice_a: VectorSliceMut<'_, f64, Column> = + VectorSliceMut::from_range(&mut a, 0..3); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..3); + vslice_a.mut_lerp(&vslice_b, 0.5).unwrap(); + assert_eq!(vslice_a.as_slice(), &[2.5, 3.5, 4.5]); + } + + #[test] + fn test_vector_slice_mut_mut_lerp_weight_out_of_bounds() { + let mut a = [1.0, 2.0]; + let mut b = [3.0, 4.0]; + let mut vslice_a: VectorSliceMut<'_, f64, Column> = + VectorSliceMut::from_range(&mut a, 0..2); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + assert!(vslice_a.mut_lerp(&vslice_b, -0.1).is_err()); + assert!(vslice_a.mut_lerp(&vslice_b, 1.1).is_err()); + } } From 26587cd813d39504c18bb1565f05ff9426744f7c Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Thu, 29 May 2025 08:53:47 -0400 Subject: [PATCH 64/82] Impl VectorOpsComplex for VectorSlice and VectorSliceMut --- src/types/vectorslice.rs | 1064 +++++++++++++++++++++++++++++++++++++- 1 file changed, 1057 insertions(+), 7 deletions(-) diff --git a/src/types/vectorslice.rs b/src/types/vectorslice.rs index dfe9252..a1c8eb6 100644 --- a/src/types/vectorslice.rs +++ b/src/types/vectorslice.rs @@ -4,20 +4,25 @@ use crate::errors::VectorError; use crate::types::flexvector::FlexVector; use crate::types::orientation::Column; use crate::types::traits::{ - VectorBase, VectorBaseMut, VectorOps, VectorOpsFloat, VectorOpsFloatMut, VectorOpsMut, - VectorOrientationName, + VectorBase, VectorBaseMut, VectorOps, VectorOpsComplex, VectorOpsFloat, VectorOpsFloatMut, + VectorOpsMut, VectorOrientationName, }; use crate::types::utils::{ - angle_with_impl, chebyshev_distance_impl, cosine_similarity_impl, cross_impl, cross_into_impl, - distance_impl, dot_impl, dot_to_f64_impl, elementwise_max_impl, elementwise_max_into_impl, - elementwise_min_impl, elementwise_min_into_impl, lerp_impl, manhattan_distance_impl, - minkowski_distance_impl, mut_lerp_impl, mut_normalize_impl, mut_normalize_to_impl, - mut_translate_impl, normalize_impl, normalize_to_impl, project_onto_impl, translate_impl, + angle_with_impl, chebyshev_distance_complex_impl, chebyshev_distance_impl, + cosine_similarity_complex_impl, cosine_similarity_impl, cross_impl, cross_into_impl, + distance_complex_impl, distance_impl, dot_impl, dot_to_f64_impl, elementwise_max_impl, + elementwise_max_into_impl, elementwise_min_impl, elementwise_min_into_impl, hermitian_dot_impl, + lerp_impl, manhattan_distance_complex_impl, manhattan_distance_impl, + minkowski_distance_complex_impl, minkowski_distance_impl, mut_lerp_impl, mut_normalize_impl, + mut_normalize_to_impl, mut_translate_impl, normalize_impl, normalize_to_impl, + project_onto_impl, translate_impl, }; use std::fmt; use std::marker::PhantomData; +use num::{Complex, Zero}; + // ///////////////////////////////// // ================================ // @@ -428,6 +433,155 @@ where } } +impl<'a, N, O> VectorOpsComplex for VectorSlice<'a, Complex, O> +where + N: num::Float + Clone + std::iter::Sum, +{ + type Output = FlexVector, O>; + + #[inline] + fn normalize(&self) -> Result + where + Complex: Copy + PartialEq + std::ops::Div, Output = Complex>, + Self::Output: std::iter::FromIterator>, + { + normalize_impl(self.as_slice(), Complex::new(self.norm(), N::zero())) + } + + #[inline] + fn normalize_to(&self, magnitude: N) -> Result + where + Complex: Copy + + PartialEq + + std::ops::Div, Output = Complex> + + std::ops::Mul, Output = Complex> + + num::Zero, + Self::Output: std::iter::FromIterator>, + { + normalize_to_impl( + self.as_slice(), + Complex::new(self.norm(), N::zero()), + Complex::new(magnitude, N::zero()), + ) + } + + #[inline] + fn dot(&self, other: &Self) -> Result, VectorError> + where + N: num::Num + Copy + std::iter::Sum + std::ops::Neg, + { + self.check_same_length_and_raise(other)?; + Ok(hermitian_dot_impl(self.as_slice(), other.as_slice())) + } + + #[inline] + fn lerp(&self, end: &Self, weight: N) -> Result + where + N: num::Float + Clone + PartialOrd, + Complex: Copy + + std::ops::Add> + + std::ops::Mul> + + std::ops::Sub> + + num::One, + { + self.check_same_length_and_raise(end)?; + if weight < N::zero() || weight > N::one() { + return Err(VectorError::OutOfRangeError("weight must be in [0, 1]".to_string())); + } + let w = Complex::new(weight, N::zero()); + let mut out = FlexVector::zero(self.len()); + lerp_impl(self.as_slice(), end.as_slice(), w, out.as_mut_slice()); + Ok(out) + } + + #[inline] + fn midpoint(&self, end: &Self) -> Result + where + N: num::Float + Clone, + { + self.check_same_length_and_raise(end)?; + self.lerp(end, num::cast(0.5).unwrap()) + } + + #[inline] + fn distance(&self, other: &Self) -> Result + where + N: num::Float + Clone + std::iter::Sum, + { + self.check_same_length_and_raise(other)?; + Ok(distance_complex_impl(self.as_slice(), other.as_slice())) + } + + #[inline] + fn manhattan_distance(&self, other: &Self) -> Result + where + N: num::Float + Clone + std::iter::Sum, + { + self.check_same_length_and_raise(other)?; + Ok(manhattan_distance_complex_impl(self.as_slice(), other.as_slice())) + } + + #[inline] + fn chebyshev_distance(&self, other: &Self) -> Result + where + N: num::Float + Clone + PartialOrd, + { + self.check_same_length_and_raise(other)?; + Ok(chebyshev_distance_complex_impl(self.as_slice(), other.as_slice())) + } + + #[inline] + fn minkowski_distance(&self, other: &Self, p: N) -> Result + where + N: num::Float + Clone + std::iter::Sum, + { + self.check_same_length_and_raise(other)?; + if p < N::one() { + return Err(VectorError::OutOfRangeError("p must be >= 1".to_string())); + } + Ok(minkowski_distance_complex_impl(self.as_slice(), other.as_slice(), p)) + } + + #[inline] + fn project_onto(&self, other: &Self) -> Result + where + N: num::Float + Clone + std::iter::Sum + std::ops::Neg, + Complex: Copy + + std::ops::Mul> + + std::ops::Add> + + std::ops::Div, Output = Complex> + + num::Zero, + Self::Output: std::iter::FromIterator>, + { + self.check_same_length_and_raise(other)?; + let denom = hermitian_dot_impl(other.as_slice(), other.as_slice()); + if denom == Complex::::zero() { + return Err(VectorError::ZeroVectorError( + "Cannot project onto zero vector".to_string(), + )); + } + let scalar = hermitian_dot_impl(self.as_slice(), other.as_slice()) / denom; + Ok(project_onto_impl(other.as_slice(), scalar)) + } + + #[inline] + fn cosine_similarity(&self, other: &Self) -> Result, VectorError> + where + N: num::Float + Clone + std::iter::Sum + std::ops::Neg, + Complex: std::ops::Div>, + { + self.check_same_length_and_raise(other)?; + let norm_self = self.norm(); + let norm_other = other.norm(); + if norm_self == N::zero() || norm_other == N::zero() { + return Err(VectorError::ZeroVectorError( + "Cannot compute cosine similarity with zero vector".to_string(), + )); + } + Ok(cosine_similarity_complex_impl(self.as_slice(), other.as_slice(), norm_self, norm_other)) + } +} + // ================================ // // Methods @@ -905,6 +1059,155 @@ where } } +impl<'a, N, O> VectorOpsComplex for VectorSliceMut<'a, Complex, O> +where + N: num::Float + Clone + std::iter::Sum, +{ + type Output = FlexVector, O>; + + #[inline] + fn normalize(&self) -> Result + where + Complex: Copy + PartialEq + std::ops::Div, Output = Complex>, + Self::Output: std::iter::FromIterator>, + { + normalize_impl(self.as_slice(), Complex::new(self.norm(), N::zero())) + } + + #[inline] + fn normalize_to(&self, magnitude: N) -> Result + where + Complex: Copy + + PartialEq + + std::ops::Div, Output = Complex> + + std::ops::Mul, Output = Complex> + + num::Zero, + Self::Output: std::iter::FromIterator>, + { + normalize_to_impl( + self.as_slice(), + Complex::new(self.norm(), N::zero()), + Complex::new(magnitude, N::zero()), + ) + } + + #[inline] + fn dot(&self, other: &Self) -> Result, VectorError> + where + N: num::Num + Copy + std::iter::Sum + std::ops::Neg, + { + self.check_same_length_and_raise(other)?; + Ok(hermitian_dot_impl(self.as_slice(), other.as_slice())) + } + + #[inline] + fn lerp(&self, end: &Self, weight: N) -> Result + where + N: num::Float + Clone + PartialOrd, + Complex: Copy + + std::ops::Add> + + std::ops::Mul> + + std::ops::Sub> + + num::One, + { + self.check_same_length_and_raise(end)?; + if weight < N::zero() || weight > N::one() { + return Err(VectorError::OutOfRangeError("weight must be in [0, 1]".to_string())); + } + let w = Complex::new(weight, N::zero()); + let mut out = FlexVector::zero(self.len()); + lerp_impl(self.as_slice(), end.as_slice(), w, out.as_mut_slice()); + Ok(out) + } + + #[inline] + fn midpoint(&self, end: &Self) -> Result + where + N: num::Float + Clone, + { + self.check_same_length_and_raise(end)?; + self.lerp(end, num::cast(0.5).unwrap()) + } + + #[inline] + fn distance(&self, other: &Self) -> Result + where + N: num::Float + Clone + std::iter::Sum, + { + self.check_same_length_and_raise(other)?; + Ok(distance_complex_impl(self.as_slice(), other.as_slice())) + } + + #[inline] + fn manhattan_distance(&self, other: &Self) -> Result + where + N: num::Float + Clone + std::iter::Sum, + { + self.check_same_length_and_raise(other)?; + Ok(manhattan_distance_complex_impl(self.as_slice(), other.as_slice())) + } + + #[inline] + fn chebyshev_distance(&self, other: &Self) -> Result + where + N: num::Float + Clone + PartialOrd, + { + self.check_same_length_and_raise(other)?; + Ok(chebyshev_distance_complex_impl(self.as_slice(), other.as_slice())) + } + + #[inline] + fn minkowski_distance(&self, other: &Self, p: N) -> Result + where + N: num::Float + Clone + std::iter::Sum, + { + self.check_same_length_and_raise(other)?; + if p < N::one() { + return Err(VectorError::OutOfRangeError("p must be >= 1".to_string())); + } + Ok(minkowski_distance_complex_impl(self.as_slice(), other.as_slice(), p)) + } + + #[inline] + fn project_onto(&self, other: &Self) -> Result + where + N: num::Float + Clone + std::iter::Sum + std::ops::Neg, + Complex: Copy + + std::ops::Mul> + + std::ops::Add> + + std::ops::Div, Output = Complex> + + num::Zero, + Self::Output: std::iter::FromIterator>, + { + self.check_same_length_and_raise(other)?; + let denom = hermitian_dot_impl(other.as_slice(), other.as_slice()); + if denom == Complex::::zero() { + return Err(VectorError::ZeroVectorError( + "Cannot project onto zero vector".to_string(), + )); + } + let scalar = hermitian_dot_impl(self.as_slice(), other.as_slice()) / denom; + Ok(project_onto_impl(other.as_slice(), scalar)) + } + + #[inline] + fn cosine_similarity(&self, other: &Self) -> Result, VectorError> + where + N: num::Float + Clone + std::iter::Sum + std::ops::Neg, + Complex: std::ops::Div>, + { + self.check_same_length_and_raise(other)?; + let norm_self = self.norm(); + let norm_other = other.norm(); + if norm_self == N::zero() || norm_other == N::zero() { + return Err(VectorError::ZeroVectorError( + "Cannot compute cosine similarity with zero vector".to_string(), + )); + } + Ok(cosine_similarity_complex_impl(self.as_slice(), other.as_slice(), norm_self, norm_other)) + } +} + // ================================ // // Methods @@ -1766,6 +2069,373 @@ mod tests { assert!((result - 0.0).abs() < 1e-8); } + // -- VectorOpsComplex for VectorSlice -- + + // -- normalize -- + + #[test] + fn test_vector_slice_complex_normalize() { + use num::Complex; + let a = [Complex::new(3.0, 4.0), Complex::new(0.0, 0.0)]; + let vslice: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..2); + // The norm is sqrt(|3+4i|^2 + |0|^2) = sqrt(25) = 5 + let result = vslice.normalize().unwrap(); + let expected = [Complex::new(3.0 / 5.0, 4.0 / 5.0), Complex::new(0.0, 0.0)]; + for (x, y) in result.as_slice().iter().zip(expected.iter()) { + assert!((x.re - y.re).abs() < 1e-8); + assert!((x.im - y.im).abs() < 1e-8); + } + } + + #[test] + fn test_vector_slice_complex_normalize_zero_vector() { + use num::Complex; + let a = [Complex::new(0.0, 0.0), Complex::new(0.0, 0.0)]; + let vslice: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..2); + let result = vslice.normalize(); + assert!(result.is_err()); + } + + // -- normalize_to -- + + #[test] + fn test_vector_slice_complex_normalize_to() { + use num::Complex; + let a = [Complex::new(3.0, 4.0), Complex::new(0.0, 0.0)]; + let vslice: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..2); + // The norm is 5, so scaling to magnitude 10 multiplies by 2 + let result = vslice.normalize_to(10.0).unwrap(); + let expected = [Complex::new(6.0, 8.0), Complex::new(0.0, 0.0)]; + for (x, y) in result.as_slice().iter().zip(expected.iter()) { + assert!((x.re - y.re).abs() < 1e-8); + assert!((x.im - y.im).abs() < 1e-8); + } + } + + #[test] + fn test_vector_slice_complex_normalize_to_zero_vector() { + use num::Complex; + let a = [Complex::new(0.0, 0.0), Complex::new(0.0, 0.0)]; + let vslice: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..2); + let result = vslice.normalize_to(10.0); + assert!(result.is_err()); + } + + // -- dot -- + + #[test] + fn test_vector_slice_complex_dot_basic() { + use num::Complex; + let a = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; + let b = [Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]; + let vslice_a: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..2); + let vslice_b = VectorSlice::from_range(&b, 0..2); + // Hermitian dot: conj(a0)*b0 + conj(a1)*b1 + let expected = a[0].conj() * b[0] + a[1].conj() * b[1]; + let result = VectorOpsComplex::dot(&vslice_a, &vslice_b).unwrap(); + assert!((result.re - expected.re).abs() < 1e-12); + assert!((result.im - expected.im).abs() < 1e-12); + } + + #[test] + fn test_vector_slice_complex_dot_negative_values() { + use num::Complex; + let a = [Complex::new(-1.0, -2.0), Complex::new(-3.0, -4.0)]; + let b = [Complex::new(2.0, 1.0), Complex::new(4.0, 3.0)]; + let vslice_a: VectorSlice<'_, Complex, Row> = VectorSlice::from_range(&a, 0..2); + let vslice_b = VectorSlice::from_range(&b, 0..2); + let expected = a[0].conj() * b[0] + a[1].conj() * b[1]; + let result = VectorOpsComplex::dot(&vslice_a, &vslice_b).unwrap(); + assert!((result.re - expected.re).abs() < 1e-12); + assert!((result.im - expected.im).abs() < 1e-12); + } + + #[test] + fn test_vector_slice_complex_dot_zero_vector() { + use num::Complex; + let a = [Complex::new(0.0, 0.0), Complex::new(0.0, 0.0)]; + let b = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; + let vslice_a: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..2); + let vslice_b = VectorSlice::from_range(&b, 0..2); + let result = VectorOpsComplex::dot(&vslice_a, &vslice_b).unwrap(); + assert!((result.re).abs() < 1e-12); + assert!((result.im).abs() < 1e-12); + } + + #[test] + fn test_vector_slice_complex_dot_empty() { + use num::Complex; + let a: [Complex; 0] = []; + let b: [Complex; 0] = []; + let vslice_a: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..0); + let vslice_b = VectorSlice::from_range(&b, 0..0); + let result = VectorOpsComplex::dot(&vslice_a, &vslice_b).unwrap(); + assert!((result.re).abs() < 1e-12); + assert!((result.im).abs() < 1e-12); + } + + #[test] + fn test_vector_slice_complex_dot_mismatched_length() { + use num::Complex; + let a = [Complex::new(1.0, 2.0)]; + let b = [Complex::new(3.0, 4.0), Complex::new(5.0, 6.0)]; + let vslice_a: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..1); + let vslice_b = VectorSlice::from_range(&b, 0..2); + let result = VectorOpsComplex::dot(&vslice_a, &vslice_b); + assert!(result.is_err()); + } + + // -- lerp -- + + #[test] + fn test_vector_slice_complex_lerp() { + use num::Complex; + let a = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; + let b = [Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]; + let vslice_a: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..2); + let vslice_b = VectorSlice::from_range(&b, 0..2); + // Lerp with weight 0.25 + let result = vslice_a.lerp(&vslice_b, 0.25).unwrap(); + let expected = [ + Complex::new(1.0 + 0.25 * (5.0 - 1.0), 2.0 + 0.25 * (6.0 - 2.0)), + Complex::new(3.0 + 0.25 * (7.0 - 3.0), 4.0 + 0.25 * (8.0 - 4.0)), + ]; + for (x, y) in result.as_slice().iter().zip(expected.iter()) { + assert!((x.re - y.re).abs() < 1e-8); + assert!((x.im - y.im).abs() < 1e-8); + } + } + + #[test] + fn test_vector_slice_complex_lerp_weight_out_of_bounds() { + use num::Complex; + let a = [Complex::new(1.0, 2.0)]; + let b = [Complex::new(3.0, 4.0)]; + let vslice_a: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..1); + let vslice_b = VectorSlice::from_range(&b, 0..1); + assert!(vslice_a.lerp(&vslice_b, -0.1).is_err()); + assert!(vslice_a.lerp(&vslice_b, 1.1).is_err()); + } + + // -- midpoint -- + + #[test] + fn test_vector_slice_complex_midpoint() { + use num::Complex; + let a = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; + let b = [Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]; + let vslice_a: VectorSlice<'_, Complex, Row> = VectorSlice::from_range(&a, 0..2); + let vslice_b = VectorSlice::from_range(&b, 0..2); + let result = vslice_a.midpoint(&vslice_b).unwrap(); + let expected = [ + Complex::new((1.0 + 5.0) / 2.0, (2.0 + 6.0) / 2.0), + Complex::new((3.0 + 7.0) / 2.0, (4.0 + 8.0) / 2.0), + ]; + for (x, y) in result.as_slice().iter().zip(expected.iter()) { + assert!((x.re - y.re).abs() < 1e-8); + assert!((x.im - y.im).abs() < 1e-8); + } + } + + // -- distance -- + + #[test] + fn test_vector_slice_complex_distance() { + use num::Complex; + let a = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; + let b = [Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]; + let vslice_a: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..2); + let vslice_b = VectorSlice::from_range(&b, 0..2); + // Euclidean distance: sqrt(sum_i |a[i] - b[i]|^2) + let d0 = (a[0] - b[0]).norm_sqr(); + let d1 = (a[1] - b[1]).norm_sqr(); + let expected = (d0 + d1).sqrt(); + let dist = vslice_a.distance(&vslice_b).unwrap(); + assert!((dist - expected).abs() < 1e-12); + } + + // -- manhattan_distance -- + + #[test] + fn test_vector_slice_complex_manhattan_distance() { + use num::Complex; + let a = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; + let b = [Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]; + let vslice_a: VectorSlice<'_, Complex, Row> = VectorSlice::from_range(&a, 0..2); + let vslice_b = VectorSlice::from_range(&b, 0..2); + // Manhattan distance: sum_i |a[i] - b[i]| + let d0 = (a[0] - b[0]).norm(); + let d1 = (a[1] - b[1]).norm(); + let expected = d0 + d1; + let dist = vslice_a.manhattan_distance(&vslice_b).unwrap(); + assert!((dist - expected).abs() < 1e-12); + } + + // -- chebyshev_distance -- + + #[test] + fn test_vector_slice_complex_chebyshev_distance() { + use num::Complex; + let a = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; + let b = [Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]; + let vslice_a: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..2); + let vslice_b = VectorSlice::from_range(&b, 0..2); + // Chebyshev distance: max_i |a[i] - b[i]| + let d0 = (a[0] - b[0]).norm(); + let d1 = (a[1] - b[1]).norm(); + let expected = d0.max(d1); + let dist = vslice_a.chebyshev_distance(&vslice_b).unwrap(); + assert!((dist - expected).abs() < 1e-12); + } + + // -- minkowski_distance -- + + #[test] + fn test_vector_slice_complex_minkowski_distance() { + use num::Complex; + let a = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; + let b = [Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]; + let vslice_a: VectorSlice<'_, Complex, Row> = VectorSlice::from_range(&a, 0..2); + let vslice_b = VectorSlice::from_range(&b, 0..2); + let p = 3.0; + // Minkowski distance: (|a[0]-b[0]|^p + |a[1]-b[1]|^p)^(1/p) + let d0 = (a[0] - b[0]).norm().powf(p); + let d1 = (a[1] - b[1]).norm().powf(p); + let expected = (d0 + d1).powf(1.0 / p); + let dist = vslice_a.minkowski_distance(&vslice_b, p).unwrap(); + assert!((dist - expected).abs() < 1e-12); + } + + // -- project_onto -- + + #[test] + fn test_vector_slice_complex_project_onto_basic() { + use num::Complex; + let a = [Complex::new(3.0, 4.0), Complex::new(0.0, 0.0)]; + let b = [Complex::new(1.0, 0.0), Complex::new(0.0, 0.0)]; + let vslice_a: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..2); + let vslice_b = VectorSlice::from_range(&b, 0..2); + // Project a onto b: should be [3.0 - 4.0i, 0.0] + let proj = vslice_a.project_onto(&vslice_b).unwrap(); + assert!((proj.as_slice()[0] - Complex::new(3.0, -4.0)).norm() < 1e-12); + assert!((proj.as_slice()[1] - Complex::new(0.0, 0.0)).norm() < 1e-12); + } + + #[test] + fn test_vector_slice_complex_project_onto_parallel() { + use num::Complex; + let a = [Complex::new(2.0, 2.0), Complex::new(4.0, 4.0)]; + let b = [Complex::new(1.0, 1.0), Complex::new(2.0, 2.0)]; + let vslice_a: VectorSlice<'_, Complex, Row> = VectorSlice::from_range(&a, 0..2); + let vslice_b = VectorSlice::from_range(&b, 0..2); + let proj = vslice_a.project_onto(&vslice_b).unwrap(); + assert!((proj.as_slice()[0] - Complex::new(2.0, 2.0)).norm() < 1e-12); + assert!((proj.as_slice()[1] - Complex::new(4.0, 4.0)).norm() < 1e-12); + } + + #[test] + fn test_vector_slice_complex_project_onto_orthogonal() { + use num::Complex; + let a = [Complex::new(0.0, 1.0), Complex::new(0.0, 0.0)]; + let b = [Complex::new(1.0, 0.0), Complex::new(0.0, 0.0)]; + let vslice_a: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..2); + let vslice_b = VectorSlice::from_range(&b, 0..2); + let proj = vslice_a.project_onto(&vslice_b).unwrap(); + assert!((proj.as_slice()[0] - Complex::new(0.0, -1.0)).norm() < 1e-12); + assert!((proj.as_slice()[1] - Complex::new(0.0, 0.0)).norm() < 1e-12); + } + + #[test] + fn test_vector_slice_complex_project_onto_identical() { + use num::Complex; + let a = [Complex::new(5.0, 5.0), Complex::new(5.0, 5.0)]; + let b = [Complex::new(5.0, 5.0), Complex::new(5.0, 5.0)]; + let vslice_a: VectorSlice<'_, Complex, Row> = VectorSlice::from_range(&a, 0..2); + let vslice_b = VectorSlice::from_range(&b, 0..2); + let proj = vslice_a.project_onto(&vslice_b).unwrap(); + assert!((proj.as_slice()[0] - Complex::new(5.0, 5.0)).norm() < 1e-12); + assert!((proj.as_slice()[1] - Complex::new(5.0, 5.0)).norm() < 1e-12); + } + + #[test] + fn test_vector_slice_complex_project_onto_zero_vector() { + use num::Complex; + let a = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; + let b = [Complex::new(0.0, 0.0), Complex::new(0.0, 0.0)]; + let vslice_a: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..2); + let vslice_b = VectorSlice::from_range(&b, 0..2); + let result = vslice_a.project_onto(&vslice_b); + assert!(result.is_err()); + } + + // -- cosine_similarity -- + + #[test] + fn test_vector_slice_complex_cosine_similarity_parallel() { + use num::Complex; + let a = [Complex::new(1.0, 2.0), Complex::new(2.0, 4.0)]; + let b = [Complex::new(2.0, 4.0), Complex::new(4.0, 8.0)]; + let vslice_a: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..2); + let vslice_b = VectorSlice::from_range(&b, 0..2); + let cos_sim = vslice_a.cosine_similarity(&vslice_b).unwrap(); + assert!((cos_sim - Complex::new(1.0, 0.0)).norm() < 1e-12); + } + + #[test] + fn test_vector_slice_complex_cosine_similarity_orthogonal() { + use num::Complex; + let a = [Complex::new(1.0, 0.0), Complex::new(0.0, 0.0)]; + let b = [Complex::new(0.0, 0.0), Complex::new(1.0, 0.0)]; + let vslice_a: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..2); + let vslice_b = VectorSlice::from_range(&b, 0..2); + let cos_sim = vslice_a.cosine_similarity(&vslice_b).unwrap(); + assert!((cos_sim - Complex::new(0.0, 0.0)).norm() < 1e-12); + } + + #[test] + fn test_vector_slice_complex_cosine_similarity_opposite() { + use num::Complex; + let a = [Complex::new(1.0, 0.0)]; + let b = [Complex::new(-1.0, 0.0)]; + let vslice_a: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..1); + let vslice_b = VectorSlice::from_range(&b, 0..1); + let cos_sim = vslice_a.cosine_similarity(&vslice_b).unwrap(); + assert!((cos_sim + Complex::new(1.0, 0.0)).norm() < 1e-12); + } + + #[test] + fn test_vector_slice_complex_cosine_similarity_identical() { + use num::Complex; + let a = [Complex::new(3.0, 4.0)]; + let b = [Complex::new(3.0, 4.0)]; + let vslice_a: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..1); + let vslice_b = VectorSlice::from_range(&b, 0..1); + let cos_sim = vslice_a.cosine_similarity(&vslice_b).unwrap(); + assert!((cos_sim - Complex::new(1.0, 0.0)).norm() < 1e-12); + } + + #[test] + fn test_vector_slice_complex_cosine_similarity_arbitrary() { + use num::Complex; + let a = [Complex::new(1.0, 2.0)]; + let b = [Complex::new(2.0, 1.0)]; + let vslice_a: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..1); + let vslice_b = VectorSlice::from_range(&b, 0..1); + let cos_sim = vslice_a.cosine_similarity(&vslice_b).unwrap(); + assert!(cos_sim.norm() <= 1.0 + 1e-12); + } + + #[test] + fn test_vector_slice_complex_cosine_similarity_zero_vector() { + use num::Complex; + let a = [Complex::new(0.0, 0.0)]; + let b = [Complex::new(1.0, 2.0)]; + let vslice_a: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..1); + let vslice_b = VectorSlice::from_range(&b, 0..1); + let result = vslice_a.cosine_similarity(&vslice_b); + assert!(result.is_err()); + } + // ///////////////////////////////// // ================================ // @@ -2717,4 +3387,384 @@ mod tests { assert!(vslice_a.mut_lerp(&vslice_b, -0.1).is_err()); assert!(vslice_a.mut_lerp(&vslice_b, 1.1).is_err()); } + + // -- VectorOpsComplex trait for VectorSliceMut -- + + // -- normalize -- + + #[test] + fn test_vector_slice_mut_complex_normalize() { + use num::Complex; + let mut a = [Complex::new(3.0, 4.0), Complex::new(0.0, 0.0)]; + let vslice: VectorSliceMut<'_, Complex, Column> = + VectorSliceMut::from_range(&mut a, 0..2); + // The norm is sqrt(|3+4i|^2 + |0|^2) = sqrt(25) = 5 + let result = vslice.normalize().unwrap(); + let expected = [Complex::new(3.0 / 5.0, 4.0 / 5.0), Complex::new(0.0, 0.0)]; + for (x, y) in result.as_slice().iter().zip(expected.iter()) { + assert!((x.re - y.re).abs() < 1e-8); + assert!((x.im - y.im).abs() < 1e-8); + } + } + + #[test] + fn test_vector_slice_mut_complex_normalize_zero_vector() { + use num::Complex; + let mut a = [Complex::new(0.0, 0.0), Complex::new(0.0, 0.0)]; + let vslice: VectorSliceMut<'_, Complex, Column> = + VectorSliceMut::from_range(&mut a, 0..2); + let result = vslice.normalize(); + assert!(result.is_err()); + } + + // -- normalize_to -- + + #[test] + fn test_vector_slice_mut_complex_normalize_to() { + use num::Complex; + let mut a = [Complex::new(3.0, 4.0), Complex::new(0.0, 0.0)]; + let vslice: VectorSliceMut<'_, Complex, Column> = + VectorSliceMut::from_range(&mut a, 0..2); + // The norm is 5, so scaling to magnitude 10 multiplies by 2 + let result = vslice.normalize_to(10.0).unwrap(); + let expected = [Complex::new(6.0, 8.0), Complex::new(0.0, 0.0)]; + for (x, y) in result.as_slice().iter().zip(expected.iter()) { + assert!((x.re - y.re).abs() < 1e-8); + assert!((x.im - y.im).abs() < 1e-8); + } + } + + #[test] + fn test_vector_slice_mut_complex_normalize_to_zero_vector() { + use num::Complex; + let mut a = [Complex::new(0.0, 0.0), Complex::new(0.0, 0.0)]; + let vslice: VectorSliceMut<'_, Complex, Column> = + VectorSliceMut::from_range(&mut a, 0..2); + let result = vslice.normalize_to(10.0); + assert!(result.is_err()); + } + + // -- dot -- + + #[test] + fn test_vector_slice_mut_complex_dot_basic() { + use num::Complex; + let mut a = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; + let mut b = [Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]; + // Compute expected before mutable borrow + let expected = a[0].conj() * b[0] + a[1].conj() * b[1]; + let vslice_a: VectorSliceMut<'_, Complex, Column> = + VectorSliceMut::from_range(&mut a, 0..2); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + let result = VectorOpsComplex::dot(&vslice_a, &vslice_b).unwrap(); + assert!((result.re - expected.re).abs() < 1e-12); + assert!((result.im - expected.im).abs() < 1e-12); + } + + #[test] + fn test_vector_slice_mut_complex_dot_mismatched_length() { + use num::Complex; + let mut a = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; + let mut b = [Complex::new(5.0, 6.0)]; + let vslice_a: VectorSliceMut<'_, Complex, Column> = + VectorSliceMut::from_range(&mut a, 0..2); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..1); + let result = VectorOpsComplex::dot(&vslice_a, &vslice_b); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_mut_complex_dot_zero() { + use num::Complex; + let mut a = [Complex::new(0.0, 0.0), Complex::new(0.0, 0.0)]; + let mut b = [Complex::new(0.0, 0.0), Complex::new(0.0, 0.0)]; + let vslice_a: VectorSliceMut<'_, Complex, Column> = + VectorSliceMut::from_range(&mut a, 0..2); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + let result = VectorOpsComplex::dot(&vslice_a, &vslice_b).unwrap(); + assert!((result.re).abs() < 1e-12); + assert!((result.im).abs() < 1e-12); + } + + #[test] + fn test_vector_slice_mut_complex_dot_empty() { + use num::Complex; + let mut a: [Complex; 0] = []; + let mut b: [Complex; 0] = []; + let vslice_a: VectorSliceMut<'_, Complex, Column> = + VectorSliceMut::from_range(&mut a, 0..0); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..0); + let result = VectorOpsComplex::dot(&vslice_a, &vslice_b).unwrap(); + assert!((result.re).abs() < 1e-12); + assert!((result.im).abs() < 1e-12); + } + + // -- lerp -- + + #[test] + fn test_vector_slice_mut_complex_lerp() { + use num::Complex; + let mut a = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; + let mut b = [Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]; + let vslice_a: VectorSliceMut<'_, Complex, Column> = + VectorSliceMut::from_range(&mut a, 0..2); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + // Lerp with weight 0.25 + let result = vslice_a.lerp(&vslice_b, 0.25).unwrap(); + let expected = [ + Complex::new(1.0 + 0.25 * (5.0 - 1.0), 2.0 + 0.25 * (6.0 - 2.0)), + Complex::new(3.0 + 0.25 * (7.0 - 3.0), 4.0 + 0.25 * (8.0 - 4.0)), + ]; + for (x, y) in result.as_slice().iter().zip(expected.iter()) { + assert!((x.re - y.re).abs() < 1e-8); + assert!((x.im - y.im).abs() < 1e-8); + } + } + + #[test] + fn test_vector_slice_mut_complex_lerp_weight_out_of_bounds() { + use num::Complex; + let mut a = [Complex::new(1.0, 2.0)]; + let mut b = [Complex::new(3.0, 4.0)]; + let vslice_a: VectorSliceMut<'_, Complex, Column> = + VectorSliceMut::from_range(&mut a, 0..1); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..1); + assert!(vslice_a.lerp(&vslice_b, -0.1).is_err()); + assert!(vslice_a.lerp(&vslice_b, 1.1).is_err()); + } + + // -- midpoint -- + + #[test] + fn test_vector_slice_mut_complex_midpoint() { + use num::Complex; + let mut a = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; + let mut b = [Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]; + let vslice_a: VectorSliceMut<'_, Complex, Row> = + VectorSliceMut::from_range(&mut a, 0..2); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + let result = vslice_a.midpoint(&vslice_b).unwrap(); + let expected = [ + Complex::new((1.0 + 5.0) / 2.0, (2.0 + 6.0) / 2.0), + Complex::new((3.0 + 7.0) / 2.0, (4.0 + 8.0) / 2.0), + ]; + for (x, y) in result.as_slice().iter().zip(expected.iter()) { + assert!((x.re - y.re).abs() < 1e-8); + assert!((x.im - y.im).abs() < 1e-8); + } + } + + // -- distance -- + + #[test] + fn test_vector_slice_mut_complex_distance() { + use num::Complex; + let mut a = [Complex::new(1.0_f64, 2.0), Complex::new(3.0, 4.0)]; + let mut b = [Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]; + // Euclidean distance: sqrt(sum_i |a[i] - b[i]|^2) + let d0 = (a[0] - b[0]).norm_sqr(); + let d1 = (a[1] - b[1]).norm_sqr(); + let expected = (d0 + d1).sqrt(); + let vslice_a: VectorSliceMut<'_, Complex, Column> = + VectorSliceMut::from_range(&mut a, 0..2); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + let dist = vslice_a.distance(&vslice_b).unwrap(); + assert!((dist - expected).abs() < 1e-12); + } + + // -- manhattan_distance -- + + #[test] + fn test_vector_slice_mut_complex_manhattan_distance() { + use num::Complex; + let mut a = [Complex::new(1.0_f64, 2.0), Complex::new(3.0, 4.0)]; + let mut b = [Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]; + // Manhattan distance: sum_i |a[i] - b[i]| + let d0 = (a[0] - b[0]).norm(); + let d1 = (a[1] - b[1]).norm(); + let expected = d0 + d1; + let vslice_a: VectorSliceMut<'_, Complex, Row> = + VectorSliceMut::from_range(&mut a, 0..2); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + let dist = vslice_a.manhattan_distance(&vslice_b).unwrap(); + assert!((dist - expected).abs() < 1e-12); + } + + // -- chebyshev_distance -- + + #[test] + fn test_vector_slice_mut_complex_chebyshev_distance() { + use num::Complex; + let mut a = [Complex::new(1.0_f64, 2.0), Complex::new(3.0, 4.0)]; + let mut b = [Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]; + // Chebyshev distance: max_i |a[i] - b[i]| + let d0 = (a[0] - b[0]).norm(); + let d1 = (a[1] - b[1]).norm(); + let expected = d0.max(d1); + let vslice_a: VectorSliceMut<'_, Complex, Column> = + VectorSliceMut::from_range(&mut a, 0..2); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + let dist = vslice_a.chebyshev_distance(&vslice_b).unwrap(); + assert!((dist - expected).abs() < 1e-12); + } + + // -- minkowski_distance -- + + #[test] + fn test_vector_slice_mut_complex_minkowski_distance() { + use num::Complex; + let mut a = [Complex::new(1.0_f64, 2.0), Complex::new(3.0, 4.0)]; + let mut b = [Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]; + // Minkowski distance: (|a[0]-b[0]|^p + |a[1]-b[1]|^p)^(1/p) + let p = 3.0; + let d0 = (a[0] - b[0]).norm().powf(p); + let d1 = (a[1] - b[1]).norm().powf(p); + let expected = (d0 + d1).powf(1.0 / p); + let vslice_a: VectorSliceMut<'_, Complex, Row> = + VectorSliceMut::from_range(&mut a, 0..2); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + let dist = vslice_a.minkowski_distance(&vslice_b, p).unwrap(); + assert!((dist - expected).abs() < 1e-12); + } + + // -- project_onto -- + + #[test] + fn test_vector_slice_mut_complex_project_onto_basic() { + use num::Complex; + let mut a = [Complex::new(3.0, 4.0), Complex::new(0.0, 0.0)]; + let mut b = [Complex::new(1.0, 0.0), Complex::new(0.0, 0.0)]; + let vslice_a: VectorSliceMut<'_, Complex, Column> = + VectorSliceMut::from_range(&mut a, 0..2); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + // Project a onto b: should be [3.0 - 4.0i, 0.0] + let proj = vslice_a.project_onto(&vslice_b).unwrap(); + assert!((proj.as_slice()[0] - Complex::new(3.0, -4.0)).norm() < 1e-12); + assert!((proj.as_slice()[1] - Complex::new(0.0, 0.0)).norm() < 1e-12); + } + + #[test] + fn test_vector_slice_mut_complex_project_onto_parallel() { + use num::Complex; + let mut a = [Complex::new(2.0, 2.0), Complex::new(4.0, 4.0)]; + let mut b = [Complex::new(1.0, 1.0), Complex::new(2.0, 2.0)]; + let vslice_a: VectorSliceMut<'_, Complex, Row> = + VectorSliceMut::from_range(&mut a, 0..2); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + let proj = vslice_a.project_onto(&vslice_b).unwrap(); + assert!((proj.as_slice()[0] - Complex::new(2.0, 2.0)).norm() < 1e-12); + assert!((proj.as_slice()[1] - Complex::new(4.0, 4.0)).norm() < 1e-12); + } + + #[test] + fn test_vector_slice_mut_complex_project_onto_orthogonal() { + use num::Complex; + let mut a = [Complex::new(0.0, 1.0), Complex::new(0.0, 0.0)]; + let mut b = [Complex::new(1.0, 0.0), Complex::new(0.0, 0.0)]; + let vslice_a: VectorSliceMut<'_, Complex, Column> = + VectorSliceMut::from_range(&mut a, 0..2); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + let proj = vslice_a.project_onto(&vslice_b).unwrap(); + assert!((proj.as_slice()[0] - Complex::new(0.0, -1.0)).norm() < 1e-12); + assert!((proj.as_slice()[1] - Complex::new(0.0, 0.0)).norm() < 1e-12); + } + + #[test] + fn test_vector_slice_mut_complex_project_onto_identical() { + use num::Complex; + let mut a = [Complex::new(5.0, 5.0), Complex::new(5.0, 5.0)]; + let mut b = [Complex::new(5.0, 5.0), Complex::new(5.0, 5.0)]; + let vslice_a: VectorSliceMut<'_, Complex, Row> = + VectorSliceMut::from_range(&mut a, 0..2); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + let proj = vslice_a.project_onto(&vslice_b).unwrap(); + assert!((proj.as_slice()[0] - Complex::new(5.0, 5.0)).norm() < 1e-12); + assert!((proj.as_slice()[1] - Complex::new(5.0, 5.0)).norm() < 1e-12); + } + + #[test] + fn test_vector_slice_mut_complex_project_onto_zero_vector() { + use num::Complex; + let mut a = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; + let mut b = [Complex::new(0.0, 0.0), Complex::new(0.0, 0.0)]; + let vslice_a: VectorSliceMut<'_, Complex, Column> = + VectorSliceMut::from_range(&mut a, 0..2); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + let result = vslice_a.project_onto(&vslice_b); + assert!(result.is_err()); + } + + // -- cosine_similarity -- + + #[test] + fn test_vector_slice_mut_complex_cosine_similarity_parallel() { + use num::Complex; + let mut a = [Complex::new(1.0, 2.0), Complex::new(2.0, 4.0)]; + let mut b = [Complex::new(2.0, 4.0), Complex::new(4.0, 8.0)]; + let vslice_a: VectorSliceMut<'_, Complex, Column> = + VectorSliceMut::from_range(&mut a, 0..2); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + let cos_sim = vslice_a.cosine_similarity(&vslice_b).unwrap(); + assert!((cos_sim - Complex::new(1.0, 0.0)).norm() < 1e-12); + } + + #[test] + fn test_vector_slice_mut_complex_cosine_similarity_orthogonal() { + use num::Complex; + let mut a = [Complex::new(1.0, 0.0), Complex::new(0.0, 0.0)]; + let mut b = [Complex::new(0.0, 0.0), Complex::new(1.0, 0.0)]; + let vslice_a: VectorSliceMut<'_, Complex, Column> = + VectorSliceMut::from_range(&mut a, 0..2); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + let cos_sim = vslice_a.cosine_similarity(&vslice_b).unwrap(); + assert!((cos_sim - Complex::new(0.0, 0.0)).norm() < 1e-12); + } + + #[test] + fn test_vector_slice_mut_complex_cosine_similarity_opposite() { + use num::Complex; + let mut a = [Complex::new(1.0, 0.0)]; + let mut b = [Complex::new(-1.0, 0.0)]; + let vslice_a: VectorSliceMut<'_, Complex, Column> = + VectorSliceMut::from_range(&mut a, 0..1); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..1); + let cos_sim = vslice_a.cosine_similarity(&vslice_b).unwrap(); + assert!((cos_sim + Complex::new(1.0, 0.0)).norm() < 1e-12); + } + + #[test] + fn test_vector_slice_mut_complex_cosine_similarity_identical() { + use num::Complex; + let mut a = [Complex::new(3.0, 4.0)]; + let mut b = [Complex::new(3.0, 4.0)]; + let vslice_a: VectorSliceMut<'_, Complex, Column> = + VectorSliceMut::from_range(&mut a, 0..1); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..1); + let cos_sim = vslice_a.cosine_similarity(&vslice_b).unwrap(); + assert!((cos_sim - Complex::new(1.0, 0.0)).norm() < 1e-12); + } + + #[test] + fn test_vector_slice_mut_complex_cosine_similarity_arbitrary() { + use num::Complex; + let mut a = [Complex::new(1.0, 2.0)]; + let mut b = [Complex::new(2.0, 1.0)]; + let vslice_a: VectorSliceMut<'_, Complex, Column> = + VectorSliceMut::from_range(&mut a, 0..1); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..1); + let cos_sim = vslice_a.cosine_similarity(&vslice_b).unwrap(); + assert!(cos_sim.norm() <= 1.0 + 1e-12); + } + + #[test] + fn test_vector_slice_mut_complex_cosine_similarity_zero_vector() { + use num::Complex; + let mut a = [Complex::new(0.0, 0.0)]; + let mut b = [Complex::new(1.0, 2.0)]; + let vslice_a: VectorSliceMut<'_, Complex, Column> = + VectorSliceMut::from_range(&mut a, 0..1); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..1); + let result = vslice_a.cosine_similarity(&vslice_b); + assert!(result.is_err()); + } } From 20133971b617cc3bcceacf16a886b9aa2e8a8118 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Thu, 29 May 2025 21:42:41 -0400 Subject: [PATCH 65/82] add VectorOpsFloat normalize_into, normalize_to_into, lerp_into methods with preallocated buffer support --- src/types/flexvector.rs | 190 +++++++++++++++++++- src/types/traits.rs | 19 ++ src/types/utils.rs | 91 ++++++++++ src/types/vectorslice.rs | 378 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 674 insertions(+), 4 deletions(-) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index 1361fe0..eaaf64b 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -29,8 +29,8 @@ use crate::types::utils::{ elementwise_max_into_impl, elementwise_min_impl, elementwise_min_into_impl, hermitian_dot_impl, lerp_impl, manhattan_distance_complex_impl, manhattan_distance_impl, minkowski_distance_complex_impl, minkowski_distance_impl, mut_lerp_impl, mut_normalize_impl, - mut_normalize_to_impl, mut_translate_impl, normalize_impl, normalize_to_impl, - project_onto_impl, translate_impl, + mut_normalize_to_impl, mut_translate_impl, normalize_impl, normalize_into_impl, + normalize_to_impl, normalize_to_into_impl, project_onto_impl, translate_impl, }; use crate::errors::VectorError; @@ -900,6 +900,15 @@ where normalize_impl(self.as_slice(), self.norm()) } + #[inline] + fn normalize_into(&self, out: &mut [T]) -> Result<(), VectorError> + where + T: Copy + PartialEq + std::ops::Div + num::Zero, + { + let norm = self.norm(); + normalize_into_impl(self.as_slice(), norm, out) + } + /// Returns a new vector with the same direction and the given magnitude. #[inline] fn normalize_to(&self, magnitude: T) -> Result @@ -914,6 +923,19 @@ where normalize_to_impl(self.as_slice(), self.norm(), magnitude) } + #[inline] + fn normalize_to_into(&self, magnitude: T, out: &mut [T]) -> Result<(), VectorError> + where + T: Copy + + PartialEq + + std::ops::Div + + std::ops::Mul + + num::Zero, + { + let norm = self.norm(); + normalize_to_into_impl(self.as_slice(), norm, magnitude, out) + } + #[inline] fn lerp(&self, end: &Self, weight: T) -> Result where @@ -928,6 +950,24 @@ where Ok(out) } + #[inline] + fn lerp_into(&self, end: &Self, weight: T, out: &mut [T]) -> Result<(), VectorError> + where + T: num::Float + Clone, + { + self.check_same_length_and_raise(end)?; + if self.len() != out.len() { + return Err(VectorError::MismatchedLengthError( + "Output buffer has different length than input vectors".to_string(), + )); + } + if weight < T::zero() || weight > T::one() { + return Err(VectorError::OutOfRangeError("weight must be in [0, 1]".to_string())); + } + lerp_impl(self.as_slice(), end.as_slice(), weight, out); + Ok(()) + } + #[inline] fn midpoint(&self, other: &Self) -> Result where @@ -6537,6 +6577,36 @@ mod tests { assert!((normalized.as_slice()[1] + 0.8).abs() < 1e-12); } + // -- normalize_into -- + + #[test] + fn test_normalize_into_f64() { + let v = FlexVector::::from_vec(vec![3.0, 4.0]); + let mut out = [0.0; 2]; + v.normalize_into(&mut out).unwrap(); + // The norm is 5.0, so the normalized vector should be [0.6, 0.8] + assert!((out[0] - 0.6).abs() < 1e-12); + assert!((out[1] - 0.8).abs() < 1e-12); + } + + #[test] + fn test_normalize_into_f64_zero_vector() { + let v = FVector::from_vec(vec![0.0, 0.0]); + let mut out = [0.0; 2]; + let result = v.normalize_into(&mut out); + assert!(result.is_err()); + } + + #[test] + fn test_normalize_into_f64_negative_values() { + let v = FVector::from_vec(vec![-3.0, -4.0]); + let mut out = [0.0; 2]; + let _ = v.normalize_into(&mut out).unwrap(); + // The norm is 5.0, so the normalized vector should be [-0.6, -0.8] + assert!((out[0] + 0.6).abs() < 1e-12); + assert!((out[1] + 0.8).abs() < 1e-12); + } + // -- mut_normalize -- #[test] fn test_mut_normalize_f64() { @@ -6589,6 +6659,42 @@ mod tests { assert!((normalized.as_slice()[1] + 4.0).abs() < 1e-12); } + // -- normalize_to_into -- + + #[test] + fn test_flexvector_normalize_to_into_basic_f64() { + let v = FlexVector::::from_vec(vec![3.0, 4.0]); + let mut out = [0.0; 2]; + v.normalize_to_into(10.0, &mut out).unwrap(); + // The norm is 5.0, so the normalized vector with magnitude 10.0 should be [6.0, 8.0] + assert!((out[0] - 6.0).abs() < 1e-8); + assert!((out[1] - 8.0).abs() < 1e-8); + } + + #[test] + fn test_flexvector_normalize_to_into_zero_vector_f64() { + let v = FlexVector::::from_vec(vec![0.0, 0.0]); + let mut out = [0.0; 2]; + let result = v.normalize_to_into(10.0, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_flexvector_normalize_to_into_wrong_length_f64() { + let v = FlexVector::::from_vec(vec![3.0, 4.0]); + let mut out = [0.0; 1]; + let result = v.normalize_to_into(10.0, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_flexvector_normalize_to_into_empty_f64() { + let v = FlexVector::::from_vec(vec![]); + let mut out: [f64; 0] = []; + let result = v.normalize_to_into(10.0, &mut out); + assert!(result.is_err()); + } + // -- mut_normalize_to -- #[test] fn test_mut_normalize_to_f64() { @@ -6659,6 +6765,86 @@ mod tests { assert!(result_high.is_err()); } + // -- lerp_into -- + + #[test] + fn test_flexvector_lerp_into_basic_f64() { + let v1 = FlexVector::::from_vec(vec![1.0, 2.0, 3.0]); + let v2 = FlexVector::::from_vec(vec![4.0, 5.0, 6.0]); + let mut out = [0.0; 3]; + v1.lerp_into(&v2, 0.5, &mut out).unwrap(); + assert!((out[0] - 2.5).abs() < 1e-12); + assert!((out[1] - 3.5).abs() < 1e-12); + assert!((out[2] - 4.5).abs() < 1e-12); + } + + #[test] + fn test_flexvector_lerp_into_weight_zero_f64() { + let v1 = FlexVector::::from_vec(vec![1.0, 2.0, 3.0]); + let v2 = FlexVector::::from_vec(vec![4.0, 5.0, 6.0]); + let mut out = [0.0; 3]; + v1.lerp_into(&v2, 0.0, &mut out).unwrap(); + assert!((out[0] - 1.0).abs() < 1e-12); + assert!((out[1] - 2.0).abs() < 1e-12); + assert!((out[2] - 3.0).abs() < 1e-12); + } + + #[test] + fn test_flexvector_lerp_into_weight_one_f64() { + let v1 = FlexVector::::from_vec(vec![1.0, 2.0, 3.0]); + let v2 = FlexVector::::from_vec(vec![4.0, 5.0, 6.0]); + let mut out = [0.0; 3]; + v1.lerp_into(&v2, 1.0, &mut out).unwrap(); + assert!((out[0] - 4.0).abs() < 1e-12); + assert!((out[1] - 5.0).abs() < 1e-12); + assert!((out[2] - 6.0).abs() < 1e-12); + } + + #[test] + fn test_flexvector_lerp_into_mismatched_length_end_f64() { + let v1 = FlexVector::::from_vec(vec![1.0, 2.0, 3.0]); + let v2 = FlexVector::::from_vec(vec![4.0, 5.0]); + let mut out = [0.0; 3]; + let result = v1.lerp_into(&v2, 0.5, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_flexvector_lerp_into_mismatched_length_out_f64() { + let v1 = FlexVector::::from_vec(vec![1.0, 2.0, 3.0]); + let v2 = FlexVector::::from_vec(vec![4.0, 5.0, 6.0]); + let mut out = [0.0; 2]; + let result = v1.lerp_into(&v2, 0.5, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_flexvector_lerp_into_weight_out_of_bounds_low_f64() { + let v1 = FlexVector::::from_vec(vec![1.0, 2.0]); + let v2 = FlexVector::::from_vec(vec![3.0, 4.0]); + let mut out = [0.0; 2]; + let result = v1.lerp_into(&v2, -0.1, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_flexvector_lerp_into_weight_out_of_bounds_high_f64() { + let v1 = FlexVector::::from_vec(vec![1.0, 2.0]); + let v2 = FlexVector::::from_vec(vec![3.0, 4.0]); + let mut out = [0.0; 2]; + let result = v1.lerp_into(&v2, 1.1, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_flexvector_lerp_into_empty_f64() { + let v1 = FlexVector::::from_vec(vec![]); + let v2 = FlexVector::::from_vec(vec![]); + let mut out: [f64; 0] = []; + v1.lerp_into(&v2, 0.5, &mut out).unwrap(); + assert_eq!(out, []); + } + // -- mut_lerp -- #[test] fn test_mut_lerp_f64_weight_zero() { diff --git a/src/types/traits.rs b/src/types/traits.rs index b2dc26f..404556a 100644 --- a/src/types/traits.rs +++ b/src/types/traits.rs @@ -475,17 +475,36 @@ pub trait VectorOpsFloat: VectorBase { T: Copy + PartialEq + std::ops::Div, Self::Output: std::iter::FromIterator; + /// ... + fn normalize_into(&self, out: &mut [T]) -> Result<(), VectorError> + where + T: Copy + PartialEq + std::ops::Div + num::Zero; + /// Returns a new vector with the same direction and the given magnitude. fn normalize_to(&self, magnitude: T) -> Result where T: Copy + PartialEq + std::ops::Div + std::ops::Mul, Self::Output: std::iter::FromIterator; + /// ... + fn normalize_to_into(&self, magnitude: T, out: &mut [T]) -> Result<(), VectorError> + where + T: Copy + + PartialEq + + std::ops::Div + + std::ops::Mul + + num::Zero; + /// Linear interpolation between self and end by weight in [0, 1]. fn lerp(&self, end: &Self, weight: T) -> Result where T: num::Float + Clone + PartialOrd; + /// ... + fn lerp_into(&self, end: &Self, weight: T, out: &mut [T]) -> Result<(), VectorError> + where + T: num::Float + Clone + PartialOrd; + /// Midpoint fn midpoint(&self, other: &Self) -> Result where diff --git a/src/types/utils.rs b/src/types/utils.rs index 12b9b6e..5ad99d6 100644 --- a/src/types/utils.rs +++ b/src/types/utils.rs @@ -225,6 +225,25 @@ where Ok(slice.iter().map(|&a| a / norm).collect()) } +#[inline] +pub(crate) fn normalize_into_impl(input: &[T], norm: T, out: &mut [T]) -> Result<(), VectorError> +where + T: Copy + PartialEq + std::ops::Div + num::Zero, +{ + if input.len() != out.len() { + return Err(VectorError::MismatchedLengthError( + "Output buffer has wrong length".to_string(), + )); + } + if norm == T::zero() { + return Err(VectorError::ZeroVectorError("Cannot normalize zero vector".to_string())); + } + for (o, &x) in out.iter_mut().zip(input.iter()) { + *o = x / norm; + } + Ok(()) +} + #[inline] pub(crate) fn mut_normalize_impl(slice: &mut [T], norm: T) -> Result<(), VectorError> where @@ -256,6 +275,31 @@ where Ok(slice.iter().map(|&a| a * scale).collect()) } +#[inline] +pub(crate) fn normalize_to_into_impl( + input: &[T], + norm: T, + magnitude: T, + out: &mut [T], +) -> Result<(), VectorError> +where + T: Copy + PartialEq + std::ops::Div + std::ops::Mul + num::Zero, +{ + if input.len() != out.len() { + return Err(VectorError::MismatchedLengthError( + "Output buffer has wrong length".to_string(), + )); + } + if norm == T::zero() { + return Err(VectorError::ZeroVectorError("Cannot normalize zero vector".to_string())); + } + let scale = magnitude / norm; + for (o, &x) in out.iter_mut().zip(input.iter()) { + *o = x * scale; + } + Ok(()) +} + #[inline] pub(crate) fn mut_normalize_to_impl( slice: &mut [T], @@ -1416,7 +1460,53 @@ mod tests { assert!((result[0].im - expected.im).abs() < 1e-12); } + // -- normalize_to_into_impl -- + + #[test] + fn test_normalize_to_into_impl_f64_basic() { + let v = [3.0f64, 4.0]; + let norm = (3.0f64 * 3.0 + 4.0 * 4.0).sqrt(); + let magnitude = 10.0f64; + let mut out = [0.0f64; 2]; + normalize_to_into_impl(&v, norm, magnitude, &mut out).unwrap(); + // The normalized vector should have the same direction as v and norm 10 + let expected = [3.0 / 5.0 * 10.0, 4.0 / 5.0 * 10.0]; + assert!((out[0] - expected[0]).abs() < 1e-12); + assert!((out[1] - expected[1]).abs() < 1e-12); + } + + #[test] + fn test_normalize_to_into_impl_f64_zero_vector() { + let v = [0.0f64, 0.0]; + let norm = 0.0f64; + let magnitude = 10.0f64; + let mut out = [0.0f64; 2]; + let result = normalize_to_into_impl(&v, norm, magnitude, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_normalize_to_into_impl_f64_empty() { + let v: [f64; 0] = []; + let norm = 1.0f64; + let magnitude = 2.0f64; + let mut out: [f64; 0] = []; + normalize_to_into_impl(&v, norm, magnitude, &mut out).unwrap(); + assert!(out.is_empty()); + } + + #[test] + fn test_normalize_to_into_impl_f64_wrong_length() { + let v = [3.0f64, 4.0]; + let norm = 5.0f64; + let magnitude = 10.0f64; + let mut out = [0.0f64; 1]; + let result = normalize_to_into_impl(&v, norm, magnitude, &mut out); + assert!(result.is_err()); + } + // -- mut_normalize_to_impl -- + #[test] fn test_mut_normalize_to_impl_f64_basic() { let mut v = [3.0f64, 4.0]; @@ -1469,6 +1559,7 @@ mod tests { } // -- cosine_similarity_impl -- + #[test] fn test_cosine_similarity_impl_basic() { let a = [1.0f64, 0.0]; diff --git a/src/types/vectorslice.rs b/src/types/vectorslice.rs index a1c8eb6..54b0246 100644 --- a/src/types/vectorslice.rs +++ b/src/types/vectorslice.rs @@ -14,8 +14,8 @@ use crate::types::utils::{ elementwise_max_into_impl, elementwise_min_impl, elementwise_min_into_impl, hermitian_dot_impl, lerp_impl, manhattan_distance_complex_impl, manhattan_distance_impl, minkowski_distance_complex_impl, minkowski_distance_impl, mut_lerp_impl, mut_normalize_impl, - mut_normalize_to_impl, mut_translate_impl, normalize_impl, normalize_to_impl, - project_onto_impl, translate_impl, + mut_normalize_to_impl, mut_translate_impl, normalize_impl, normalize_into_impl, + normalize_to_impl, normalize_to_into_impl, project_onto_impl, translate_impl, }; use std::fmt; @@ -306,6 +306,15 @@ where normalize_impl(self.as_slice(), self.norm()) } + #[inline] + fn normalize_into(&self, out: &mut [T]) -> Result<(), VectorError> + where + T: Copy + PartialEq + std::ops::Div + num::Zero, + { + let norm = self.norm(); + normalize_into_impl(self.as_slice(), norm, out) + } + #[inline] fn normalize_to(&self, magnitude: T) -> Result where @@ -319,6 +328,19 @@ where normalize_to_impl(self.as_slice(), self.norm(), magnitude) } + #[inline] + fn normalize_to_into(&self, magnitude: T, out: &mut [T]) -> Result<(), VectorError> + where + T: Copy + + PartialEq + + std::ops::Div + + std::ops::Mul + + num::Zero, + { + let norm = self.norm(); + normalize_to_into_impl(self.as_slice(), norm, magnitude, out) + } + #[inline] fn lerp(&self, end: &Self, weight: T) -> Result where @@ -333,6 +355,24 @@ where Ok(out) } + #[inline] + fn lerp_into(&self, end: &Self, weight: T, out: &mut [T]) -> Result<(), VectorError> + where + T: num::Float + Clone, + { + self.check_same_length_and_raise(end)?; + if self.len() != out.len() { + return Err(VectorError::MismatchedLengthError( + "Output buffer has different length than input vectors".to_string(), + )); + } + if weight < T::zero() || weight > T::one() { + return Err(VectorError::OutOfRangeError("weight must be in [0, 1]".to_string())); + } + lerp_impl(self.as_slice(), end.as_slice(), weight, out); + Ok(()) + } + #[inline] fn midpoint(&self, other: &Self) -> Result where @@ -890,6 +930,15 @@ where normalize_impl(self.as_slice(), self.norm()) } + #[inline] + fn normalize_into(&self, out: &mut [T]) -> Result<(), VectorError> + where + T: Copy + PartialEq + std::ops::Div + num::Zero, + { + let norm = self.norm(); + normalize_into_impl(self.as_slice(), norm, out) + } + #[inline] fn normalize_to(&self, magnitude: T) -> Result where @@ -903,6 +952,19 @@ where normalize_to_impl(self.as_slice(), self.norm(), magnitude) } + #[inline] + fn normalize_to_into(&self, magnitude: T, out: &mut [T]) -> Result<(), VectorError> + where + T: Copy + + PartialEq + + std::ops::Div + + std::ops::Mul + + num::Zero, + { + let norm = self.norm(); + normalize_to_into_impl(self.as_slice(), norm, magnitude, out) + } + #[inline] fn lerp(&self, end: &Self, weight: T) -> Result where @@ -917,6 +979,24 @@ where Ok(out) } + #[inline] + fn lerp_into(&self, end: &Self, weight: T, out: &mut [T]) -> Result<(), VectorError> + where + T: num::Float + Clone, + { + self.check_same_length_and_raise(end)?; + if self.len() != out.len() { + return Err(VectorError::MismatchedLengthError( + "Output buffer has different length than input vectors".to_string(), + )); + } + if weight < T::zero() || weight > T::one() { + return Err(VectorError::OutOfRangeError("weight must be in [0, 1]".to_string())); + } + lerp_impl(self.as_slice(), end.as_slice(), weight, out); + Ok(()) + } + #[inline] fn midpoint(&self, other: &Self) -> Result where @@ -1955,6 +2035,20 @@ mod tests { assert!(result.is_err()); } + // -- normalize_into -- + + #[test] + fn test_vector_slice_normalize_into() { + let a = [3.0, 4.0]; + let vslice: VectorSlice<'_, f64, Column> = VectorSlice::from_range(&a, 0..2); + let mut out = [0.0; 2]; + vslice.normalize_into(&mut out).unwrap(); + assert!((out[0] - 0.6).abs() < 1e-8); + assert!((out[1] - 0.8).abs() < 1e-8); + } + + // -- normalize_to -- + #[test] fn test_vector_slice_normalize_to() { let a = [3.0, 4.0]; @@ -1966,6 +2060,48 @@ mod tests { } } + // -- normalize_to_into -- + + #[test] + fn test_vector_slice_normalize_to_into_basic() { + let a = [3.0, 4.0]; + let vslice: VectorSlice<'_, f64, Column> = VectorSlice::from_range(&a, 0..2); + let mut out = [0.0; 2]; + vslice.normalize_to_into(10.0, &mut out).unwrap(); + // The norm is 5.0, so the normalized vector with magnitude 10.0 should be [6.0, 8.0] + assert!((out[0] - 6.0).abs() < 1e-8); + assert!((out[1] - 8.0).abs() < 1e-8); + } + + #[test] + fn test_vector_slice_normalize_to_into_zero_vector() { + let a = [0.0, 0.0]; + let vslice: VectorSlice<'_, f64, Column> = VectorSlice::from_range(&a, 0..2); + let mut out = [0.0; 2]; + let result = vslice.normalize_to_into(10.0, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_normalize_to_into_wrong_length() { + let a = [3.0, 4.0]; + let vslice: VectorSlice<'_, f64, Column> = VectorSlice::from_range(&a, 0..2); + let mut out = [0.0; 1]; + let result = vslice.normalize_to_into(10.0, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_normalize_to_into_empty() { + let a: [f64; 0] = []; + let vslice: VectorSlice<'_, f64, Column> = VectorSlice::from_range(&a, 0..0); + let mut out: [f64; 0] = []; + let result = vslice.normalize_to_into(10.0, &mut out); + assert!(result.is_err()); + } + + // -- lerp -- + #[test] fn test_vector_slice_lerp() { let a = [1.0, 2.0, 3.0]; @@ -1986,6 +2122,98 @@ mod tests { assert!(vslice_a.lerp(&vslice_b, 1.1).is_err()); } + // -- lerp_into -- + + #[test] + fn test_vector_slice_lerp_into_basic() { + let a_data = [1.0, 2.0, 3.0]; + let b_data = [4.0, 5.0, 6.0]; + let vslice_a: VectorSlice<'_, f64, Column> = VectorSlice::from_range(&a_data, 0..3); + let vslice_b: VectorSlice<'_, f64, Column> = VectorSlice::from_range(&b_data, 0..3); + let mut out = [0.0; 3]; + vslice_a.lerp_into(&vslice_b, 0.5, &mut out).unwrap(); + assert_eq!(out, [2.5, 3.5, 4.5]); + } + + #[test] + fn test_vector_slice_lerp_into_weight_zero() { + let a_data = [1.0, 2.0, 3.0]; + let b_data = [4.0, 5.0, 6.0]; + let vslice_a: VectorSlice<'_, f64, Column> = VectorSlice::from_range(&a_data, 0..3); + let vslice_b: VectorSlice<'_, f64, Column> = VectorSlice::from_range(&b_data, 0..3); + let mut out = [0.0; 3]; + vslice_a.lerp_into(&vslice_b, 0.0, &mut out).unwrap(); + assert_eq!(out, [1.0, 2.0, 3.0]); // Should be equal to a_data + } + + #[test] + fn test_vector_slice_lerp_into_weight_one() { + let a_data = [1.0, 2.0, 3.0]; + let b_data = [4.0, 5.0, 6.0]; + let vslice_a: VectorSlice<'_, f64, Column> = VectorSlice::from_range(&a_data, 0..3); + let vslice_b: VectorSlice<'_, f64, Column> = VectorSlice::from_range(&b_data, 0..3); + let mut out = [0.0; 3]; + vslice_a.lerp_into(&vslice_b, 1.0, &mut out).unwrap(); + assert_eq!(out, [4.0, 5.0, 6.0]); // Should be equal to b_data + } + + #[test] + fn test_vector_slice_lerp_into_mismatched_length_end() { + let a_data = [1.0, 2.0, 3.0]; + let b_data = [4.0, 5.0]; + let vslice_a: VectorSlice<'_, f64, Column> = VectorSlice::from_range(&a_data, 0..3); + let vslice_b: VectorSlice<'_, f64, Column> = VectorSlice::from_range(&b_data, 0..2); + let mut out = [0.0; 3]; + let result = vslice_a.lerp_into(&vslice_b, 0.5, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_lerp_into_mismatched_length_out() { + let a_data = [1.0, 2.0, 3.0]; + let b_data = [4.0, 5.0, 6.0]; + let vslice_a: VectorSlice<'_, f64, Column> = VectorSlice::from_range(&a_data, 0..3); + let vslice_b: VectorSlice<'_, f64, Column> = VectorSlice::from_range(&b_data, 0..3); + let mut out = [0.0; 2]; // out buffer is shorter + let result = vslice_a.lerp_into(&vslice_b, 0.5, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_lerp_into_weight_out_of_bounds_low() { + let a_data = [1.0, 2.0]; + let b_data = [3.0, 4.0]; + let vslice_a: VectorSlice<'_, f64, Column> = VectorSlice::from_range(&a_data, 0..2); + let vslice_b: VectorSlice<'_, f64, Column> = VectorSlice::from_range(&b_data, 0..2); + let mut out = [0.0; 2]; + let result = vslice_a.lerp_into(&vslice_b, -0.1, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_lerp_into_weight_out_of_bounds_high() { + let a_data = [1.0, 2.0]; + let b_data = [3.0, 4.0]; + let vslice_a: VectorSlice<'_, f64, Column> = VectorSlice::from_range(&a_data, 0..2); + let vslice_b: VectorSlice<'_, f64, Column> = VectorSlice::from_range(&b_data, 0..2); + let mut out = [0.0; 2]; + let result = vslice_a.lerp_into(&vslice_b, 1.1, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_lerp_into_empty() { + let a_data: [f64; 0] = []; + let b_data: [f64; 0] = []; + let vslice_a: VectorSlice<'_, f64, Column> = VectorSlice::from_range(&a_data, 0..0); + let vslice_b: VectorSlice<'_, f64, Column> = VectorSlice::from_range(&b_data, 0..0); + let mut out: [f64; 0] = []; + vslice_a.lerp_into(&vslice_b, 0.5, &mut out).unwrap(); + assert_eq!(out, [] as [f64; 0]); + } + + // -- midpoint -- + #[test] fn test_vector_slice_midpoint() { let a = [1.0, 2.0, 3.0]; @@ -3220,6 +3448,18 @@ mod tests { assert!(result.is_err()); } + #[test] + fn test_vector_slice_mut_normalize_into() { + let mut a = [3.0, 4.0]; + let vslice: VectorSliceMut<'_, f64, Column> = VectorSliceMut::from_range(&mut a, 0..2); + let mut out: [f64; 2] = [0.0; 2]; + let result = vslice.normalize_into(&mut out); + assert!(result.is_ok()); + for (x, y) in out.iter().zip([0.6, 0.8].iter()) { + assert!((x - y).abs() < 1e-12); + } + } + #[test] fn test_vector_slice_mut_normalize_to() { let mut a = [3.0, 4.0]; @@ -3231,6 +3471,48 @@ mod tests { } } + // -- normalize_to_into -- + + #[test] + fn test_vector_slice_mut_normalize_to_into_basic() { + let mut a = [3.0, 4.0]; + let vslice: VectorSliceMut<'_, f64, Column> = VectorSliceMut::from_range(&mut a, 0..2); + let mut out = [0.0; 2]; + vslice.normalize_to_into(10.0, &mut out).unwrap(); + // The norm is 5.0, so the normalized vector with magnitude 10.0 should be [6.0, 8.0] + assert!((out[0] - 6.0).abs() < 1e-8); + assert!((out[1] - 8.0).abs() < 1e-8); + } + + #[test] + fn test_vector_slice_mut_normalize_to_into_zero_vector() { + let mut a = [0.0, 0.0]; + let vslice: VectorSliceMut<'_, f64, Column> = VectorSliceMut::from_range(&mut a, 0..2); + let mut out = [0.0; 2]; + let result = vslice.normalize_to_into(10.0, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_mut_normalize_to_into_wrong_length() { + let mut a = [3.0, 4.0]; + let vslice: VectorSliceMut<'_, f64, Column> = VectorSliceMut::from_range(&mut a, 0..2); + let mut out = [0.0; 1]; + let result = vslice.normalize_to_into(10.0, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_mut_normalize_to_into_empty() { + let mut a: [f64; 0] = []; + let vslice: VectorSliceMut<'_, f64, Column> = VectorSliceMut::from_range(&mut a, 0..0); + let mut out: [f64; 0] = []; + let result = vslice.normalize_to_into(10.0, &mut out); + assert!(result.is_err()); + } + + // -- lerp -- + #[test] fn test_vector_slice_mut_lerp() { let mut a = [1.0, 2.0, 3.0]; @@ -3251,6 +3533,98 @@ mod tests { assert!(vslice_a.lerp(&vslice_b, 1.1).is_err()); } + // -- lerp_into -- + + #[test] + fn test_vector_slice_mut_lerp_into_basic() { + let mut a = [1.0, 2.0, 3.0]; + let mut b = [4.0, 5.0, 6.0]; + let vslice_a: VectorSliceMut<'_, f64, Column> = VectorSliceMut::from_range(&mut a, 0..3); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..3); + let mut out = [0.0; 3]; + vslice_a.lerp_into(&vslice_b, 0.5, &mut out).unwrap(); + assert_eq!(out, [2.5, 3.5, 4.5]); + } + + #[test] + fn test_vector_slice_mut_lerp_into_weight_zero() { + let mut a = [1.0, 2.0, 3.0]; + let mut b = [4.0, 5.0, 6.0]; + let vslice_a: VectorSliceMut<'_, f64, Column> = VectorSliceMut::from_range(&mut a, 0..3); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..3); + let mut out = [0.0; 3]; + vslice_a.lerp_into(&vslice_b, 0.0, &mut out).unwrap(); + assert_eq!(out, [1.0, 2.0, 3.0]); + } + + #[test] + fn test_vector_slice_mut_lerp_into_weight_one() { + let mut a = [1.0, 2.0, 3.0]; + let mut b = [4.0, 5.0, 6.0]; + let vslice_a: VectorSliceMut<'_, f64, Column> = VectorSliceMut::from_range(&mut a, 0..3); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..3); + let mut out = [0.0; 3]; + vslice_a.lerp_into(&vslice_b, 1.0, &mut out).unwrap(); + assert_eq!(out, [4.0, 5.0, 6.0]); + } + + #[test] + fn test_vector_slice_mut_lerp_into_mismatched_length_end() { + let mut a = [1.0, 2.0, 3.0]; + let mut b = [4.0, 5.0]; + let vslice_a: VectorSliceMut<'_, f64, Column> = VectorSliceMut::from_range(&mut a, 0..3); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + let mut out = [0.0; 3]; + let result = vslice_a.lerp_into(&vslice_b, 0.5, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_mut_lerp_into_mismatched_length_out() { + let mut a = [1.0, 2.0, 3.0]; + let mut b = [4.0, 5.0, 6.0]; + let vslice_a: VectorSliceMut<'_, f64, Column> = VectorSliceMut::from_range(&mut a, 0..3); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..3); + let mut out = [0.0; 2]; + let result = vslice_a.lerp_into(&vslice_b, 0.5, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_mut_lerp_into_weight_out_of_bounds_low() { + let mut a = [1.0, 2.0]; + let mut b = [3.0, 4.0]; + let vslice_a: VectorSliceMut<'_, f64, Column> = VectorSliceMut::from_range(&mut a, 0..2); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + let mut out = [0.0; 2]; + let result = vslice_a.lerp_into(&vslice_b, -0.1, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_mut_lerp_into_weight_out_of_bounds_high() { + let mut a = [1.0, 2.0]; + let mut b = [3.0, 4.0]; + let vslice_a: VectorSliceMut<'_, f64, Column> = VectorSliceMut::from_range(&mut a, 0..2); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + let mut out = [0.0; 2]; + let result = vslice_a.lerp_into(&vslice_b, 1.1, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_mut_lerp_into_empty() { + let mut a: [f64; 0] = []; + let mut b: [f64; 0] = []; + let vslice_a: VectorSliceMut<'_, f64, Column> = VectorSliceMut::from_range(&mut a, 0..0); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..0); + let mut out: [f64; 0] = []; + vslice_a.lerp_into(&vslice_b, 0.5, &mut out).unwrap(); + assert_eq!(out, []); + } + + // -- midpoint -- + #[test] fn test_vector_slice_mut_midpoint() { let mut a = [1.0, 2.0, 3.0]; From 402deb817ff9f18cd9ae28910ecf8b962c109798 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Thu, 29 May 2025 23:35:37 -0400 Subject: [PATCH 66/82] refactor numeric type only operations to a Copy bound --- src/types/flexvector.rs | 95 +++++++++++------------ src/types/traits.rs | 121 ++++++++++++++--------------- src/types/utils.rs | 38 ++++----- src/types/vectorslice.rs | 163 ++++++++++++++++----------------------- 4 files changed, 188 insertions(+), 229 deletions(-) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index eaaf64b..d5c14ed 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -187,7 +187,7 @@ impl FlexVector { "pattern must not be empty if len > 0".to_string(), )); } - let elements = pattern.iter().cloned().cycle().take(len).collect(); + let elements = pattern.iter().cycle().take(len).cloned().collect(); Ok(FlexVector { elements, _orientation: PhantomData }) } } @@ -726,7 +726,7 @@ where // ================================ impl VectorOps for FlexVector where - T: Clone, + T: Copy, { type Output = Self; @@ -813,7 +813,7 @@ where #[inline] fn elementwise_min(&self, other: &Self) -> Result where - T: PartialOrd + Clone, + T: PartialOrd + Copy, { self.check_same_length_and_raise(other)?; Ok(FlexVector::from_vec(elementwise_min_impl(self.as_slice(), other.as_slice()))) @@ -822,7 +822,7 @@ where #[inline] fn elementwise_min_into(&self, other: &Self, out: &mut [T]) -> Result<(), VectorError> where - T: PartialOrd + Clone, + T: PartialOrd + Copy, { if self.len() != other.len() || out.len() != self.len() { return Err(VectorError::MismatchedLengthError( @@ -837,7 +837,7 @@ where #[inline] fn elementwise_max(&self, other: &Self) -> Result where - T: PartialOrd + Clone, + T: PartialOrd + Copy, { self.check_same_length_and_raise(other)?; Ok(FlexVector::from_vec(elementwise_max_impl(self.as_slice(), other.as_slice()))) @@ -846,7 +846,7 @@ where #[inline] fn elementwise_max_into(&self, other: &Self, out: &mut [T]) -> Result<(), VectorError> where - T: PartialOrd + Clone, + T: PartialOrd + Copy, { if self.len() != other.len() || out.len() != self.len() { return Err(VectorError::MismatchedLengthError( @@ -865,7 +865,7 @@ where // ================================ impl VectorOpsMut for FlexVector where - T: Clone, + T: Copy, { type Output = Self; @@ -887,14 +887,14 @@ where // ================================ impl VectorOpsFloat for FlexVector where - T: num::Float + Clone + std::iter::Sum, + T: num::Float + std::iter::Sum, { type Output = Self; #[inline] fn normalize(&self) -> Result where - T: Copy + PartialEq + std::ops::Div + num::Zero, + T: num::Float + std::ops::Div + num::Zero, Self::Output: std::iter::FromIterator, { normalize_impl(self.as_slice(), self.norm()) @@ -903,7 +903,7 @@ where #[inline] fn normalize_into(&self, out: &mut [T]) -> Result<(), VectorError> where - T: Copy + PartialEq + std::ops::Div + num::Zero, + T: num::Float + std::ops::Div + num::Zero, { let norm = self.norm(); normalize_into_impl(self.as_slice(), norm, out) @@ -913,11 +913,7 @@ where #[inline] fn normalize_to(&self, magnitude: T) -> Result where - T: Copy - + PartialEq - + std::ops::Div - + std::ops::Mul - + num::Zero, + T: num::Float + std::ops::Div + std::ops::Mul + num::Zero, Self::Output: std::iter::FromIterator, { normalize_to_impl(self.as_slice(), self.norm(), magnitude) @@ -926,11 +922,7 @@ where #[inline] fn normalize_to_into(&self, magnitude: T, out: &mut [T]) -> Result<(), VectorError> where - T: Copy - + PartialEq - + std::ops::Div - + std::ops::Mul - + num::Zero, + T: num::Float + std::ops::Div + std::ops::Mul + num::Zero, { let norm = self.norm(); normalize_to_into_impl(self.as_slice(), norm, magnitude, out) @@ -939,7 +931,7 @@ where #[inline] fn lerp(&self, end: &Self, weight: T) -> Result where - T: num::Float + Clone, + T: num::Float, { self.check_same_length_and_raise(end)?; if weight < T::zero() || weight > T::one() { @@ -953,7 +945,7 @@ where #[inline] fn lerp_into(&self, end: &Self, weight: T, out: &mut [T]) -> Result<(), VectorError> where - T: num::Float + Clone, + T: num::Float, { self.check_same_length_and_raise(end)?; if self.len() != out.len() { @@ -971,7 +963,7 @@ where #[inline] fn midpoint(&self, other: &Self) -> Result where - T: num::Float + Clone, + T: num::Float, { self.check_same_length_and_raise(other)?; let mut out = FlexVector::zero(self.len()); @@ -982,7 +974,7 @@ where #[inline] fn distance(&self, other: &Self) -> Result where - T: num::Float + Clone + std::iter::Sum, + T: num::Float + std::iter::Sum, { self.check_same_length_and_raise(other)?; Ok(distance_impl(self.as_slice(), other.as_slice())) @@ -991,7 +983,7 @@ where #[inline] fn manhattan_distance(&self, other: &Self) -> Result where - T: num::Float + Clone + std::iter::Sum, + T: num::Float + std::iter::Sum, { self.check_same_length_and_raise(other)?; Ok(manhattan_distance_impl(self.as_slice(), other.as_slice())) @@ -1000,7 +992,7 @@ where #[inline] fn chebyshev_distance(&self, other: &Self) -> Result where - T: num::Float + Clone + PartialOrd, + T: num::Float + PartialOrd, { self.check_same_length_and_raise(other)?; Ok(chebyshev_distance_impl(self.as_slice(), other.as_slice())) @@ -1009,7 +1001,7 @@ where #[inline] fn minkowski_distance(&self, other: &Self, p: T) -> Result where - T: num::Float + Clone + std::iter::Sum, + T: num::Float + std::iter::Sum, { self.check_same_length_and_raise(other)?; if p < T::one() { @@ -1021,7 +1013,7 @@ where #[inline] fn angle_with(&self, other: &Self) -> Result where - T: num::Float + Clone + std::iter::Sum, + T: num::Float + std::iter::Sum, { self.check_same_length_and_raise(other)?; let norm_self = self.norm(); @@ -1037,7 +1029,7 @@ where #[inline] fn project_onto(&self, other: &Self) -> Result where - T: num::Float + Clone + std::iter::Sum, + T: num::Float + std::iter::Sum, Self::Output: std::iter::FromIterator, { self.check_same_length_and_raise(other)?; @@ -1054,7 +1046,7 @@ where #[inline] fn cosine_similarity(&self, other: &Self) -> Result where - T: num::Float + Clone + std::iter::Sum + std::ops::Div, + T: num::Float + std::iter::Sum + std::ops::Div, { self.check_same_length_and_raise(other)?; let norm_self = self.norm(); @@ -1075,14 +1067,14 @@ where // ================================ impl VectorOpsFloatMut for FlexVector where - T: num::Float + Clone + std::iter::Sum, + T: num::Float + std::iter::Sum, { type Output = Self; #[inline] fn mut_normalize(&mut self) -> Result<(), VectorError> where - T: Copy + PartialEq + std::ops::Div + num::Zero, + T: num::Float + std::ops::Div + num::Zero, { let norm = self.norm(); mut_normalize_impl(self.as_mut_slice(), norm) @@ -1091,11 +1083,7 @@ where #[inline] fn mut_normalize_to(&mut self, magnitude: T) -> Result<(), VectorError> where - T: Copy - + PartialEq - + std::ops::Div - + std::ops::Mul - + num::Zero, + T: num::Float + std::ops::Div + std::ops::Mul + num::Zero, { let n = self.norm(); mut_normalize_to_impl(self.as_mut_slice(), n, magnitude) @@ -1104,7 +1092,7 @@ where #[inline] fn mut_lerp(&mut self, end: &Self, weight: T) -> Result<(), VectorError> where - T: num::Float + Copy + PartialOrd, + T: num::Float, { self.check_same_length_and_raise(end)?; if weight < T::zero() || weight > T::one() { @@ -1123,13 +1111,14 @@ where impl VectorOpsComplex for FlexVector, O> where - N: num::Float + Clone + std::iter::Sum, + N: num::Num + Copy + std::iter::Sum, { type Output = Self; #[inline] fn normalize(&self) -> Result where + N: num::Float, Complex: Copy + PartialEq + std::ops::Div, Output = Complex>, Self::Output: std::iter::FromIterator>, { @@ -1139,6 +1128,7 @@ where #[inline] fn normalize_to(&self, magnitude: N) -> Result where + N: num::Float, Complex: Copy + PartialEq + std::ops::Div, Output = Complex> @@ -1156,7 +1146,7 @@ where #[inline] fn dot(&self, other: &Self) -> Result, VectorError> where - N: num::Num + Copy + std::iter::Sum + std::ops::Neg, + N: num::Num + std::iter::Sum + std::ops::Neg, { self.check_same_length_and_raise(other)?; Ok(hermitian_dot_impl(self.as_slice(), other.as_slice())) @@ -1165,7 +1155,7 @@ where #[inline] fn lerp(&self, end: &Self, weight: N) -> Result where - N: num::Float + Clone + PartialOrd, + N: num::Float, Complex: Copy + std::ops::Add> + std::ops::Mul> @@ -1185,7 +1175,7 @@ where #[inline] fn midpoint(&self, end: &Self) -> Result where - N: num::Float + Clone, + N: num::Float, { self.check_same_length_and_raise(end)?; self.lerp(end, num::cast(0.5).unwrap()) @@ -1194,7 +1184,7 @@ where #[inline] fn distance(&self, other: &Self) -> Result where - N: num::Float + Clone + std::iter::Sum, + N: num::Float + std::iter::Sum, { self.check_same_length_and_raise(other)?; Ok(distance_complex_impl(self.as_slice(), other.as_slice())) @@ -1203,7 +1193,7 @@ where #[inline] fn manhattan_distance(&self, other: &Self) -> Result where - N: num::Float + Clone + std::iter::Sum, + N: num::Float + std::iter::Sum, { self.check_same_length_and_raise(other)?; Ok(manhattan_distance_complex_impl(self.as_slice(), other.as_slice())) @@ -1212,7 +1202,7 @@ where #[inline] fn chebyshev_distance(&self, other: &Self) -> Result where - N: num::Float + Clone + PartialOrd, + N: num::Float, { self.check_same_length_and_raise(other)?; Ok(chebyshev_distance_complex_impl(self.as_slice(), other.as_slice())) @@ -1221,7 +1211,7 @@ where #[inline] fn minkowski_distance(&self, other: &Self, p: N) -> Result where - N: num::Float + Clone + std::iter::Sum, + N: num::Float + std::iter::Sum, { self.check_same_length_and_raise(other)?; if p < N::one() { @@ -1233,7 +1223,7 @@ where #[inline] fn project_onto(&self, other: &Self) -> Result where - N: num::Float + Clone + std::iter::Sum + std::ops::Neg, + N: num::Float + std::iter::Sum + std::ops::Neg, Complex: Copy + std::ops::Mul> + std::ops::Add> @@ -1255,7 +1245,7 @@ where #[inline] fn cosine_similarity(&self, other: &Self) -> Result, VectorError> where - N: num::Float + Clone + std::iter::Sum + std::ops::Neg, + N: num::Float + std::iter::Sum + std::ops::Neg, Complex: std::ops::Div>, { self.check_same_length_and_raise(other)?; @@ -1272,13 +1262,14 @@ where impl VectorOpsComplexMut for FlexVector, O> where - N: num::Float + Clone + std::iter::Sum, + N: num::Num + Copy + std::iter::Sum, { type Output = Self; #[inline] fn mut_normalize(&mut self) -> Result<(), VectorError> where + N: num::Float, Complex: Copy + PartialEq + std::ops::Div, Output = Complex> + num::Zero, { let norm = self.norm(); @@ -1288,6 +1279,7 @@ where #[inline] fn mut_normalize_to(&mut self, magnitude: N) -> Result<(), VectorError> where + N: num::Float, Complex: Copy + PartialEq + std::ops::Div, Output = Complex> @@ -1305,12 +1297,11 @@ where #[inline] fn mut_lerp(&mut self, end: &Self, weight: N) -> Result<(), VectorError> where - N: num::Float + Copy + PartialOrd, + N: num::Float, Complex: Copy + std::ops::Add> + std::ops::Mul> - + std::ops::Sub> - + num::One, + + std::ops::Sub>, { self.check_same_length_and_raise(end)?; if weight < N::zero() || weight > N::one() { diff --git a/src/types/traits.rs b/src/types/traits.rs index 404556a..2de5374 100644 --- a/src/types/traits.rs +++ b/src/types/traits.rs @@ -262,20 +262,20 @@ pub trait VectorOps: VectorBase { #[inline] fn scale(&self, scalar: T) -> Self::Output where - T: num::Num + Clone, + T: num::Num + Copy, Self::Output: std::iter::FromIterator, { - self.as_slice().iter().map(|a| a.clone() * scalar.clone()).collect() + self.as_slice().iter().map(|a| *a * scalar).collect() } /// Returns a new vector with all elements negated. #[inline] fn negate(&self) -> Self::Output where - T: std::ops::Neg + Clone, + T: std::ops::Neg + Copy, Self::Output: std::iter::FromIterator, { - self.as_slice().iter().map(|a| -(a.clone())).collect() + self.as_slice().iter().map(|a| -*a).collect() } /// ... @@ -329,12 +329,12 @@ pub trait VectorOps: VectorBase { /// Element-wise minimum fn elementwise_min(&self, other: &Self) -> Result where - T: PartialOrd + Clone; + T: PartialOrd + Copy; /// ... fn elementwise_min_into(&self, other: &Self, out: &mut [T]) -> Result<(), VectorError> where - T: PartialOrd + Clone; + T: PartialOrd + Copy; /// ... #[inline] @@ -348,29 +348,29 @@ pub trait VectorOps: VectorBase { /// Element-wise maximum fn elementwise_max(&self, other: &Self) -> Result where - T: PartialOrd + Clone; + T: PartialOrd + Copy; /// ... fn elementwise_max_into(&self, other: &Self, out: &mut [T]) -> Result<(), VectorError> where - T: PartialOrd + Clone; + T: PartialOrd + Copy; /// Returns a new vector where each element is clamped to the [min, max] range. #[inline] fn elementwise_clamp(&self, min: T, max: T) -> Self::Output where - T: PartialOrd + Clone, + T: PartialOrd + Copy, Self::Output: std::iter::FromIterator, { self.as_slice() .iter() .map(|x| { if *x < min { - min.clone() + min } else if *x > max { - max.clone() + max } else { - x.clone() + *x } }) .collect() @@ -380,7 +380,7 @@ pub trait VectorOps: VectorBase { #[inline] fn elementwise_clamp_into(&self, min: T, max: T, out: &mut [T]) -> Result<(), VectorError> where - T: PartialOrd + Clone, + T: PartialOrd + Copy, { if out.len() != self.len() { return Err(VectorError::MismatchedLengthError( @@ -389,11 +389,11 @@ pub trait VectorOps: VectorBase { } for (out_elem, x) in out.iter_mut().zip(self.as_slice()) { *out_elem = if *x < min { - min.clone() + min } else if *x > max { - max.clone() + max } else { - x.clone() + *x }; } Ok(()) @@ -446,10 +446,10 @@ pub trait VectorOpsMut: VectorBaseMut { #[inline] fn mut_negate(&mut self) where - T: std::ops::Neg + Clone, + T: std::ops::Neg + Copy, { for a in self.as_mut_slice().iter_mut() { - *a = -a.clone(); + *a = -*a; } } @@ -472,69 +472,65 @@ pub trait VectorOpsFloat: VectorBase { /// ... fn normalize(&self) -> Result where - T: Copy + PartialEq + std::ops::Div, + T: num::Float + std::ops::Div, Self::Output: std::iter::FromIterator; /// ... fn normalize_into(&self, out: &mut [T]) -> Result<(), VectorError> where - T: Copy + PartialEq + std::ops::Div + num::Zero; + T: num::Float + std::ops::Div + num::Zero; /// Returns a new vector with the same direction and the given magnitude. fn normalize_to(&self, magnitude: T) -> Result where - T: Copy + PartialEq + std::ops::Div + std::ops::Mul, + T: num::Float + std::ops::Div + std::ops::Mul, Self::Output: std::iter::FromIterator; /// ... fn normalize_to_into(&self, magnitude: T, out: &mut [T]) -> Result<(), VectorError> where - T: Copy - + PartialEq - + std::ops::Div - + std::ops::Mul - + num::Zero; + T: num::Float + std::ops::Div + std::ops::Mul + num::Zero; /// Linear interpolation between self and end by weight in [0, 1]. fn lerp(&self, end: &Self, weight: T) -> Result where - T: num::Float + Clone + PartialOrd; + T: num::Float; /// ... fn lerp_into(&self, end: &Self, weight: T, out: &mut [T]) -> Result<(), VectorError> where - T: num::Float + Clone + PartialOrd; + T: num::Float; /// Midpoint fn midpoint(&self, other: &Self) -> Result where - T: num::Float + Clone; + T: num::Float; /// Euclidean distance between self and other. fn distance(&self, other: &Self) -> Result where - T: num::Float + Clone + std::iter::Sum; + T: num::Float + std::iter::Sum; /// Manhattan (L1) distance between self and other. fn manhattan_distance(&self, other: &Self) -> Result where - T: num::Float + Clone + std::iter::Sum; + T: num::Float + std::iter::Sum; /// Chebyshev (L∞) distance between self and other. fn chebyshev_distance(&self, other: &Self) -> Result where - T: num::Float + Clone + PartialOrd; + T: num::Float + PartialOrd; /// Minkowski (Lp) distance between self and other. fn minkowski_distance(&self, other: &Self, p: T) -> Result where - T: num::Float + Clone + std::iter::Sum; + T: num::Float + std::iter::Sum; /// Euclidean norm (magnitude) of the vector. #[inline] fn norm(&self) -> T where - T: num::Float + Clone + std::iter::Sum, + T: num::Float + std::iter::Sum, { self.as_slice().iter().map(|a| (*a).powi(2)).sum::().sqrt() } @@ -543,7 +539,7 @@ pub trait VectorOpsFloat: VectorBase { #[inline] fn magnitude(&self) -> T where - T: num::Float + Clone + std::iter::Sum, + T: num::Float + std::iter::Sum, { self.norm() } @@ -552,7 +548,7 @@ pub trait VectorOpsFloat: VectorBase { #[inline] fn lp_norm(&self, p: T) -> Result where - T: num::Float + Copy + std::iter::Sum, + T: num::Float + std::iter::Sum, { if p < T::one() { return Err(VectorError::OutOfRangeError("p must be >= 1".to_string())); @@ -563,19 +559,19 @@ pub trait VectorOpsFloat: VectorBase { /// ... fn angle_with(&self, other: &Self) -> Result where - T: num::Float + Clone + std::iter::Sum; + T: num::Float + std::iter::Sum; /// Projects self onto other. /// Returns an error if `other` is the zero vector. fn project_onto(&self, other: &Self) -> Result where - T: num::Float + Clone + std::iter::Sum, + T: num::Float + std::iter::Sum, Self::Output: std::iter::FromIterator; /// ... fn cosine_similarity(&self, other: &Self) -> Result where - T: num::Float + Clone + std::iter::Sum + std::ops::Div; + T: num::Float + std::iter::Sum + std::ops::Div; } /// ... @@ -586,21 +582,17 @@ pub trait VectorOpsFloatMut: VectorBaseMut { /// ... fn mut_normalize(&mut self) -> Result<(), VectorError> where - T: Copy + PartialEq + std::ops::Div; + T: num::Float + std::ops::Div; /// ... fn mut_normalize_to(&mut self, magnitude: T) -> Result<(), VectorError> where - T: Copy - + PartialEq - + std::ops::Div - + std::ops::Mul - + num::Zero; + T: num::Float + std::ops::Div + std::ops::Mul + num::Zero; /// In-place linear interpolation between self and end by weight in [0, 1]. fn mut_lerp(&mut self, end: &Self, weight: T) -> Result<(), VectorError> where - T: num::Float + Copy + PartialOrd; + T: num::Float; } /// ... pub trait VectorOpsComplex: VectorBase> { @@ -610,12 +602,14 @@ pub trait VectorOpsComplex: VectorBase> { /// ... fn normalize(&self) -> Result where + N: num::Float, Complex: Copy + PartialEq + std::ops::Div, Output = Complex>, Self::Output: std::iter::FromIterator>; /// Returns a new vector with the same direction and the given magnitude (real). fn normalize_to(&self, magnitude: N) -> Result where + N: num::Float, Complex: Copy + PartialEq + std::ops::Div, Output = Complex> @@ -630,38 +624,38 @@ pub trait VectorOpsComplex: VectorBase> { /// Linear interpolation between self and end by real weight in [0, 1]. fn lerp(&self, end: &Self, weight: N) -> Result where - N: num::Float + Clone + PartialOrd; + N: num::Float; /// Midpoint fn midpoint(&self, end: &Self) -> Result where - N: num::Float + Clone; + N: num::Float; /// Euclidean distance (L2 norm) between self and other (returns real). fn distance(&self, other: &Self) -> Result where - N: num::Float + Clone + std::iter::Sum; + N: num::Float + std::iter::Sum; /// Manhattan (L1) distance between self and other (sum of magnitudes of differences). fn manhattan_distance(&self, other: &Self) -> Result where - N: num::Float + Clone + std::iter::Sum; + N: num::Float + std::iter::Sum; /// Chebyshev (L∞) distance between self and other (maximum magnitude of differences). fn chebyshev_distance(&self, other: &Self) -> Result where - N: num::Float + Clone + PartialOrd; + N: num::Float + PartialOrd; /// Minkowski (Lp) distance between self and other. fn minkowski_distance(&self, other: &Self, p: N) -> Result where - N: num::Float + Clone + std::iter::Sum; + N: num::Float + std::iter::Sum; /// Euclidean norm (magnitude) of the vector (returns real). #[inline] fn norm(&self) -> N where - N: num::Float + Clone + std::iter::Sum, + N: num::Float + std::iter::Sum, { self.as_slice().iter().map(|a| a.norm_sqr()).sum::().sqrt() } @@ -670,7 +664,7 @@ pub trait VectorOpsComplex: VectorBase> { #[inline] fn magnitude(&self) -> N where - N: num::Float + Clone + std::iter::Sum, + N: num::Float + std::iter::Sum, { self.norm() } @@ -679,7 +673,7 @@ pub trait VectorOpsComplex: VectorBase> { #[inline] fn l1_norm(&self) -> N where - N: num::Float + Clone + std::iter::Sum, + N: num::Float + std::iter::Sum, { self.as_slice().iter().map(|a| a.norm()).sum() } @@ -688,7 +682,7 @@ pub trait VectorOpsComplex: VectorBase> { #[inline] fn linf_norm(&self) -> N where - N: num::Float + Clone + PartialOrd, + N: num::Float + PartialOrd, { self.as_slice().iter().map(|a| a.norm()).fold(N::zero(), |acc, x| acc.max(x)) } @@ -697,7 +691,7 @@ pub trait VectorOpsComplex: VectorBase> { #[inline] fn lp_norm(&self, p: N) -> Result where - N: num::Float + Clone + std::iter::Sum, + N: num::Float + std::iter::Sum, { if p < N::one() { return Err(VectorError::OutOfRangeError("p must be >= 1".to_string())); @@ -708,13 +702,13 @@ pub trait VectorOpsComplex: VectorBase> { /// ... fn project_onto(&self, other: &Self) -> Result where - N: num::Float + Clone + std::iter::Sum, + N: num::Float + std::iter::Sum, Self::Output: std::iter::FromIterator>; /// ... fn cosine_similarity(&self, other: &Self) -> Result, VectorError> where - N: num::Float + Clone + std::iter::Sum + std::ops::Neg, + N: num::Float + std::iter::Sum + std::ops::Neg, Complex: std::ops::Div>; } @@ -726,21 +720,22 @@ pub trait VectorOpsComplexMut: VectorBaseMut> { /// ... fn mut_normalize(&mut self) -> Result<(), VectorError> where - Complex: Copy + PartialEq + std::ops::Div, Output = Complex>; + N: num::Float, + Complex: Copy + PartialEq + std::ops::Div, Output = Complex> + num::Zero; /// Scales the complex vector in place to the given (real) magnitude. fn mut_normalize_to(&mut self, magnitude: N) -> Result<(), VectorError> where + N: num::Float, Complex: Copy + PartialEq + std::ops::Div, Output = Complex> - + std::ops::Mul, Output = Complex> - + num::Zero; + + std::ops::Mul, Output = Complex>; /// In-place linear interpolation between self and end by real weight in [0, 1]. fn mut_lerp(&mut self, end: &Self, weight: N) -> Result<(), VectorError> where - N: num::Float + Copy + PartialOrd; + N: num::Float; } /// ... pub trait VectorHasOrientation { diff --git a/src/types/utils.rs b/src/types/utils.rs index 5ad99d6..07adc86 100644 --- a/src/types/utils.rs +++ b/src/types/utils.rs @@ -61,38 +61,38 @@ pub(crate) fn cross_into_impl(a: &[T], b: &[T], out: &mut [T /// Returns a Vec containing the elementwise minimum of two slices. /// Assumes all slices are the same length. -pub(crate) fn elementwise_min_impl(a: &[T], b: &[T]) -> Vec { +pub(crate) fn elementwise_min_impl(a: &[T], b: &[T]) -> Vec { a.iter() .zip(b.iter()) - .map(|(a_elem, b_elem)| if a_elem < b_elem { a_elem.clone() } else { b_elem.clone() }) + .map(|(a_elem, b_elem)| if a_elem < b_elem { *a_elem } else { *b_elem }) .collect() } /// Writes the elementwise minimum of two slices into the provided output buffer. /// Assumes all slices are the same length. #[inline] -pub(crate) fn elementwise_min_into_impl(a: &[T], b: &[T], out: &mut [T]) { +pub(crate) fn elementwise_min_into_impl(a: &[T], b: &[T], out: &mut [T]) { for ((out_elem, a_elem), b_elem) in out.iter_mut().zip(a.iter()).zip(b.iter()) { - *out_elem = if a_elem < b_elem { a_elem.clone() } else { b_elem.clone() }; + *out_elem = if a_elem < b_elem { *a_elem } else { *b_elem }; } } /// Returns a Vec containing the elementwise maximum of two slices. /// Assumes all slices are the same length. #[inline] -pub(crate) fn elementwise_max_impl(a: &[T], b: &[T]) -> Vec { +pub(crate) fn elementwise_max_impl(a: &[T], b: &[T]) -> Vec { a.iter() .zip(b.iter()) - .map(|(a_elem, b_elem)| if a_elem > b_elem { a_elem.clone() } else { b_elem.clone() }) + .map(|(a_elem, b_elem)| if a_elem > b_elem { *a_elem } else { *b_elem }) .collect() } /// Writes the elementwise maximum of two slices into the provided output buffer. /// Assumes all slices are the same length. #[inline] -pub(crate) fn elementwise_max_into_impl(a: &[T], b: &[T], out: &mut [T]) { +pub(crate) fn elementwise_max_into_impl(a: &[T], b: &[T], out: &mut [T]) { for ((out_elem, a_elem), b_elem) in out.iter_mut().zip(a.iter()).zip(b.iter()) { - *out_elem = if a_elem > b_elem { a_elem.clone() } else { b_elem.clone() }; + *out_elem = if a_elem > b_elem { *a_elem } else { *b_elem }; } } @@ -129,7 +129,7 @@ where #[inline] pub(crate) fn distance_impl(a: &[T], b: &[T]) -> T where - T: num::Float + Clone + std::iter::Sum, + T: num::Float + std::iter::Sum, { a.iter().zip(b.iter()).map(|(a, b)| (*a - *b).powi(2)).sum::().sqrt() } @@ -137,7 +137,7 @@ where #[inline] pub(crate) fn distance_complex_impl(a: &[num::Complex], b: &[num::Complex]) -> N where - N: num::Float + Clone + std::iter::Sum, + N: num::Float + std::iter::Sum, { a.iter().zip(b.iter()).map(|(a, b)| (*a - *b).norm_sqr()).sum::().sqrt() } @@ -145,14 +145,14 @@ where #[inline] pub(crate) fn manhattan_distance_impl(a: &[T], b: &[T]) -> T where - T: num::Float + Clone + std::iter::Sum, + T: num::Float + std::iter::Sum, { a.iter().zip(b.iter()).map(|(a, b)| (*a - *b).abs()).sum() } pub(crate) fn manhattan_distance_complex_impl(a: &[num::Complex], b: &[num::Complex]) -> N where - N: num::Float + Clone + std::iter::Sum, + N: num::Float + std::iter::Sum, { a.iter().zip(b.iter()).map(|(a, b)| (*a - *b).norm()).sum() } @@ -160,7 +160,7 @@ where #[inline] pub(crate) fn chebyshev_distance_impl(a: &[T], b: &[T]) -> T where - T: num::Float + Clone + PartialOrd, + T: num::Float, { a.iter().zip(b.iter()).map(|(a, b)| (*a - *b).abs()).fold(T::zero(), |acc, x| acc.max(x)) } @@ -168,7 +168,7 @@ where #[inline] pub(crate) fn chebyshev_distance_complex_impl(a: &[num::Complex], b: &[num::Complex]) -> N where - N: num::Float + Clone + PartialOrd, + N: num::Float, { a.iter().zip(b.iter()).map(|(a, b)| (*a - *b).norm()).fold(N::zero(), |acc, x| acc.max(x)) } @@ -176,7 +176,7 @@ where #[inline] pub(crate) fn minkowski_distance_impl(a: &[T], b: &[T], p: T) -> T where - T: num::Float + Clone + std::iter::Sum, + T: num::Float + std::iter::Sum, { a.iter().zip(b.iter()).map(|(a, b)| (*a - *b).abs().powf(p)).sum::().powf(T::one() / p) } @@ -188,7 +188,7 @@ pub(crate) fn minkowski_distance_complex_impl( p: N, ) -> N where - N: num::Float + Clone + std::iter::Sum, + N: num::Float + std::iter::Sum, { a.iter().zip(b.iter()).map(|(a, b)| (*a - *b).norm().powf(p)).sum::().powf(N::one() / p) } @@ -196,7 +196,7 @@ where #[inline] pub(crate) fn angle_with_impl(a: &[T], b: &[T], norm_a: T, norm_b: T) -> T where - T: num::Float + Clone + std::iter::Sum, + T: num::Float + std::iter::Sum, { let dot = dot_impl(a, b); let cos_theta = dot / (norm_a * norm_b); @@ -322,7 +322,7 @@ where #[inline] pub(crate) fn cosine_similarity_impl(a: &[T], b: &[T], norm_a: T, norm_b: T) -> T where - T: num::Float + Clone + std::iter::Sum + std::ops::Div, + T: num::Float + std::iter::Sum + std::ops::Div, { let dot = dot_impl(a, b); dot / (norm_a * norm_b) @@ -336,7 +336,7 @@ pub(crate) fn cosine_similarity_complex_impl( norm_b: N, ) -> num::Complex where - N: num::Float + Clone + std::iter::Sum + std::ops::Neg, + N: num::Float + std::iter::Sum + std::ops::Neg, num::Complex: std::ops::Div>, { let dot = hermitian_dot_impl(a, b); diff --git a/src/types/vectorslice.rs b/src/types/vectorslice.rs index 54b0246..5ed259e 100644 --- a/src/types/vectorslice.rs +++ b/src/types/vectorslice.rs @@ -163,7 +163,7 @@ impl<'a, T, O> VectorBase for VectorSlice<'a, T, O> { impl<'a, T, O> VectorOps for VectorSlice<'a, T, O> where - T: Clone, + T: Copy, { type Output = FlexVector; @@ -247,7 +247,7 @@ where #[inline] fn elementwise_min(&self, other: &Self) -> Result where - T: PartialOrd + Clone, + T: PartialOrd + Copy, { self.check_same_length_and_raise(other)?; Ok(FlexVector::from_vec(elementwise_min_impl(self.as_slice(), other.as_slice()))) @@ -256,7 +256,7 @@ where #[inline] fn elementwise_min_into(&self, other: &Self, out: &mut [T]) -> Result<(), VectorError> where - T: PartialOrd + Clone, + T: PartialOrd + Copy, { if self.len() != other.len() || out.len() != self.len() { return Err(VectorError::MismatchedLengthError( @@ -270,7 +270,7 @@ where #[inline] fn elementwise_max(&self, other: &Self) -> Result where - T: PartialOrd + Clone, + T: PartialOrd + Copy, { self.check_same_length_and_raise(other)?; Ok(FlexVector::from_vec(elementwise_max_impl(self.as_slice(), other.as_slice()))) @@ -279,7 +279,7 @@ where #[inline] fn elementwise_max_into(&self, other: &Self, out: &mut [T]) -> Result<(), VectorError> where - T: PartialOrd + Clone, + T: PartialOrd + Copy, { if self.len() != other.len() || out.len() != self.len() { return Err(VectorError::MismatchedLengthError( @@ -293,14 +293,14 @@ where impl<'a, T, O> VectorOpsFloat for VectorSlice<'a, T, O> where - T: num::Float + Clone + std::iter::Sum, + T: num::Float + std::iter::Sum, { type Output = FlexVector; #[inline] fn normalize(&self) -> Result where - T: Copy + PartialEq + std::ops::Div + num::Zero, + T: num::Float + std::ops::Div + num::Zero, Self::Output: std::iter::FromIterator, { normalize_impl(self.as_slice(), self.norm()) @@ -309,7 +309,7 @@ where #[inline] fn normalize_into(&self, out: &mut [T]) -> Result<(), VectorError> where - T: Copy + PartialEq + std::ops::Div + num::Zero, + T: num::Float + std::ops::Div + num::Zero, { let norm = self.norm(); normalize_into_impl(self.as_slice(), norm, out) @@ -318,11 +318,7 @@ where #[inline] fn normalize_to(&self, magnitude: T) -> Result where - T: Copy - + PartialEq - + std::ops::Div - + std::ops::Mul - + num::Zero, + T: num::Float + std::ops::Div + std::ops::Mul + num::Zero, Self::Output: std::iter::FromIterator, { normalize_to_impl(self.as_slice(), self.norm(), magnitude) @@ -331,11 +327,7 @@ where #[inline] fn normalize_to_into(&self, magnitude: T, out: &mut [T]) -> Result<(), VectorError> where - T: Copy - + PartialEq - + std::ops::Div - + std::ops::Mul - + num::Zero, + T: num::Float + std::ops::Div + std::ops::Mul + num::Zero, { let norm = self.norm(); normalize_to_into_impl(self.as_slice(), norm, magnitude, out) @@ -344,7 +336,7 @@ where #[inline] fn lerp(&self, end: &Self, weight: T) -> Result where - T: num::Float + Clone, + T: num::Float, { self.check_same_length_and_raise(end)?; if weight < T::zero() || weight > T::one() { @@ -358,7 +350,7 @@ where #[inline] fn lerp_into(&self, end: &Self, weight: T, out: &mut [T]) -> Result<(), VectorError> where - T: num::Float + Clone, + T: num::Float, { self.check_same_length_and_raise(end)?; if self.len() != out.len() { @@ -376,7 +368,7 @@ where #[inline] fn midpoint(&self, other: &Self) -> Result where - T: num::Float + Clone, + T: num::Float, { self.check_same_length_and_raise(other)?; let mut out = FlexVector::zero(self.len()); @@ -387,7 +379,7 @@ where #[inline] fn distance(&self, other: &Self) -> Result where - T: num::Float + Clone + std::iter::Sum, + T: num::Float + std::iter::Sum, { self.check_same_length_and_raise(other)?; Ok(distance_impl(self.as_slice(), other.as_slice())) @@ -396,7 +388,7 @@ where #[inline] fn manhattan_distance(&self, other: &Self) -> Result where - T: num::Float + Clone + std::iter::Sum, + T: num::Float + std::iter::Sum, { self.check_same_length_and_raise(other)?; Ok(manhattan_distance_impl(self.as_slice(), other.as_slice())) @@ -405,7 +397,7 @@ where #[inline] fn chebyshev_distance(&self, other: &Self) -> Result where - T: num::Float + Clone + PartialOrd, + T: num::Float + PartialOrd, { self.check_same_length_and_raise(other)?; Ok(chebyshev_distance_impl(self.as_slice(), other.as_slice())) @@ -414,7 +406,7 @@ where #[inline] fn minkowski_distance(&self, other: &Self, p: T) -> Result where - T: num::Float + Clone + std::iter::Sum, + T: num::Float + std::iter::Sum, { self.check_same_length_and_raise(other)?; if p < T::one() { @@ -426,7 +418,7 @@ where #[inline] fn angle_with(&self, other: &Self) -> Result where - T: num::Float + Clone + std::iter::Sum, + T: num::Float + std::iter::Sum, { self.check_same_length_and_raise(other)?; let norm_self = self.norm(); @@ -442,7 +434,7 @@ where #[inline] fn project_onto(&self, other: &Self) -> Result where - T: num::Float + Clone + std::iter::Sum, + T: num::Float + std::iter::Sum, Self::Output: std::iter::FromIterator, { self.check_same_length_and_raise(other)?; @@ -459,7 +451,7 @@ where #[inline] fn cosine_similarity(&self, other: &Self) -> Result where - T: num::Float + Clone + std::iter::Sum + std::ops::Div, + T: num::Float + std::iter::Sum + std::ops::Div, { self.check_same_length_and_raise(other)?; let norm_self = self.norm(); @@ -475,13 +467,14 @@ where impl<'a, N, O> VectorOpsComplex for VectorSlice<'a, Complex, O> where - N: num::Float + Clone + std::iter::Sum, + N: num::Num + Copy + std::iter::Sum, { type Output = FlexVector, O>; #[inline] fn normalize(&self) -> Result where + N: num::Float, Complex: Copy + PartialEq + std::ops::Div, Output = Complex>, Self::Output: std::iter::FromIterator>, { @@ -491,6 +484,7 @@ where #[inline] fn normalize_to(&self, magnitude: N) -> Result where + N: num::Float, Complex: Copy + PartialEq + std::ops::Div, Output = Complex> @@ -517,7 +511,7 @@ where #[inline] fn lerp(&self, end: &Self, weight: N) -> Result where - N: num::Float + Clone + PartialOrd, + N: num::Float, Complex: Copy + std::ops::Add> + std::ops::Mul> @@ -537,7 +531,7 @@ where #[inline] fn midpoint(&self, end: &Self) -> Result where - N: num::Float + Clone, + N: num::Float, { self.check_same_length_and_raise(end)?; self.lerp(end, num::cast(0.5).unwrap()) @@ -546,7 +540,7 @@ where #[inline] fn distance(&self, other: &Self) -> Result where - N: num::Float + Clone + std::iter::Sum, + N: num::Float + std::iter::Sum, { self.check_same_length_and_raise(other)?; Ok(distance_complex_impl(self.as_slice(), other.as_slice())) @@ -555,7 +549,7 @@ where #[inline] fn manhattan_distance(&self, other: &Self) -> Result where - N: num::Float + Clone + std::iter::Sum, + N: num::Float + std::iter::Sum, { self.check_same_length_and_raise(other)?; Ok(manhattan_distance_complex_impl(self.as_slice(), other.as_slice())) @@ -564,7 +558,7 @@ where #[inline] fn chebyshev_distance(&self, other: &Self) -> Result where - N: num::Float + Clone + PartialOrd, + N: num::Float, { self.check_same_length_and_raise(other)?; Ok(chebyshev_distance_complex_impl(self.as_slice(), other.as_slice())) @@ -573,7 +567,7 @@ where #[inline] fn minkowski_distance(&self, other: &Self, p: N) -> Result where - N: num::Float + Clone + std::iter::Sum, + N: num::Float + std::iter::Sum, { self.check_same_length_and_raise(other)?; if p < N::one() { @@ -585,7 +579,7 @@ where #[inline] fn project_onto(&self, other: &Self) -> Result where - N: num::Float + Clone + std::iter::Sum + std::ops::Neg, + N: num::Float + std::iter::Sum + std::ops::Neg, Complex: Copy + std::ops::Mul> + std::ops::Add> @@ -607,7 +601,7 @@ where #[inline] fn cosine_similarity(&self, other: &Self) -> Result, VectorError> where - N: num::Float + Clone + std::iter::Sum + std::ops::Neg, + N: num::Float + std::iter::Sum + std::ops::Neg, Complex: std::ops::Div>, { self.check_same_length_and_raise(other)?; @@ -770,7 +764,7 @@ impl<'a, T, O> VectorBaseMut for VectorSliceMut<'a, T, O> { impl<'a, T, O> VectorOps for VectorSliceMut<'a, T, O> where - T: Clone, + T: Copy, { type Output = FlexVector; @@ -854,7 +848,7 @@ where #[inline] fn elementwise_min(&self, other: &Self) -> Result where - T: PartialOrd + Clone, + T: PartialOrd + Copy, { self.check_same_length_and_raise(other)?; Ok(FlexVector::from_vec(elementwise_min_impl(self.as_slice(), other.as_slice()))) @@ -863,7 +857,7 @@ where #[inline] fn elementwise_min_into(&self, other: &Self, out: &mut [T]) -> Result<(), VectorError> where - T: PartialOrd + Clone, + T: PartialOrd + Copy, { if self.len() != other.len() || out.len() != self.len() { return Err(VectorError::MismatchedLengthError( @@ -877,7 +871,7 @@ where #[inline] fn elementwise_max(&self, other: &Self) -> Result where - T: PartialOrd + Clone, + T: PartialOrd + Copy, { self.check_same_length_and_raise(other)?; Ok(FlexVector::from_vec(elementwise_max_impl(self.as_slice(), other.as_slice()))) @@ -886,7 +880,7 @@ where #[inline] fn elementwise_max_into(&self, other: &Self, out: &mut [T]) -> Result<(), VectorError> where - T: PartialOrd + Clone, + T: PartialOrd + Copy, { if self.len() != other.len() || out.len() != self.len() { return Err(VectorError::MismatchedLengthError( @@ -900,7 +894,7 @@ where impl<'a, T, O> VectorOpsMut for VectorSliceMut<'a, T, O> where - T: Clone, + T: Copy, { type Output = Self; @@ -917,14 +911,14 @@ where impl<'a, T, O> VectorOpsFloat for VectorSliceMut<'a, T, O> where - T: num::Float + Clone + std::iter::Sum, + T: num::Float + std::iter::Sum, { type Output = FlexVector; #[inline] fn normalize(&self) -> Result where - T: Copy + PartialEq + std::ops::Div + num::Zero, + T: num::Float + std::ops::Div, Self::Output: std::iter::FromIterator, { normalize_impl(self.as_slice(), self.norm()) @@ -933,7 +927,7 @@ where #[inline] fn normalize_into(&self, out: &mut [T]) -> Result<(), VectorError> where - T: Copy + PartialEq + std::ops::Div + num::Zero, + T: num::Float + std::ops::Div + num::Zero, { let norm = self.norm(); normalize_into_impl(self.as_slice(), norm, out) @@ -942,11 +936,7 @@ where #[inline] fn normalize_to(&self, magnitude: T) -> Result where - T: Copy - + PartialEq - + std::ops::Div - + std::ops::Mul - + num::Zero, + T: num::Float + std::ops::Div + std::ops::Mul, Self::Output: std::iter::FromIterator, { normalize_to_impl(self.as_slice(), self.norm(), magnitude) @@ -955,11 +945,7 @@ where #[inline] fn normalize_to_into(&self, magnitude: T, out: &mut [T]) -> Result<(), VectorError> where - T: Copy - + PartialEq - + std::ops::Div - + std::ops::Mul - + num::Zero, + T: num::Float + std::ops::Div + std::ops::Mul + num::Zero, { let norm = self.norm(); normalize_to_into_impl(self.as_slice(), norm, magnitude, out) @@ -968,7 +954,7 @@ where #[inline] fn lerp(&self, end: &Self, weight: T) -> Result where - T: num::Float + Clone, + T: num::Float, { self.check_same_length_and_raise(end)?; if weight < T::zero() || weight > T::one() { @@ -982,7 +968,7 @@ where #[inline] fn lerp_into(&self, end: &Self, weight: T, out: &mut [T]) -> Result<(), VectorError> where - T: num::Float + Clone, + T: num::Float, { self.check_same_length_and_raise(end)?; if self.len() != out.len() { @@ -1000,7 +986,7 @@ where #[inline] fn midpoint(&self, other: &Self) -> Result where - T: num::Float + Clone, + T: num::Float, { self.check_same_length_and_raise(other)?; let mut out = FlexVector::zero(self.len()); @@ -1011,7 +997,7 @@ where #[inline] fn distance(&self, other: &Self) -> Result where - T: num::Float + Clone + std::iter::Sum, + T: num::Float + std::iter::Sum, { self.check_same_length_and_raise(other)?; Ok(distance_impl(self.as_slice(), other.as_slice())) @@ -1020,7 +1006,7 @@ where #[inline] fn manhattan_distance(&self, other: &Self) -> Result where - T: num::Float + Clone + std::iter::Sum, + T: num::Float + std::iter::Sum, { self.check_same_length_and_raise(other)?; Ok(manhattan_distance_impl(self.as_slice(), other.as_slice())) @@ -1029,7 +1015,7 @@ where #[inline] fn chebyshev_distance(&self, other: &Self) -> Result where - T: num::Float + Clone + PartialOrd, + T: num::Float + PartialOrd, { self.check_same_length_and_raise(other)?; Ok(chebyshev_distance_impl(self.as_slice(), other.as_slice())) @@ -1038,7 +1024,7 @@ where #[inline] fn minkowski_distance(&self, other: &Self, p: T) -> Result where - T: num::Float + Clone + std::iter::Sum, + T: num::Float + std::iter::Sum, { self.check_same_length_and_raise(other)?; if p < T::one() { @@ -1050,7 +1036,7 @@ where #[inline] fn angle_with(&self, other: &Self) -> Result where - T: num::Float + Clone + std::iter::Sum, + T: num::Float + std::iter::Sum, { self.check_same_length_and_raise(other)?; let norm_self = self.norm(); @@ -1066,7 +1052,7 @@ where #[inline] fn project_onto(&self, other: &Self) -> Result where - T: num::Float + Clone + std::iter::Sum, + T: num::Float + std::iter::Sum, Self::Output: std::iter::FromIterator, { self.check_same_length_and_raise(other)?; @@ -1083,7 +1069,7 @@ where #[inline] fn cosine_similarity(&self, other: &Self) -> Result where - T: num::Float + Clone + std::iter::Sum + std::ops::Div, + T: num::Float + std::iter::Sum + std::ops::Div, { self.check_same_length_and_raise(other)?; let norm_self = self.norm(); @@ -1099,14 +1085,14 @@ where impl<'a, T, O> VectorOpsFloatMut for VectorSliceMut<'a, T, O> where - T: num::Float + Clone + std::iter::Sum, + T: num::Float + std::iter::Sum, { type Output = Self; #[inline] fn mut_normalize(&mut self) -> Result<(), VectorError> where - T: Copy + PartialEq + std::ops::Div + num::Zero, + T: num::Float + std::ops::Div, { let norm = self.norm(); mut_normalize_impl(self.as_mut_slice(), norm) @@ -1115,11 +1101,7 @@ where #[inline] fn mut_normalize_to(&mut self, magnitude: T) -> Result<(), VectorError> where - T: Copy - + PartialEq - + std::ops::Div - + std::ops::Mul - + num::Zero, + T: num::Float + std::ops::Div + std::ops::Mul + num::Zero, { let n = self.norm(); mut_normalize_to_impl(self.as_mut_slice(), n, magnitude) @@ -1128,7 +1110,7 @@ where #[inline] fn mut_lerp(&mut self, end: &Self, weight: T) -> Result<(), VectorError> where - T: num::Float + Copy + PartialOrd, + T: num::Float, { self.check_same_length_and_raise(end)?; if weight < T::zero() || weight > T::one() { @@ -1141,13 +1123,14 @@ where impl<'a, N, O> VectorOpsComplex for VectorSliceMut<'a, Complex, O> where - N: num::Float + Clone + std::iter::Sum, + N: num::Num + Copy + std::iter::Sum, { type Output = FlexVector, O>; #[inline] fn normalize(&self) -> Result where + N: num::Float, Complex: Copy + PartialEq + std::ops::Div, Output = Complex>, Self::Output: std::iter::FromIterator>, { @@ -1157,11 +1140,11 @@ where #[inline] fn normalize_to(&self, magnitude: N) -> Result where + N: num::Float, Complex: Copy + PartialEq + std::ops::Div, Output = Complex> - + std::ops::Mul, Output = Complex> - + num::Zero, + + std::ops::Mul, Output = Complex>, Self::Output: std::iter::FromIterator>, { normalize_to_impl( @@ -1183,12 +1166,7 @@ where #[inline] fn lerp(&self, end: &Self, weight: N) -> Result where - N: num::Float + Clone + PartialOrd, - Complex: Copy - + std::ops::Add> - + std::ops::Mul> - + std::ops::Sub> - + num::One, + N: num::Float, { self.check_same_length_and_raise(end)?; if weight < N::zero() || weight > N::one() { @@ -1203,7 +1181,7 @@ where #[inline] fn midpoint(&self, end: &Self) -> Result where - N: num::Float + Clone, + N: num::Float, { self.check_same_length_and_raise(end)?; self.lerp(end, num::cast(0.5).unwrap()) @@ -1212,7 +1190,7 @@ where #[inline] fn distance(&self, other: &Self) -> Result where - N: num::Float + Clone + std::iter::Sum, + N: num::Float + std::iter::Sum, { self.check_same_length_and_raise(other)?; Ok(distance_complex_impl(self.as_slice(), other.as_slice())) @@ -1221,7 +1199,7 @@ where #[inline] fn manhattan_distance(&self, other: &Self) -> Result where - N: num::Float + Clone + std::iter::Sum, + N: num::Float + std::iter::Sum, { self.check_same_length_and_raise(other)?; Ok(manhattan_distance_complex_impl(self.as_slice(), other.as_slice())) @@ -1230,7 +1208,7 @@ where #[inline] fn chebyshev_distance(&self, other: &Self) -> Result where - N: num::Float + Clone + PartialOrd, + N: num::Float, { self.check_same_length_and_raise(other)?; Ok(chebyshev_distance_complex_impl(self.as_slice(), other.as_slice())) @@ -1239,7 +1217,7 @@ where #[inline] fn minkowski_distance(&self, other: &Self, p: N) -> Result where - N: num::Float + Clone + std::iter::Sum, + N: num::Float + std::iter::Sum, { self.check_same_length_and_raise(other)?; if p < N::one() { @@ -1251,12 +1229,7 @@ where #[inline] fn project_onto(&self, other: &Self) -> Result where - N: num::Float + Clone + std::iter::Sum + std::ops::Neg, - Complex: Copy - + std::ops::Mul> - + std::ops::Add> - + std::ops::Div, Output = Complex> - + num::Zero, + N: num::Float + std::iter::Sum, Self::Output: std::iter::FromIterator>, { self.check_same_length_and_raise(other)?; @@ -1273,7 +1246,7 @@ where #[inline] fn cosine_similarity(&self, other: &Self) -> Result, VectorError> where - N: num::Float + Clone + std::iter::Sum + std::ops::Neg, + N: num::Float + std::iter::Sum + std::ops::Neg, Complex: std::ops::Div>, { self.check_same_length_and_raise(other)?; From 762641b79d0a62b59a79f8a047742ee8e7a579dc Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Fri, 30 May 2025 10:59:35 -0400 Subject: [PATCH 67/82] Add VectorOpsFloat midpoint_into, project_onto_into methods, VectorSlice + VectorSliceMut + FlexVector impls --- src/types/flexvector.rs | 169 ++++++++++++++++- src/types/traits.rs | 14 +- src/types/utils.rs | 71 +++++++ src/types/vectorslice.rs | 389 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 640 insertions(+), 3 deletions(-) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index d5c14ed..a4c3fee 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -30,7 +30,8 @@ use crate::types::utils::{ lerp_impl, manhattan_distance_complex_impl, manhattan_distance_impl, minkowski_distance_complex_impl, minkowski_distance_impl, mut_lerp_impl, mut_normalize_impl, mut_normalize_to_impl, mut_translate_impl, normalize_impl, normalize_into_impl, - normalize_to_impl, normalize_to_into_impl, project_onto_impl, translate_impl, + normalize_to_impl, normalize_to_into_impl, project_onto_impl, project_onto_into_impl, + translate_impl, }; use crate::errors::VectorError; @@ -971,6 +972,21 @@ where Ok(out) } + #[inline] + fn midpoint_into(&self, end: &Self, out: &mut [T]) -> Result<(), VectorError> + where + T: num::Float, + { + self.check_same_length_and_raise(end)?; + if self.len() != out.len() { + return Err(VectorError::MismatchedLengthError( + "Output buffer has different length than input vectors".to_string(), + )); + } + lerp_impl(self.as_slice(), end.as_slice(), T::from(0.5).unwrap(), out); + Ok(()) + } + #[inline] fn distance(&self, other: &Self) -> Result where @@ -1043,6 +1059,28 @@ where Ok(project_onto_impl(other.as_slice(), scalar)) } + #[inline] + fn project_onto_into(&self, other: &Self, out: &mut [T]) -> Result<(), VectorError> + where + T: num::Float + std::iter::Sum, + { + self.check_same_length_and_raise(other)?; + if out.len() != self.len() { + return Err(VectorError::MismatchedLengthError( + "Output buffer has different length than input vectors".to_string(), + )); + } + let denom = dot_impl(other.as_slice(), other.as_slice()); + if denom == T::zero() { + return Err(VectorError::ZeroVectorError( + "Cannot project onto zero vector".to_string(), + )); + } + let scalar = dot_impl(self.as_slice(), other.as_slice()) / denom; + project_onto_into_impl(other.as_slice(), scalar, out); + Ok(()) + } + #[inline] fn cosine_similarity(&self, other: &Self) -> Result where @@ -6911,6 +6949,57 @@ mod tests { assert!(result.is_err()); } + // -- midpoint_into -- + + #[test] + fn test_flexvector_midpoint_into_basic_f64() { + let v1 = FlexVector::::from_vec(vec![1.0, 2.0, 3.0]); + let v2 = FlexVector::::from_vec(vec![4.0, 5.0, 6.0]); + let mut out = [0.0; 3]; + v1.midpoint_into(&v2, &mut out).unwrap(); + assert!((out[0] - 2.5).abs() < 1e-12); + assert!((out[1] - 3.5).abs() < 1e-12); + assert!((out[2] - 4.5).abs() < 1e-12); + } + + #[test] + fn test_flexvector_midpoint_into_negative_values_f64() { + let v1 = FlexVector::::from_vec(vec![-1.0, -2.0, -3.0]); + let v2 = FlexVector::::from_vec(vec![1.0, 2.0, 3.0]); + let mut out = [0.0; 3]; + v1.midpoint_into(&v2, &mut out).unwrap(); + assert!((out[0]).abs() < 1e-12); + assert!((out[1]).abs() < 1e-12); + assert!((out[2]).abs() < 1e-12); + } + + #[test] + fn test_flexvector_midpoint_into_mismatched_length_end_f64() { + let v1 = FlexVector::::from_vec(vec![1.0, 2.0, 3.0]); + let v2 = FlexVector::::from_vec(vec![4.0, 5.0]); + let mut out = [0.0; 3]; + let result = v1.midpoint_into(&v2, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_flexvector_midpoint_into_mismatched_length_out_f64() { + let v1 = FlexVector::::from_vec(vec![1.0, 2.0, 3.0]); + let v2 = FlexVector::::from_vec(vec![4.0, 5.0, 6.0]); + let mut out = [0.0; 2]; + let result = v1.midpoint_into(&v2, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_flexvector_midpoint_into_empty_f64() { + let v1 = FlexVector::::from_vec(vec![]); + let v2 = FlexVector::::from_vec(vec![]); + let mut out: [f64; 0] = []; + v1.midpoint_into(&v2, &mut out).unwrap(); + assert_eq!(out, []); + } + // -- distance -- #[test] fn test_distance_f64_basic() { @@ -7296,6 +7385,84 @@ mod tests { assert!(result.is_err()); } + // -- project_onto_into -- + + #[test] + fn test_flexvector_project_onto_into_basic_f64() { + let v1 = FlexVector::::from_vec(vec![3.0, 4.0]); + let v2 = FlexVector::::from_vec(vec![6.0, 8.0]); + let mut out = [0.0; 2]; + v1.project_onto_into(&v2, &mut out).unwrap(); + assert!((out[0] - 3.0).abs() < 1e-8); + assert!((out[1] - 4.0).abs() < 1e-8); + } + + #[test] + fn test_flexvector_project_onto_into_parallel_f64() { + let v1 = FlexVector::::from_vec(vec![2.0, 4.0]); + let v2 = FlexVector::::from_vec(vec![1.0, 2.0]); + let mut out = [0.0; 2]; + v1.project_onto_into(&v2, &mut out).unwrap(); + assert!((out[0] - 2.0).abs() < 1e-8); + assert!((out[1] - 4.0).abs() < 1e-8); + } + + #[test] + fn test_flexvector_project_onto_into_orthogonal_f64() { + let v1 = FlexVector::::from_vec(vec![1.0, 0.0]); + let v2 = FlexVector::::from_vec(vec![0.0, 1.0]); + let mut out = [99.0, 99.0]; + v1.project_onto_into(&v2, &mut out).unwrap(); + assert!((out[0]).abs() < 1e-8); + assert!((out[1]).abs() < 1e-8); + } + + #[test] + fn test_flexvector_project_onto_into_identical_f64() { + let v1 = FlexVector::::from_vec(vec![5.0, 5.0]); + let v2 = FlexVector::::from_vec(vec![5.0, 5.0]); + let mut out = [0.0, 0.0]; + v1.project_onto_into(&v2, &mut out).unwrap(); + assert!((out[0] - 5.0).abs() < 1e-8); + assert!((out[1] - 5.0).abs() < 1e-8); + } + + #[test] + fn test_flexvector_project_onto_into_zero_vector_f64() { + let v1 = FlexVector::::from_vec(vec![1.0, 2.0]); + let v2 = FlexVector::::from_vec(vec![0.0, 0.0]); + let mut out = [0.0, 0.0]; + let result = v1.project_onto_into(&v2, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_flexvector_project_onto_into_mismatched_length_other_f64() { + let v1 = FlexVector::::from_vec(vec![1.0, 2.0]); + let v2 = FlexVector::::from_vec(vec![3.0]); + let mut out = [0.0, 0.0]; + let result = v1.project_onto_into(&v2, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_flexvector_project_onto_into_mismatched_length_out_f64() { + let v1 = FlexVector::::from_vec(vec![1.0, 2.0]); + let v2 = FlexVector::::from_vec(vec![3.0, 4.0]); + let mut out = [0.0; 1]; + let result = v1.project_onto_into(&v2, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_flexvector_project_onto_into_empty_f64() { + let v1 = FlexVector::::from_vec(vec![]); + let v2 = FlexVector::::from_vec(vec![]); + let mut out: [f64; 0] = []; + let result = v1.project_onto_into(&v2, &mut out); + assert!(result.is_err()); // zero vector error + } + // --- cosine_similarity --- #[test] fn test_cosine_similarity_f64_parallel() { diff --git a/src/types/traits.rs b/src/types/traits.rs index 2de5374..2a2a1b2 100644 --- a/src/types/traits.rs +++ b/src/types/traits.rs @@ -502,7 +502,12 @@ pub trait VectorOpsFloat: VectorBase { T: num::Float; /// Midpoint - fn midpoint(&self, other: &Self) -> Result + fn midpoint(&self, end: &Self) -> Result + where + T: num::Float; + + /// ... + fn midpoint_into(&self, end: &Self, out: &mut [T]) -> Result<(), VectorError> where T: num::Float; @@ -568,6 +573,11 @@ pub trait VectorOpsFloat: VectorBase { T: num::Float + std::iter::Sum, Self::Output: std::iter::FromIterator; + /// ... + fn project_onto_into(&self, other: &Self, out: &mut [T]) -> Result<(), VectorError> + where + T: num::Float + std::iter::Sum; + /// ... fn cosine_similarity(&self, other: &Self) -> Result where @@ -616,6 +626,8 @@ pub trait VectorOpsComplex: VectorBase> { + std::ops::Mul, Output = Complex>, Self::Output: std::iter::FromIterator>; + // TODO: add complex type *_into methods with pre-allocated buffer support + /// Hermitian dot product: for all complex types fn dot(&self, other: &Self) -> Result, VectorError> where diff --git a/src/types/utils.rs b/src/types/utils.rs index 07adc86..b456959 100644 --- a/src/types/utils.rs +++ b/src/types/utils.rs @@ -213,6 +213,16 @@ where b.iter().map(|x| *x * scalar).collect() } +#[inline] +pub(crate) fn project_onto_into_impl(other: &[T], scalar: T, out: &mut [T]) +where + T: num::Float + Copy, +{ + for (o, &x) in out.iter_mut().zip(other.iter()) { + *o = x * scalar; + } +} + #[inline] pub(crate) fn normalize_impl(slice: &[T], norm: T) -> Result where @@ -1322,6 +1332,67 @@ mod tests { assert!((proj[1] - 0.0).abs() < 1e-12); } + // -- project_onto_into_impl -- + + #[test] + fn test_project_onto_into_impl_basic() { + let b = [1.0f64, 0.0]; + let scalar = 3.0; + let mut out = [0.0f64; 2]; + project_onto_into_impl(&b, scalar, &mut out); + assert!((out[0] - 3.0).abs() < 1e-12); + assert!((out[1] - 0.0).abs() < 1e-12); + } + + #[test] + fn test_project_onto_into_impl_parallel() { + let b = [1.0f64, 2.0]; + let scalar = 2.0; + let mut out = [0.0f64; 2]; + project_onto_into_impl(&b, scalar, &mut out); + assert!((out[0] - 2.0).abs() < 1e-12); + assert!((out[1] - 4.0).abs() < 1e-12); + } + + #[test] + fn test_project_onto_into_impl_orthogonal() { + let b = [1.0f64, 0.0]; + let scalar = 0.0; + let mut out = [0.0f64; 2]; + project_onto_into_impl(&b, scalar, &mut out); + assert!((out[0] - 0.0).abs() < 1e-12); + assert!((out[1] - 0.0).abs() < 1e-12); + } + + #[test] + fn test_project_onto_into_impl_identical() { + let b = [5.0f64, 5.0]; + let scalar = 1.0; + let mut out = [0.0f64; 2]; + project_onto_into_impl(&b, scalar, &mut out); + assert!((out[0] - 5.0).abs() < 1e-12); + assert!((out[1] - 5.0).abs() < 1e-12); + } + + #[test] + fn test_project_onto_into_impl_zero_vector() { + let b = [0.0f64, 0.0]; + let scalar = 42.0; + let mut out = [1.0f64, 2.0]; + project_onto_into_impl(&b, scalar, &mut out); + assert!((out[0] - 0.0).abs() < 1e-12); + assert!((out[1] - 0.0).abs() < 1e-12); + } + + #[test] + fn test_project_onto_into_impl_empty() { + let b: [f64; 0] = []; + let scalar = 1.0; + let mut out: [f64; 0] = []; + project_onto_into_impl(&b, scalar, &mut out); + assert!(out.is_empty()); + } + // -- normalize_impl -- #[test] fn test_normalize_impl_f64_basic() { diff --git a/src/types/vectorslice.rs b/src/types/vectorslice.rs index 5ed259e..48ba5b2 100644 --- a/src/types/vectorslice.rs +++ b/src/types/vectorslice.rs @@ -15,7 +15,8 @@ use crate::types::utils::{ lerp_impl, manhattan_distance_complex_impl, manhattan_distance_impl, minkowski_distance_complex_impl, minkowski_distance_impl, mut_lerp_impl, mut_normalize_impl, mut_normalize_to_impl, mut_translate_impl, normalize_impl, normalize_into_impl, - normalize_to_impl, normalize_to_into_impl, project_onto_impl, translate_impl, + normalize_to_impl, normalize_to_into_impl, project_onto_impl, project_onto_into_impl, + translate_impl, }; use std::fmt; @@ -376,6 +377,21 @@ where Ok(out) } + #[inline] + fn midpoint_into(&self, end: &Self, out: &mut [T]) -> Result<(), VectorError> + where + T: num::Float, + { + self.check_same_length_and_raise(end)?; + if self.len() != out.len() { + return Err(VectorError::MismatchedLengthError( + "Output buffer has different length than input vectors".to_string(), + )); + } + lerp_impl(self.as_slice(), end.as_slice(), T::from(0.5).unwrap(), out); + Ok(()) + } + #[inline] fn distance(&self, other: &Self) -> Result where @@ -448,6 +464,28 @@ where Ok(project_onto_impl(other.as_slice(), scalar)) } + #[inline] + fn project_onto_into(&self, other: &Self, out: &mut [T]) -> Result<(), VectorError> + where + T: num::Float + std::iter::Sum, + { + self.check_same_length_and_raise(other)?; + if out.len() != self.len() { + return Err(VectorError::MismatchedLengthError( + "Output buffer has different length than input vectors".to_string(), + )); + } + let denom = dot_impl(other.as_slice(), other.as_slice()); + if denom == T::zero() { + return Err(VectorError::ZeroVectorError( + "Cannot project onto zero vector".to_string(), + )); + } + let scalar = dot_impl(self.as_slice(), other.as_slice()) / denom; + project_onto_into_impl(other.as_slice(), scalar, out); + Ok(()) + } + #[inline] fn cosine_similarity(&self, other: &Self) -> Result where @@ -994,6 +1032,21 @@ where Ok(out) } + #[inline] + fn midpoint_into(&self, end: &Self, out: &mut [T]) -> Result<(), VectorError> + where + T: num::Float, + { + self.check_same_length_and_raise(end)?; + if self.len() != out.len() { + return Err(VectorError::MismatchedLengthError( + "Output buffer has different length than input vectors".to_string(), + )); + } + lerp_impl(self.as_slice(), end.as_slice(), T::from(0.5).unwrap(), out); + Ok(()) + } + #[inline] fn distance(&self, other: &Self) -> Result where @@ -1066,6 +1119,28 @@ where Ok(project_onto_impl(other.as_slice(), scalar)) } + #[inline] + fn project_onto_into(&self, other: &Self, out: &mut [T]) -> Result<(), VectorError> + where + T: num::Float + std::iter::Sum, + { + self.check_same_length_and_raise(other)?; + if out.len() != self.len() { + return Err(VectorError::MismatchedLengthError( + "Output buffer has different length than input vectors".to_string(), + )); + } + let denom = dot_impl(other.as_slice(), other.as_slice()); + if denom == T::zero() { + return Err(VectorError::ZeroVectorError( + "Cannot project onto zero vector".to_string(), + )); + } + let scalar = dot_impl(self.as_slice(), other.as_slice()) / denom; + project_onto_into_impl(other.as_slice(), scalar, out); + Ok(()) + } + #[inline] fn cosine_similarity(&self, other: &Self) -> Result where @@ -2197,6 +2272,67 @@ mod tests { assert_eq!(result.as_slice(), &[2.5, 3.5, 4.5]); } + // -- midpoint_into -- + + #[test] + fn test_vector_slice_midpoint_into_basic() { + let a = [1.0, 2.0, 3.0]; + let b = [4.0, 5.0, 6.0]; + let vslice_a: VectorSlice<'_, f64, Row> = VectorSlice::from_range(&a, 0..3); + let vslice_b = VectorSlice::from_range(&b, 0..3); + let mut out = [0.0; 3]; + vslice_a.midpoint_into(&vslice_b, &mut out).unwrap(); + assert_eq!(out, [2.5, 3.5, 4.5]); + } + + #[test] + fn test_vector_slice_midpoint_into_weight_matches_midpoint() { + let a = [10.0, 20.0]; + let b = [30.0, 40.0]; + let vslice_a: VectorSlice<'_, f64, Column> = VectorSlice::from_range(&a, 0..2); + let vslice_b = VectorSlice::from_range(&b, 0..2); + let mut out = [0.0; 2]; + vslice_a.midpoint_into(&vslice_b, &mut out).unwrap(); + // Should match midpoint formula + assert!((out[0] - 20.0).abs() < 1e-8); + assert!((out[1] - 30.0).abs() < 1e-8); + } + + #[test] + fn test_vector_slice_midpoint_into_mismatched_length_end() { + let a = [1.0, 2.0, 3.0]; + let b = [4.0, 5.0]; + let vslice_a: VectorSlice<'_, f64, Row> = VectorSlice::from_range(&a, 0..3); + let vslice_b = VectorSlice::from_range(&b, 0..2); + let mut out = [0.0; 3]; + let result = vslice_a.midpoint_into(&vslice_b, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_midpoint_into_mismatched_length_out() { + let a = [1.0, 2.0, 3.0]; + let b = [4.0, 5.0, 6.0]; + let vslice_a: VectorSlice<'_, f64, Row> = VectorSlice::from_range(&a, 0..3); + let vslice_b = VectorSlice::from_range(&b, 0..3); + let mut out = [0.0; 2]; + let result = vslice_a.midpoint_into(&vslice_b, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_midpoint_into_empty() { + let a: [f64; 0] = []; + let b: [f64; 0] = []; + let vslice_a: VectorSlice<'_, f64, Column> = VectorSlice::from_range(&a, 0..0); + let vslice_b = VectorSlice::from_range(&b, 0..0); + let mut out: [f64; 0] = []; + vslice_a.midpoint_into(&vslice_b, &mut out).unwrap(); + assert_eq!(out, []); + } + + // -- distance -- + #[test] fn test_vector_slice_distance() { let a = [1.0, 2.0, 3.0]; @@ -2270,6 +2406,103 @@ mod tests { assert!((result - 0.0).abs() < 1e-8); } + // -- project_onto_into -- + + #[test] + fn test_vector_slice_project_onto_into_basic() { + let a = [3.0, 4.0]; + let b = [6.0, 8.0]; + let vslice_a: VectorSlice<'_, f64, Column> = VectorSlice::from_range(&a, 0..2); + let vslice_b = VectorSlice::from_range(&b, 0..2); + let mut out = [0.0; 2]; + vslice_a.project_onto_into(&vslice_b, &mut out).unwrap(); + // a is already parallel to b, so projection should be a + assert!((out[0] - 3.0).abs() < 1e-8); + assert!((out[1] - 4.0).abs() < 1e-8); + } + + #[test] + fn test_vector_slice_project_onto_into_parallel() { + let a = [2.0, 4.0]; + let b = [1.0, 2.0]; + let vslice_a: VectorSlice<'_, f64, Row> = VectorSlice::from_range(&a, 0..2); + let vslice_b = VectorSlice::from_range(&b, 0..2); + let mut out = [0.0; 2]; + vslice_a.project_onto_into(&vslice_b, &mut out).unwrap(); + // a is parallel to b, so projection should be a + assert!((out[0] - 2.0).abs() < 1e-8); + assert!((out[1] - 4.0).abs() < 1e-8); + } + + #[test] + fn test_vector_slice_project_onto_into_orthogonal() { + let a = [1.0, 0.0]; + let b = [0.0, 1.0]; + let vslice_a: VectorSlice<'_, f64, Row> = VectorSlice::from_range(&a, 0..2); + let vslice_b = VectorSlice::from_range(&b, 0..2); + let mut out = [99.0, 99.0]; + vslice_a.project_onto_into(&vslice_b, &mut out).unwrap(); + // a is orthogonal to b, so projection should be [0, 0] + assert!((out[0]).abs() < 1e-8); + assert!((out[1]).abs() < 1e-8); + } + + #[test] + fn test_vector_slice_project_onto_into_identical() { + let a = [5.0, 5.0]; + let b = [5.0, 5.0]; + let vslice_a: VectorSlice<'_, f64, Row> = VectorSlice::from_range(&a, 0..2); + let vslice_b = VectorSlice::from_range(&b, 0..2); + let mut out = [0.0, 0.0]; + vslice_a.project_onto_into(&vslice_b, &mut out).unwrap(); + assert!((out[0] - 5.0).abs() < 1e-8); + assert!((out[1] - 5.0).abs() < 1e-8); + } + + #[test] + fn test_vector_slice_project_onto_into_zero_vector() { + let a = [1.0, 2.0]; + let b = [0.0, 0.0]; + let vslice_a: VectorSlice<'_, f64, Column> = VectorSlice::from_range(&a, 0..2); + let vslice_b = VectorSlice::from_range(&b, 0..2); + let mut out = [0.0, 0.0]; + let result = vslice_a.project_onto_into(&vslice_b, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_project_onto_into_mismatched_length_other() { + let a = [1.0, 2.0]; + let b = [3.0]; + let vslice_a: VectorSlice<'_, f64, Column> = VectorSlice::from_range(&a, 0..2); + let vslice_b = VectorSlice::from_range(&b, 0..1); + let mut out = [0.0, 0.0]; + let result = vslice_a.project_onto_into(&vslice_b, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_project_onto_into_mismatched_length_out() { + let a = [1.0, 2.0]; + let b = [3.0, 4.0]; + let vslice_a: VectorSlice<'_, f64, Column> = VectorSlice::from_range(&a, 0..2); + let vslice_b = VectorSlice::from_range(&b, 0..2); + let mut out = [0.0; 1]; + let result = vslice_a.project_onto_into(&vslice_b, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_project_onto_into_empty() { + let a: [f64; 0] = []; + let b: [f64; 0] = []; + let vslice_a: VectorSlice<'_, f64, Column> = VectorSlice::from_range(&a, 0..0); + let vslice_b = VectorSlice::from_range(&b, 0..0); + let mut out: [f64; 0] = []; + let result = vslice_a.project_onto_into(&vslice_b, &mut out); + assert!(result.is_err()); // zero vector error + } + // -- VectorOpsComplex for VectorSlice -- // -- normalize -- @@ -3608,6 +3841,66 @@ mod tests { assert_eq!(result.as_slice(), &[2.5, 3.5, 4.5]); } + // -- midpoint_into -- + + #[test] + fn test_vector_slice_mut_midpoint_into_basic() { + let mut a = [1.0, 2.0, 3.0]; + let mut b = [4.0, 5.0, 6.0]; + let vslice_a: VectorSliceMut<'_, f64, Row> = VectorSliceMut::from_range(&mut a, 0..3); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..3); + let mut out = [0.0; 3]; + vslice_a.midpoint_into(&vslice_b, &mut out).unwrap(); + assert_eq!(out, [2.5, 3.5, 4.5]); + } + + #[test] + fn test_vector_slice_mut_midpoint_into_weight_matches_midpoint() { + let mut a = [10.0, 20.0]; + let mut b = [30.0, 40.0]; + let vslice_a: VectorSliceMut<'_, f64, Column> = VectorSliceMut::from_range(&mut a, 0..2); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + let mut out = [0.0; 2]; + vslice_a.midpoint_into(&vslice_b, &mut out).unwrap(); + assert!((out[0] - 20.0).abs() < 1e-8); + assert!((out[1] - 30.0).abs() < 1e-8); + } + + #[test] + fn test_vector_slice_mut_midpoint_into_mismatched_length_end() { + let mut a = [1.0, 2.0, 3.0]; + let mut b = [4.0, 5.0]; + let vslice_a: VectorSliceMut<'_, f64, Row> = VectorSliceMut::from_range(&mut a, 0..3); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + let mut out = [0.0; 3]; + let result = vslice_a.midpoint_into(&vslice_b, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_mut_midpoint_into_mismatched_length_out() { + let mut a = [1.0, 2.0, 3.0]; + let mut b = [4.0, 5.0, 6.0]; + let vslice_a: VectorSliceMut<'_, f64, Row> = VectorSliceMut::from_range(&mut a, 0..3); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..3); + let mut out = [0.0; 2]; + let result = vslice_a.midpoint_into(&vslice_b, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_mut_midpoint_into_empty() { + let mut a: [f64; 0] = []; + let mut b: [f64; 0] = []; + let vslice_a: VectorSliceMut<'_, f64, Column> = VectorSliceMut::from_range(&mut a, 0..0); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..0); + let mut out: [f64; 0] = []; + vslice_a.midpoint_into(&vslice_b, &mut out).unwrap(); + assert_eq!(out, []); + } + + // -- distance -- + #[test] fn test_vector_slice_mut_distance() { let mut a = [1.0, 2.0, 3.0]; @@ -3681,6 +3974,100 @@ mod tests { assert!((result - 0.0).abs() < 1e-8); } + // -- project_onto_into -- + + #[test] + fn test_vector_slice_mut_project_onto_into_basic() { + let mut a = [3.0, 4.0]; + let mut b = [6.0, 8.0]; + let vslice_a: VectorSliceMut<'_, f64, Column> = VectorSliceMut::from_range(&mut a, 0..2); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + let mut out = [0.0; 2]; + vslice_a.project_onto_into(&vslice_b, &mut out).unwrap(); + assert!((out[0] - 3.0).abs() < 1e-8); + assert!((out[1] - 4.0).abs() < 1e-8); + } + + #[test] + fn test_vector_slice_mut_project_onto_into_parallel() { + let mut a = [2.0, 4.0]; + let mut b = [1.0, 2.0]; + let vslice_a: VectorSliceMut<'_, f64, Row> = VectorSliceMut::from_range(&mut a, 0..2); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + let mut out = [0.0; 2]; + vslice_a.project_onto_into(&vslice_b, &mut out).unwrap(); + assert!((out[0] - 2.0).abs() < 1e-8); + assert!((out[1] - 4.0).abs() < 1e-8); + } + + #[test] + fn test_vector_slice_mut_project_onto_into_orthogonal() { + let mut a = [1.0, 0.0]; + let mut b = [0.0, 1.0]; + let vslice_a: VectorSliceMut<'_, f64, Row> = VectorSliceMut::from_range(&mut a, 0..2); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + let mut out = [99.0, 99.0]; + vslice_a.project_onto_into(&vslice_b, &mut out).unwrap(); + assert!((out[0]).abs() < 1e-8); + assert!((out[1]).abs() < 1e-8); + } + + #[test] + fn test_vector_slice_mut_project_onto_into_identical() { + let mut a = [5.0, 5.0]; + let mut b = [5.0, 5.0]; + let vslice_a: VectorSliceMut<'_, f64, Row> = VectorSliceMut::from_range(&mut a, 0..2); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + let mut out = [0.0, 0.0]; + vslice_a.project_onto_into(&vslice_b, &mut out).unwrap(); + assert!((out[0] - 5.0).abs() < 1e-8); + assert!((out[1] - 5.0).abs() < 1e-8); + } + + #[test] + fn test_vector_slice_mut_project_onto_into_zero_vector() { + let mut a = [1.0, 2.0]; + let mut b = [0.0, 0.0]; + let vslice_a: VectorSliceMut<'_, f64, Column> = VectorSliceMut::from_range(&mut a, 0..2); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + let mut out = [0.0, 0.0]; + let result = vslice_a.project_onto_into(&vslice_b, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_mut_project_onto_into_mismatched_length_other() { + let mut a = [1.0, 2.0]; + let mut b = [3.0]; + let vslice_a: VectorSliceMut<'_, f64, Column> = VectorSliceMut::from_range(&mut a, 0..2); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..1); + let mut out = [0.0, 0.0]; + let result = vslice_a.project_onto_into(&vslice_b, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_mut_project_onto_into_mismatched_length_out() { + let mut a = [1.0, 2.0]; + let mut b = [3.0, 4.0]; + let vslice_a: VectorSliceMut<'_, f64, Column> = VectorSliceMut::from_range(&mut a, 0..2); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + let mut out = [0.0; 1]; + let result = vslice_a.project_onto_into(&vslice_b, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_mut_project_onto_into_empty() { + let mut a: [f64; 0] = []; + let mut b: [f64; 0] = []; + let vslice_a: VectorSliceMut<'_, f64, Column> = VectorSliceMut::from_range(&mut a, 0..0); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..0); + let mut out: [f64; 0] = []; + let result = vslice_a.project_onto_into(&vslice_b, &mut out); + assert!(result.is_err()); // zero vector error + } + // -- VectorOpsFloatMut trait for VectorSliceMut -- #[test] From a4032ab415f9ee5c9280bd1c60929948fffd8971 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Fri, 30 May 2025 20:21:57 -0400 Subject: [PATCH 68/82] Add VectorOpsComplex normalize_into, normalize_to_into, add FlexVector, VectorSlice, VectorSliceMut impls --- src/types/flexvector.rs | 148 ++++++++++++++++- src/types/traits.rs | 17 +- src/types/vectorslice.rs | 339 +++++++++++++++++++++++++++++++++------ 3 files changed, 448 insertions(+), 56 deletions(-) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index a4c3fee..09008c7 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -1163,6 +1163,16 @@ where normalize_impl(self.as_slice(), Complex::new(self.norm(), N::zero())) } + #[inline] + fn normalize_into(&self, out: &mut [Complex]) -> Result<(), VectorError> + where + N: num::Float, + Complex: Copy + PartialEq + std::ops::Div, Output = Complex>, + Self::Output: std::iter::FromIterator>, + { + normalize_into_impl(self.as_slice(), Complex::new(self.norm(), N::zero()), out) + } + #[inline] fn normalize_to(&self, magnitude: N) -> Result where @@ -1170,8 +1180,7 @@ where Complex: Copy + PartialEq + std::ops::Div, Output = Complex> - + std::ops::Mul, Output = Complex> - + num::Zero, + + std::ops::Mul, Output = Complex>, Self::Output: std::iter::FromIterator>, { normalize_to_impl( @@ -1181,6 +1190,23 @@ where ) } + #[inline] + fn normalize_to_into(&self, magnitude: N, out: &mut [Complex]) -> Result<(), VectorError> + where + N: num::Float, + Complex: Copy + + PartialEq + + std::ops::Div, Output = Complex> + + std::ops::Mul, Output = Complex>, + { + normalize_to_into_impl( + self.as_slice(), + Complex::new(self.norm(), N::zero()), + Complex::new(magnitude, N::zero()), + out, + ) + } + #[inline] fn dot(&self, other: &Self) -> Result, VectorError> where @@ -7537,6 +7563,65 @@ mod tests { assert!(normalized.is_err()); } + // -- normalize_into -- + + #[test] + fn test_normalize_into_complex_f64_basic() { + use num::Complex; + let v = FlexVector::>::from_vec(vec![Complex::new(3.0, 4.0)]); + let mut out = [Complex::new(0.0, 0.0); 1]; + v.normalize_into(&mut out).unwrap(); + // The norm is 5.0, so the normalized vector should be [0.6 + 0.8i] + assert!((out[0].re - 0.6).abs() < 1e-12); + assert!((out[0].im - 0.8).abs() < 1e-12); + } + + #[test] + fn test_normalize_into_complex_f64_multiple_elements() { + use num::Complex; + let v = FlexVector::>::from_vec(vec![ + Complex::new(1.0, 2.0), + Complex::new(3.0, 4.0), + ]); + let norm = ((1.0 * 1.0 + 2.0 * 2.0) + (3.0 * 3.0 + 4.0 * 4.0)).sqrt(); + let mut out = [Complex::new(0.0, 0.0); 2]; + v.normalize_into(&mut out).unwrap(); + assert!((out[0].re - 1.0 / norm).abs() < 1e-12); + assert!((out[0].im - 2.0 / norm).abs() < 1e-12); + assert!((out[1].re - 3.0 / norm).abs() < 1e-12); + assert!((out[1].im - 4.0 / norm).abs() < 1e-12); + } + + #[test] + fn test_normalize_into_complex_f64_zero_vector() { + use num::Complex; + let v = FlexVector::>::from_vec(vec![Complex::new(0.0, 0.0)]); + let mut out = [Complex::new(0.0, 0.0); 1]; + let result = v.normalize_into(&mut out); + assert!(result.is_err()); + } + + #[test] + fn test_normalize_into_complex_f64_empty() { + use num::Complex; + let v: FlexVector> = FlexVector::new(); + let mut out: [Complex; 0] = []; + let result = v.normalize_into(&mut out); + assert!(result.is_err()); + } + + #[test] + fn test_normalize_into_complex_f64_wrong_length() { + use num::Complex; + let v = FlexVector::>::from_vec(vec![ + Complex::new(1.0, 2.0), + Complex::new(3.0, 4.0), + ]); + let mut out = [Complex::new(0.0, 0.0); 1]; + let result = v.normalize_into(&mut out); + assert!(result.is_err()); + } + // -- normalize_to -- #[test] @@ -7579,6 +7664,65 @@ mod tests { assert!(normalized.is_err()); } + // -- normalize_to_into -- + + #[test] + fn test_flexvector_normalize_to_into_basic_complex_f64() { + use num::Complex; + let v = FlexVector::>::from_vec(vec![Complex::new(3.0, 4.0)]); + let mut out = [Complex::new(0.0, 0.0); 1]; + v.normalize_to_into(10.0, &mut out).unwrap(); + // The original norm is 5.0, so the normalized vector should be [6.0 + 8.0i] + assert!((out[0].re - 6.0).abs() < 1e-12); + assert!((out[0].im - 8.0).abs() < 1e-12); + } + + #[test] + fn test_flexvector_normalize_to_into_multiple_elements_complex_f64() { + use num::Complex; + let v = FlexVector::>::from_vec(vec![ + Complex::new(1.0, 2.0), + Complex::new(3.0, 4.0), + ]); + let norm = ((1.0 * 1.0 + 2.0 * 2.0) + (3.0 * 3.0 + 4.0 * 4.0)).sqrt(); + let mut out = [Complex::new(0.0, 0.0); 2]; + v.normalize_to_into(2.0, &mut out).unwrap(); + assert!((out[0].re - 1.0 / norm * 2.0).abs() < 1e-12); + assert!((out[0].im - 2.0 / norm * 2.0).abs() < 1e-12); + assert!((out[1].re - 3.0 / norm * 2.0).abs() < 1e-12); + assert!((out[1].im - 4.0 / norm * 2.0).abs() < 1e-12); + } + + #[test] + fn test_flexvector_normalize_to_into_zero_vector_complex_f64() { + use num::Complex; + let v = FlexVector::>::from_vec(vec![Complex::new(0.0, 0.0)]); + let mut out = [Complex::new(0.0, 0.0); 1]; + let result = v.normalize_to_into(1.0, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_flexvector_normalize_to_into_empty_complex_f64() { + use num::Complex; + let v: FlexVector> = FlexVector::new(); + let mut out: [Complex; 0] = []; + let result = v.normalize_to_into(1.0, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_flexvector_normalize_to_into_wrong_length_complex_f64() { + use num::Complex; + let v = FlexVector::>::from_vec(vec![ + Complex::new(1.0, 2.0), + Complex::new(3.0, 4.0), + ]); + let mut out = [Complex::new(0.0, 0.0); 1]; + let result = v.normalize_to_into(1.0, &mut out); + assert!(result.is_err()); + } + // -- dot -- #[test] diff --git a/src/types/traits.rs b/src/types/traits.rs index 2a2a1b2..8f3a8ac 100644 --- a/src/types/traits.rs +++ b/src/types/traits.rs @@ -616,6 +616,13 @@ pub trait VectorOpsComplex: VectorBase> { Complex: Copy + PartialEq + std::ops::Div, Output = Complex>, Self::Output: std::iter::FromIterator>; + /// ... + fn normalize_into(&self, out: &mut [Complex]) -> Result<(), VectorError> + where + N: num::Float, + Complex: Copy + PartialEq + std::ops::Div, Output = Complex>, + Self::Output: std::iter::FromIterator>; + /// Returns a new vector with the same direction and the given magnitude (real). fn normalize_to(&self, magnitude: N) -> Result where @@ -626,7 +633,15 @@ pub trait VectorOpsComplex: VectorBase> { + std::ops::Mul, Output = Complex>, Self::Output: std::iter::FromIterator>; - // TODO: add complex type *_into methods with pre-allocated buffer support + /// Writes a normalized version of self with the given magnitude into the provided buffer. + /// The output buffer must have the same length as self. + fn normalize_to_into(&self, magnitude: N, out: &mut [Complex]) -> Result<(), VectorError> + where + N: num::Float, + Complex: Copy + + PartialEq + + std::ops::Div, Output = Complex> + + std::ops::Mul, Output = Complex>; /// Hermitian dot product: for all complex types fn dot(&self, other: &Self) -> Result, VectorError> diff --git a/src/types/vectorslice.rs b/src/types/vectorslice.rs index 48ba5b2..9a3f665 100644 --- a/src/types/vectorslice.rs +++ b/src/types/vectorslice.rs @@ -519,6 +519,16 @@ where normalize_impl(self.as_slice(), Complex::new(self.norm(), N::zero())) } + #[inline] + fn normalize_into(&self, out: &mut [Complex]) -> Result<(), VectorError> + where + N: num::Float, + Complex: Copy + PartialEq + std::ops::Div, Output = Complex>, + Self::Output: std::iter::FromIterator>, + { + normalize_into_impl(self.as_slice(), Complex::new(self.norm(), N::zero()), out) + } + #[inline] fn normalize_to(&self, magnitude: N) -> Result where @@ -537,6 +547,23 @@ where ) } + #[inline] + fn normalize_to_into(&self, magnitude: N, out: &mut [Complex]) -> Result<(), VectorError> + where + N: num::Float, + Complex: Copy + + PartialEq + + std::ops::Div, Output = Complex> + + std::ops::Mul, Output = Complex>, + { + normalize_to_into_impl( + self.as_slice(), + Complex::new(self.norm(), N::zero()), + Complex::new(magnitude, N::zero()), + out, + ) + } + #[inline] fn dot(&self, other: &Self) -> Result, VectorError> where @@ -1212,6 +1239,16 @@ where normalize_impl(self.as_slice(), Complex::new(self.norm(), N::zero())) } + #[inline] + fn normalize_into(&self, out: &mut [Complex]) -> Result<(), VectorError> + where + N: num::Float, + Complex: Copy + PartialEq + std::ops::Div, Output = Complex>, + Self::Output: std::iter::FromIterator>, + { + normalize_into_impl(self.as_slice(), Complex::new(self.norm(), N::zero()), out) + } + #[inline] fn normalize_to(&self, magnitude: N) -> Result where @@ -1229,6 +1266,23 @@ where ) } + #[inline] + fn normalize_to_into(&self, magnitude: N, out: &mut [Complex]) -> Result<(), VectorError> + where + N: num::Float, + Complex: Copy + + PartialEq + + std::ops::Div, Output = Complex> + + std::ops::Mul, Output = Complex>, + { + normalize_to_into_impl( + self.as_slice(), + Complex::new(self.norm(), N::zero()), + Complex::new(magnitude, N::zero()), + out, + ) + } + #[inline] fn dot(&self, other: &Self) -> Result, VectorError> where @@ -2509,7 +2563,6 @@ mod tests { #[test] fn test_vector_slice_complex_normalize() { - use num::Complex; let a = [Complex::new(3.0, 4.0), Complex::new(0.0, 0.0)]; let vslice: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..2); // The norm is sqrt(|3+4i|^2 + |0|^2) = sqrt(25) = 5 @@ -2523,18 +2576,69 @@ mod tests { #[test] fn test_vector_slice_complex_normalize_zero_vector() { - use num::Complex; let a = [Complex::new(0.0, 0.0), Complex::new(0.0, 0.0)]; let vslice: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..2); let result = vslice.normalize(); assert!(result.is_err()); } + // -- normalize_into (Complex) -- + + #[test] + fn test_vector_slice_complex_normalize_into_basic() { + let a = [Complex::new(3.0, 4.0)]; + let vslice: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..1); + let mut out = [Complex::new(0.0, 0.0); 1]; + vslice.normalize_into(&mut out).unwrap(); + // The norm is 5.0, so the normalized vector should be [0.6 + 0.8i] + assert!((out[0].re - 0.6).abs() < 1e-12); + assert!((out[0].im - 0.8).abs() < 1e-12); + } + + #[test] + fn test_vector_slice_complex_normalize_into_multiple_elements() { + let a = [Complex::new(1.0_f64, 2.0), Complex::new(3.0, 4.0)]; + let vslice: VectorSlice<'_, Complex, Row> = VectorSlice::from_range(&a, 0..2); + let norm = ((1.0_f64 * 1.0 + 2.0 * 2.0) + (3.0 * 3.0 + 4.0 * 4.0)).sqrt(); + let mut out = [Complex::new(0.0, 0.0); 2]; + vslice.normalize_into(&mut out).unwrap(); + assert!((out[0].re - 1.0 / norm).abs() < 1e-12); + assert!((out[0].im - 2.0 / norm).abs() < 1e-12); + assert!((out[1].re - 3.0 / norm).abs() < 1e-12); + assert!((out[1].im - 4.0 / norm).abs() < 1e-12); + } + + #[test] + fn test_vector_slice_complex_normalize_into_zero_vector() { + let a = [Complex::new(0.0, 0.0)]; + let vslice: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..1); + let mut out = [Complex::new(0.0, 0.0); 1]; + let result = vslice.normalize_into(&mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_complex_normalize_into_empty() { + let a: [Complex; 0] = []; + let vslice: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..0); + let mut out: [Complex; 0] = []; + let result = vslice.normalize_into(&mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_complex_normalize_into_wrong_length() { + let a = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; + let vslice: VectorSlice<'_, Complex, Row> = VectorSlice::from_range(&a, 0..2); + let mut out = [Complex::new(0.0, 0.0); 1]; + let result = vslice.normalize_into(&mut out); + assert!(result.is_err()); + } + // -- normalize_to -- #[test] fn test_vector_slice_complex_normalize_to() { - use num::Complex; let a = [Complex::new(3.0, 4.0), Complex::new(0.0, 0.0)]; let vslice: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..2); // The norm is 5, so scaling to magnitude 10 multiplies by 2 @@ -2548,18 +2652,69 @@ mod tests { #[test] fn test_vector_slice_complex_normalize_to_zero_vector() { - use num::Complex; let a = [Complex::new(0.0, 0.0), Complex::new(0.0, 0.0)]; let vslice: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..2); let result = vslice.normalize_to(10.0); assert!(result.is_err()); } + // -- normalize_to_into -- + + #[test] + fn test_vector_slice_complex_normalize_to_into_basic() { + let a = [Complex::new(3.0, 4.0)]; + let vslice: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..1); + let mut out = [Complex::new(0.0, 0.0); 1]; + vslice.normalize_to_into(10.0, &mut out).unwrap(); + // The original norm is 5.0, so the normalized vector should be [6.0 + 8.0i] + assert!((out[0].re - 6.0).abs() < 1e-12); + assert!((out[0].im - 8.0).abs() < 1e-12); + } + + #[test] + fn test_vector_slice_complex_normalize_to_into_multiple_elements() { + let a = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; + let vslice: VectorSlice<'_, Complex, Row> = VectorSlice::from_range(&a, 0..2); + let norm = ((1.0_f64 * 1.0 + 2.0 * 2.0) + (3.0 * 3.0 + 4.0 * 4.0)).sqrt(); + let mut out = [Complex::new(0.0, 0.0); 2]; + vslice.normalize_to_into(2.0, &mut out).unwrap(); + assert!((out[0].re - 1.0 / norm * 2.0).abs() < 1e-12); + assert!((out[0].im - 2.0 / norm * 2.0).abs() < 1e-12); + assert!((out[1].re - 3.0 / norm * 2.0).abs() < 1e-12); + assert!((out[1].im - 4.0 / norm * 2.0).abs() < 1e-12); + } + + #[test] + fn test_vector_slice_complex_normalize_to_into_zero_vector() { + let a = [Complex::new(0.0, 0.0)]; + let vslice: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..1); + let mut out = [Complex::new(0.0, 0.0); 1]; + let result = vslice.normalize_to_into(1.0, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_complex_normalize_to_into_empty() { + let a: [Complex; 0] = []; + let vslice: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..0); + let mut out: [Complex; 0] = []; + let result = vslice.normalize_to_into(1.0, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_complex_normalize_to_into_wrong_length() { + let a = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; + let vslice: VectorSlice<'_, Complex, Row> = VectorSlice::from_range(&a, 0..2); + let mut out = [Complex::new(0.0, 0.0); 1]; + let result = vslice.normalize_to_into(1.0, &mut out); + assert!(result.is_err()); + } + // -- dot -- #[test] fn test_vector_slice_complex_dot_basic() { - use num::Complex; let a = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; let b = [Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]; let vslice_a: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..2); @@ -2573,7 +2728,6 @@ mod tests { #[test] fn test_vector_slice_complex_dot_negative_values() { - use num::Complex; let a = [Complex::new(-1.0, -2.0), Complex::new(-3.0, -4.0)]; let b = [Complex::new(2.0, 1.0), Complex::new(4.0, 3.0)]; let vslice_a: VectorSlice<'_, Complex, Row> = VectorSlice::from_range(&a, 0..2); @@ -2586,7 +2740,6 @@ mod tests { #[test] fn test_vector_slice_complex_dot_zero_vector() { - use num::Complex; let a = [Complex::new(0.0, 0.0), Complex::new(0.0, 0.0)]; let b = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; let vslice_a: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..2); @@ -2598,7 +2751,6 @@ mod tests { #[test] fn test_vector_slice_complex_dot_empty() { - use num::Complex; let a: [Complex; 0] = []; let b: [Complex; 0] = []; let vslice_a: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..0); @@ -2610,7 +2762,6 @@ mod tests { #[test] fn test_vector_slice_complex_dot_mismatched_length() { - use num::Complex; let a = [Complex::new(1.0, 2.0)]; let b = [Complex::new(3.0, 4.0), Complex::new(5.0, 6.0)]; let vslice_a: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..1); @@ -2623,7 +2774,6 @@ mod tests { #[test] fn test_vector_slice_complex_lerp() { - use num::Complex; let a = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; let b = [Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]; let vslice_a: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..2); @@ -2642,7 +2792,6 @@ mod tests { #[test] fn test_vector_slice_complex_lerp_weight_out_of_bounds() { - use num::Complex; let a = [Complex::new(1.0, 2.0)]; let b = [Complex::new(3.0, 4.0)]; let vslice_a: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..1); @@ -2655,7 +2804,6 @@ mod tests { #[test] fn test_vector_slice_complex_midpoint() { - use num::Complex; let a = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; let b = [Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]; let vslice_a: VectorSlice<'_, Complex, Row> = VectorSlice::from_range(&a, 0..2); @@ -2675,7 +2823,6 @@ mod tests { #[test] fn test_vector_slice_complex_distance() { - use num::Complex; let a = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; let b = [Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]; let vslice_a: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..2); @@ -2692,7 +2839,6 @@ mod tests { #[test] fn test_vector_slice_complex_manhattan_distance() { - use num::Complex; let a = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; let b = [Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]; let vslice_a: VectorSlice<'_, Complex, Row> = VectorSlice::from_range(&a, 0..2); @@ -2709,7 +2855,6 @@ mod tests { #[test] fn test_vector_slice_complex_chebyshev_distance() { - use num::Complex; let a = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; let b = [Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]; let vslice_a: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..2); @@ -2726,7 +2871,6 @@ mod tests { #[test] fn test_vector_slice_complex_minkowski_distance() { - use num::Complex; let a = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; let b = [Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]; let vslice_a: VectorSlice<'_, Complex, Row> = VectorSlice::from_range(&a, 0..2); @@ -2744,7 +2888,6 @@ mod tests { #[test] fn test_vector_slice_complex_project_onto_basic() { - use num::Complex; let a = [Complex::new(3.0, 4.0), Complex::new(0.0, 0.0)]; let b = [Complex::new(1.0, 0.0), Complex::new(0.0, 0.0)]; let vslice_a: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..2); @@ -2757,7 +2900,6 @@ mod tests { #[test] fn test_vector_slice_complex_project_onto_parallel() { - use num::Complex; let a = [Complex::new(2.0, 2.0), Complex::new(4.0, 4.0)]; let b = [Complex::new(1.0, 1.0), Complex::new(2.0, 2.0)]; let vslice_a: VectorSlice<'_, Complex, Row> = VectorSlice::from_range(&a, 0..2); @@ -2769,7 +2911,6 @@ mod tests { #[test] fn test_vector_slice_complex_project_onto_orthogonal() { - use num::Complex; let a = [Complex::new(0.0, 1.0), Complex::new(0.0, 0.0)]; let b = [Complex::new(1.0, 0.0), Complex::new(0.0, 0.0)]; let vslice_a: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..2); @@ -2781,7 +2922,6 @@ mod tests { #[test] fn test_vector_slice_complex_project_onto_identical() { - use num::Complex; let a = [Complex::new(5.0, 5.0), Complex::new(5.0, 5.0)]; let b = [Complex::new(5.0, 5.0), Complex::new(5.0, 5.0)]; let vslice_a: VectorSlice<'_, Complex, Row> = VectorSlice::from_range(&a, 0..2); @@ -2793,7 +2933,6 @@ mod tests { #[test] fn test_vector_slice_complex_project_onto_zero_vector() { - use num::Complex; let a = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; let b = [Complex::new(0.0, 0.0), Complex::new(0.0, 0.0)]; let vslice_a: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..2); @@ -2806,7 +2945,6 @@ mod tests { #[test] fn test_vector_slice_complex_cosine_similarity_parallel() { - use num::Complex; let a = [Complex::new(1.0, 2.0), Complex::new(2.0, 4.0)]; let b = [Complex::new(2.0, 4.0), Complex::new(4.0, 8.0)]; let vslice_a: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..2); @@ -2817,7 +2955,6 @@ mod tests { #[test] fn test_vector_slice_complex_cosine_similarity_orthogonal() { - use num::Complex; let a = [Complex::new(1.0, 0.0), Complex::new(0.0, 0.0)]; let b = [Complex::new(0.0, 0.0), Complex::new(1.0, 0.0)]; let vslice_a: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..2); @@ -2828,7 +2965,6 @@ mod tests { #[test] fn test_vector_slice_complex_cosine_similarity_opposite() { - use num::Complex; let a = [Complex::new(1.0, 0.0)]; let b = [Complex::new(-1.0, 0.0)]; let vslice_a: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..1); @@ -2839,7 +2975,6 @@ mod tests { #[test] fn test_vector_slice_complex_cosine_similarity_identical() { - use num::Complex; let a = [Complex::new(3.0, 4.0)]; let b = [Complex::new(3.0, 4.0)]; let vslice_a: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..1); @@ -2850,7 +2985,6 @@ mod tests { #[test] fn test_vector_slice_complex_cosine_similarity_arbitrary() { - use num::Complex; let a = [Complex::new(1.0, 2.0)]; let b = [Complex::new(2.0, 1.0)]; let vslice_a: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..1); @@ -2861,7 +2995,6 @@ mod tests { #[test] fn test_vector_slice_complex_cosine_similarity_zero_vector() { - use num::Complex; let a = [Complex::new(0.0, 0.0)]; let b = [Complex::new(1.0, 2.0)]; let vslice_a: VectorSlice<'_, Complex, Column> = VectorSlice::from_range(&a, 0..1); @@ -3717,6 +3850,69 @@ mod tests { assert!(result.is_err()); } + // -- normalize_to_into -- + + #[test] + fn test_vector_slice_mut_complex_normalize_to_into_basic() { + use num::Complex; + let mut a = [Complex::new(3.0, 4.0)]; + let vslice: VectorSliceMut<'_, Complex, Column> = + VectorSliceMut::from_range(&mut a, 0..1); + let mut out = [Complex::new(0.0, 0.0); 1]; + vslice.normalize_to_into(10.0, &mut out).unwrap(); + // The original norm is 5.0, so the normalized vector should be [6.0 + 8.0i] + assert!((out[0].re - 6.0).abs() < 1e-12); + assert!((out[0].im - 8.0).abs() < 1e-12); + } + + #[test] + fn test_vector_slice_mut_complex_normalize_to_into_multiple_elements() { + use num::Complex; + let mut a = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; + let vslice: VectorSliceMut<'_, Complex, Row> = + VectorSliceMut::from_range(&mut a, 0..2); + let norm = ((1.0_f64 * 1.0 + 2.0 * 2.0) + (3.0 * 3.0 + 4.0 * 4.0)).sqrt(); + let mut out = [Complex::new(0.0, 0.0); 2]; + vslice.normalize_to_into(2.0, &mut out).unwrap(); + assert!((out[0].re - 1.0 / norm * 2.0).abs() < 1e-12); + assert!((out[0].im - 2.0 / norm * 2.0).abs() < 1e-12); + assert!((out[1].re - 3.0 / norm * 2.0).abs() < 1e-12); + assert!((out[1].im - 4.0 / norm * 2.0).abs() < 1e-12); + } + + #[test] + fn test_vector_slice_mut_complex_normalize_to_into_zero_vector() { + use num::Complex; + let mut a = [Complex::new(0.0, 0.0)]; + let vslice: VectorSliceMut<'_, Complex, Column> = + VectorSliceMut::from_range(&mut a, 0..1); + let mut out = [Complex::new(0.0, 0.0); 1]; + let result = vslice.normalize_to_into(1.0, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_mut_complex_normalize_to_into_empty() { + use num::Complex; + let mut a: [Complex; 0] = []; + let vslice: VectorSliceMut<'_, Complex, Column> = + VectorSliceMut::from_range(&mut a, 0..0); + let mut out: [Complex; 0] = []; + let result = vslice.normalize_to_into(1.0, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_mut_complex_normalize_to_into_wrong_length() { + use num::Complex; + let mut a = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; + let vslice: VectorSliceMut<'_, Complex, Row> = + VectorSliceMut::from_range(&mut a, 0..2); + let mut out = [Complex::new(0.0, 0.0); 1]; + let result = vslice.normalize_to_into(1.0, &mut out); + assert!(result.is_err()); + } + // -- lerp -- #[test] @@ -4128,7 +4324,6 @@ mod tests { #[test] fn test_vector_slice_mut_complex_normalize() { - use num::Complex; let mut a = [Complex::new(3.0, 4.0), Complex::new(0.0, 0.0)]; let vslice: VectorSliceMut<'_, Complex, Column> = VectorSliceMut::from_range(&mut a, 0..2); @@ -4143,7 +4338,6 @@ mod tests { #[test] fn test_vector_slice_mut_complex_normalize_zero_vector() { - use num::Complex; let mut a = [Complex::new(0.0, 0.0), Complex::new(0.0, 0.0)]; let vslice: VectorSliceMut<'_, Complex, Column> = VectorSliceMut::from_range(&mut a, 0..2); @@ -4151,11 +4345,73 @@ mod tests { assert!(result.is_err()); } + // -- normalize_into -- + + #[test] + fn test_vector_slice_mut_complex_normalize_into_basic() { + use num::Complex; + let mut a = [Complex::new(3.0, 4.0)]; + let vslice: VectorSliceMut<'_, Complex, Column> = + VectorSliceMut::from_range(&mut a, 0..1); + let mut out = [Complex::new(0.0, 0.0); 1]; + vslice.normalize_into(&mut out).unwrap(); + // The norm is 5.0, so the normalized vector should be [0.6 + 0.8i] + assert!((out[0].re - 0.6).abs() < 1e-12); + assert!((out[0].im - 0.8).abs() < 1e-12); + } + + #[test] + fn test_vector_slice_mut_complex_normalize_into_multiple_elements() { + use num::Complex; + let mut a = [Complex::new(1.0_f64, 2.0), Complex::new(3.0, 4.0)]; + let vslice: VectorSliceMut<'_, Complex, Row> = + VectorSliceMut::from_range(&mut a, 0..2); + let norm = ((1.0_f64 * 1.0 + 2.0 * 2.0) + (3.0 * 3.0 + 4.0 * 4.0)).sqrt(); + let mut out = [Complex::new(0.0, 0.0); 2]; + vslice.normalize_into(&mut out).unwrap(); + assert!((out[0].re - 1.0 / norm).abs() < 1e-12); + assert!((out[0].im - 2.0 / norm).abs() < 1e-12); + assert!((out[1].re - 3.0 / norm).abs() < 1e-12); + assert!((out[1].im - 4.0 / norm).abs() < 1e-12); + } + + #[test] + fn test_vector_slice_mut_complex_normalize_into_zero_vector() { + use num::Complex; + let mut a = [Complex::new(0.0, 0.0)]; + let vslice: VectorSliceMut<'_, Complex, Column> = + VectorSliceMut::from_range(&mut a, 0..1); + let mut out = [Complex::new(0.0, 0.0); 1]; + let result = vslice.normalize_into(&mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_mut_complex_normalize_into_empty() { + use num::Complex; + let mut a: [Complex; 0] = []; + let vslice: VectorSliceMut<'_, Complex, Column> = + VectorSliceMut::from_range(&mut a, 0..0); + let mut out: [Complex; 0] = []; + let result = vslice.normalize_into(&mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_mut_complex_normalize_into_wrong_length() { + use num::Complex; + let mut a = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; + let vslice: VectorSliceMut<'_, Complex, Row> = + VectorSliceMut::from_range(&mut a, 0..2); + let mut out = [Complex::new(0.0, 0.0); 1]; + let result = vslice.normalize_into(&mut out); + assert!(result.is_err()); + } + // -- normalize_to -- #[test] fn test_vector_slice_mut_complex_normalize_to() { - use num::Complex; let mut a = [Complex::new(3.0, 4.0), Complex::new(0.0, 0.0)]; let vslice: VectorSliceMut<'_, Complex, Column> = VectorSliceMut::from_range(&mut a, 0..2); @@ -4170,7 +4426,6 @@ mod tests { #[test] fn test_vector_slice_mut_complex_normalize_to_zero_vector() { - use num::Complex; let mut a = [Complex::new(0.0, 0.0), Complex::new(0.0, 0.0)]; let vslice: VectorSliceMut<'_, Complex, Column> = VectorSliceMut::from_range(&mut a, 0..2); @@ -4182,7 +4437,6 @@ mod tests { #[test] fn test_vector_slice_mut_complex_dot_basic() { - use num::Complex; let mut a = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; let mut b = [Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]; // Compute expected before mutable borrow @@ -4197,7 +4451,6 @@ mod tests { #[test] fn test_vector_slice_mut_complex_dot_mismatched_length() { - use num::Complex; let mut a = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; let mut b = [Complex::new(5.0, 6.0)]; let vslice_a: VectorSliceMut<'_, Complex, Column> = @@ -4209,7 +4462,6 @@ mod tests { #[test] fn test_vector_slice_mut_complex_dot_zero() { - use num::Complex; let mut a = [Complex::new(0.0, 0.0), Complex::new(0.0, 0.0)]; let mut b = [Complex::new(0.0, 0.0), Complex::new(0.0, 0.0)]; let vslice_a: VectorSliceMut<'_, Complex, Column> = @@ -4222,7 +4474,6 @@ mod tests { #[test] fn test_vector_slice_mut_complex_dot_empty() { - use num::Complex; let mut a: [Complex; 0] = []; let mut b: [Complex; 0] = []; let vslice_a: VectorSliceMut<'_, Complex, Column> = @@ -4237,7 +4488,6 @@ mod tests { #[test] fn test_vector_slice_mut_complex_lerp() { - use num::Complex; let mut a = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; let mut b = [Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]; let vslice_a: VectorSliceMut<'_, Complex, Column> = @@ -4257,7 +4507,6 @@ mod tests { #[test] fn test_vector_slice_mut_complex_lerp_weight_out_of_bounds() { - use num::Complex; let mut a = [Complex::new(1.0, 2.0)]; let mut b = [Complex::new(3.0, 4.0)]; let vslice_a: VectorSliceMut<'_, Complex, Column> = @@ -4271,7 +4520,6 @@ mod tests { #[test] fn test_vector_slice_mut_complex_midpoint() { - use num::Complex; let mut a = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; let mut b = [Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]; let vslice_a: VectorSliceMut<'_, Complex, Row> = @@ -4292,7 +4540,6 @@ mod tests { #[test] fn test_vector_slice_mut_complex_distance() { - use num::Complex; let mut a = [Complex::new(1.0_f64, 2.0), Complex::new(3.0, 4.0)]; let mut b = [Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]; // Euclidean distance: sqrt(sum_i |a[i] - b[i]|^2) @@ -4310,7 +4557,6 @@ mod tests { #[test] fn test_vector_slice_mut_complex_manhattan_distance() { - use num::Complex; let mut a = [Complex::new(1.0_f64, 2.0), Complex::new(3.0, 4.0)]; let mut b = [Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]; // Manhattan distance: sum_i |a[i] - b[i]| @@ -4328,7 +4574,6 @@ mod tests { #[test] fn test_vector_slice_mut_complex_chebyshev_distance() { - use num::Complex; let mut a = [Complex::new(1.0_f64, 2.0), Complex::new(3.0, 4.0)]; let mut b = [Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]; // Chebyshev distance: max_i |a[i] - b[i]| @@ -4346,7 +4591,6 @@ mod tests { #[test] fn test_vector_slice_mut_complex_minkowski_distance() { - use num::Complex; let mut a = [Complex::new(1.0_f64, 2.0), Complex::new(3.0, 4.0)]; let mut b = [Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]; // Minkowski distance: (|a[0]-b[0]|^p + |a[1]-b[1]|^p)^(1/p) @@ -4365,7 +4609,6 @@ mod tests { #[test] fn test_vector_slice_mut_complex_project_onto_basic() { - use num::Complex; let mut a = [Complex::new(3.0, 4.0), Complex::new(0.0, 0.0)]; let mut b = [Complex::new(1.0, 0.0), Complex::new(0.0, 0.0)]; let vslice_a: VectorSliceMut<'_, Complex, Column> = @@ -4379,7 +4622,6 @@ mod tests { #[test] fn test_vector_slice_mut_complex_project_onto_parallel() { - use num::Complex; let mut a = [Complex::new(2.0, 2.0), Complex::new(4.0, 4.0)]; let mut b = [Complex::new(1.0, 1.0), Complex::new(2.0, 2.0)]; let vslice_a: VectorSliceMut<'_, Complex, Row> = @@ -4392,7 +4634,6 @@ mod tests { #[test] fn test_vector_slice_mut_complex_project_onto_orthogonal() { - use num::Complex; let mut a = [Complex::new(0.0, 1.0), Complex::new(0.0, 0.0)]; let mut b = [Complex::new(1.0, 0.0), Complex::new(0.0, 0.0)]; let vslice_a: VectorSliceMut<'_, Complex, Column> = @@ -4405,7 +4646,6 @@ mod tests { #[test] fn test_vector_slice_mut_complex_project_onto_identical() { - use num::Complex; let mut a = [Complex::new(5.0, 5.0), Complex::new(5.0, 5.0)]; let mut b = [Complex::new(5.0, 5.0), Complex::new(5.0, 5.0)]; let vslice_a: VectorSliceMut<'_, Complex, Row> = @@ -4418,7 +4658,6 @@ mod tests { #[test] fn test_vector_slice_mut_complex_project_onto_zero_vector() { - use num::Complex; let mut a = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; let mut b = [Complex::new(0.0, 0.0), Complex::new(0.0, 0.0)]; let vslice_a: VectorSliceMut<'_, Complex, Column> = @@ -4432,7 +4671,6 @@ mod tests { #[test] fn test_vector_slice_mut_complex_cosine_similarity_parallel() { - use num::Complex; let mut a = [Complex::new(1.0, 2.0), Complex::new(2.0, 4.0)]; let mut b = [Complex::new(2.0, 4.0), Complex::new(4.0, 8.0)]; let vslice_a: VectorSliceMut<'_, Complex, Column> = @@ -4444,7 +4682,6 @@ mod tests { #[test] fn test_vector_slice_mut_complex_cosine_similarity_orthogonal() { - use num::Complex; let mut a = [Complex::new(1.0, 0.0), Complex::new(0.0, 0.0)]; let mut b = [Complex::new(0.0, 0.0), Complex::new(1.0, 0.0)]; let vslice_a: VectorSliceMut<'_, Complex, Column> = @@ -4456,7 +4693,6 @@ mod tests { #[test] fn test_vector_slice_mut_complex_cosine_similarity_opposite() { - use num::Complex; let mut a = [Complex::new(1.0, 0.0)]; let mut b = [Complex::new(-1.0, 0.0)]; let vslice_a: VectorSliceMut<'_, Complex, Column> = @@ -4468,7 +4704,6 @@ mod tests { #[test] fn test_vector_slice_mut_complex_cosine_similarity_identical() { - use num::Complex; let mut a = [Complex::new(3.0, 4.0)]; let mut b = [Complex::new(3.0, 4.0)]; let vslice_a: VectorSliceMut<'_, Complex, Column> = @@ -4480,7 +4715,6 @@ mod tests { #[test] fn test_vector_slice_mut_complex_cosine_similarity_arbitrary() { - use num::Complex; let mut a = [Complex::new(1.0, 2.0)]; let mut b = [Complex::new(2.0, 1.0)]; let vslice_a: VectorSliceMut<'_, Complex, Column> = @@ -4492,7 +4726,6 @@ mod tests { #[test] fn test_vector_slice_mut_complex_cosine_similarity_zero_vector() { - use num::Complex; let mut a = [Complex::new(0.0, 0.0)]; let mut b = [Complex::new(1.0, 2.0)]; let vslice_a: VectorSliceMut<'_, Complex, Column> = From 9dadd105f4a7d5068abe649d726f6ba89592fa67 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Sat, 31 May 2025 10:54:42 -0400 Subject: [PATCH 69/82] refactor number type casts implementation --- src/types/flexvector.rs | 4 ++-- src/types/vectorslice.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index 09008c7..4d2a7b2 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -968,7 +968,7 @@ where { self.check_same_length_and_raise(other)?; let mut out = FlexVector::zero(self.len()); - lerp_impl(self.as_slice(), other.as_slice(), T::from(0.5).unwrap(), out.as_mut_slice()); + lerp_impl(self.as_slice(), other.as_slice(), num::cast(0.5).unwrap(), out.as_mut_slice()); Ok(out) } @@ -983,7 +983,7 @@ where "Output buffer has different length than input vectors".to_string(), )); } - lerp_impl(self.as_slice(), end.as_slice(), T::from(0.5).unwrap(), out); + lerp_impl(self.as_slice(), end.as_slice(), num::cast(0.5).unwrap(), out); Ok(()) } diff --git a/src/types/vectorslice.rs b/src/types/vectorslice.rs index 9a3f665..67ab556 100644 --- a/src/types/vectorslice.rs +++ b/src/types/vectorslice.rs @@ -373,7 +373,7 @@ where { self.check_same_length_and_raise(other)?; let mut out = FlexVector::zero(self.len()); - lerp_impl(self.as_slice(), other.as_slice(), T::from(0.5).unwrap(), out.as_mut_slice()); + lerp_impl(self.as_slice(), other.as_slice(), num::cast(0.5).unwrap(), out.as_mut_slice()); Ok(out) } @@ -388,7 +388,7 @@ where "Output buffer has different length than input vectors".to_string(), )); } - lerp_impl(self.as_slice(), end.as_slice(), T::from(0.5).unwrap(), out); + lerp_impl(self.as_slice(), end.as_slice(), num::cast(0.5).unwrap(), out); Ok(()) } From 393107595de1ea642fa3b312fde663f9dedbefce Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Sat, 31 May 2025 11:28:36 -0400 Subject: [PATCH 70/82] Add VectorOpsComplex lerp_into, midpoint_into; add FlexVector, VectorSlice, VectorSlice impls --- src/types/flexvector.rs | 233 ++++++++++++++++++-- src/types/traits.rs | 22 ++ src/types/vectorslice.rs | 451 ++++++++++++++++++++++++++++++++++++--- 3 files changed, 664 insertions(+), 42 deletions(-) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index 4d2a7b2..4816af9 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -962,14 +962,11 @@ where } #[inline] - fn midpoint(&self, other: &Self) -> Result + fn midpoint(&self, end: &Self) -> Result where T: num::Float, { - self.check_same_length_and_raise(other)?; - let mut out = FlexVector::zero(self.len()); - lerp_impl(self.as_slice(), other.as_slice(), num::cast(0.5).unwrap(), out.as_mut_slice()); - Ok(out) + self.lerp(end, num::cast(0.5).unwrap()) } #[inline] @@ -977,14 +974,7 @@ where where T: num::Float, { - self.check_same_length_and_raise(end)?; - if self.len() != out.len() { - return Err(VectorError::MismatchedLengthError( - "Output buffer has different length than input vectors".to_string(), - )); - } - lerp_impl(self.as_slice(), end.as_slice(), num::cast(0.5).unwrap(), out); - Ok(()) + self.lerp_into(end, num::cast(0.5).unwrap(), out) } #[inline] @@ -1237,14 +1227,50 @@ where } #[inline] - fn midpoint(&self, end: &Self) -> Result + fn lerp_into(&self, end: &Self, weight: N, out: &mut [Complex]) -> Result<(), VectorError> where N: num::Float, + Complex: Copy + + std::ops::Add> + + std::ops::Mul> + + std::ops::Sub> + + num::One, { self.check_same_length_and_raise(end)?; + if out.len() != self.len() { + return Err(VectorError::MismatchedLengthError( + "Output buffer has different length than input vectors".to_string(), + )); + } + if weight < N::zero() || weight > N::one() { + return Err(VectorError::OutOfRangeError("weight must be in [0, 1]".to_string())); + } + let w = Complex::new(weight, N::zero()); + lerp_impl(self.as_slice(), end.as_slice(), w, out); + Ok(()) + } + + #[inline] + fn midpoint(&self, end: &Self) -> Result + where + N: num::Float, + { self.lerp(end, num::cast(0.5).unwrap()) } + #[inline] + fn midpoint_into(&self, end: &Self, out: &mut [Complex]) -> Result<(), VectorError> + where + N: num::Float, + Complex: Copy + + std::ops::Add> + + std::ops::Mul> + + std::ops::Sub> + + num::One, + { + self.lerp_into(end, num::cast(0.5).unwrap(), out) + } + #[inline] fn distance(&self, other: &Self) -> Result where @@ -7829,6 +7855,92 @@ mod tests { assert!(result.is_err()); } + // -- lerp_into -- + + #[test] + fn test_flexvector_lerp_into_complex_f64_basic() { + let v1 = FlexVector::>::from_vec(vec![ + Complex::new(1.0, 2.0), + Complex::new(3.0, 4.0), + ]); + let v2 = FlexVector::>::from_vec(vec![ + Complex::new(5.0, 6.0), + Complex::new(7.0, 8.0), + ]); + let mut out = [Complex::new(0.0, 0.0); 2]; + v1.lerp_into(&v2, 0.5, &mut out).unwrap(); + // Should be the midpoint + assert!((out[0] - Complex::new(3.0, 4.0)).norm() < 1e-12); + assert!((out[1] - Complex::new(5.0, 6.0)).norm() < 1e-12); + } + + #[test] + fn test_flexvector_lerp_into_complex_f64_weight_zero() { + let v1 = FlexVector::>::from_vec(vec![Complex::new(1.0, 2.0)]); + let v2 = FlexVector::>::from_vec(vec![Complex::new(5.0, 6.0)]); + let mut out = [Complex::new(0.0, 0.0); 1]; + v1.lerp_into(&v2, 0.0, &mut out).unwrap(); + // Should be equal to v1 + assert!((out[0] - Complex::new(1.0, 2.0)).norm() < 1e-12); + } + + #[test] + fn test_flexvector_lerp_into_complex_f64_weight_one() { + let v1 = FlexVector::>::from_vec(vec![Complex::new(1.0, 2.0)]); + let v2 = FlexVector::>::from_vec(vec![Complex::new(5.0, 6.0)]); + let mut out = [Complex::new(0.0, 0.0); 1]; + v1.lerp_into(&v2, 1.0, &mut out).unwrap(); + // Should be equal to v2 + assert!((out[0] - Complex::new(5.0, 6.0)).norm() < 1e-12); + } + + #[test] + fn test_flexvector_lerp_into_complex_f64_weight_out_of_bounds() { + let v1 = FlexVector::>::from_vec(vec![Complex::new(1.0, 2.0)]); + let v2 = FlexVector::>::from_vec(vec![Complex::new(5.0, 6.0)]); + let mut out = [Complex::new(0.0, 0.0); 1]; + let result_low = v1.lerp_into(&v2, -0.1, &mut out); + let result_high = v1.lerp_into(&v2, 1.1, &mut out); + assert!(result_low.is_err()); + assert!(result_high.is_err()); + } + + #[test] + fn test_flexvector_lerp_into_complex_f64_mismatched_length_end() { + let v1 = FlexVector::>::from_vec(vec![Complex::new(1.0, 2.0)]); + let v2 = FlexVector::>::from_vec(vec![ + Complex::new(5.0, 6.0), + Complex::new(7.0, 8.0), + ]); + let mut out = [Complex::new(0.0, 0.0); 1]; + let result = v1.lerp_into(&v2, 0.5, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_flexvector_lerp_into_complex_f64_mismatched_length_out() { + let v1 = FlexVector::>::from_vec(vec![ + Complex::new(1.0, 2.0), + Complex::new(3.0, 4.0), + ]); + let v2 = FlexVector::>::from_vec(vec![ + Complex::new(5.0, 6.0), + Complex::new(7.0, 8.0), + ]); + let mut out = [Complex::new(0.0, 0.0); 1]; + let result = v1.lerp_into(&v2, 0.5, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_flexvector_lerp_into_complex_f64_empty() { + let v1: FlexVector> = FlexVector::new(); + let v2: FlexVector> = FlexVector::new(); + let mut out: [Complex; 0] = []; + v1.lerp_into(&v2, 0.5, &mut out).unwrap(); + assert_eq!(out, []); + } + // -- midpoint -- #[test] @@ -7869,6 +7981,99 @@ mod tests { assert!(result.is_err()); } + // -- midpoint_into -- + + #[test] + fn test_flexvector_midpoint_into_complex_f64_basic() { + use num::Complex; + let v1 = FlexVector::>::from_vec(vec![ + Complex::new(1.0, 2.0), + Complex::new(3.0, 4.0), + ]); + let v2 = FlexVector::>::from_vec(vec![ + Complex::new(5.0, 6.0), + Complex::new(7.0, 8.0), + ]); + let mut out = [Complex::new(0.0, 0.0); 2]; + v1.midpoint_into(&v2, &mut out).unwrap(); + // Should be the midpoint + assert!((out[0] - Complex::new(3.0, 4.0)).norm() < 1e-12); + assert!((out[1] - Complex::new(5.0, 6.0)).norm() < 1e-12); + } + + #[test] + fn test_flexvector_midpoint_into_complex_f64_negative_values() { + use num::Complex; + let v1 = FlexVector::>::from_vec(vec![ + Complex::new(-1.0, -2.0), + Complex::new(-3.0, -4.0), + ]); + let v2 = FlexVector::>::from_vec(vec![ + Complex::new(1.0, 2.0), + Complex::new(3.0, 4.0), + ]); + let mut out = [Complex::new(0.0, 0.0); 2]; + v1.midpoint_into(&v2, &mut out).unwrap(); + assert!((out[0] - Complex::new(0.0, 0.0)).norm() < 1e-12); + assert!((out[1] - Complex::new(0.0, 0.0)).norm() < 1e-12); + } + + #[test] + fn test_flexvector_midpoint_into_complex_f64_identical() { + use num::Complex; + let v1 = FlexVector::>::from_vec(vec![ + Complex::new(2.0, 3.0), + Complex::new(4.0, 5.0), + ]); + let v2 = FlexVector::>::from_vec(vec![ + Complex::new(2.0, 3.0), + Complex::new(4.0, 5.0), + ]); + let mut out = [Complex::new(0.0, 0.0); 2]; + v1.midpoint_into(&v2, &mut out).unwrap(); + assert!((out[0] - Complex::new(2.0, 3.0)).norm() < 1e-12); + assert!((out[1] - Complex::new(4.0, 5.0)).norm() < 1e-12); + } + + #[test] + fn test_flexvector_midpoint_into_complex_f64_mismatched_length_end() { + use num::Complex; + let v1 = FlexVector::>::from_vec(vec![Complex::new(1.0, 2.0)]); + let v2 = FlexVector::>::from_vec(vec![ + Complex::new(3.0, 4.0), + Complex::new(5.0, 6.0), + ]); + let mut out = [Complex::new(0.0, 0.0); 1]; + let result = v1.midpoint_into(&v2, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_flexvector_midpoint_into_complex_f64_mismatched_length_out() { + use num::Complex; + let v1 = FlexVector::>::from_vec(vec![ + Complex::new(1.0, 2.0), + Complex::new(3.0, 4.0), + ]); + let v2 = FlexVector::>::from_vec(vec![ + Complex::new(5.0, 6.0), + Complex::new(7.0, 8.0), + ]); + let mut out = [Complex::new(0.0, 0.0); 1]; + let result = v1.midpoint_into(&v2, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_flexvector_midpoint_into_complex_f64_empty() { + use num::Complex; + let v1: FlexVector> = FlexVector::new(); + let v2: FlexVector> = FlexVector::new(); + let mut out: [Complex; 0] = []; + v1.midpoint_into(&v2, &mut out).unwrap(); + assert_eq!(out, []); + } + // -- distance -- #[test] diff --git a/src/types/traits.rs b/src/types/traits.rs index 8f3a8ac..02b497c 100644 --- a/src/types/traits.rs +++ b/src/types/traits.rs @@ -653,11 +653,33 @@ pub trait VectorOpsComplex: VectorBase> { where N: num::Float; + /// Linear interpolation between self and end by real weight in [0, 1], writing into a preallocated buffer. + /// The output buffer must have the same length as self. + fn lerp_into(&self, end: &Self, weight: N, out: &mut [Complex]) -> Result<(), VectorError> + where + N: num::Float, + Complex: Copy + + std::ops::Add> + + std::ops::Mul> + + std::ops::Sub> + + num::One; + /// Midpoint fn midpoint(&self, end: &Self) -> Result where N: num::Float; + /// Midpoint linear interpolation between self and end, writing into a preallocated buffer. + /// The output buffer must have the same length as self. + fn midpoint_into(&self, end: &Self, out: &mut [Complex]) -> Result<(), VectorError> + where + N: num::Float, + Complex: Copy + + std::ops::Add> + + std::ops::Mul> + + std::ops::Sub> + + num::One; + /// Euclidean distance (L2 norm) between self and other (returns real). fn distance(&self, other: &Self) -> Result where diff --git a/src/types/vectorslice.rs b/src/types/vectorslice.rs index 67ab556..4774010 100644 --- a/src/types/vectorslice.rs +++ b/src/types/vectorslice.rs @@ -367,14 +367,11 @@ where } #[inline] - fn midpoint(&self, other: &Self) -> Result + fn midpoint(&self, end: &Self) -> Result where T: num::Float, { - self.check_same_length_and_raise(other)?; - let mut out = FlexVector::zero(self.len()); - lerp_impl(self.as_slice(), other.as_slice(), num::cast(0.5).unwrap(), out.as_mut_slice()); - Ok(out) + self.lerp(end, num::cast(0.5).unwrap()) } #[inline] @@ -382,14 +379,7 @@ where where T: num::Float, { - self.check_same_length_and_raise(end)?; - if self.len() != out.len() { - return Err(VectorError::MismatchedLengthError( - "Output buffer has different length than input vectors".to_string(), - )); - } - lerp_impl(self.as_slice(), end.as_slice(), num::cast(0.5).unwrap(), out); - Ok(()) + self.lerp_into(end, num::cast(0.5).unwrap(), out) } #[inline] @@ -594,14 +584,50 @@ where } #[inline] - fn midpoint(&self, end: &Self) -> Result + fn lerp_into(&self, end: &Self, weight: N, out: &mut [Complex]) -> Result<(), VectorError> where N: num::Float, + Complex: Copy + + std::ops::Add> + + std::ops::Mul> + + std::ops::Sub> + + num::One, { self.check_same_length_and_raise(end)?; + if out.len() != self.len() { + return Err(VectorError::MismatchedLengthError( + "Output buffer has different length than input vectors".to_string(), + )); + } + if weight < N::zero() || weight > N::one() { + return Err(VectorError::OutOfRangeError("weight must be in [0, 1]".to_string())); + } + let w = Complex::new(weight, N::zero()); + lerp_impl(self.as_slice(), end.as_slice(), w, out); + Ok(()) + } + + #[inline] + fn midpoint(&self, end: &Self) -> Result + where + N: num::Float, + { self.lerp(end, num::cast(0.5).unwrap()) } + #[inline] + fn midpoint_into(&self, end: &Self, out: &mut [Complex]) -> Result<(), VectorError> + where + N: num::Float, + Complex: Copy + + std::ops::Add> + + std::ops::Mul> + + std::ops::Sub> + + num::One, + { + self.lerp_into(end, num::cast(0.5).unwrap(), out) + } + #[inline] fn distance(&self, other: &Self) -> Result where @@ -1049,14 +1075,11 @@ where } #[inline] - fn midpoint(&self, other: &Self) -> Result + fn midpoint(&self, end: &Self) -> Result where T: num::Float, { - self.check_same_length_and_raise(other)?; - let mut out = FlexVector::zero(self.len()); - lerp_impl(self.as_slice(), other.as_slice(), T::from(0.5).unwrap(), out.as_mut_slice()); - Ok(out) + self.lerp(end, num::cast(0.5).unwrap()) } #[inline] @@ -1064,14 +1087,7 @@ where where T: num::Float, { - self.check_same_length_and_raise(end)?; - if self.len() != out.len() { - return Err(VectorError::MismatchedLengthError( - "Output buffer has different length than input vectors".to_string(), - )); - } - lerp_impl(self.as_slice(), end.as_slice(), T::from(0.5).unwrap(), out); - Ok(()) + self.lerp_into(end, num::cast(0.5).unwrap(), out) } #[inline] @@ -1308,14 +1324,50 @@ where } #[inline] - fn midpoint(&self, end: &Self) -> Result + fn lerp_into(&self, end: &Self, weight: N, out: &mut [Complex]) -> Result<(), VectorError> where N: num::Float, + Complex: Copy + + std::ops::Add> + + std::ops::Mul> + + std::ops::Sub> + + num::One, { self.check_same_length_and_raise(end)?; + if out.len() != self.len() { + return Err(VectorError::MismatchedLengthError( + "Output buffer has different length than input vectors".to_string(), + )); + } + if weight < N::zero() || weight > N::one() { + return Err(VectorError::OutOfRangeError("weight must be in [0, 1]".to_string())); + } + let w = Complex::new(weight, N::zero()); + lerp_impl(self.as_slice(), end.as_slice(), w, out); + Ok(()) + } + + #[inline] + fn midpoint(&self, end: &Self) -> Result + where + N: num::Float, + { self.lerp(end, num::cast(0.5).unwrap()) } + #[inline] + fn midpoint_into(&self, end: &Self, out: &mut [Complex]) -> Result<(), VectorError> + where + N: num::Float, + Complex: Copy + + std::ops::Add> + + std::ops::Mul> + + std::ops::Sub> + + num::One, + { + self.lerp_into(end, num::cast(0.5).unwrap(), out) + } + #[inline] fn distance(&self, other: &Self) -> Result where @@ -2800,6 +2852,99 @@ mod tests { assert!(vslice_a.lerp(&vslice_b, 1.1).is_err()); } + // -- lerp_into -- + + #[test] + fn test_vector_slice_complex_lerp_into_basic() { + let a = [num::Complex::new(1.0, 2.0), num::Complex::new(3.0, 4.0)]; + let b = [num::Complex::new(5.0, 6.0), num::Complex::new(7.0, 8.0)]; + let vslice_a: VectorSlice<'_, num::Complex, Column> = + VectorSlice::from_range(&a, 0..2); + let vslice_b = VectorSlice::from_range(&b, 0..2); + let mut out = [num::Complex::new(0.0, 0.0); 2]; + vslice_a.lerp_into(&vslice_b, 0.5, &mut out).unwrap(); + // Should be the midpoint + assert!((out[0] - num::Complex::new(3.0, 4.0)).norm() < 1e-12); + assert!((out[1] - num::Complex::new(5.0, 6.0)).norm() < 1e-12); + } + + #[test] + fn test_vector_slice_complex_lerp_into_weight_zero() { + let a = [num::Complex::new(1.0, 2.0)]; + let b = [num::Complex::new(5.0, 6.0)]; + let vslice_a: VectorSlice<'_, num::Complex, Column> = + VectorSlice::from_range(&a, 0..1); + let vslice_b = VectorSlice::from_range(&b, 0..1); + let mut out = [num::Complex::new(0.0, 0.0); 1]; + vslice_a.lerp_into(&vslice_b, 0.0, &mut out).unwrap(); + // Should be equal to a + assert!((out[0] - num::Complex::new(1.0, 2.0)).norm() < 1e-12); + } + + #[test] + fn test_vector_slice_complex_lerp_into_weight_one() { + let a = [num::Complex::new(1.0, 2.0)]; + let b = [num::Complex::new(5.0, 6.0)]; + let vslice_a: VectorSlice<'_, num::Complex, Column> = + VectorSlice::from_range(&a, 0..1); + let vslice_b = VectorSlice::from_range(&b, 0..1); + let mut out = [num::Complex::new(0.0, 0.0); 1]; + vslice_a.lerp_into(&vslice_b, 1.0, &mut out).unwrap(); + // Should be equal to b + assert!((out[0] - num::Complex::new(5.0, 6.0)).norm() < 1e-12); + } + + #[test] + fn test_vector_slice_complex_lerp_into_weight_out_of_bounds() { + let a = [num::Complex::new(1.0, 2.0)]; + let b = [num::Complex::new(5.0, 6.0)]; + let vslice_a: VectorSlice<'_, num::Complex, Column> = + VectorSlice::from_range(&a, 0..1); + let vslice_b = VectorSlice::from_range(&b, 0..1); + let mut out = [num::Complex::new(0.0, 0.0); 1]; + let result_low = vslice_a.lerp_into(&vslice_b, -0.1, &mut out); + let result_high = vslice_a.lerp_into(&vslice_b, 1.1, &mut out); + assert!(result_low.is_err()); + assert!(result_high.is_err()); + } + + #[test] + fn test_vector_slice_complex_lerp_into_mismatched_length_end() { + let a = [num::Complex::new(1.0, 2.0)]; + let b = [num::Complex::new(5.0, 6.0), num::Complex::new(7.0, 8.0)]; + let vslice_a: VectorSlice<'_, num::Complex, Column> = + VectorSlice::from_range(&a, 0..1); + let vslice_b = VectorSlice::from_range(&b, 0..2); + let mut out = [num::Complex::new(0.0, 0.0); 1]; + let result = vslice_a.lerp_into(&vslice_b, 0.5, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_complex_lerp_into_mismatched_length_out() { + let a = [num::Complex::new(1.0, 2.0), num::Complex::new(3.0, 4.0)]; + let b = [num::Complex::new(5.0, 6.0), num::Complex::new(7.0, 8.0)]; + let vslice_a: VectorSlice<'_, num::Complex, Column> = + VectorSlice::from_range(&a, 0..2); + let vslice_b = VectorSlice::from_range(&b, 0..2); + let mut out = [num::Complex::new(0.0, 0.0); 1]; + let result = vslice_a.lerp_into(&vslice_b, 0.5, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_complex_lerp_into_empty() { + let a: [num::Complex; 0] = []; + let b: [num::Complex; 0] = []; + let vslice_a: VectorSlice<'_, num::Complex, Column> = + VectorSlice::from_range(&a, 0..0); + let vslice_b: VectorSlice<'_, num::Complex, Column> = + VectorSlice::from_range(&b, 0..0); + let mut out: [num::Complex; 0] = []; + vslice_a.lerp_into(&vslice_b, 0.5, &mut out).unwrap(); + assert_eq!(out, []); + } + // -- midpoint -- #[test] @@ -2819,6 +2964,84 @@ mod tests { } } + // -- midpoint_into -- + + #[test] + fn test_vector_slice_complex_midpoint_into_basic() { + let a = [num::Complex::new(1.0, 2.0), num::Complex::new(3.0, 4.0)]; + let b = [num::Complex::new(5.0, 6.0), num::Complex::new(7.0, 8.0)]; + let vslice_a: VectorSlice<'_, num::Complex, Column> = + VectorSlice::from_range(&a, 0..2); + let vslice_b = VectorSlice::from_range(&b, 0..2); + let mut out = [num::Complex::new(0.0, 0.0); 2]; + vslice_a.midpoint_into(&vslice_b, &mut out).unwrap(); + // Should be the midpoint + assert!((out[0] - num::Complex::new(3.0, 4.0)).norm() < 1e-12); + assert!((out[1] - num::Complex::new(5.0, 6.0)).norm() < 1e-12); + } + + #[test] + fn test_vector_slice_complex_midpoint_into_negative_values() { + let a = [num::Complex::new(-1.0, -2.0), num::Complex::new(-3.0, -4.0)]; + let b = [num::Complex::new(1.0, 2.0), num::Complex::new(3.0, 4.0)]; + let vslice_a: VectorSlice<'_, num::Complex, Row> = VectorSlice::from_range(&a, 0..2); + let vslice_b = VectorSlice::from_range(&b, 0..2); + let mut out = [num::Complex::new(0.0, 0.0); 2]; + vslice_a.midpoint_into(&vslice_b, &mut out).unwrap(); + assert!((out[0] - num::Complex::new(0.0, 0.0)).norm() < 1e-12); + assert!((out[1] - num::Complex::new(0.0, 0.0)).norm() < 1e-12); + } + + #[test] + fn test_vector_slice_complex_midpoint_into_identical() { + let a = [num::Complex::new(2.0, 3.0), num::Complex::new(4.0, 5.0)]; + let b = [num::Complex::new(2.0, 3.0), num::Complex::new(4.0, 5.0)]; + let vslice_a: VectorSlice<'_, num::Complex, Column> = + VectorSlice::from_range(&a, 0..2); + let vslice_b = VectorSlice::from_range(&b, 0..2); + let mut out = [num::Complex::new(0.0, 0.0); 2]; + vslice_a.midpoint_into(&vslice_b, &mut out).unwrap(); + assert!((out[0] - num::Complex::new(2.0, 3.0)).norm() < 1e-12); + assert!((out[1] - num::Complex::new(4.0, 5.0)).norm() < 1e-12); + } + + #[test] + fn test_vector_slice_complex_midpoint_into_mismatched_length_end() { + let a = [num::Complex::new(1.0, 2.0)]; + let b = [num::Complex::new(3.0, 4.0), num::Complex::new(5.0, 6.0)]; + let vslice_a: VectorSlice<'_, num::Complex, Column> = + VectorSlice::from_range(&a, 0..1); + let vslice_b = VectorSlice::from_range(&b, 0..2); + let mut out = [num::Complex::new(0.0, 0.0); 1]; + let result = vslice_a.midpoint_into(&vslice_b, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_complex_midpoint_into_mismatched_length_out() { + let a = [num::Complex::new(1.0, 2.0), num::Complex::new(3.0, 4.0)]; + let b = [num::Complex::new(5.0, 6.0), num::Complex::new(7.0, 8.0)]; + let vslice_a: VectorSlice<'_, num::Complex, Column> = + VectorSlice::from_range(&a, 0..2); + let vslice_b = VectorSlice::from_range(&b, 0..2); + let mut out = [num::Complex::new(0.0, 0.0); 1]; + let result = vslice_a.midpoint_into(&vslice_b, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_complex_midpoint_into_empty() { + let a: [num::Complex; 0] = []; + let b: [num::Complex; 0] = []; + let vslice_a: VectorSlice<'_, num::Complex, Column> = + VectorSlice::from_range(&a, 0..0); + let vslice_b: VectorSlice<'_, num::Complex, Column> = + VectorSlice::from_range(&b, 0..0); + let mut out: [num::Complex; 0] = []; + vslice_a.midpoint_into(&vslice_b, &mut out).unwrap(); + assert_eq!(out, []); + } + // -- distance -- #[test] @@ -4516,6 +4739,99 @@ mod tests { assert!(vslice_a.lerp(&vslice_b, 1.1).is_err()); } + // -- lerp_into -- + + #[test] + fn test_vector_slice_mut_complex_lerp_into_basic() { + let mut a = [num::Complex::new(1.0, 2.0), num::Complex::new(3.0, 4.0)]; + let mut b = [num::Complex::new(5.0, 6.0), num::Complex::new(7.0, 8.0)]; + let vslice_a: VectorSliceMut<'_, num::Complex, Column> = + VectorSliceMut::from_range(&mut a, 0..2); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + let mut out = [num::Complex::new(0.0, 0.0); 2]; + vslice_a.lerp_into(&vslice_b, 0.5, &mut out).unwrap(); + // Should be the midpoint + assert!((out[0] - num::Complex::new(3.0, 4.0)).norm() < 1e-12); + assert!((out[1] - num::Complex::new(5.0, 6.0)).norm() < 1e-12); + } + + #[test] + fn test_vector_slice_mut_complex_lerp_into_weight_zero() { + let mut a = [num::Complex::new(1.0, 2.0)]; + let mut b = [num::Complex::new(5.0, 6.0)]; + let vslice_a: VectorSliceMut<'_, num::Complex, Column> = + VectorSliceMut::from_range(&mut a, 0..1); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..1); + let mut out = [num::Complex::new(0.0, 0.0); 1]; + vslice_a.lerp_into(&vslice_b, 0.0, &mut out).unwrap(); + // Should be equal to a + assert!((out[0] - num::Complex::new(1.0, 2.0)).norm() < 1e-12); + } + + #[test] + fn test_vector_slice_mut_complex_lerp_into_weight_one() { + let mut a = [num::Complex::new(1.0, 2.0)]; + let mut b = [num::Complex::new(5.0, 6.0)]; + let vslice_a: VectorSliceMut<'_, num::Complex, Column> = + VectorSliceMut::from_range(&mut a, 0..1); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..1); + let mut out = [num::Complex::new(0.0, 0.0); 1]; + vslice_a.lerp_into(&vslice_b, 1.0, &mut out).unwrap(); + // Should be equal to b + assert!((out[0] - num::Complex::new(5.0, 6.0)).norm() < 1e-12); + } + + #[test] + fn test_vector_slice_mut_complex_lerp_into_weight_out_of_bounds() { + let mut a = [num::Complex::new(1.0, 2.0)]; + let mut b = [num::Complex::new(5.0, 6.0)]; + let vslice_a: VectorSliceMut<'_, num::Complex, Column> = + VectorSliceMut::from_range(&mut a, 0..1); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..1); + let mut out = [num::Complex::new(0.0, 0.0); 1]; + let result_low = vslice_a.lerp_into(&vslice_b, -0.1, &mut out); + let result_high = vslice_a.lerp_into(&vslice_b, 1.1, &mut out); + assert!(result_low.is_err()); + assert!(result_high.is_err()); + } + + #[test] + fn test_vector_slice_mut_complex_lerp_into_mismatched_length_end() { + let mut a = [num::Complex::new(1.0, 2.0)]; + let mut b = [num::Complex::new(5.0, 6.0), num::Complex::new(7.0, 8.0)]; + let vslice_a: VectorSliceMut<'_, num::Complex, Column> = + VectorSliceMut::from_range(&mut a, 0..1); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + let mut out = [num::Complex::new(0.0, 0.0); 1]; + let result = vslice_a.lerp_into(&vslice_b, 0.5, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_mut_complex_lerp_into_mismatched_length_out() { + let mut a = [num::Complex::new(1.0, 2.0), num::Complex::new(3.0, 4.0)]; + let mut b = [num::Complex::new(5.0, 6.0), num::Complex::new(7.0, 8.0)]; + let vslice_a: VectorSliceMut<'_, num::Complex, Column> = + VectorSliceMut::from_range(&mut a, 0..2); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + let mut out = [num::Complex::new(0.0, 0.0); 1]; + let result = vslice_a.lerp_into(&vslice_b, 0.5, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_mut_complex_lerp_into_empty() { + let mut a: [num::Complex; 0] = []; + let mut b: [num::Complex; 0] = []; + let vslice_a: VectorSliceMut<'_, num::Complex, Column> = + VectorSliceMut::from_range(&mut a, 0..0); + let vslice_b: VectorSliceMut<'_, num::Complex, Column> = + VectorSliceMut::from_range(&mut b, 0..0); + let mut out: [num::Complex; 0] = []; + vslice_a.lerp_into(&vslice_b, 0.5, &mut out).unwrap(); + assert_eq!(out, []); + } + // -- midpoint -- #[test] @@ -4536,6 +4852,85 @@ mod tests { } } + // -- midpoint_into -- + + #[test] + fn test_vector_slice_mut_complex_midpoint_into_basic() { + let mut a = [num::Complex::new(1.0, 2.0), num::Complex::new(3.0, 4.0)]; + let mut b = [num::Complex::new(5.0, 6.0), num::Complex::new(7.0, 8.0)]; + let vslice_a: VectorSliceMut<'_, num::Complex, Column> = + VectorSliceMut::from_range(&mut a, 0..2); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + let mut out = [num::Complex::new(0.0, 0.0); 2]; + vslice_a.midpoint_into(&vslice_b, &mut out).unwrap(); + // Should be the midpoint + assert!((out[0] - num::Complex::new(3.0, 4.0)).norm() < 1e-12); + assert!((out[1] - num::Complex::new(5.0, 6.0)).norm() < 1e-12); + } + + #[test] + fn test_vector_slice_mut_complex_midpoint_into_negative_values() { + let mut a = [num::Complex::new(-1.0, -2.0), num::Complex::new(-3.0, -4.0)]; + let mut b = [num::Complex::new(1.0, 2.0), num::Complex::new(3.0, 4.0)]; + let vslice_a: VectorSliceMut<'_, num::Complex, Row> = + VectorSliceMut::from_range(&mut a, 0..2); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + let mut out = [num::Complex::new(0.0, 0.0); 2]; + vslice_a.midpoint_into(&vslice_b, &mut out).unwrap(); + assert!((out[0] - num::Complex::new(0.0, 0.0)).norm() < 1e-12); + assert!((out[1] - num::Complex::new(0.0, 0.0)).norm() < 1e-12); + } + + #[test] + fn test_vector_slice_mut_complex_midpoint_into_identical() { + let mut a = [num::Complex::new(2.0, 3.0), num::Complex::new(4.0, 5.0)]; + let mut b = [num::Complex::new(2.0, 3.0), num::Complex::new(4.0, 5.0)]; + let vslice_a: VectorSliceMut<'_, num::Complex, Column> = + VectorSliceMut::from_range(&mut a, 0..2); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + let mut out = [num::Complex::new(0.0, 0.0); 2]; + vslice_a.midpoint_into(&vslice_b, &mut out).unwrap(); + assert!((out[0] - num::Complex::new(2.0, 3.0)).norm() < 1e-12); + assert!((out[1] - num::Complex::new(4.0, 5.0)).norm() < 1e-12); + } + + #[test] + fn test_vector_slice_mut_complex_midpoint_into_mismatched_length_end() { + let mut a = [num::Complex::new(1.0, 2.0)]; + let mut b = [num::Complex::new(3.0, 4.0), num::Complex::new(5.0, 6.0)]; + let vslice_a: VectorSliceMut<'_, num::Complex, Column> = + VectorSliceMut::from_range(&mut a, 0..1); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + let mut out = [num::Complex::new(0.0, 0.0); 1]; + let result = vslice_a.midpoint_into(&vslice_b, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_mut_complex_midpoint_into_mismatched_length_out() { + let mut a = [num::Complex::new(1.0, 2.0), num::Complex::new(3.0, 4.0)]; + let mut b = [num::Complex::new(5.0, 6.0), num::Complex::new(7.0, 8.0)]; + let vslice_a: VectorSliceMut<'_, num::Complex, Column> = + VectorSliceMut::from_range(&mut a, 0..2); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + let mut out = [num::Complex::new(0.0, 0.0); 1]; + let result = vslice_a.midpoint_into(&vslice_b, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_mut_complex_midpoint_into_empty() { + let mut a: [num::Complex; 0] = []; + let mut b: [num::Complex; 0] = []; + let vslice_a: VectorSliceMut<'_, num::Complex, Column> = + VectorSliceMut::from_range(&mut a, 0..0); + let vslice_b: VectorSliceMut<'_, num::Complex, Column> = + VectorSliceMut::from_range(&mut b, 0..0); + let mut out: [num::Complex; 0] = []; + vslice_a.midpoint_into(&vslice_b, &mut out).unwrap(); + assert_eq!(out, []); + } + // -- distance -- #[test] From e1088d4a23fcb38cfd7efb09273a51395a496099 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Sat, 31 May 2025 12:53:13 -0400 Subject: [PATCH 71/82] add VectorOpsComplex project_onto_into; add FlexVector, VectorSlice, VectorSliceMut impl --- src/types/flexvector.rs | 152 +++++++++++++++++++++++ src/types/traits.rs | 6 + src/types/utils.rs | 2 +- src/types/vectorslice.rs | 258 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 417 insertions(+), 1 deletion(-) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index 4816af9..3a0d62d 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -1332,6 +1332,30 @@ where Ok(project_onto_impl(other.as_slice(), scalar)) } + #[inline] + fn project_onto_into(&self, other: &Self, out: &mut [Complex]) -> Result<(), VectorError> + where + N: num::Float, + Complex: Copy, + { + self.check_same_length_and_raise(other)?; + if out.len() != self.len() { + return Err(VectorError::MismatchedLengthError( + "Output buffer has different length than input vectors".to_string(), + )); + } + let numerator = hermitian_dot_impl(self.as_slice(), other.as_slice()); + let denominator = hermitian_dot_impl(other.as_slice(), other.as_slice()); + if denominator == Complex::new(N::zero(), N::zero()) { + return Err(VectorError::ZeroVectorError( + "Cannot project onto zero vector".to_string(), + )); + } + let scalar = numerator / denominator; + project_onto_into_impl(other.as_slice(), scalar, out); + Ok(()) + } + #[inline] fn cosine_similarity(&self, other: &Self) -> Result, VectorError> where @@ -8377,6 +8401,134 @@ mod tests { assert!(result.is_err()); } + // -- project_onto_into -- + + #[test] + fn test_flexvector_project_onto_into_complex_f64_basic() { + use num::Complex; + let v1 = FlexVector::>::from_vec(vec![ + Complex::new(3.0, 4.0), + Complex::new(0.0, 0.0), + ]); + let v2 = FlexVector::>::from_vec(vec![ + Complex::new(1.0, 0.0), + Complex::new(0.0, 0.0), + ]); + let mut out = [Complex::new(0.0, 0.0); 2]; + v1.project_onto_into(&v2, &mut out).unwrap(); + // Project v1 onto v2: should be [3.0 - 4.0i, 0.0] + assert!((out[0] - Complex::new(3.0, -4.0)).norm() < 1e-12); + assert!((out[1] - Complex::new(0.0, 0.0)).norm() < 1e-12); + } + + #[test] + fn test_flexvector_project_onto_into_complex_f64_parallel() { + use num::Complex; + let v1 = FlexVector::>::from_vec(vec![ + Complex::new(2.0, 2.0), + Complex::new(4.0, 4.0), + ]); + let v2 = FlexVector::>::from_vec(vec![ + Complex::new(1.0, 1.0), + Complex::new(2.0, 2.0), + ]); + let mut out = [Complex::new(0.0, 0.0); 2]; + v1.project_onto_into(&v2, &mut out).unwrap(); + // v1 is parallel to v2, so projection should be v1 itself + assert!((out[0] - Complex::new(2.0, 2.0)).norm() < 1e-12); + assert!((out[1] - Complex::new(4.0, 4.0)).norm() < 1e-12); + } + + #[test] + fn test_flexvector_project_onto_into_complex_f64_orthogonal() { + use num::Complex; + let v1 = FlexVector::>::from_vec(vec![ + Complex::new(0.0, 1.0), + Complex::new(0.0, 0.0), + ]); + let v2 = FlexVector::>::from_vec(vec![ + Complex::new(1.0, 0.0), + Complex::new(0.0, 0.0), + ]); + let mut out = [Complex::new(99.0, 99.0); 2]; + v1.project_onto_into(&v2, &mut out).unwrap(); + // Hermitian projection: should be [0.0 - 1.0i, 0.0] + assert!((out[0] - Complex::new(0.0, -1.0)).norm() < 1e-12); + assert!((out[1] - Complex::new(0.0, 0.0)).norm() < 1e-12); + } + + #[test] + fn test_flexvector_project_onto_into_complex_f64_identical() { + use num::Complex; + let v1 = FlexVector::>::from_vec(vec![ + Complex::new(5.0, 5.0), + Complex::new(5.0, 5.0), + ]); + let v2 = FlexVector::>::from_vec(vec![ + Complex::new(5.0, 5.0), + Complex::new(5.0, 5.0), + ]); + let mut out = [Complex::new(0.0, 0.0); 2]; + v1.project_onto_into(&v2, &mut out).unwrap(); + assert!((out[0] - Complex::new(5.0, 5.0)).norm() < 1e-12); + assert!((out[1] - Complex::new(5.0, 5.0)).norm() < 1e-12); + } + + #[test] + fn test_flexvector_project_onto_into_complex_f64_zero_vector() { + use num::Complex; + let v1 = FlexVector::>::from_vec(vec![ + Complex::new(1.0, 2.0), + Complex::new(3.0, 4.0), + ]); + let v2 = FlexVector::>::from_vec(vec![ + Complex::new(0.0, 0.0), + Complex::new(0.0, 0.0), + ]); + let mut out = [Complex::new(0.0, 0.0); 2]; + let result = v1.project_onto_into(&v2, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_flexvector_project_onto_into_complex_f64_mismatched_length_other() { + use num::Complex; + let v1 = FlexVector::>::from_vec(vec![Complex::new(1.0, 2.0)]); + let v2 = FlexVector::>::from_vec(vec![ + Complex::new(3.0, 4.0), + Complex::new(5.0, 6.0), + ]); + let mut out = [Complex::new(0.0, 0.0); 1]; + let result = v1.project_onto_into(&v2, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_flexvector_project_onto_into_complex_f64_mismatched_length_out() { + use num::Complex; + let v1 = FlexVector::>::from_vec(vec![ + Complex::new(1.0, 2.0), + Complex::new(3.0, 4.0), + ]); + let v2 = FlexVector::>::from_vec(vec![ + Complex::new(5.0, 6.0), + Complex::new(7.0, 8.0), + ]); + let mut out = [Complex::new(0.0, 0.0); 1]; + let result = v1.project_onto_into(&v2, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_flexvector_project_onto_into_complex_f64_empty() { + use num::Complex; + let v1: FlexVector> = FlexVector::new(); + let v2: FlexVector> = FlexVector::new(); + let mut out: [Complex; 0] = []; + let result = v1.project_onto_into(&v2, &mut out); + assert!(result.is_err()); // zero vector error + } + // -- cosine_similarity -- #[test] diff --git a/src/types/traits.rs b/src/types/traits.rs index 02b497c..35ac26a 100644 --- a/src/types/traits.rs +++ b/src/types/traits.rs @@ -754,6 +754,12 @@ pub trait VectorOpsComplex: VectorBase> { N: num::Float + std::iter::Sum, Self::Output: std::iter::FromIterator>; + /// ... + fn project_onto_into(&self, other: &Self, out: &mut [Complex]) -> Result<(), VectorError> + where + N: num::Float, + Complex: Copy; + /// ... fn cosine_similarity(&self, other: &Self) -> Result, VectorError> where diff --git a/src/types/utils.rs b/src/types/utils.rs index b456959..4eaaa7d 100644 --- a/src/types/utils.rs +++ b/src/types/utils.rs @@ -216,7 +216,7 @@ where #[inline] pub(crate) fn project_onto_into_impl(other: &[T], scalar: T, out: &mut [T]) where - T: num::Float + Copy, + T: Copy + std::ops::Mul, { for (o, &x) in out.iter_mut().zip(other.iter()) { *o = x * scalar; diff --git a/src/types/vectorslice.rs b/src/types/vectorslice.rs index 4774010..2bcd639 100644 --- a/src/types/vectorslice.rs +++ b/src/types/vectorslice.rs @@ -689,6 +689,30 @@ where Ok(project_onto_impl(other.as_slice(), scalar)) } + #[inline] + fn project_onto_into(&self, other: &Self, out: &mut [Complex]) -> Result<(), VectorError> + where + N: num::Float, + Complex: Copy, + { + self.check_same_length_and_raise(other)?; + if out.len() != self.len() { + return Err(VectorError::MismatchedLengthError( + "Output buffer has different length than input vectors".to_string(), + )); + } + let numerator = hermitian_dot_impl(self.as_slice(), other.as_slice()); + let denominator = hermitian_dot_impl(other.as_slice(), other.as_slice()); + if denominator == Complex::new(N::zero(), N::zero()) { + return Err(VectorError::ZeroVectorError( + "Cannot project onto zero vector".to_string(), + )); + } + let scalar = numerator / denominator; + project_onto_into_impl(other.as_slice(), scalar, out); + Ok(()) + } + #[inline] fn cosine_similarity(&self, other: &Self) -> Result, VectorError> where @@ -1424,6 +1448,30 @@ where Ok(project_onto_impl(other.as_slice(), scalar)) } + #[inline] + fn project_onto_into(&self, other: &Self, out: &mut [Complex]) -> Result<(), VectorError> + where + N: num::Float, + Complex: Copy, + { + self.check_same_length_and_raise(other)?; + if out.len() != self.len() { + return Err(VectorError::MismatchedLengthError( + "Output buffer has different length than input vectors".to_string(), + )); + } + let numerator = hermitian_dot_impl(self.as_slice(), other.as_slice()); + let denominator = hermitian_dot_impl(other.as_slice(), other.as_slice()); + if denominator == Complex::new(N::zero(), N::zero()) { + return Err(VectorError::ZeroVectorError( + "Cannot project onto zero vector".to_string(), + )); + } + let scalar = numerator / denominator; + project_onto_into_impl(other.as_slice(), scalar, out); + Ok(()) + } + #[inline] fn cosine_similarity(&self, other: &Self) -> Result, VectorError> where @@ -3164,6 +3212,110 @@ mod tests { assert!(result.is_err()); } + // -- project_onto_into -- + + #[test] + fn test_vector_slice_complex_project_onto_into_basic() { + let a = [num::Complex::new(3.0, 4.0), num::Complex::new(0.0, 0.0)]; + let b = [num::Complex::new(1.0, 0.0), num::Complex::new(0.0, 0.0)]; + let vslice_a: VectorSlice<'_, num::Complex, Column> = + VectorSlice::from_range(&a, 0..2); + let vslice_b = VectorSlice::from_range(&b, 0..2); + let mut out = [num::Complex::new(0.0, 0.0); 2]; + vslice_a.project_onto_into(&vslice_b, &mut out).unwrap(); + // Project a onto b: should be [3.0 - 4.0i, 0.0] + assert!((out[0] - num::Complex::new(3.0, -4.0)).norm() < 1e-12); + assert!((out[1] - num::Complex::new(0.0, 0.0)).norm() < 1e-12); + } + + #[test] + fn test_vector_slice_complex_project_onto_into_parallel() { + let a = [num::Complex::new(2.0, 2.0), num::Complex::new(4.0, 4.0)]; + let b = [num::Complex::new(1.0, 1.0), num::Complex::new(2.0, 2.0)]; + let vslice_a: VectorSlice<'_, num::Complex, Row> = VectorSlice::from_range(&a, 0..2); + let vslice_b = VectorSlice::from_range(&b, 0..2); + let mut out = [num::Complex::new(0.0, 0.0); 2]; + vslice_a.project_onto_into(&vslice_b, &mut out).unwrap(); + // a is parallel to b, so projection should be a + assert!((out[0] - num::Complex::new(2.0, 2.0)).norm() < 1e-12); + assert!((out[1] - num::Complex::new(4.0, 4.0)).norm() < 1e-12); + } + + #[test] + fn test_vector_slice_complex_project_onto_into_orthogonal() { + let a = [num::Complex::new(0.0, 1.0), num::Complex::new(0.0, 0.0)]; + let b = [num::Complex::new(1.0, 0.0), num::Complex::new(0.0, 0.0)]; + let vslice_a: VectorSlice<'_, num::Complex, Column> = + VectorSlice::from_range(&a, 0..2); + let vslice_b = VectorSlice::from_range(&b, 0..2); + let mut out = [num::Complex::new(99.0, 99.0); 2]; + vslice_a.project_onto_into(&vslice_b, &mut out).unwrap(); + // Hermitian projection: should be [0.0 - 1.0i, 0.0] + assert!((out[0] - num::Complex::new(0.0, -1.0)).norm() < 1e-12); + assert!((out[1] - num::Complex::new(0.0, 0.0)).norm() < 1e-12); + } + + #[test] + fn test_vector_slice_complex_project_onto_into_identical() { + let a = [num::Complex::new(5.0, 5.0), num::Complex::new(5.0, 5.0)]; + let b = [num::Complex::new(5.0, 5.0), num::Complex::new(5.0, 5.0)]; + let vslice_a: VectorSlice<'_, num::Complex, Row> = VectorSlice::from_range(&a, 0..2); + let vslice_b = VectorSlice::from_range(&b, 0..2); + let mut out = [num::Complex::new(0.0, 0.0); 2]; + vslice_a.project_onto_into(&vslice_b, &mut out).unwrap(); + assert!((out[0] - num::Complex::new(5.0, 5.0)).norm() < 1e-12); + assert!((out[1] - num::Complex::new(5.0, 5.0)).norm() < 1e-12); + } + + #[test] + fn test_vector_slice_complex_project_onto_into_zero_vector() { + let a = [num::Complex::new(1.0, 2.0), num::Complex::new(3.0, 4.0)]; + let b = [num::Complex::new(0.0, 0.0), num::Complex::new(0.0, 0.0)]; + let vslice_a: VectorSlice<'_, num::Complex, Column> = + VectorSlice::from_range(&a, 0..2); + let vslice_b = VectorSlice::from_range(&b, 0..2); + let mut out = [num::Complex::new(0.0, 0.0); 2]; + let result = vslice_a.project_onto_into(&vslice_b, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_complex_project_onto_into_mismatched_length_other() { + let a = [num::Complex::new(1.0, 2.0)]; + let b = [num::Complex::new(3.0, 4.0), num::Complex::new(5.0, 6.0)]; + let vslice_a: VectorSlice<'_, num::Complex, Column> = + VectorSlice::from_range(&a, 0..1); + let vslice_b = VectorSlice::from_range(&b, 0..2); + let mut out = [num::Complex::new(0.0, 0.0); 1]; + let result = vslice_a.project_onto_into(&vslice_b, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_complex_project_onto_into_mismatched_length_out() { + let a = [num::Complex::new(1.0, 2.0), num::Complex::new(3.0, 4.0)]; + let b = [num::Complex::new(5.0, 6.0), num::Complex::new(7.0, 8.0)]; + let vslice_a: VectorSlice<'_, num::Complex, Column> = + VectorSlice::from_range(&a, 0..2); + let vslice_b = VectorSlice::from_range(&b, 0..2); + let mut out = [num::Complex::new(0.0, 0.0); 1]; + let result = vslice_a.project_onto_into(&vslice_b, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_complex_project_onto_into_empty() { + let a: [num::Complex; 0] = []; + let b: [num::Complex; 0] = []; + let vslice_a: VectorSlice<'_, num::Complex, Column> = + VectorSlice::from_range(&a, 0..0); + let vslice_b: VectorSlice<'_, num::Complex, Column> = + VectorSlice::from_range(&b, 0..0); + let mut out: [num::Complex; 0] = []; + let result = vslice_a.project_onto_into(&vslice_b, &mut out); + assert!(result.is_err()); // zero vector error + } + // -- cosine_similarity -- #[test] @@ -5062,6 +5214,112 @@ mod tests { assert!(result.is_err()); } + // -- project_onto -- + + #[test] + fn test_vector_slice_mut_complex_project_onto_into_basic() { + let mut a = [num::Complex::new(3.0, 4.0), num::Complex::new(0.0, 0.0)]; + let mut b = [num::Complex::new(1.0, 0.0), num::Complex::new(0.0, 0.0)]; + let vslice_a: VectorSliceMut<'_, num::Complex, Column> = + VectorSliceMut::from_range(&mut a, 0..2); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + let mut out = [num::Complex::new(0.0, 0.0); 2]; + vslice_a.project_onto_into(&vslice_b, &mut out).unwrap(); + // Project a onto b: should be [3.0 - 4.0i, 0.0] + assert!((out[0] - num::Complex::new(3.0, -4.0)).norm() < 1e-12); + assert!((out[1] - num::Complex::new(0.0, 0.0)).norm() < 1e-12); + } + + #[test] + fn test_vector_slice_mut_complex_project_onto_into_parallel() { + let mut a = [num::Complex::new(2.0, 2.0), num::Complex::new(4.0, 4.0)]; + let mut b = [num::Complex::new(1.0, 1.0), num::Complex::new(2.0, 2.0)]; + let vslice_a: VectorSliceMut<'_, num::Complex, Row> = + VectorSliceMut::from_range(&mut a, 0..2); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + let mut out = [num::Complex::new(0.0, 0.0); 2]; + vslice_a.project_onto_into(&vslice_b, &mut out).unwrap(); + // a is parallel to b, so projection should be a + assert!((out[0] - num::Complex::new(2.0, 2.0)).norm() < 1e-12); + assert!((out[1] - num::Complex::new(4.0, 4.0)).norm() < 1e-12); + } + + #[test] + fn test_vector_slice_mut_complex_project_onto_into_orthogonal() { + let mut a = [num::Complex::new(0.0, 1.0), num::Complex::new(0.0, 0.0)]; + let mut b = [num::Complex::new(1.0, 0.0), num::Complex::new(0.0, 0.0)]; + let vslice_a: VectorSliceMut<'_, num::Complex, Column> = + VectorSliceMut::from_range(&mut a, 0..2); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + let mut out = [num::Complex::new(99.0, 99.0); 2]; + vslice_a.project_onto_into(&vslice_b, &mut out).unwrap(); + // Hermitian projection: should be [0.0 - 1.0i, 0.0] + assert!((out[0] - num::Complex::new(0.0, -1.0)).norm() < 1e-12); + assert!((out[1] - num::Complex::new(0.0, 0.0)).norm() < 1e-12); + } + + #[test] + fn test_vector_slice_mut_complex_project_onto_into_identical() { + let mut a = [num::Complex::new(5.0, 5.0), num::Complex::new(5.0, 5.0)]; + let mut b = [num::Complex::new(5.0, 5.0), num::Complex::new(5.0, 5.0)]; + let vslice_a: VectorSliceMut<'_, num::Complex, Row> = + VectorSliceMut::from_range(&mut a, 0..2); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + let mut out = [num::Complex::new(0.0, 0.0); 2]; + vslice_a.project_onto_into(&vslice_b, &mut out).unwrap(); + assert!((out[0] - num::Complex::new(5.0, 5.0)).norm() < 1e-12); + assert!((out[1] - num::Complex::new(5.0, 5.0)).norm() < 1e-12); + } + + #[test] + fn test_vector_slice_mut_complex_project_onto_into_zero_vector() { + let mut a = [num::Complex::new(1.0, 2.0), num::Complex::new(3.0, 4.0)]; + let mut b = [num::Complex::new(0.0, 0.0), num::Complex::new(0.0, 0.0)]; + let vslice_a: VectorSliceMut<'_, num::Complex, Column> = + VectorSliceMut::from_range(&mut a, 0..2); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + let mut out = [num::Complex::new(0.0, 0.0); 2]; + let result = vslice_a.project_onto_into(&vslice_b, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_mut_complex_project_onto_into_mismatched_length_other() { + let mut a = [num::Complex::new(1.0, 2.0)]; + let mut b = [num::Complex::new(3.0, 4.0), num::Complex::new(5.0, 6.0)]; + let vslice_a: VectorSliceMut<'_, num::Complex, Column> = + VectorSliceMut::from_range(&mut a, 0..1); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + let mut out = [num::Complex::new(0.0, 0.0); 1]; + let result = vslice_a.project_onto_into(&vslice_b, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_mut_complex_project_onto_into_mismatched_length_out() { + let mut a = [num::Complex::new(1.0, 2.0), num::Complex::new(3.0, 4.0)]; + let mut b = [num::Complex::new(5.0, 6.0), num::Complex::new(7.0, 8.0)]; + let vslice_a: VectorSliceMut<'_, num::Complex, Column> = + VectorSliceMut::from_range(&mut a, 0..2); + let vslice_b = VectorSliceMut::from_range(&mut b, 0..2); + let mut out = [num::Complex::new(0.0, 0.0); 1]; + let result = vslice_a.project_onto_into(&vslice_b, &mut out); + assert!(result.is_err()); + } + + #[test] + fn test_vector_slice_mut_complex_project_onto_into_empty() { + let mut a: [num::Complex; 0] = []; + let mut b: [num::Complex; 0] = []; + let vslice_a: VectorSliceMut<'_, num::Complex, Column> = + VectorSliceMut::from_range(&mut a, 0..0); + let vslice_b: VectorSliceMut<'_, num::Complex, Column> = + VectorSliceMut::from_range(&mut b, 0..0); + let mut out: [num::Complex; 0] = []; + let result = vslice_a.project_onto_into(&vslice_b, &mut out); + assert!(result.is_err()); // zero vector error + } + // -- cosine_similarity -- #[test] From da396966d7961acb727a285f0ea221d02e34bcc7 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Sat, 31 May 2025 13:29:01 -0400 Subject: [PATCH 72/82] minor refactor of utility function trait bounds --- src/types/utils.rs | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/src/types/utils.rs b/src/types/utils.rs index 4eaaa7d..1f4d527 100644 --- a/src/types/utils.rs +++ b/src/types/utils.rs @@ -5,14 +5,20 @@ use crate::errors::VectorError; use num::Complex; #[inline] -pub(crate) fn mut_translate_impl(out: &mut [T], b: &[T]) { +pub(crate) fn mut_translate_impl(out: &mut [T], b: &[T]) +where + T: Copy + std::ops::Add, +{ for (out_elem, &b_elem) in out.iter_mut().zip(b) { *out_elem = *out_elem + b_elem; } } #[inline] -pub(crate) fn translate_impl(a: &[T], b: &[T], out: &mut [T]) { +pub(crate) fn translate_impl(a: &[T], b: &[T], out: &mut [T]) +where + T: Copy + std::ops::Add, +{ for ((out_elem, &a_elem), &b_elem) in out.iter_mut().zip(a).zip(b) { *out_elem = a_elem + b_elem; } @@ -53,7 +59,10 @@ where /// Writes the cross product of two 3D vectors into the provided output buffer. /// Assumes all slices are of length 3. #[inline] -pub(crate) fn cross_into_impl(a: &[T], b: &[T], out: &mut [T]) { +pub(crate) fn cross_into_impl(a: &[T], b: &[T], out: &mut [T]) +where + T: num::Num + Copy, +{ out[0] = a[1] * b[2] - a[2] * b[1]; out[1] = a[2] * b[0] - a[0] * b[2]; out[2] = a[0] * b[1] - a[1] * b[0]; @@ -61,7 +70,10 @@ pub(crate) fn cross_into_impl(a: &[T], b: &[T], out: &mut [T /// Returns a Vec containing the elementwise minimum of two slices. /// Assumes all slices are the same length. -pub(crate) fn elementwise_min_impl(a: &[T], b: &[T]) -> Vec { +pub(crate) fn elementwise_min_impl(a: &[T], b: &[T]) -> Vec +where + T: PartialOrd + Copy, +{ a.iter() .zip(b.iter()) .map(|(a_elem, b_elem)| if a_elem < b_elem { *a_elem } else { *b_elem }) @@ -71,7 +83,10 @@ pub(crate) fn elementwise_min_impl(a: &[T], b: &[T]) -> Ve /// Writes the elementwise minimum of two slices into the provided output buffer. /// Assumes all slices are the same length. #[inline] -pub(crate) fn elementwise_min_into_impl(a: &[T], b: &[T], out: &mut [T]) { +pub(crate) fn elementwise_min_into_impl(a: &[T], b: &[T], out: &mut [T]) +where + T: PartialOrd + Copy, +{ for ((out_elem, a_elem), b_elem) in out.iter_mut().zip(a.iter()).zip(b.iter()) { *out_elem = if a_elem < b_elem { *a_elem } else { *b_elem }; } @@ -80,7 +95,10 @@ pub(crate) fn elementwise_min_into_impl(a: &[T], b: &[T], /// Returns a Vec containing the elementwise maximum of two slices. /// Assumes all slices are the same length. #[inline] -pub(crate) fn elementwise_max_impl(a: &[T], b: &[T]) -> Vec { +pub(crate) fn elementwise_max_impl(a: &[T], b: &[T]) -> Vec +where + T: PartialOrd + Copy, +{ a.iter() .zip(b.iter()) .map(|(a_elem, b_elem)| if a_elem > b_elem { *a_elem } else { *b_elem }) @@ -90,7 +108,10 @@ pub(crate) fn elementwise_max_impl(a: &[T], b: &[T]) -> Ve /// Writes the elementwise maximum of two slices into the provided output buffer. /// Assumes all slices are the same length. #[inline] -pub(crate) fn elementwise_max_into_impl(a: &[T], b: &[T], out: &mut [T]) { +pub(crate) fn elementwise_max_into_impl(a: &[T], b: &[T], out: &mut [T]) +where + T: PartialOrd + Copy, +{ for ((out_elem, a_elem), b_elem) in out.iter_mut().zip(a.iter()).zip(b.iter()) { *out_elem = if a_elem > b_elem { *a_elem } else { *b_elem }; } From 730ec5d0e86191d02c13bde8ea473d582b8310fb Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Sat, 31 May 2025 15:07:07 -0400 Subject: [PATCH 73/82] impl VectorSlice and VectorSliceMut is_row, is_column, to_flexvector methods --- src/types/vectorslice.rs | 150 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 149 insertions(+), 1 deletion(-) diff --git a/src/types/vectorslice.rs b/src/types/vectorslice.rs index 2bcd639..19d4fd7 100644 --- a/src/types/vectorslice.rs +++ b/src/types/vectorslice.rs @@ -2,7 +2,7 @@ use crate::errors::VectorError; use crate::types::flexvector::FlexVector; -use crate::types::orientation::Column; +use crate::types::orientation::{Column, Row}; use crate::types::traits::{ VectorBase, VectorBaseMut, VectorOps, VectorOpsComplex, VectorOpsFloat, VectorOpsFloatMut, VectorOpsMut, VectorOrientationName, @@ -19,6 +19,7 @@ use crate::types::utils::{ translate_impl, }; +use std::any::TypeId; use std::fmt; use std::marker::PhantomData; @@ -738,6 +739,33 @@ where // ================================ impl<'a, T, O> VectorSlice<'a, T, O> { + /// ... + #[inline] + pub fn is_row(&self) -> bool + where + O: 'static, + { + TypeId::of::() == TypeId::of::() + } + + /// ... + #[inline] + pub fn is_column(&self) -> bool + where + O: 'static, + { + TypeId::of::() == TypeId::of::() + } + + /// Returns a new FlexVector with the same elements and orientation. + #[inline] + pub fn to_flexvector(&self) -> FlexVector + where + T: Clone, + { + FlexVector::from_vec(self.elements.to_vec()) + } + // ================================ // // Private methods @@ -1497,6 +1525,32 @@ where // ================================ impl<'a, T, O> VectorSliceMut<'a, T, O> { + /// ... + #[inline] + pub fn is_row(&self) -> bool + where + O: 'static, + { + TypeId::of::() == TypeId::of::() + } + + /// ... + #[inline] + pub fn is_column(&self) -> bool + where + O: 'static, + { + TypeId::of::() == TypeId::of::() + } + + /// Returns a new FlexVector with the same elements and orientation. + #[inline] + pub fn to_flexvector(&self) -> FlexVector + where + T: Clone, + { + FlexVector::from_vec(self.elements.to_vec()) + } // ================================ // // Private methods @@ -3378,6 +3432,52 @@ mod tests { assert!(result.is_err()); } + // ================================ + // + // VectorSlice methods + // + // ================================ + + // -- is_row & is_column -- + + #[test] + fn test_vector_slice_is_row_and_is_column() { + let data = [1, 2, 3]; + let vslice_row: VectorSlice<'_, i32, Row> = VectorSlice::from_range(&data, 0..3); + let vslice_col: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&data, 0..3); + assert!(vslice_row.is_row()); + assert!(!vslice_row.is_column()); + assert!(vslice_col.is_column()); + assert!(!vslice_col.is_row()); + } + + // -- to_flexvector -- + + #[test] + fn test_vector_slice_to_flexvector_basic() { + let data = [10, 20, 30]; + let vslice: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&data, 0..3); + let flex: FlexVector = vslice.to_flexvector(); + assert_eq!(flex.as_slice(), &[10, 20, 30]); + } + + #[test] + fn test_vector_slice_to_flexvector_empty() { + let data: [i32; 0] = []; + let vslice: VectorSlice<'_, i32, Row> = VectorSlice::from_range(&data, 0..0); + let flex: FlexVector = vslice.to_flexvector(); + assert_eq!(flex.as_slice(), &[]); + } + + #[test] + fn test_vector_slice_to_flexvector_complex() { + let data = [num::Complex::new(1.0, 2.0), num::Complex::new(3.0, 4.0)]; + let vslice: VectorSlice<'_, num::Complex, Column> = + VectorSlice::from_range(&data, 0..2); + let flex: FlexVector> = vslice.to_flexvector(); + assert_eq!(flex.as_slice(), &data); + } + // ///////////////////////////////// // ================================ // @@ -5387,4 +5487,52 @@ mod tests { let result = vslice_a.cosine_similarity(&vslice_b); assert!(result.is_err()); } + + // ================================ + // + // VectorSliceMut methods + // + // ================================ + + // -- is_row and is_column -- + + #[test] + fn test_vector_slice_mut_is_row_and_is_column() { + let mut data1 = [1, 2, 3]; + let mut data2 = [1, 2, 3]; + let vslice_row: VectorSliceMut<'_, i32, Row> = VectorSliceMut::from_range(&mut data1, 0..3); + let vslice_col: VectorSliceMut<'_, i32, Column> = + VectorSliceMut::from_range(&mut data2, 0..3); + assert!(vslice_row.is_row()); + assert!(!vslice_row.is_column()); + assert!(vslice_col.is_column()); + assert!(!vslice_col.is_row()); + } + + // -- to_flexvector -- + + #[test] + fn test_vector_slice_mut_to_flexvector_basic() { + let mut data = [10, 20, 30]; + let vslice: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut data, 0..3); + let flex: FlexVector = vslice.to_flexvector(); + assert_eq!(flex.as_slice(), &[10, 20, 30]); + } + + #[test] + fn test_vector_slice_mut_to_flexvector_empty() { + let mut data: [i32; 0] = []; + let vslice: VectorSliceMut<'_, i32, Row> = VectorSliceMut::from_range(&mut data, 0..0); + let flex: FlexVector = vslice.to_flexvector(); + assert_eq!(flex.as_slice(), &[]); + } + + #[test] + fn test_vector_slice_mut_to_flexvector_complex() { + let mut data = [num::Complex::new(1.0, 2.0), num::Complex::new(3.0, 4.0)]; + let vslice: VectorSliceMut<'_, num::Complex, Column> = + VectorSliceMut::from_range(&mut data, 0..2); + let flex: FlexVector> = vslice.to_flexvector(); + assert_eq!(flex.as_slice(), &data); + } } From 1171564769c2dc0f59bf7bc2dfce3f2c2e90c6d8 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Sat, 31 May 2025 15:42:56 -0400 Subject: [PATCH 74/82] Add VectorHasOrientation trait impl for VectorSlice and VectorSliceMut --- src/types/vectorslice.rs | 66 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 3 deletions(-) diff --git a/src/types/vectorslice.rs b/src/types/vectorslice.rs index 19d4fd7..f4e1b2d 100644 --- a/src/types/vectorslice.rs +++ b/src/types/vectorslice.rs @@ -2,10 +2,10 @@ use crate::errors::VectorError; use crate::types::flexvector::FlexVector; -use crate::types::orientation::{Column, Row}; +use crate::types::orientation::{Column, Row, VectorOrientation}; use crate::types::traits::{ - VectorBase, VectorBaseMut, VectorOps, VectorOpsComplex, VectorOpsFloat, VectorOpsFloatMut, - VectorOpsMut, VectorOrientationName, + VectorBase, VectorBaseMut, VectorHasOrientation, VectorOps, VectorOpsComplex, VectorOpsFloat, + VectorOpsFloatMut, VectorOpsMut, VectorOrientationName, }; use crate::types::utils::{ angle_with_impl, chebyshev_distance_complex_impl, chebyshev_distance_impl, @@ -163,6 +163,20 @@ impl<'a, T, O> VectorBase for VectorSlice<'a, T, O> { } } +impl<'a, T, O> VectorHasOrientation for VectorSlice<'a, T, O> +where + O: 'static, +{ + #[inline] + fn orientation(&self) -> VectorOrientation { + if self.is_column() { + VectorOrientation::Column + } else { + VectorOrientation::Row + } + } +} + impl<'a, T, O> VectorOps for VectorSlice<'a, T, O> where T: Copy, @@ -905,6 +919,20 @@ impl<'a, T, O> VectorBaseMut for VectorSliceMut<'a, T, O> { } } +impl<'a, T, O> VectorHasOrientation for VectorSliceMut<'a, T, O> +where + O: 'static, +{ + #[inline] + fn orientation(&self) -> VectorOrientation { + if self.is_column() { + VectorOrientation::Column + } else { + VectorOrientation::Row + } + } +} + impl<'a, T, O> VectorOps for VectorSliceMut<'a, T, O> where T: Copy, @@ -1942,6 +1970,22 @@ mod tests { assert_eq!(vslice.to_vec(), vec![1, 2, 3]); } + // -- VectorHasOrientation trait for VectorSlice -- + + #[test] + fn test_vector_slice_orientation_row() { + let data = [1, 2, 3]; + let vslice: VectorSlice<'_, i32, Row> = VectorSlice::from_range(&data, 0..3); + assert_eq!(vslice.orientation(), VectorOrientation::Row); + } + + #[test] + fn test_vector_slice_orientation_column() { + let data = [1, 2, 3]; + let vslice: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&data, 0..3); + assert_eq!(vslice.orientation(), VectorOrientation::Column); + } + // -- VectorOps trait for VectorSlice -- // -- translate -- @@ -3863,6 +3907,22 @@ mod tests { assert_eq!(data, [num::Complex::new(1.0, 2.0), num::Complex::new(9.0, 9.0)]); } + // -- VectorHasOrientation trait for VectorSliceMut -- + + #[test] + fn test_vector_slice_mut_orientation_row() { + let mut data = [1, 2, 3]; + let vslice: VectorSliceMut<'_, i32, Row> = VectorSliceMut::from_range(&mut data, 0..3); + assert_eq!(vslice.orientation(), VectorOrientation::Row); + } + + #[test] + fn test_vector_slice_mut_orientation_column() { + let mut data = [1, 2, 3]; + let vslice: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut data, 0..3); + assert_eq!(vslice.orientation(), VectorOrientation::Column); + } + // -- VectorOps trait for VectorSliceMut -- // -- translate -- From 9baf79dc7264aee5b84033b3bed7dea00ff04cd0 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Tue, 3 Jun 2025 21:59:15 -0400 Subject: [PATCH 75/82] refactor VectorHasOrientation to include the orientation_name method --- src/types/flexvector.rs | 11 +++++------ src/types/orientation.rs | 23 ++++++++++------------- src/types/traits.rs | 15 ++++----------- src/types/vectorslice.rs | 18 +++++++++--------- 4 files changed, 28 insertions(+), 39 deletions(-) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index 3a0d62d..5ea9066 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -17,8 +17,7 @@ use crate::{ types::traits::Transposable, types::traits::VectorBase, types::traits::VectorBaseMut, types::traits::VectorHasOrientation, types::traits::VectorOps, types::traits::VectorOpsComplex, types::traits::VectorOpsComplexMut, types::traits::VectorOpsFloat, - types::traits::VectorOpsFloatMut, types::traits::VectorOpsMut, - types::traits::VectorOrientationName, types::vectorslice::VectorSlice, + types::traits::VectorOpsFloatMut, types::traits::VectorOpsMut, types::vectorslice::VectorSlice, types::vectorslice::VectorSliceMut, }; @@ -213,10 +212,10 @@ impl Default for FlexVector { impl std::fmt::Display for FlexVector where T: std::fmt::Debug, - O: VectorOrientationName + 'static, + O: 'static, { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{} FlexVector {:?}", O::orientation_name(), self.elements) + write!(f, "{} FlexVector {:?}", &self.orientation_name(), self.elements) } } @@ -228,11 +227,11 @@ where impl std::fmt::Debug for FlexVector where T: std::fmt::Debug, - O: VectorOrientationName + 'static, + O: 'static, { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("FlexVector") - .field("orientation", &O::orientation_name()) + .field("orientation", &self.orientation_name()) .field("elements", &self.elements) .finish() } diff --git a/src/types/orientation.rs b/src/types/orientation.rs index fac0b51..4e47c49 100644 --- a/src/types/orientation.rs +++ b/src/types/orientation.rs @@ -3,7 +3,7 @@ //! These zero-sized types are used as type parameters to distinguish between row and column vectors //! at compile time for both `Vector` and `FlexVector`. -use crate::types::traits::VectorOrientationName; +use std::fmt; /// Marker type for row vectors. #[derive(Clone, Debug, PartialEq, Eq, Hash)] @@ -13,18 +13,6 @@ pub struct Row; #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct Column; -impl VectorOrientationName for Row { - fn orientation_name() -> &'static str { - "Row" - } -} - -impl VectorOrientationName for Column { - fn orientation_name() -> &'static str { - "Column" - } -} - /// ... #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] pub enum VectorOrientation { @@ -33,3 +21,12 @@ pub enum VectorOrientation { /// ... Column, } + +impl fmt::Display for VectorOrientation { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + VectorOrientation::Row => write!(f, "Row"), + VectorOrientation::Column => write!(f, "Column"), + } + } +} diff --git a/src/types/traits.rs b/src/types/traits.rs index 35ac26a..039c995 100644 --- a/src/types/traits.rs +++ b/src/types/traits.rs @@ -796,16 +796,9 @@ pub trait VectorOpsComplexMut: VectorBaseMut> { pub trait VectorHasOrientation { /// ... fn orientation(&self) -> VectorOrientation; -} - -// ================================ -// -// pub(crate) trait impls -// -// ================================ -/// Helper trait for orientation name. -pub(crate) trait VectorOrientationName { - /// Returns orientation name. - fn orientation_name() -> &'static str; + /// Returns the orientation name as a string. + fn orientation_name(&self) -> String { + self.orientation().to_string() + } } diff --git a/src/types/vectorslice.rs b/src/types/vectorslice.rs index f4e1b2d..994babf 100644 --- a/src/types/vectorslice.rs +++ b/src/types/vectorslice.rs @@ -5,7 +5,7 @@ use crate::types::flexvector::FlexVector; use crate::types::orientation::{Column, Row, VectorOrientation}; use crate::types::traits::{ VectorBase, VectorBaseMut, VectorHasOrientation, VectorOps, VectorOpsComplex, VectorOpsFloat, - VectorOpsFloatMut, VectorOpsMut, VectorOrientationName, + VectorOpsFloatMut, VectorOpsMut, }; use crate::types::utils::{ angle_with_impl, chebyshev_distance_complex_impl, chebyshev_distance_impl, @@ -101,21 +101,21 @@ impl<'a, T, O> IntoIterator for &'a VectorSlice<'a, T, O> { impl<'a, T, O> fmt::Display for VectorSlice<'a, T, O> where T: fmt::Debug, - O: VectorOrientationName + 'static, + O: 'static, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{} VectorSlice {:?}", O::orientation_name(), self.elements) + write!(f, "{} VectorSlice {:?}", &self.orientation_name(), self.elements) } } impl<'a, T, O> std::fmt::Debug for VectorSlice<'a, T, O> where T: std::fmt::Debug, - O: VectorOrientationName + 'static, + O: 'static, { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("VectorSlice") - .field("orientation", &O::orientation_name()) + .field("orientation", &self.orientation_name()) .field("elements", &self.elements) .finish() } @@ -879,21 +879,21 @@ impl<'a, T, O> IntoIterator for &'a mut VectorSliceMut<'a, T, O> { impl<'a, T, O> fmt::Display for VectorSliceMut<'a, T, O> where T: fmt::Debug, - O: VectorOrientationName + 'static, + O: 'static, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{} VectorSliceMut {:?}", O::orientation_name(), self.elements) + write!(f, "{} VectorSliceMut {:?}", &self.orientation_name(), self.elements) } } impl<'a, T, O> std::fmt::Debug for VectorSliceMut<'a, T, O> where T: std::fmt::Debug, - O: VectorOrientationName + 'static, + O: 'static, { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("VectorSliceMut") - .field("orientation", &O::orientation_name()) + .field("orientation", &self.orientation_name()) .field("elements", &self.elements) .finish() } From a266c31a1af50165bcf9aa185cfe786bbc65f386 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Tue, 3 Jun 2025 22:37:40 -0400 Subject: [PATCH 76/82] add FlexVector scalar Add, AddAssign, Sub, SubAssign operator overload support --- src/types/flexvector.rs | 89 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 88 insertions(+), 1 deletion(-) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index 5ea9066..94475c4 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -1773,9 +1773,12 @@ impl_vector_binop_assign!(check_len, FlexVector, SubAssign, sub_assign, -); impl_vector_binop_assign!(check_len, FlexVector, MulAssign, mul_assign, *); impl_vector_binop_div_assign!(check_len, FlexVector); +impl_vector_scalar_op!(FlexVector, Add, add, +); +impl_vector_scalar_op_assign!(FlexVector, AddAssign, add_assign, +); +impl_vector_scalar_op!(FlexVector, Sub, sub, -); +impl_vector_scalar_op_assign!(FlexVector, SubAssign, sub_assign, -); impl_vector_scalar_op!(FlexVector, Mul, mul, *); impl_vector_scalar_op_assign!(FlexVector, MulAssign, mul_assign, *); - impl_vector_scalar_div_op!(FlexVector); impl_vector_scalar_div_op_assign!(FlexVector); @@ -9088,6 +9091,90 @@ mod tests { v1 /= v2; } + #[test] + fn test_scalar_add() { + let v = FVector::from_vec(vec![1, 2, 3]); + let sum = v.clone() + 10; + assert_eq!(sum.as_slice(), &[11, 12, 13]); + } + + #[test] + fn test_scalar_add_f64() { + let v = FVector::from_vec(vec![1.5, -2.0, 0.0]); + let sum = v.clone() + 2.0; + assert_eq!(sum.as_slice(), &[3.5, 0.0, 2.0]); + } + + #[test] + fn test_scalar_add_complex_f64() { + let v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + let sum = v.clone() + Complex::new(2.0, 1.0); + assert_eq!(sum.as_slice(), &[Complex::new(3.0, 3.0), Complex::new(-1.0, 5.0)]); + } + + #[test] + fn test_scalar_add_assign() { + let mut v = FVector::from_vec(vec![1, 2, 3]); + v += 10; + assert_eq!(v.as_slice(), &[11, 12, 13]); + } + + #[test] + fn test_scalar_add_assign_f64() { + let mut v = FVector::from_vec(vec![1.5, -2.0, 0.0]); + v += 2.0; + assert_eq!(v.as_slice(), &[3.5, 0.0, 2.0]); + } + + #[test] + fn test_scalar_add_assign_complex_f64() { + let mut v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + v += Complex::new(2.0, 1.0); + assert_eq!(v.as_slice(), &[Complex::new(3.0, 3.0), Complex::new(-1.0, 5.0)]); + } + + #[test] + fn test_scalar_sub() { + let v = FVector::from_vec(vec![10, 20, 30]); + let diff = v.clone() - 5; + assert_eq!(diff.as_slice(), &[5, 15, 25]); + } + + #[test] + fn test_scalar_sub_f64() { + let v = FVector::from_vec(vec![1.5, -2.0, 0.0]); + let diff = v.clone() - 2.0; + assert_eq!(diff.as_slice(), &[-0.5, -4.0, -2.0]); + } + + #[test] + fn test_scalar_sub_complex_f64() { + let v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + let diff = v.clone() - Complex::new(2.0, 1.0); + assert_eq!(diff.as_slice(), &[Complex::new(-1.0, 1.0), Complex::new(-5.0, 3.0)]); + } + + #[test] + fn test_scalar_sub_assign() { + let mut v = FVector::from_vec(vec![10, 20, 30]); + v -= 5; + assert_eq!(v.as_slice(), &[5, 15, 25]); + } + + #[test] + fn test_scalar_sub_assign_f64() { + let mut v = FVector::from_vec(vec![1.5, -2.0, 0.0]); + v -= 2.0; + assert_eq!(v.as_slice(), &[-0.5, -4.0, -2.0]); + } + + #[test] + fn test_scalar_sub_assign_complex_f64() { + let mut v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + v -= Complex::new(2.0, 1.0); + assert_eq!(v.as_slice(), &[Complex::new(-1.0, 1.0), Complex::new(-5.0, 3.0)]); + } + #[test] fn test_scalar_mul() { let v = FVector::from_vec(vec![2, -3, 4]); From 602940d00484d477aee95a12b3d01fcd170faaf7 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Tue, 3 Jun 2025 23:37:42 -0400 Subject: [PATCH 77/82] Add FlexVector broadcast_to method to support broadcasting --- src/types/flexvector.rs | 85 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index 94475c4..e3ce62a 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -1665,6 +1665,19 @@ impl FlexVector { Ok(FlexVector { elements, _orientation: PhantomData }) } + /// Broadcasts this vector to the given length by repeating its elements. + /// Returns an error if self is empty and len > 0. + pub fn broadcast_to(&self, len: usize) -> Result, VectorError> + where + T: Clone, + { + if self.is_empty() && len > 0 { + return Err(VectorError::ValueError("Cannot broadcast empty vector".to_string())); + } + let elements = self.elements.iter().cycle().take(len).cloned().collect(); + Ok(FlexVector { elements, _orientation: PhantomData }) + } + /// Consumes the FlexVector and returns a Vec. #[inline] pub fn into_vec(self) -> Vec { @@ -5659,6 +5672,78 @@ mod tests { assert!(result.is_err()); } + // -- broadcast_to -- + + #[test] + fn test_broadcast_to_i32() { + let v = FVector::from_vec(vec![1, 2]); + let b = v.broadcast_to(5).unwrap(); + assert_eq!(b.as_slice(), &[1, 2, 1, 2, 1]); + + let v = FVector::from_vec(vec![7]); + let b = v.broadcast_to(4).unwrap(); + assert_eq!(b.as_slice(), &[7, 7, 7, 7]); + + let v: FlexVector = FVector::from_vec(vec![]); + let b = v.broadcast_to(0).unwrap(); + assert!(b.is_empty()); + + let v: FlexVector = FVector::from_vec(vec![]); + let result = v.broadcast_to(3); + assert!(result.is_err()); + } + + #[test] + fn test_broadcast_to_f64() { + let v = FVector::from_vec(vec![1.5, -2.0]); + let b = v.broadcast_to(5).unwrap(); + assert_eq!(b.as_slice(), &[1.5, -2.0, 1.5, -2.0, 1.5]); + + let v = FVector::from_vec(vec![3.14]); + let b = v.broadcast_to(3).unwrap(); + assert_eq!(b.as_slice(), &[3.14, 3.14, 3.14]); + + let v: FlexVector = FVector::from_vec(vec![]); + let b = v.broadcast_to(0).unwrap(); + assert!(b.is_empty()); + + let v: FlexVector = FVector::from_vec(vec![]); + let result = v.broadcast_to(2); + assert!(result.is_err()); + } + + #[test] + fn test_broadcast_to_complex_f64() { + use num::Complex; + let v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + let b = v.broadcast_to(5).unwrap(); + assert_eq!( + b.as_slice(), + &[ + Complex::new(1.0, 2.0), + Complex::new(-3.0, 4.0), + Complex::new(1.0, 2.0), + Complex::new(-3.0, 4.0), + Complex::new(1.0, 2.0) + ] + ); + + let v = FVector::from_vec(vec![Complex::new(0.0, 1.0)]); + let b = v.broadcast_to(3).unwrap(); + assert_eq!( + b.as_slice(), + &[Complex::new(0.0, 1.0), Complex::new(0.0, 1.0), Complex::new(0.0, 1.0)] + ); + + let v: FlexVector, Column> = FVector::from_vec(vec![]); + let b = v.broadcast_to(0).unwrap(); + assert!(b.is_empty()); + + let v: FlexVector, Column> = FVector::from_vec(vec![]); + let result = v.broadcast_to(1); + assert!(result.is_err()); + } + // -- into_vec -- #[test] From 77f473d4ff9f9fde602a8bdcf07fe15f306feb75 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Wed, 4 Jun 2025 18:09:06 -0400 Subject: [PATCH 78/82] add From VectorSlice and VectorSliceMut impl on FlexVector --- src/types/flexvector.rs | 90 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 86 insertions(+), 4 deletions(-) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index e3ce62a..fd78816 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -506,17 +506,35 @@ where } } +impl<'a, T, O> From> for FlexVector +where + T: Clone, +{ + fn from(vs: VectorSlice<'a, T, O>) -> Self { + FlexVector::from_slice(vs.as_slice()) + } +} + +impl<'a, T, O> From> for FlexVector +where + T: Clone, +{ + fn from(vs: VectorSliceMut<'a, T, O>) -> Self { + FlexVector::from_slice(vs.as_slice()) + } +} + // From trait impl for Column <==> Row FlexVector conversions impl From> for FlexVector { #[inline] - fn from(v: FlexVector) -> Self { - FlexVector { elements: v.elements, _orientation: PhantomData } + fn from(fv: FlexVector) -> Self { + FlexVector { elements: fv.elements, _orientation: PhantomData } } } impl From> for FlexVector { #[inline] - fn from(v: FlexVector) -> Self { - FlexVector { elements: v.elements, _orientation: PhantomData } + fn from(fv: FlexVector) -> Self { + FlexVector { elements: fv.elements, _orientation: PhantomData } } } @@ -3556,6 +3574,70 @@ mod tests { assert!(fv_row.is_empty()); } + #[test] + fn test_from_vectorslice_i32() { + let v = FVector::from_vec(vec![1, 2, 3, 4]); + let vslice = v.as_vslice(1..3); + let fv: FlexVector = FlexVector::from(vslice); + assert_eq!(fv.as_slice(), &[2, 3]); + } + + #[test] + fn test_from_vectorslice_f64() { + let v = FVector::from_vec(vec![1.1, 2.2, 3.3, 4.4]); + let vslice = v.as_vslice(0..2); + let fv: FlexVector = FlexVector::from(vslice); + assert_eq!(fv.as_slice(), &[1.1, 2.2]); + } + + #[test] + fn test_from_vectorslice_complex_f64() { + use num::Complex; + let v = FVector::from_vec(vec![ + Complex::new(1.0, 2.0), + Complex::new(3.0, 4.0), + Complex::new(5.0, 6.0), + ]); + let vslice = v.as_vslice(1..3); + let fv: FlexVector> = FlexVector::from(vslice); + assert_eq!(fv.as_slice(), &[Complex::new(3.0, 4.0), Complex::new(5.0, 6.0)]); + } + + #[test] + fn test_from_vectorslicemut_i32() { + let mut v = FVector::from_vec(vec![10, 20, 30, 40]); + { + let vslice_mut = v.as_mut_vslice(2..4); + let fv: FlexVector = FlexVector::from(vslice_mut); + assert_eq!(fv.as_slice(), &[30, 40]); + } + } + + #[test] + fn test_from_vectorslicemut_f64() { + let mut v = FVector::from_vec(vec![1.5, 2.5, 3.5]); + { + let vslice_mut = v.as_mut_vslice(0..2); + let fv: FlexVector = FlexVector::from(vslice_mut); + assert_eq!(fv.as_slice(), &[1.5, 2.5]); + } + } + + #[test] + fn test_from_vectorslicemut_complex_f64() { + use num::Complex; + let mut v = FVector::from_vec(vec![ + Complex::new(7.0, 8.0), + Complex::new(9.0, 10.0), + Complex::new(11.0, 12.0), + ]); + { + let vslice_mut = v.as_mut_vslice(1..3); + let fv: FlexVector> = FlexVector::from(vslice_mut); + assert_eq!(fv.as_slice(), &[Complex::new(9.0, 10.0), Complex::new(11.0, 12.0)]); + } + } + #[test] fn test_from_column_to_row() { let col = FlexVector::::from_vec(vec![1, 2, 3]); From 2687c9809f9f20cb7ca235f003e444b31eb4f704 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Thu, 5 Jun 2025 09:37:06 -0400 Subject: [PATCH 79/82] Add From trait impl on VectorSlice, add PartialEq, Eq, Ord, PartialOrd, Hash, From trait impl on VectorSliceMut --- src/types/vectorslice.rs | 315 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 306 insertions(+), 9 deletions(-) diff --git a/src/types/vectorslice.rs b/src/types/vectorslice.rs index 994babf..60cf6b1 100644 --- a/src/types/vectorslice.rs +++ b/src/types/vectorslice.rs @@ -121,15 +121,6 @@ where } } -impl<'a, T, O> std::hash::Hash for VectorSlice<'a, T, O> -where - T: std::hash::Hash, -{ - fn hash(&self, state: &mut H) { - self.elements.hash(state); - } -} - impl<'a, T, O> Ord for VectorSlice<'a, T, O> where T: Ord, @@ -150,6 +141,21 @@ where } } +impl<'a, T, O> std::hash::Hash for VectorSlice<'a, T, O> +where + T: std::hash::Hash, +{ + fn hash(&self, state: &mut H) { + self.elements.hash(state); + } +} + +impl<'a, T, O> From<&'a FlexVector> for VectorSlice<'a, T, O> { + fn from(fv: &'a FlexVector) -> Self { + VectorSlice::new(fv.as_slice()) + } +} + // ================================ // // Crate trait impls @@ -807,6 +813,7 @@ impl<'a, T, O> VectorSlice<'a, T, O> { // ///////////////////////////////// /// ... +#[derive(PartialEq, Eq)] pub struct VectorSliceMut<'a, T, O = Column> { /// ... pub elements: &'a mut [T], @@ -852,6 +859,12 @@ impl<'a, T, O> std::ops::DerefMut for VectorSliceMut<'a, T, O> { } } +impl<'a, T, O> AsRef<[T]> for VectorSliceMut<'a, T, O> { + fn as_ref(&self) -> &[T] { + self.elements + } +} + impl<'a, T, O> AsMut<[T]> for VectorSliceMut<'a, T, O> { fn as_mut(&mut self) -> &mut [T] { self.elements @@ -899,6 +912,41 @@ where } } +impl<'a, T, O> Ord for VectorSliceMut<'a, T, O> +where + T: Ord, + O: Eq, +{ + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.elements.cmp(&other.elements) + } +} + +impl<'a, T, O> PartialOrd for VectorSliceMut<'a, T, O> +where + T: PartialOrd, + O: PartialEq, +{ + fn partial_cmp(&self, other: &Self) -> Option { + self.elements.partial_cmp(&other.elements) + } +} + +impl<'a, T, O> std::hash::Hash for VectorSliceMut<'a, T, O> +where + T: std::hash::Hash, +{ + fn hash(&self, state: &mut H) { + self.elements.hash(state); + } +} + +impl<'a, T, O> From<&'a mut FlexVector> for VectorSliceMut<'a, T, O> { + fn from(fv: &'a mut FlexVector) -> Self { + VectorSliceMut::new(fv.as_mut_slice()) + } +} + // ================================ // // Crate trait impls @@ -1874,6 +1922,33 @@ mod tests { assert_eq!(hash1, hash2); } + // -- Eq / PartialEq traits for VectorSlice -- + + #[test] + fn test_vectorslice_partial_eq_same_elements_and_orientation() { + let data = [1, 2, 3]; + let vslice1: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&data, 0..3); + let vslice2: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&data, 0..3); + assert_eq!(vslice1, vslice2); + } + + #[test] + fn test_vectorslice_partial_eq_different_elements() { + let data1 = [1, 2, 3]; + let data2 = [1, 2, 4]; + let vslice1: VectorSlice<'_, i32, Row> = VectorSlice::from_range(&data1, 0..3); + let vslice2: VectorSlice<'_, i32, Row> = VectorSlice::from_range(&data2, 0..3); + assert_ne!(vslice1, vslice2); + } + + #[test] + fn test_vectorslice_partial_eq_empty_slices_same_orientation() { + let data: [i32; 0] = []; + let vslice1: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&data, 0..0); + let vslice2: VectorSlice<'_, i32, Column> = VectorSlice::from_range(&data, 0..0); + assert_eq!(vslice1, vslice2); + } + // -- Ord / PartialOrd traits for VectorSlice -- #[test] @@ -1906,6 +1981,37 @@ mod tests { assert_eq!(vslice1.partial_cmp(&vslice1), Some(std::cmp::Ordering::Equal)); } + // -- From trait for VectorSlice -- + + #[test] + fn test_vectorslice_from_flexvector_i32() { + let fv = FlexVector::from_vec(vec![1, 2, 3, 4]); + let vslice: VectorSlice<'_, i32, Column> = VectorSlice::from(&fv); + assert_eq!(vslice.elements, &[1, 2, 3, 4]); + } + + #[test] + fn test_vectorslice_from_flexvector_f64() { + let fv = FlexVector::from_vec(vec![1.5, 2.5, 3.5]); + let vslice: VectorSlice<'_, f64, Row> = VectorSlice::from(&fv); + assert_eq!(vslice.elements, &[1.5, 2.5, 3.5]); + } + + #[test] + fn test_vectorslice_from_flexvector_complex_f64() { + use num::Complex; + let fv = FlexVector::from_vec(vec![ + Complex::new(1.0, 2.0), + Complex::new(3.0, 4.0), + Complex::new(5.0, 6.0), + ]); + let vslice: VectorSlice<'_, Complex, Column> = VectorSlice::from(&fv); + assert_eq!( + vslice.elements, + &[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0), Complex::new(5.0, 6.0)] + ); + } + // -- VectorBase trait for VectorSlice -- #[test] @@ -3687,6 +3793,35 @@ mod tests { assert_eq!(fv.as_slice(), &[10, 400, 30, 100, 50]); } + // -- AsRef trait for VectorSliceMut -- + + #[test] + fn test_vector_slice_mut_as_ref_basic() { + let mut data = [1, 2, 3, 4, 5]; + let vslice: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut data, 2..5); + let slice_ref: &[i32] = vslice.as_ref(); + assert_eq!(slice_ref, &[3, 4, 5]); + } + + #[test] + fn test_vector_slice_mut_as_ref_complex() { + let mut data = + [num::Complex::new(1.0, 2.0), num::Complex::new(3.0, 4.0), num::Complex::new(5.0, 6.0)]; + let vslice: VectorSliceMut<'_, num::Complex, Column> = + VectorSliceMut::from_range(&mut data, 1..3); + let slice_ref: &[num::Complex] = vslice.as_ref(); + assert_eq!(slice_ref, &[num::Complex::new(3.0, 4.0), num::Complex::new(5.0, 6.0)]); + } + + #[test] + fn test_vector_slice_mut_as_ref_with_std_function() { + let mut data = [10, 20, 30, 40]; + let vslice: VectorSliceMut<'_, i32, Row> = VectorSliceMut::from_range(&mut data, 1..4); + // Use a standard library function that takes AsRef<[i32]> + let sum: i32 = vslice.as_ref().iter().sum(); + assert_eq!(sum, 20 + 30 + 40); + } + // -- AsMut trait for VectorSliceMut -- #[test] fn test_vector_slice_mut_as_mut_basic() { @@ -3815,6 +3950,168 @@ mod tests { assert!(debug.contains("Complex { re: 7.0, im: 8.0 }")); } + // -- Eq/PartialEq trait for VectorSliceMut -- + + #[test] + fn test_vectorslicemut_partial_eq_same_elements_and_orientation() { + let mut data1 = [1, 2, 3]; + let mut data2 = [1, 2, 3]; + let vslice1: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut data1, 0..3); + let vslice2: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut data2, 0..3); + assert_eq!(vslice1, vslice2); + } + + #[test] + fn test_vectorslicemut_partial_eq_different_elements() { + let mut data1 = [1, 2, 3]; + let mut data2 = [1, 2, 4]; + let vslice1: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut data1, 0..3); + let vslice2: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut data2, 0..3); + assert_ne!(vslice1, vslice2); + } + + #[test] + fn test_vectorslicemut_partial_eq_empty_slices_same_orientation() { + let mut data1: [i32; 0] = []; + let mut data2: [i32; 0] = []; + let vslice1: VectorSliceMut<'_, i32, Row> = VectorSliceMut::from_range(&mut data1, 0..0); + let vslice2: VectorSliceMut<'_, i32, Row> = VectorSliceMut::from_range(&mut data2, 0..0); + assert_eq!(vslice1, vslice2); + } + + // -- Ord/PartialOrd trait for VectorSliceMut -- + + #[test] + fn test_vectorslicemut_ord_basic() { + let mut data1 = [1, 2, 3, 4, 5]; + let mut data2 = data1.clone(); + let vslice1: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut data1, 1..4); // [2, 3, 4] + let vslice2: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut data2, 2..5); // [3, 4, 5] + assert!(vslice1 < vslice2); + assert!(vslice2 > vslice1); + } + + #[test] + fn test_vectorslicemut_ord_equal() { + let mut data1 = [1, 2, 3, 4]; + let mut data2 = data1.clone(); + let vslice1: VectorSliceMut<'_, i32, Row> = VectorSliceMut::from_range(&mut data1, 0..3); + let vslice2: VectorSliceMut<'_, i32, Row> = VectorSliceMut::from_range(&mut data2, 0..3); + assert_eq!(vslice1, vslice2); + assert!(vslice1 <= vslice2); + assert!(vslice1 >= vslice2); + } + + #[test] + fn test_vectorslicemut_partial_ord_f64() { + let mut data1 = [1.0, 2.0, 3.0, 4.0]; + let mut data2 = data1.clone(); + let vslice1: VectorSliceMut<'_, f64, Column> = VectorSliceMut::from_range(&mut data1, 0..3); // [1.0, 2.0, 3.0] + let vslice2: VectorSliceMut<'_, f64, Column> = VectorSliceMut::from_range(&mut data2, 1..4); // [2.0, 3.0, 4.0] + assert!(vslice1 < vslice2); + assert!(vslice2 > vslice1); + assert_eq!(vslice1.partial_cmp(&vslice1), Some(std::cmp::Ordering::Equal)); + } + + // -- Hash trait for VectorSliceMut -- + + #[test] + fn test_vector_slice_mut_hash_basic() { + use std::collections::hash_map::DefaultHasher; + use std::hash::{Hash, Hasher}; + + let mut data1 = [1, 2, 3, 4]; + let mut data2 = [1, 2, 3, 4]; + let vslice1: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut data1, 1..4); + let vslice2: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut data2, 1..4); + + let mut hasher1 = DefaultHasher::new(); + vslice1.hash(&mut hasher1); + let hash1 = hasher1.finish(); + + let mut hasher2 = DefaultHasher::new(); + vslice2.hash(&mut hasher2); + let hash2 = hasher2.finish(); + + assert_eq!(hash1, hash2); + } + + #[test] + fn test_vector_slice_mut_hash_different() { + use std::collections::hash_map::DefaultHasher; + use std::hash::{Hash, Hasher}; + + let mut data1 = [1, 2, 3, 4]; + let mut data2 = [1, 2, 3, 4]; + let vslice1: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut data1, 0..3); + let vslice2: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from_range(&mut data2, 1..4); + + let mut hasher1 = DefaultHasher::new(); + vslice1.hash(&mut hasher1); + let hash1 = hasher1.finish(); + + let mut hasher2 = DefaultHasher::new(); + vslice2.hash(&mut hasher2); + let hash2 = hasher2.finish(); + + assert_ne!(hash1, hash2); + } + + #[test] + fn test_vector_slice_mut_hash_complex() { + use num::Complex; + use std::collections::hash_map::DefaultHasher; + use std::hash::{Hash, Hasher}; + + let mut data1 = [Complex::new(1, 2), Complex::new(3, 4), Complex::new(5, 6)]; + let mut data2 = data1.clone(); + let vslice1: VectorSliceMut<'_, Complex, Row> = + VectorSliceMut::from_range(&mut data1, 0..2); + let vslice2: VectorSliceMut<'_, Complex, Row> = + VectorSliceMut::from_range(&mut data2, 0..2); + + let mut hasher1 = DefaultHasher::new(); + vslice1.hash(&mut hasher1); + let hash1 = hasher1.finish(); + + let mut hasher2 = DefaultHasher::new(); + vslice2.hash(&mut hasher2); + let hash2 = hasher2.finish(); + + assert_eq!(hash1, hash2); + } + + // -- From trait for VectorSliceMut -- + + #[test] + fn test_vectorslicemut_from_flexvector_i32() { + let mut fv = FlexVector::from_vec(vec![1, 2, 3, 4]); + let vslice: VectorSliceMut<'_, i32, Column> = VectorSliceMut::from(&mut fv); + assert_eq!(vslice.elements, &mut [1, 2, 3, 4]); + } + + #[test] + fn test_vectorslicemut_from_flexvector_f64() { + let mut fv = FlexVector::from_vec(vec![1.5, 2.5, 3.5]); + let vslice: VectorSliceMut<'_, f64, Row> = VectorSliceMut::from(&mut fv); + assert_eq!(vslice.elements, &mut [1.5, 2.5, 3.5]); + } + + #[test] + fn test_vectorslicemut_from_flexvector_complex_f64() { + use num::Complex; + let mut fv = FlexVector::from_vec(vec![ + Complex::new(1.0, 2.0), + Complex::new(3.0, 4.0), + Complex::new(5.0, 6.0), + ]); + let vslice: VectorSliceMut<'_, Complex, Column> = VectorSliceMut::from(&mut fv); + assert_eq!( + vslice.elements, + &mut [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0), Complex::new(5.0, 6.0)] + ); + } + // -- VectorBase trait for VectorSliceMut -- #[test] From 47af72de5d7e8c98fdaed76800da5f458d59d8d5 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Sat, 7 Jun 2025 14:09:26 -0400 Subject: [PATCH 80/82] Add short type aliases for column and row VectorSlice and VectorSliceMut types --- src/types/vectorslice.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/types/vectorslice.rs b/src/types/vectorslice.rs index 60cf6b1..b1ffa3d 100644 --- a/src/types/vectorslice.rs +++ b/src/types/vectorslice.rs @@ -33,6 +33,11 @@ use num::{Complex, Zero}; // ================================ // ///////////////////////////////// +/// ... +pub type ColVS<'a, T> = VectorSlice<'a, T, Column>; +/// ... +pub type RowVS<'a, T> = VectorSlice<'a, T, Row>; + /// ... #[derive(Clone, PartialEq, Eq)] pub struct VectorSlice<'a, T, O = Column> { @@ -812,6 +817,11 @@ impl<'a, T, O> VectorSlice<'a, T, O> { // ================================ // ///////////////////////////////// +/// ... +pub type ColVSMut<'a, T> = VectorSliceMut<'a, T, Column>; +/// ... +pub type RowVSMut<'a, T> = VectorSliceMut<'a, T, Row>; + /// ... #[derive(PartialEq, Eq)] pub struct VectorSliceMut<'a, T, O = Column> { From aa983f2bb5186db0b8c7aab4a02b2ba1e9ceaca4 Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Sat, 7 Jun 2025 14:18:31 -0400 Subject: [PATCH 81/82] remove FVector public type alias --- src/types/flexvector.rs | 1176 +++++++++++++++++++-------------------- 1 file changed, 587 insertions(+), 589 deletions(-) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index fd78816..c168246 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -48,8 +48,6 @@ pub struct FlexVector { _orientation: PhantomData, } -/// ... -pub type FVector = FlexVector; /// ... pub type ColFVector = FlexVector; /// ... @@ -2056,19 +2054,19 @@ mod tests { #[test] fn test_from_fn_i32() { - let v = FVector::from_fn(5, |i| i as i32 * 2); + let v = ColFVector::from_fn(5, |i| i as i32 * 2); assert_eq!(v.as_slice(), &[0, 2, 4, 6, 8]); } #[test] fn test_from_fn_f64() { - let v = FVector::from_fn(4, |i| (i as f64).powi(2)); + let v = ColFVector::from_fn(4, |i| (i as f64).powi(2)); assert_eq!(v.as_slice(), &[0.0, 1.0, 4.0, 9.0]); } #[test] fn test_from_fn_complex() { - let v = FVector::from_fn(3, |i| Complex::new(i as f64, -(i as f64))); + let v = ColFVector::from_fn(3, |i| Complex::new(i as f64, -(i as f64))); assert_eq!( v.as_slice(), &[Complex::new(0.0, 0.0), Complex::new(1.0, -1.0), Complex::new(2.0, -2.0)] @@ -2077,13 +2075,13 @@ mod tests { #[test] fn test_from_fn_zero_length() { - let v: FlexVector = FVector::from_fn(0, |_| 42); + let v: FlexVector = ColFVector::from_fn(0, |_| 42); assert!(v.is_empty()); } #[test] fn test_from_fn_with_fn_pointer() { - let v = FVector::from_fn(4, square_usize); + let v = ColFVector::from_fn(4, square_usize); assert_eq!(v.as_slice(), &[0, 1, 4, 9]); } @@ -2118,14 +2116,14 @@ mod tests { #[test] fn test_repeat_pattern_i32_basic() { let pattern = [1, 2, 3]; - let v = FVector::repeat_pattern(&pattern, 8).unwrap(); + let v = ColFVector::repeat_pattern(&pattern, 8).unwrap(); assert_eq!(v.as_slice(), &[1, 2, 3, 1, 2, 3, 1, 2]); } #[test] fn test_repeat_pattern_i32_exact_multiple() { let pattern = [4, 5]; - let v = FVector::repeat_pattern(&pattern, 6).unwrap(); + let v = ColFVector::repeat_pattern(&pattern, 6).unwrap(); assert_eq!(v.as_slice(), &[4, 5, 4, 5, 4, 5]); } @@ -2139,35 +2137,35 @@ mod tests { #[test] fn test_repeat_pattern_i32_pattern_empty_len_zero() { let pattern: [i32; 0] = []; - let v = FVector::repeat_pattern(&pattern, 0).unwrap(); + let v = ColFVector::repeat_pattern(&pattern, 0).unwrap(); assert!(v.is_empty()); } #[test] fn test_repeat_pattern_i32_pattern_empty_len_nonzero() { let pattern: [i32; 0] = []; - let result = FVector::repeat_pattern(&pattern, 3); + let result = ColFVector::repeat_pattern(&pattern, 3); assert!(result.is_err()); } #[test] fn test_repeat_pattern_f64_basic() { let pattern = [1.5, 2.5]; - let v = FVector::repeat_pattern(&pattern, 5).unwrap(); + let v = ColFVector::repeat_pattern(&pattern, 5).unwrap(); assert_eq!(v.as_slice(), &[1.5, 2.5, 1.5, 2.5, 1.5]); } #[test] fn test_repeat_pattern_f64_empty_pattern_len_zero() { let pattern: [f64; 0] = []; - let v = FVector::repeat_pattern(&pattern, 0).unwrap(); + let v = ColFVector::repeat_pattern(&pattern, 0).unwrap(); assert!(v.is_empty()); } #[test] fn test_repeat_pattern_f64_empty_pattern_len_nonzero() { let pattern: [f64; 0] = []; - let result = FVector::repeat_pattern(&pattern, 2); + let result = ColFVector::repeat_pattern(&pattern, 2); assert!(result.is_err()); } @@ -2175,7 +2173,7 @@ mod tests { fn test_repeat_pattern_complex_f64_basic() { use num::Complex; let pattern = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; - let v = FVector::repeat_pattern(&pattern, 5).unwrap(); + let v = ColFVector::repeat_pattern(&pattern, 5).unwrap(); assert_eq!( v.as_slice(), &[ @@ -2192,7 +2190,7 @@ mod tests { fn test_repeat_pattern_complex_f64_empty_pattern_len_zero() { use num::Complex; let pattern: [Complex; 0] = []; - let v = FVector::repeat_pattern(&pattern, 0).unwrap(); + let v = ColFVector::repeat_pattern(&pattern, 0).unwrap(); assert!(v.is_empty()); } @@ -2200,7 +2198,7 @@ mod tests { fn test_repeat_pattern_complex_f64_empty_pattern_len_nonzero() { use num::Complex; let pattern: [Complex; 0] = []; - let result = FVector::repeat_pattern(&pattern, 1); + let result = ColFVector::repeat_pattern(&pattern, 1); assert!(result.is_err()); } @@ -2307,7 +2305,7 @@ mod tests { let a = 1; let b = 2; let c = 3; - let refs = FVector::from_vec(vec![&a, &b, &c]); + let refs = ColFVector::from_vec(vec![&a, &b, &c]); let owned = refs.cloned(); assert_eq!(owned.as_slice(), &[1, 2, 3]); } @@ -2324,30 +2322,30 @@ mod tests { use num::Complex; let a = Complex::new(1.0, 2.0); let b = Complex::new(3.0, 4.0); - let refs = FVector::from_vec(vec![&a, &b]); + let refs = ColFVector::from_vec(vec![&a, &b]); let owned = refs.cloned(); assert_eq!(owned.as_slice(), &[a, b]); } #[test] fn test_flatten_flexvector_of_flexvector() { - let row1 = FVector::from_vec(vec![1, 2]); - let row2 = FVector::from_vec(vec![3, 4, 5]); - let nested = FVector::from_vec(vec![row1, row2]); + let row1 = ColFVector::from_vec(vec![1, 2]); + let row2 = ColFVector::from_vec(vec![3, 4, 5]); + let nested = ColFVector::from_vec(vec![row1, row2]); let flat = nested.flatten(); assert_eq!(flat.as_slice(), &[1, 2, 3, 4, 5]); } #[test] fn test_flatten_flexvector_of_vec() { - let nested = FVector::from_vec(vec![vec![10, 20], vec![30]]); + let nested = ColFVector::from_vec(vec![vec![10, 20], vec![30]]); let flat = nested.flatten(); assert_eq!(flat.as_slice(), &[10, 20, 30]); } #[test] fn test_flatten_flexvector_of_array() { - let nested = FVector::from_vec(vec![[1, 2], [3, 4]]); + let nested = ColFVector::from_vec(vec![[1, 2], [3, 4]]); let flat = nested.flatten(); assert_eq!(flat.as_slice(), &[1, 2, 3, 4]); } @@ -2356,7 +2354,7 @@ mod tests { fn test_flatten_flexvector_of_slice_refs() { let a = [7, 8]; let b = [9]; - let nested = FVector::from_vec(vec![&a[..], &b[..]]); + let nested = ColFVector::from_vec(vec![&a[..], &b[..]]); let flat = nested.flatten(); assert_eq!(flat.as_slice(), &[&7, &8, &9]); } @@ -2370,7 +2368,7 @@ mod tests { #[test] fn test_flatten_with_empty_inner() { - let nested = FVector::from_vec(vec![vec![], vec![1, 2], vec![]]); + let nested = ColFVector::from_vec(vec![vec![], vec![1, 2], vec![]]); let flat = nested.flatten(); assert_eq!(flat.as_slice(), &[1, 2]); } @@ -2379,7 +2377,7 @@ mod tests { fn test_flatten_cloned_flexvector_of_slice_refs() { let a = [8, 9]; let b = [10]; - let nested = FVector::from_vec(vec![&a[..], &b[..]]); + let nested = ColFVector::from_vec(vec![&a[..], &b[..]]); let flat = nested.flatten_cloned(); let expected: Vec = vec![8, 9, 10]; assert_eq!(flat.as_slice(), expected.as_slice()); @@ -2390,7 +2388,7 @@ mod tests { fn test_flatten_cloned_flexvector_of_refs() { let x = 42; let y = 43; - let nested = FVector::from_vec(vec![vec![&x, &y], vec![&x]]); + let nested = ColFVector::from_vec(vec![vec![&x, &y], vec![&x]]); let flat = nested.flatten_cloned(); assert_eq!(flat.as_slice(), &[42, 43, 42]); let _: &[i32] = flat.as_slice(); // type check: &[i32] @@ -2405,7 +2403,7 @@ mod tests { #[test] fn test_flatten_cloned_with_empty_inner() { - let nested = FVector::from_vec(vec![vec![], vec![&1, &2], vec![]]); + let nested = ColFVector::from_vec(vec![vec![], vec![&1, &2], vec![]]); let flat = nested.flatten_cloned(); assert_eq!(flat.as_slice(), &[1, 2]); } @@ -2665,7 +2663,7 @@ mod tests { // ================================ #[test] fn test_deref_access_slice_methods_i32() { - let v = FVector::from_vec(vec![3, 1, 2]); + let v = ColFVector::from_vec(vec![3, 1, 2]); // Use sort (not implemented in FlexVector directly) let mut sorted = v.clone(); sorted.sort(); @@ -2676,7 +2674,7 @@ mod tests { #[test] fn test_deref_access_slice_methods_f64() { - let v = FVector::from_vec(vec![3.5, 1.5, 2.5]); + let v = ColFVector::from_vec(vec![3.5, 1.5, 2.5]); // Use sort_by let mut sorted = v.clone(); sorted.sort_by(|a, b| a.partial_cmp(b).unwrap()); @@ -2689,7 +2687,7 @@ mod tests { #[test] fn test_deref_access_slice_methods_complex() { use num::Complex; - let v = FVector::from_vec(vec![ + let v = ColFVector::from_vec(vec![ Complex::new(1.0, 2.0), Complex::new(3.0, 4.0), Complex::new(5.0, 6.0), @@ -2709,7 +2707,7 @@ mod tests { #[test] fn test_deref_mut_i32() { - let mut v = FVector::from_vec(vec![10, 20, 30]); + let mut v = ColFVector::from_vec(vec![10, 20, 30]); // Mutate via indexing v[1] = 99; assert_eq!(v.as_slice(), &[10, 99, 30]); @@ -2720,7 +2718,7 @@ mod tests { #[test] fn test_deref_mut_f64() { - let mut v = FVector::from_vec(vec![1.5, 2.5, 3.5]); + let mut v = ColFVector::from_vec(vec![1.5, 2.5, 3.5]); // Mutate via indexing v[0] = -1.5; assert_eq!(v.as_slice(), &[-1.5, 2.5, 3.5]); @@ -2732,7 +2730,7 @@ mod tests { #[test] fn test_deref_mut_complex_f64() { use num::Complex; - let mut v = FVector::from_vec(vec![ + let mut v = ColFVector::from_vec(vec![ Complex::new(1.0, 2.0), Complex::new(3.0, 4.0), Complex::new(5.0, 6.0), @@ -2755,14 +2753,14 @@ mod tests { // ================================ #[test] fn test_asref_i32() { - let v = FVector::from_vec(vec![1, 2, 3]); + let v = ColFVector::from_vec(vec![1, 2, 3]); let slice: &[i32] = v.as_ref(); assert_eq!(slice, &[1, 2, 3]); } #[test] fn test_asref_f64() { - let v = FVector::from_vec(vec![1.1, 2.2, 3.3]); + let v = ColFVector::from_vec(vec![1.1, 2.2, 3.3]); let slice: &[f64] = v.as_ref(); assert_eq!(slice, &[1.1, 2.2, 3.3]); } @@ -2770,14 +2768,14 @@ mod tests { #[test] fn test_asref_complex_f64() { use num::Complex; - let v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let slice: &[Complex] = v.as_ref(); assert_eq!(slice, &[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); } #[test] fn test_asmut_i32() { - let mut v = FVector::from_vec(vec![1, 2, 3]); + let mut v = ColFVector::from_vec(vec![1, 2, 3]); let slice: &mut [i32] = v.as_mut(); slice[0] = 10; slice[2] = 30; @@ -2786,7 +2784,7 @@ mod tests { #[test] fn test_asmut_f64() { - let mut v = FVector::from_vec(vec![1.1, 2.2, 3.3]); + let mut v = ColFVector::from_vec(vec![1.1, 2.2, 3.3]); let slice: &mut [f64] = v.as_mut(); slice[1] = 9.9; assert_eq!(v.as_slice(), &[1.1, 9.9, 3.3]); @@ -2795,7 +2793,7 @@ mod tests { #[test] fn test_asmut_complex_f64() { use num::Complex; - let mut v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let mut v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let slice: &mut [Complex] = v.as_mut(); slice[0].re = 10.0; slice[1].im = 40.0; @@ -2811,7 +2809,7 @@ mod tests { #[test] fn test_borrow_i32() { use std::borrow::Borrow; - let v = FVector::from_vec(vec![1, 2, 3]); + let v = ColFVector::from_vec(vec![1, 2, 3]); let slice: &[i32] = v.borrow(); assert_eq!(slice, &[1, 2, 3]); } @@ -2819,7 +2817,7 @@ mod tests { #[test] fn test_borrow_f64() { use std::borrow::Borrow; - let v = FVector::from_vec(vec![1.1, 2.2, 3.3]); + let v = ColFVector::from_vec(vec![1.1, 2.2, 3.3]); let slice: &[f64] = v.borrow(); assert_eq!(slice, &[1.1, 2.2, 3.3]); } @@ -2828,7 +2826,7 @@ mod tests { fn test_borrow_complex_f64() { use num::Complex; use std::borrow::Borrow; - let v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let slice: &[Complex] = v.borrow(); assert_eq!(slice, &[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); } @@ -2836,7 +2834,7 @@ mod tests { #[test] fn test_borrow_mut_i32() { use std::borrow::BorrowMut; - let mut v = FVector::from_vec(vec![1, 2, 3]); + let mut v = ColFVector::from_vec(vec![1, 2, 3]); { let slice: &mut [i32] = v.borrow_mut(); slice[0] = 10; @@ -2848,7 +2846,7 @@ mod tests { #[test] fn test_borrow_mut_f64() { use std::borrow::BorrowMut; - let mut v = FVector::from_vec(vec![1.1, 2.2, 3.3]); + let mut v = ColFVector::from_vec(vec![1.1, 2.2, 3.3]); { let slice: &mut [f64] = v.borrow_mut(); slice[1] = 9.9; @@ -2860,7 +2858,7 @@ mod tests { fn test_borrow_mut_complex_f64() { use num::Complex; use std::borrow::BorrowMut; - let mut v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let mut v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); { let slice: &mut [Complex] = v.borrow_mut(); slice[0].re = 10.0; @@ -2876,14 +2874,14 @@ mod tests { // ================================ #[test] fn test_into_iter_i32() { - let v = FVector::from_vec(vec![1, 2, 3]); + let v = ColFVector::from_vec(vec![1, 2, 3]); let collected: Vec<_> = v.into_iter().collect(); assert_eq!(collected, vec![1, 2, 3]); } #[test] fn test_into_iter_f64() { - let v = FVector::from_vec(vec![1.1, 2.2, 3.3]); + let v = ColFVector::from_vec(vec![1.1, 2.2, 3.3]); let collected: Vec<_> = v.into_iter().collect(); assert_eq!(collected, vec![1.1, 2.2, 3.3]); } @@ -2891,21 +2889,21 @@ mod tests { #[test] fn test_into_iter_complex_f64() { use num::Complex; - let v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let collected: Vec<_> = v.into_iter().collect(); assert_eq!(collected, vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); } #[test] fn test_iter_ref_i32() { - let v = FVector::from_vec(vec![10, 20, 30]); + let v = ColFVector::from_vec(vec![10, 20, 30]); let collected: Vec<_> = (&v).into_iter().copied().collect(); assert_eq!(collected, vec![10, 20, 30]); } #[test] fn test_iter_ref_f64() { - let v = FVector::from_vec(vec![1.5, 2.5, 3.5]); + let v = ColFVector::from_vec(vec![1.5, 2.5, 3.5]); let collected: Vec<_> = (&v).into_iter().copied().collect(); assert_eq!(collected, vec![1.5, 2.5, 3.5]); } @@ -2913,14 +2911,14 @@ mod tests { #[test] fn test_iter_ref_complex_f64() { use num::Complex; - let v = FVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); + let v = ColFVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); let collected: Vec<_> = (&v).into_iter().cloned().collect(); assert_eq!(collected, vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); } #[test] fn test_iter_mutable_i32() { - let mut v = FVector::from_vec(vec![1, 2, 3]); + let mut v = ColFVector::from_vec(vec![1, 2, 3]); for x in &mut v { *x *= 10; } @@ -2929,7 +2927,7 @@ mod tests { #[test] fn test_iter_mutable_f64() { - let mut v = FVector::from_vec(vec![1.0, 2.0, 3.0]); + let mut v = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); for x in &mut v { *x += 0.5; } @@ -2939,7 +2937,7 @@ mod tests { #[test] fn test_iter_mutable_complex_f64() { use num::Complex; - let mut v = FVector::from_vec(vec![Complex::new(1.0, 1.0), Complex::new(2.0, 2.0)]); + let mut v = ColFVector::from_vec(vec![Complex::new(1.0, 1.0), Complex::new(2.0, 2.0)]); for x in &mut v { x.re *= 2.0; x.im *= 3.0; @@ -2954,18 +2952,18 @@ mod tests { // ================================ #[test] fn test_partial_eq_i32() { - let v1 = FVector::from_vec(vec![1, 2, 3]); - let v2 = FVector::from_vec(vec![1, 2, 3]); - let v3 = FVector::from_vec(vec![3, 2, 1]); + let v1 = ColFVector::from_vec(vec![1, 2, 3]); + let v2 = ColFVector::from_vec(vec![1, 2, 3]); + let v3 = ColFVector::from_vec(vec![3, 2, 1]); assert_eq!(v1, v2); assert_ne!(v1, v3); } #[test] fn test_partial_eq_f64() { - let v1 = FVector::from_vec(vec![1.1, 2.2, 3.3]); - let v2 = FVector::from_vec(vec![1.1, 2.2, 3.3]); - let v3 = FVector::from_vec(vec![3.3, 2.2, 1.1]); + let v1 = ColFVector::from_vec(vec![1.1, 2.2, 3.3]); + let v2 = ColFVector::from_vec(vec![1.1, 2.2, 3.3]); + let v3 = ColFVector::from_vec(vec![3.3, 2.2, 1.1]); assert_eq!(v1, v2); assert_ne!(v1, v3); } @@ -2973,9 +2971,9 @@ mod tests { #[test] fn test_partial_eq_complex_f64() { use num::Complex; - let v1 = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); - let v2 = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); - let v3 = FVector::from_vec(vec![Complex::new(4.0, 3.0), Complex::new(2.0, 1.0)]); + let v1 = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v2 = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v3 = ColFVector::from_vec(vec![Complex::new(4.0, 3.0), Complex::new(2.0, 1.0)]); assert_eq!(v1, v2); assert_ne!(v1, v3); } @@ -2989,28 +2987,28 @@ mod tests { #[test] fn test_partial_eq_different_lengths() { - let v1 = FVector::from_vec(vec![1, 2]); - let v2 = FVector::from_vec(vec![1, 2, 3]); + let v1 = ColFVector::from_vec(vec![1, 2]); + let v2 = ColFVector::from_vec(vec![1, 2, 3]); assert_ne!(v1, v2); } #[test] fn test_partial_eq_f64_nan() { - let v1 = FVector::from_vec(vec![f64::NAN, 1.0]); - let v2 = FVector::from_vec(vec![f64::NAN, 1.0]); + let v1 = ColFVector::from_vec(vec![f64::NAN, 1.0]); + let v2 = ColFVector::from_vec(vec![f64::NAN, 1.0]); // NaN != NaN, so these should not be equal assert_ne!(v1, v2); - let v3 = FVector::from_vec(vec![f64::NAN, 1.0]); - let v4 = FVector::from_vec(vec![f64::NAN, 2.0]); + let v3 = ColFVector::from_vec(vec![f64::NAN, 1.0]); + let v4 = ColFVector::from_vec(vec![f64::NAN, 2.0]); assert_ne!(v3, v4); } #[test] fn test_partial_eq_f64_zero_negzero() { - let v1 = FVector::from_vec(vec![0.0, -0.0]); - let v2 = FVector::from_vec(vec![0.0, -0.0]); - let v3 = FVector::from_vec(vec![-0.0, 0.0]); + let v1 = ColFVector::from_vec(vec![0.0, -0.0]); + let v2 = ColFVector::from_vec(vec![0.0, -0.0]); + let v3 = ColFVector::from_vec(vec![-0.0, 0.0]); // 0.0 == -0.0 in Rust assert_eq!(v1, v2); assert_eq!(v1, v3); @@ -3018,9 +3016,9 @@ mod tests { #[test] fn test_partial_eq_f64_infinity() { - let v1 = FVector::from_vec(vec![f64::INFINITY, f64::NEG_INFINITY]); - let v2 = FVector::from_vec(vec![f64::INFINITY, f64::NEG_INFINITY]); - let v3 = FVector::from_vec(vec![f64::NEG_INFINITY, f64::INFINITY]); + let v1 = ColFVector::from_vec(vec![f64::INFINITY, f64::NEG_INFINITY]); + let v2 = ColFVector::from_vec(vec![f64::INFINITY, f64::NEG_INFINITY]); + let v3 = ColFVector::from_vec(vec![f64::NEG_INFINITY, f64::INFINITY]); assert_eq!(v1, v2); assert_ne!(v1, v3); } @@ -3029,21 +3027,21 @@ mod tests { fn test_partial_eq_complex_nan() { use num::Complex; let nan = f64::NAN; - let v1 = FVector::from_vec(vec![Complex::new(nan, 1.0)]); - let v2 = FVector::from_vec(vec![Complex::new(nan, 1.0)]); + let v1 = ColFVector::from_vec(vec![Complex::new(nan, 1.0)]); + let v2 = ColFVector::from_vec(vec![Complex::new(nan, 1.0)]); // Complex::new(NaN, 1.0) != Complex::new(NaN, 1.0) assert_ne!(v1, v2); - let v3 = FVector::from_vec(vec![Complex::new(1.0, nan)]); - let v4 = FVector::from_vec(vec![Complex::new(1.0, nan)]); + let v3 = ColFVector::from_vec(vec![Complex::new(1.0, nan)]); + let v4 = ColFVector::from_vec(vec![Complex::new(1.0, nan)]); assert_ne!(v3, v4); } #[test] fn test_partial_eq_complex_zero_negzero() { use num::Complex; - let v1 = FVector::from_vec(vec![Complex::new(0.0, -0.0)]); - let v2 = FVector::from_vec(vec![Complex::new(-0.0, 0.0)]); + let v1 = ColFVector::from_vec(vec![Complex::new(0.0, -0.0)]); + let v2 = ColFVector::from_vec(vec![Complex::new(-0.0, 0.0)]); // 0.0 == -0.0 for both real and imaginary parts assert_eq!(v1, v2); } @@ -3051,10 +3049,10 @@ mod tests { #[test] fn test_partial_eq_complex_infinity() { use num::Complex; - let v1 = FVector::from_vec(vec![Complex::new(f64::INFINITY, 1.0)]); - let v2 = FVector::from_vec(vec![Complex::new(f64::INFINITY, 1.0)]); - let v3 = FVector::from_vec(vec![Complex::new(1.0, f64::INFINITY)]); - let v4 = FVector::from_vec(vec![Complex::new(1.0, f64::INFINITY)]); + let v1 = ColFVector::from_vec(vec![Complex::new(f64::INFINITY, 1.0)]); + let v2 = ColFVector::from_vec(vec![Complex::new(f64::INFINITY, 1.0)]); + let v3 = ColFVector::from_vec(vec![Complex::new(1.0, f64::INFINITY)]); + let v4 = ColFVector::from_vec(vec![Complex::new(1.0, f64::INFINITY)]); assert_eq!(v1, v2); assert_eq!(v3, v4); assert_ne!(v1, v3); @@ -3062,23 +3060,23 @@ mod tests { #[test] fn test_eq_trait_i32() { - let v1 = FVector::from_vec(vec![5, 6, 7]); - let v2 = FVector::from_vec(vec![5, 6, 7]); + let v1 = ColFVector::from_vec(vec![5, 6, 7]); + let v2 = ColFVector::from_vec(vec![5, 6, 7]); assert!(v1.eq(&v2)); } #[test] fn test_eq_trait_f64() { - let v1 = FVector::from_vec(vec![0.0, -0.0]); - let v2 = FVector::from_vec(vec![0.0, -0.0]); + let v1 = ColFVector::from_vec(vec![0.0, -0.0]); + let v2 = ColFVector::from_vec(vec![0.0, -0.0]); assert!(v1.eq(&v2)); } #[test] fn test_eq_trait_complex_f64() { use num::Complex; - let v1 = FVector::from_vec(vec![Complex::new(0.0, 1.0)]); - let v2 = FVector::from_vec(vec![Complex::new(0.0, 1.0)]); + let v1 = ColFVector::from_vec(vec![Complex::new(0.0, 1.0)]); + let v2 = ColFVector::from_vec(vec![Complex::new(0.0, 1.0)]); assert!(v1.eq(&v2)); } @@ -3089,9 +3087,9 @@ mod tests { // ================================ #[test] fn test_partial_ord_i32() { - let v1 = FVector::from_vec(vec![1, 2, 3]); - let v2 = FVector::from_vec(vec![1, 2, 4]); - let v3 = FVector::from_vec(vec![1, 2, 3]); + let v1 = ColFVector::from_vec(vec![1, 2, 3]); + let v2 = ColFVector::from_vec(vec![1, 2, 4]); + let v3 = ColFVector::from_vec(vec![1, 2, 3]); assert!(v1 < v2); assert!(v2 > v1); assert!(v1 <= v3); @@ -3103,9 +3101,9 @@ mod tests { #[test] fn test_ord_i32() { - let v1 = FVector::from_vec(vec![1, 2, 3]); - let v2 = FVector::from_vec(vec![1, 2, 4]); - let v3 = FVector::from_vec(vec![1, 2, 3]); + let v1 = ColFVector::from_vec(vec![1, 2, 3]); + let v2 = ColFVector::from_vec(vec![1, 2, 4]); + let v3 = ColFVector::from_vec(vec![1, 2, 3]); assert_eq!(v1.cmp(&v2), std::cmp::Ordering::Less); assert_eq!(v2.cmp(&v1), std::cmp::Ordering::Greater); assert_eq!(v1.cmp(&v3), std::cmp::Ordering::Equal); @@ -3113,9 +3111,9 @@ mod tests { #[test] fn test_partial_ord_f64() { - let v1 = FVector::from_vec(vec![1.0, 2.0, 3.0]); - let v2 = FVector::from_vec(vec![1.0, 2.0, 4.0]); - let v3 = FVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); + let v2 = ColFVector::from_vec(vec![1.0, 2.0, 4.0]); + let v3 = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); assert_eq!(v1.partial_cmp(&v2), Some(std::cmp::Ordering::Less)); assert_eq!(v2.partial_cmp(&v1), Some(std::cmp::Ordering::Greater)); assert_eq!(v1.partial_cmp(&v3), Some(std::cmp::Ordering::Equal)); @@ -3123,8 +3121,8 @@ mod tests { #[test] fn test_partial_ord_f64_nan() { - let v1 = FVector::from_vec(vec![1.0, f64::NAN]); - let v2 = FVector::from_vec(vec![1.0, 2.0]); + let v1 = ColFVector::from_vec(vec![1.0, f64::NAN]); + let v2 = ColFVector::from_vec(vec![1.0, 2.0]); // Comparison with NaN yields None assert_eq!(v1.partial_cmp(&v2), None); assert_eq!(v2.partial_cmp(&v1), None); @@ -3132,10 +3130,10 @@ mod tests { #[test] fn test_partial_ord_f64_infinity() { - let v1 = FVector::from_vec(vec![1.0, f64::INFINITY]); - let v2 = FVector::from_vec(vec![1.0, f64::NEG_INFINITY]); - let v3 = FVector::from_vec(vec![1.0, f64::INFINITY]); - let v4 = FVector::from_vec(vec![1.0, 1.0]); + let v1 = ColFVector::from_vec(vec![1.0, f64::INFINITY]); + let v2 = ColFVector::from_vec(vec![1.0, f64::NEG_INFINITY]); + let v3 = ColFVector::from_vec(vec![1.0, f64::INFINITY]); + let v4 = ColFVector::from_vec(vec![1.0, 1.0]); // INFINITY > NEG_INFINITY assert_eq!(v1.partial_cmp(&v2), Some(std::cmp::Ordering::Greater)); assert_eq!(v2.partial_cmp(&v1), Some(std::cmp::Ordering::Less)); @@ -3153,9 +3151,9 @@ mod tests { // ================================ #[test] fn test_hash_i32() { - let v1 = FVector::from_vec(vec![1, 2, 3]); - let v2 = FVector::from_vec(vec![1, 2, 3]); - let v3 = FVector::from_vec(vec![3, 2, 1]); + let v1 = ColFVector::from_vec(vec![1, 2, 3]); + let v2 = ColFVector::from_vec(vec![1, 2, 3]); + let v3 = ColFVector::from_vec(vec![3, 2, 1]); let mut hasher1 = DefaultHasher::new(); v1.hash(&mut hasher1); @@ -3176,9 +3174,9 @@ mod tests { #[test] fn test_hash_complex_i32() { use num::Complex; - let v1 = FVector::from_vec(vec![Complex::new(1, 2), Complex::new(3, 4)]); - let v2 = FVector::from_vec(vec![Complex::new(1, 2), Complex::new(3, 4)]); - let v3 = FVector::from_vec(vec![Complex::new(4, 3), Complex::new(2, 1)]); + let v1 = ColFVector::from_vec(vec![Complex::new(1, 2), Complex::new(3, 4)]); + let v2 = ColFVector::from_vec(vec![Complex::new(1, 2), Complex::new(3, 4)]); + let v3 = ColFVector::from_vec(vec![Complex::new(4, 3), Complex::new(2, 1)]); let mut hasher1 = DefaultHasher::new(); v1.hash(&mut hasher1); @@ -3254,7 +3252,7 @@ mod tests { #[test] fn test_from_slice_i32() { let slice: &[i32] = &[4, 5, 6]; - let fv = FVector::from(slice); + let fv = ColFVector::from(slice); let fv_col: FlexVector = slice.into(); let fv_row: FlexVector = slice.into(); assert_eq!(fv.as_slice(), slice); @@ -3265,7 +3263,7 @@ mod tests { #[test] fn test_from_slice_f64() { let slice: &[f64] = &[4.4, 5.5, 6.6]; - let fv = FVector::from(slice); + let fv = ColFVector::from(slice); let fv_col: FlexVector = slice.into(); let fv_row: FlexVector = slice.into(); assert_eq!(fv.as_slice(), slice); @@ -3277,7 +3275,7 @@ mod tests { fn test_from_slice_complex_f64() { use num::Complex; let slice: &[Complex] = &[Complex::new(7.0, 8.0), Complex::new(9.0, 10.0)]; - let fv = FVector::from(slice); + let fv = ColFVector::from(slice); let fv_col: FlexVector, Column> = slice.into(); let fv_row: FlexVector, Row> = slice.into(); assert_eq!(fv.as_slice(), slice); @@ -3576,7 +3574,7 @@ mod tests { #[test] fn test_from_vectorslice_i32() { - let v = FVector::from_vec(vec![1, 2, 3, 4]); + let v = ColFVector::from_vec(vec![1, 2, 3, 4]); let vslice = v.as_vslice(1..3); let fv: FlexVector = FlexVector::from(vslice); assert_eq!(fv.as_slice(), &[2, 3]); @@ -3584,7 +3582,7 @@ mod tests { #[test] fn test_from_vectorslice_f64() { - let v = FVector::from_vec(vec![1.1, 2.2, 3.3, 4.4]); + let v = ColFVector::from_vec(vec![1.1, 2.2, 3.3, 4.4]); let vslice = v.as_vslice(0..2); let fv: FlexVector = FlexVector::from(vslice); assert_eq!(fv.as_slice(), &[1.1, 2.2]); @@ -3593,7 +3591,7 @@ mod tests { #[test] fn test_from_vectorslice_complex_f64() { use num::Complex; - let v = FVector::from_vec(vec![ + let v = ColFVector::from_vec(vec![ Complex::new(1.0, 2.0), Complex::new(3.0, 4.0), Complex::new(5.0, 6.0), @@ -3605,7 +3603,7 @@ mod tests { #[test] fn test_from_vectorslicemut_i32() { - let mut v = FVector::from_vec(vec![10, 20, 30, 40]); + let mut v = ColFVector::from_vec(vec![10, 20, 30, 40]); { let vslice_mut = v.as_mut_vslice(2..4); let fv: FlexVector = FlexVector::from(vslice_mut); @@ -3615,7 +3613,7 @@ mod tests { #[test] fn test_from_vectorslicemut_f64() { - let mut v = FVector::from_vec(vec![1.5, 2.5, 3.5]); + let mut v = ColFVector::from_vec(vec![1.5, 2.5, 3.5]); { let vslice_mut = v.as_mut_vslice(0..2); let fv: FlexVector = FlexVector::from(vslice_mut); @@ -3626,7 +3624,7 @@ mod tests { #[test] fn test_from_vectorslicemut_complex_f64() { use num::Complex; - let mut v = FVector::from_vec(vec![ + let mut v = ColFVector::from_vec(vec![ Complex::new(7.0, 8.0), Complex::new(9.0, 10.0), Complex::new(11.0, 12.0), @@ -3708,7 +3706,7 @@ mod tests { #[test] fn test_index_usize() { - let v = FVector::from_vec(vec![10, 20, 30, 40]); + let v = ColFVector::from_vec(vec![10, 20, 30, 40]); assert_eq!(v[0], 10); assert_eq!(v[1], 20); assert_eq!(v[3], 40); @@ -3717,13 +3715,13 @@ mod tests { #[test] #[should_panic] fn test_index_usize_out_of_bounds() { - let v = FVector::from_vec(vec![1, 2, 3]); + let v = ColFVector::from_vec(vec![1, 2, 3]); let _ = v[10]; } #[test] fn test_index_range() { - let v = FVector::from_vec(vec![1, 2, 3, 4, 5]); + let v = ColFVector::from_vec(vec![1, 2, 3, 4, 5]); assert_eq!(&v[1..4], &[2, 3, 4]); assert_eq!(&v[0..2], &[1, 2]); assert_eq!(&v[2..5], &[3, 4, 5]); @@ -3731,34 +3729,34 @@ mod tests { #[test] fn test_index_range_from() { - let v = FVector::from_vec(vec![1, 2, 3, 4, 5]); + let v = ColFVector::from_vec(vec![1, 2, 3, 4, 5]); assert_eq!(&v[2..], &[3, 4, 5]); assert_eq!(&v[0..], &[1, 2, 3, 4, 5]); } #[test] fn test_index_range_to() { - let v = FVector::from_vec(vec![1, 2, 3, 4, 5]); + let v = ColFVector::from_vec(vec![1, 2, 3, 4, 5]); assert_eq!(&v[..3], &[1, 2, 3]); assert_eq!(&v[..1], &[1]); } #[test] fn test_index_range_full() { - let v = FVector::from_vec(vec![1, 2, 3]); + let v = ColFVector::from_vec(vec![1, 2, 3]); assert_eq!(&v[..], &[1, 2, 3]); } #[test] fn test_index_range_inclusive() { - let v = FVector::from_vec(vec![1, 2, 3, 4, 5]); + let v = ColFVector::from_vec(vec![1, 2, 3, 4, 5]); assert_eq!(&v[1..=3], &[2, 3, 4]); assert_eq!(&v[0..=4], &[1, 2, 3, 4, 5]); } #[test] fn test_index_range_to_inclusive() { - let v = FVector::from_vec(vec![1, 2, 3, 4, 5]); + let v = ColFVector::from_vec(vec![1, 2, 3, 4, 5]); assert_eq!(&v[..=2], &[1, 2, 3]); assert_eq!(&v[..=0], &[1]); } @@ -3766,21 +3764,21 @@ mod tests { #[test] #[should_panic] fn test_index_range_out_of_bounds() { - let v = FVector::from_vec(vec![1, 2, 3]); + let v = ColFVector::from_vec(vec![1, 2, 3]); let _ = &v[2..5]; } #[test] #[should_panic] fn test_index_range_inclusive_out_of_bounds() { - let v = FVector::from_vec(vec![1, 2, 3]); + let v = ColFVector::from_vec(vec![1, 2, 3]); let _ = &v[1..=5]; } #[test] #[should_panic] fn test_index_range_to_inclusive_out_of_bounds() { - let v = FVector::from_vec(vec![1, 2, 3]); + let v = ColFVector::from_vec(vec![1, 2, 3]); let _ = &v[..=5]; } @@ -3792,7 +3790,7 @@ mod tests { #[test] fn test_index_mut_usize() { - let mut v = FVector::from_vec(vec![1, 2, 3]); + let mut v = ColFVector::from_vec(vec![1, 2, 3]); v[0] = 10; v[2] = 30; assert_eq!(v.as_slice(), &[10, 2, 30]); @@ -3801,48 +3799,48 @@ mod tests { #[test] #[should_panic] fn test_index_mut_usize_out_of_bounds() { - let mut v = FVector::from_vec(vec![1, 2, 3]); + let mut v = ColFVector::from_vec(vec![1, 2, 3]); v[10] = 99; } #[test] fn test_index_mut_range() { - let mut v = FVector::from_vec(vec![1, 2, 3, 4, 5]); + let mut v = ColFVector::from_vec(vec![1, 2, 3, 4, 5]); v[1..4].copy_from_slice(&[20, 30, 40]); assert_eq!(v.as_slice(), &[1, 20, 30, 40, 5]); } #[test] fn test_index_mut_range_from() { - let mut v = FVector::from_vec(vec![1, 2, 3, 4, 5]); + let mut v = ColFVector::from_vec(vec![1, 2, 3, 4, 5]); v[2..].copy_from_slice(&[99, 100, 101]); assert_eq!(v.as_slice(), &[1, 2, 99, 100, 101]); } #[test] fn test_index_mut_range_to() { - let mut v = FVector::from_vec(vec![1, 2, 3, 4, 5]); + let mut v = ColFVector::from_vec(vec![1, 2, 3, 4, 5]); v[..3].copy_from_slice(&[7, 8, 9]); assert_eq!(v.as_slice(), &[7, 8, 9, 4, 5]); } #[test] fn test_index_mut_range_full() { - let mut v = FVector::from_vec(vec![1, 2, 3]); + let mut v = ColFVector::from_vec(vec![1, 2, 3]); v[..].copy_from_slice(&[10, 20, 30]); assert_eq!(v.as_slice(), &[10, 20, 30]); } #[test] fn test_index_mut_range_inclusive() { - let mut v = FVector::from_vec(vec![1, 2, 3, 4, 5]); + let mut v = ColFVector::from_vec(vec![1, 2, 3, 4, 5]); v[1..=3].copy_from_slice(&[21, 31, 41]); assert_eq!(v.as_slice(), &[1, 21, 31, 41, 5]); } #[test] fn test_index_mut_range_to_inclusive() { - let mut v = FVector::from_vec(vec![1, 2, 3, 4, 5]); + let mut v = ColFVector::from_vec(vec![1, 2, 3, 4, 5]); v[..=2].copy_from_slice(&[100, 200, 300]); assert_eq!(v.as_slice(), &[100, 200, 300, 4, 5]); } @@ -3850,21 +3848,21 @@ mod tests { #[test] #[should_panic] fn test_index_mut_range_out_of_bounds() { - let mut v = FVector::from_vec(vec![1, 2, 3]); + let mut v = ColFVector::from_vec(vec![1, 2, 3]); v[2..5].copy_from_slice(&[9, 9, 9]); } #[test] #[should_panic] fn test_index_mut_range_inclusive_out_of_bounds() { - let mut v = FVector::from_vec(vec![1, 2, 3]); + let mut v = ColFVector::from_vec(vec![1, 2, 3]); v[1..=5].copy_from_slice(&[9, 9, 9, 9, 9]); } #[test] #[should_panic] fn test_index_mut_range_to_inclusive_out_of_bounds() { - let mut v = FVector::from_vec(vec![1, 2, 3]); + let mut v = ColFVector::from_vec(vec![1, 2, 3]); v[..=5].copy_from_slice(&[9, 9, 9, 9, 9, 9]); } @@ -3875,14 +3873,14 @@ mod tests { // ================================ #[test] fn test_extend_i32() { - let mut v = FVector::from_vec(vec![1, 2]); + let mut v = ColFVector::from_vec(vec![1, 2]); v.extend(vec![3, 4]); assert_eq!(v.as_slice(), &[1, 2, 3, 4]); } #[test] fn test_extend_f64() { - let mut v = FVector::from_vec(vec![1.1, 2.2]); + let mut v = ColFVector::from_vec(vec![1.1, 2.2]); v.extend(vec![3.3, 4.4]); assert_eq!(v.as_slice(), &[1.1, 2.2, 3.3, 4.4]); } @@ -3890,7 +3888,7 @@ mod tests { #[test] fn test_extend_complex_f64() { use num::Complex; - let mut v = FVector::from_vec(vec![Complex::new(1.0, 2.0)]); + let mut v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0)]); v.extend(vec![Complex::new(3.0, 4.0), Complex::new(5.0, 6.0)]); assert_eq!( v.as_slice(), @@ -3907,7 +3905,7 @@ mod tests { #[test] fn test_extend_with_empty() { - let mut v = FVector::from_vec(vec![1, 2]); + let mut v = ColFVector::from_vec(vec![1, 2]); v.extend(Vec::::new()); assert_eq!(v.as_slice(), &[1, 2]); } @@ -3920,21 +3918,21 @@ mod tests { // --- as_slice --- #[test] fn test_as_slice() { - let v = FVector::from_vec(vec![1, 2, 3]); + let v = ColFVector::from_vec(vec![1, 2, 3]); let slice = v.as_slice(); assert_eq!(slice, &[1, 2, 3]); } #[test] fn test_as_slice_f64() { - let v = FVector::from_vec(vec![1.1, 2.2, 3.3]); + let v = ColFVector::from_vec(vec![1.1, 2.2, 3.3]); let slice = v.as_slice(); assert_eq!(slice, &[1.1, 2.2, 3.3]); } #[test] fn test_as_slice_complex() { - let v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let slice = v.as_slice(); assert_eq!(slice, &[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0),]); } @@ -3942,7 +3940,7 @@ mod tests { // --- len --- #[test] fn test_len() { - let v = FVector::from_vec(vec![10, 20, 30, 40]); + let v = ColFVector::from_vec(vec![10, 20, 30, 40]); assert_eq!(v.len(), 4); let empty = FlexVector::::new(); assert_eq!(empty.len(), 0); @@ -3950,7 +3948,7 @@ mod tests { #[test] fn test_len_f64() { - let v = FVector::from_vec(vec![10.0, 20.0]); + let v = ColFVector::from_vec(vec![10.0, 20.0]); assert_eq!(v.len(), 2); let empty = FlexVector::::new(); assert_eq!(empty.len(), 0); @@ -3958,7 +3956,7 @@ mod tests { #[test] fn test_len_complex() { - let v = FVector::from_vec(vec![Complex::new(1.0, 0.0)]); + let v = ColFVector::from_vec(vec![Complex::new(1.0, 0.0)]); assert_eq!(v.len(), 1); let empty = FlexVector::>::new(); assert_eq!(empty.len(), 0); @@ -3967,7 +3965,7 @@ mod tests { // --- is_empty --- #[test] fn test_is_empty() { - let v = FVector::from_vec(vec![1]); + let v = ColFVector::from_vec(vec![1]); assert!(!v.is_empty()); let empty = FlexVector::::new(); assert!(empty.is_empty()); @@ -3975,7 +3973,7 @@ mod tests { #[test] fn test_is_empty_f64() { - let v = FVector::from_vec(vec![1.0]); + let v = ColFVector::from_vec(vec![1.0]); assert!(!v.is_empty()); let empty = FlexVector::::new(); assert!(empty.is_empty()); @@ -3983,7 +3981,7 @@ mod tests { #[test] fn test_is_empty_complex() { - let v = FVector::from_vec(vec![Complex::new(0.0, 1.0)]); + let v = ColFVector::from_vec(vec![Complex::new(0.0, 1.0)]); assert!(!v.is_empty()); let empty = FlexVector::>::new(); assert!(empty.is_empty()); @@ -3992,7 +3990,7 @@ mod tests { // --- get --- #[test] fn test_get() { - let v = FVector::from_vec(vec![10, 20, 30]); + let v = ColFVector::from_vec(vec![10, 20, 30]); assert_eq!(v.get(0), Some(&10)); assert_eq!(v.get(2), Some(&30)); assert_eq!(v.get(3), None); @@ -4000,7 +3998,7 @@ mod tests { #[test] fn test_get_f64() { - let v = FVector::from_vec(vec![10.5, 20.5, 30.5]); + let v = ColFVector::from_vec(vec![10.5, 20.5, 30.5]); assert_eq!(v.get(0), Some(&10.5)); assert_eq!(v.get(2), Some(&30.5)); assert_eq!(v.get(3), None); @@ -4008,7 +4006,7 @@ mod tests { #[test] fn test_get_complex() { - let v = FVector::from_vec(vec![Complex::new(1.0, 1.0), Complex::new(2.0, 2.0)]); + let v = ColFVector::from_vec(vec![Complex::new(1.0, 1.0), Complex::new(2.0, 2.0)]); assert_eq!(v.get(0), Some(&Complex::new(1.0, 1.0))); assert_eq!(v.get(1), Some(&Complex::new(2.0, 2.0))); assert_eq!(v.get(2), None); @@ -4017,7 +4015,7 @@ mod tests { // --- first --- #[test] fn test_first() { - let v = FVector::from_vec(vec![5, 6, 7]); + let v = ColFVector::from_vec(vec![5, 6, 7]); assert_eq!(v.first(), Some(&5)); let empty = FlexVector::::new(); assert_eq!(empty.first(), None); @@ -4025,7 +4023,7 @@ mod tests { #[test] fn test_first_f64() { - let v = FVector::from_vec(vec![5.5, 6.5]); + let v = ColFVector::from_vec(vec![5.5, 6.5]); assert_eq!(v.first(), Some(&5.5)); let empty = FlexVector::::new(); assert_eq!(empty.first(), None); @@ -4033,7 +4031,7 @@ mod tests { #[test] fn test_first_complex() { - let v = FVector::from_vec(vec![Complex::new(7.0, 8.0), Complex::new(9.0, 10.0)]); + let v = ColFVector::from_vec(vec![Complex::new(7.0, 8.0), Complex::new(9.0, 10.0)]); assert_eq!(v.first(), Some(&Complex::new(7.0, 8.0))); let empty = FlexVector::>::new(); assert_eq!(empty.first(), None); @@ -4042,7 +4040,7 @@ mod tests { // --- last --- #[test] fn test_last() { - let v = FVector::from_vec(vec![1, 2, 3]); + let v = ColFVector::from_vec(vec![1, 2, 3]); assert_eq!(v.last(), Some(&3)); let empty = FlexVector::::new(); assert_eq!(empty.last(), None); @@ -4050,7 +4048,7 @@ mod tests { #[test] fn test_last_f64() { - let v = FVector::from_vec(vec![1.5, 2.5, 3.5]); + let v = ColFVector::from_vec(vec![1.5, 2.5, 3.5]); assert_eq!(v.last(), Some(&3.5)); let empty = FlexVector::::new(); assert_eq!(empty.last(), None); @@ -4058,7 +4056,7 @@ mod tests { #[test] fn test_last_complex() { - let v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); assert_eq!(v.last(), Some(&Complex::new(3.0, 4.0))); let empty = FlexVector::>::new(); assert_eq!(empty.last(), None); @@ -4067,7 +4065,7 @@ mod tests { // --- iter --- #[test] fn test_iter() { - let v = FVector::from_vec(vec![1, 2, 3]); + let v = ColFVector::from_vec(vec![1, 2, 3]); let mut iter = v.iter(); assert_eq!(iter.next(), Some(&1)); assert_eq!(iter.next(), Some(&2)); @@ -4077,14 +4075,14 @@ mod tests { #[test] fn test_iter_f64() { - let v = FVector::from_vec(vec![1.1, 2.2, 3.3]); + let v = ColFVector::from_vec(vec![1.1, 2.2, 3.3]); let collected: Vec<_> = v.iter().copied().collect(); assert_eq!(collected, vec![1.1, 2.2, 3.3]); } #[test] fn test_iter_complex() { - let v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let collected: Vec<_> = v.iter().cloned().collect(); assert_eq!(collected, vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0),]); } @@ -4092,21 +4090,21 @@ mod tests { // --- iter_rev --- #[test] fn test_iter_rev() { - let v = FVector::from_vec(vec![1, 2, 3]); + let v = ColFVector::from_vec(vec![1, 2, 3]); let collected: Vec<_> = v.iter_rev().copied().collect(); assert_eq!(collected, vec![3, 2, 1]); } #[test] fn test_iter_rev_f64() { - let v = FVector::from_vec(vec![1.1, 2.2, 3.3]); + let v = ColFVector::from_vec(vec![1.1, 2.2, 3.3]); let collected: Vec<_> = v.iter_rev().copied().collect(); assert_eq!(collected, vec![3.3, 2.2, 1.1]); } #[test] fn test_iter_rev_complex() { - let v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let collected: Vec<_> = v.iter_rev().cloned().collect(); assert_eq!(collected, vec![Complex::new(3.0, 4.0), Complex::new(1.0, 2.0),]); } @@ -4114,21 +4112,21 @@ mod tests { // --- enumerate --- #[test] fn test_enumerate() { - let v = FVector::from_vec(vec![10, 20, 30]); + let v = ColFVector::from_vec(vec![10, 20, 30]); let pairs: Vec<_> = v.enumerate().collect(); assert_eq!(pairs, vec![(0, &10), (1, &20), (2, &30)]); } #[test] fn test_enumerate_f64() { - let v = FVector::from_vec(vec![1.5, 2.5]); + let v = ColFVector::from_vec(vec![1.5, 2.5]); let pairs: Vec<_> = v.enumerate().collect(); assert_eq!(pairs, vec![(0, &1.5), (1, &2.5)]); } #[test] fn test_enumerate_complex() { - let v = FVector::from_vec(vec![Complex::new(0.0, 1.0), Complex::new(2.0, 3.0)]); + let v = ColFVector::from_vec(vec![Complex::new(0.0, 1.0), Complex::new(2.0, 3.0)]); let pairs: Vec<_> = v.enumerate().collect(); assert_eq!(pairs, vec![(0, &Complex::new(0.0, 1.0)), (1, &Complex::new(2.0, 3.0)),]); } @@ -4136,21 +4134,21 @@ mod tests { // --- to_vec --- #[test] fn test_to_vec() { - let v = FVector::from_vec(vec![1, 2, 3]); + let v = ColFVector::from_vec(vec![1, 2, 3]); let vec_copy = v.to_vec(); assert_eq!(vec_copy, vec![1, 2, 3]); } #[test] fn test_to_vec_f64() { - let v = FVector::from_vec(vec![1.5, 2.5]); + let v = ColFVector::from_vec(vec![1.5, 2.5]); let vec_copy = v.to_vec(); assert_eq!(vec_copy, vec![1.5, 2.5]); } #[test] fn test_to_vec_complex() { - let v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let vec_copy = v.to_vec(); assert_eq!(vec_copy, vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0),]); } @@ -4223,7 +4221,7 @@ mod tests { // --- pretty --- #[test] fn test_pretty() { - let v = FVector::from_vec(vec![1, 2, 3]); + let v = ColFVector::from_vec(vec![1, 2, 3]); let pretty = v.pretty(); assert!(pretty.contains("1")); assert!(pretty.contains("2")); @@ -4233,7 +4231,7 @@ mod tests { #[test] fn test_pretty_f64() { - let v = FVector::from_vec(vec![1.5, 2.5]); + let v = ColFVector::from_vec(vec![1.5, 2.5]); let pretty = v.pretty(); assert!(pretty.contains("1.5")); assert!(pretty.contains("2.5")); @@ -4242,7 +4240,7 @@ mod tests { #[test] fn test_pretty_complex() { - let v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let pretty = v.pretty(); assert!(pretty.contains("1.0")); assert!(pretty.contains("2.0")); @@ -4254,21 +4252,21 @@ mod tests { // --- contains --- #[test] fn test_contains() { - let v = FVector::from_vec(vec![1, 2, 3]); + let v = ColFVector::from_vec(vec![1, 2, 3]); assert!(v.contains(&2)); assert!(!v.contains(&4)); } #[test] fn test_contains_f64() { - let v = FVector::from_vec(vec![1.1, 2.2, 3.3]); + let v = ColFVector::from_vec(vec![1.1, 2.2, 3.3]); assert!(v.contains(&2.2)); assert!(!v.contains(&4.4)); } #[test] fn test_contains_complex() { - let v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); assert!(v.contains(&Complex::new(1.0, 2.0))); assert!(!v.contains(&Complex::new(0.0, 0.0))); } @@ -4276,21 +4274,21 @@ mod tests { // --- starts_with --- #[test] fn test_starts_with() { - let v = FVector::from_vec(vec![1, 2, 3]); + let v = ColFVector::from_vec(vec![1, 2, 3]); assert!(v.starts_with(&[1, 2])); assert!(!v.starts_with(&[2, 3])); } #[test] fn test_starts_with_f64() { - let v = FVector::from_vec(vec![1.1, 2.2, 3.3]); + let v = ColFVector::from_vec(vec![1.1, 2.2, 3.3]); assert!(v.starts_with(&[1.1, 2.2])); assert!(!v.starts_with(&[2.2, 3.3])); } #[test] fn test_starts_with_complex() { - let v = FVector::from_vec(vec![ + let v = ColFVector::from_vec(vec![ Complex::new(1.0, 2.0), Complex::new(3.0, 4.0), Complex::new(5.0, 6.0), @@ -4302,21 +4300,21 @@ mod tests { // --- ends_with --- #[test] fn test_ends_with() { - let v = FVector::from_vec(vec![1, 2, 3]); + let v = ColFVector::from_vec(vec![1, 2, 3]); assert!(v.ends_with(&[2, 3])); assert!(!v.ends_with(&[1, 2])); } #[test] fn test_ends_with_f64() { - let v = FVector::from_vec(vec![1.1, 2.2, 3.3]); + let v = ColFVector::from_vec(vec![1.1, 2.2, 3.3]); assert!(v.ends_with(&[2.2, 3.3])); assert!(!v.ends_with(&[1.1, 2.2])); } #[test] fn test_ends_with_complex() { - let v = FVector::from_vec(vec![ + let v = ColFVector::from_vec(vec![ Complex::new(1.0, 2.0), Complex::new(3.0, 4.0), Complex::new(5.0, 6.0), @@ -4328,21 +4326,21 @@ mod tests { // --- position --- #[test] fn test_position() { - let v = FVector::from_vec(vec![10, 20, 30]); + let v = ColFVector::from_vec(vec![10, 20, 30]); assert_eq!(v.position(|&x| x == 20), Some(1)); assert_eq!(v.position(|&x| x == 99), None); } #[test] fn test_position_f64() { - let v = FVector::from_vec(vec![10.0, 20.0, 30.0]); + let v = ColFVector::from_vec(vec![10.0, 20.0, 30.0]); assert_eq!(v.position(|&x| x == 20.0), Some(1)); assert_eq!(v.position(|&x| x == 99.0), None); } #[test] fn test_position_complex() { - let v = FVector::from_vec(vec![ + let v = ColFVector::from_vec(vec![ Complex::new(1.0, 1.0), Complex::new(2.0, 2.0), Complex::new(3.0, 3.0), @@ -4354,21 +4352,21 @@ mod tests { // --- rposition --- #[test] fn test_rposition() { - let v = FVector::from_vec(vec![1, 2, 3, 2]); + let v = ColFVector::from_vec(vec![1, 2, 3, 2]); assert_eq!(v.rposition(|&x| x == 2), Some(3)); assert_eq!(v.rposition(|&x| x == 99), None); } #[test] fn test_rposition_f64() { - let v = FVector::from_vec(vec![1.0, 2.0, 3.0, 2.0]); + let v = ColFVector::from_vec(vec![1.0, 2.0, 3.0, 2.0]); assert_eq!(v.rposition(|&x| x == 2.0), Some(3)); assert_eq!(v.rposition(|&x| x == 99.0), None); } #[test] fn test_rposition_complex() { - let v = FVector::from_vec(vec![ + let v = ColFVector::from_vec(vec![ Complex::new(1.0, 1.0), Complex::new(2.0, 2.0), Complex::new(3.0, 3.0), @@ -4381,21 +4379,21 @@ mod tests { // --- windows --- #[test] fn test_windows() { - let v = FVector::from_vec(vec![1, 2, 3, 4]); + let v = ColFVector::from_vec(vec![1, 2, 3, 4]); let windows: Vec<_> = v.windows(2).collect(); assert_eq!(windows, vec![&[1, 2][..], &[2, 3][..], &[3, 4][..]]); } #[test] fn test_windows_f64() { - let v = FVector::from_vec(vec![1.0, 2.0, 3.0]); + let v = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); let windows: Vec<_> = v.windows(2).collect(); assert_eq!(windows, vec![&[1.0, 2.0][..], &[2.0, 3.0][..]]); } #[test] fn test_windows_complex() { - let v = FVector::from_vec(vec![ + let v = ColFVector::from_vec(vec![ Complex::new(1.0, 0.0), Complex::new(2.0, 0.0), Complex::new(3.0, 0.0), @@ -4413,21 +4411,21 @@ mod tests { // --- chunks --- #[test] fn test_chunks() { - let v = FVector::from_vec(vec![1, 2, 3, 4, 5]); + let v = ColFVector::from_vec(vec![1, 2, 3, 4, 5]); let chunks: Vec<_> = v.chunks(2).collect(); assert_eq!(chunks, vec![&[1, 2][..], &[3, 4][..], &[5][..]]); } #[test] fn test_chunks_f64() { - let v = FVector::from_vec(vec![1.0, 2.0, 3.0]); + let v = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); let chunks: Vec<_> = v.chunks(2).collect(); assert_eq!(chunks, vec![&[1.0, 2.0][..], &[3.0][..]]); } #[test] fn test_chunks_complex() { - let v = FVector::from_vec(vec![ + let v = ColFVector::from_vec(vec![ Complex::new(1.0, 0.0), Complex::new(2.0, 0.0), Complex::new(3.0, 0.0), @@ -4445,7 +4443,7 @@ mod tests { // --- split_at --- #[test] fn test_split_at() { - let v = FVector::from_vec(vec![1, 2, 3, 4]); + let v = ColFVector::from_vec(vec![1, 2, 3, 4]); let (left, right) = v.split_at(2); assert_eq!(left, &[1, 2]); assert_eq!(right, &[3, 4]); @@ -4453,7 +4451,7 @@ mod tests { #[test] fn test_split_at_f64() { - let v = FVector::from_vec(vec![1.0, 2.0, 3.0]); + let v = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); let (left, right) = v.split_at(1); assert_eq!(left, &[1.0]); assert_eq!(right, &[2.0, 3.0]); @@ -4461,7 +4459,7 @@ mod tests { #[test] fn test_split_at_complex() { - let v = FVector::from_vec(vec![Complex::new(1.0, 0.0), Complex::new(2.0, 0.0)]); + let v = ColFVector::from_vec(vec![Complex::new(1.0, 0.0), Complex::new(2.0, 0.0)]); let (left, right) = v.split_at(1); assert_eq!(left, &[Complex::new(1.0, 0.0)]); assert_eq!(right, &[Complex::new(2.0, 0.0)]); @@ -4470,21 +4468,21 @@ mod tests { // --- split --- #[test] fn test_split() { - let v = FVector::from_vec(vec![1, 2, 0, 3, 0, 4]); + let v = ColFVector::from_vec(vec![1, 2, 0, 3, 0, 4]); let splits: Vec<_> = v.split(|&x| x == 0).collect(); assert_eq!(splits, vec![&[1, 2][..], &[3][..], &[4][..]]); } #[test] fn test_split_f64() { - let v = FVector::from_vec(vec![1.0, 0.0, 2.0, 0.0, 3.0]); + let v = ColFVector::from_vec(vec![1.0, 0.0, 2.0, 0.0, 3.0]); let splits: Vec<_> = v.split(|&x| x == 0.0).collect(); assert_eq!(splits, vec![&[1.0][..], &[2.0][..], &[3.0][..]]); } #[test] fn test_split_complex() { - let v = FVector::from_vec(vec![ + let v = ColFVector::from_vec(vec![ Complex::new(1.0, 0.0), Complex::new(0.0, 0.0), Complex::new(2.0, 0.0), @@ -4496,21 +4494,21 @@ mod tests { // --- splitn --- #[test] fn test_splitn() { - let v = FVector::from_vec(vec![1, 0, 2, 0, 3]); + let v = ColFVector::from_vec(vec![1, 0, 2, 0, 3]); let splits: Vec<_> = v.splitn(2, |&x| x == 0).collect(); assert_eq!(splits, vec![&[1][..], &[2, 0, 3][..]]); } #[test] fn test_splitn_f64() { - let v = FVector::from_vec(vec![1.0, 0.0, 2.0, 0.0, 3.0]); + let v = ColFVector::from_vec(vec![1.0, 0.0, 2.0, 0.0, 3.0]); let splits: Vec<_> = v.splitn(2, |&x| x == 0.0).collect(); assert_eq!(splits, vec![&[1.0][..], &[2.0, 0.0, 3.0][..]]); } #[test] fn test_splitn_complex() { - let v = FVector::from_vec(vec![ + let v = ColFVector::from_vec(vec![ Complex::new(1.0, 0.0), Complex::new(0.0, 0.0), Complex::new(2.0, 0.0), @@ -4530,21 +4528,21 @@ mod tests { // --- rsplit --- #[test] fn test_rsplit() { - let v = FVector::from_vec(vec![1, 0, 2, 0, 3]); + let v = ColFVector::from_vec(vec![1, 0, 2, 0, 3]); let splits: Vec<_> = v.rsplit(|&x| x == 0).collect(); assert_eq!(splits, vec![&[3][..], &[2][..], &[1][..]]); } #[test] fn test_rsplit_f64() { - let v = FVector::from_vec(vec![1.0, 0.0, 2.0, 0.0, 3.0]); + let v = ColFVector::from_vec(vec![1.0, 0.0, 2.0, 0.0, 3.0]); let splits: Vec<_> = v.rsplit(|&x| x == 0.0).collect(); assert_eq!(splits, vec![&[3.0][..], &[2.0][..], &[1.0][..]]); } #[test] fn test_rsplit_complex() { - let v = FVector::from_vec(vec![ + let v = ColFVector::from_vec(vec![ Complex::new(1.0, 0.0), Complex::new(0.0, 0.0), Complex::new(2.0, 0.0), @@ -4565,21 +4563,21 @@ mod tests { // --- rsplitn --- #[test] fn test_rsplitn() { - let v = FVector::from_vec(vec![1, 0, 2, 0, 3]); + let v = ColFVector::from_vec(vec![1, 0, 2, 0, 3]); let splits: Vec<_> = v.rsplitn(2, |&x| x == 0).collect(); assert_eq!(splits, vec![&[3][..], &[1, 0, 2][..]]); } #[test] fn test_rsplitn_f64() { - let v = FVector::from_vec(vec![1.0, 0.0, 2.0, 0.0, 3.0]); + let v = ColFVector::from_vec(vec![1.0, 0.0, 2.0, 0.0, 3.0]); let splits: Vec<_> = v.rsplitn(2, |&x| x == 0.0).collect(); assert_eq!(splits, vec![&[3.0][..], &[1.0, 0.0, 2.0][..]]); } #[test] fn test_rsplitn_complex() { - let v = FVector::from_vec(vec![ + let v = ColFVector::from_vec(vec![ Complex::new(1.0, 0.0), Complex::new(0.0, 0.0), Complex::new(2.0, 0.0), @@ -4733,7 +4731,7 @@ mod tests { // --- push --- #[test] fn test_push() { - let mut v = FVector::new(); + let mut v = ColFVector::new(); v.push(1); v.push(2); assert_eq!(v.as_slice(), &[1, 2]); @@ -4741,7 +4739,7 @@ mod tests { #[test] fn test_push_f64() { - let mut v = FVector::new(); + let mut v = ColFVector::new(); v.push(1.1); v.push(2.2); assert_eq!(v.as_slice(), &[1.1, 2.2]); @@ -4749,7 +4747,7 @@ mod tests { #[test] fn test_push_complex() { - let mut v = FVector::new(); + let mut v = ColFVector::new(); v.push(Complex::new(1.0, 2.0)); v.push(Complex::new(3.0, 4.0)); assert_eq!(v.as_slice(), &[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); @@ -4758,7 +4756,7 @@ mod tests { // --- pop --- #[test] fn test_pop() { - let mut v = FVector::from_vec(vec![1, 2]); + let mut v = ColFVector::from_vec(vec![1, 2]); assert_eq!(v.pop(), Some(2)); assert_eq!(v.pop(), Some(1)); assert_eq!(v.pop(), None); @@ -4766,7 +4764,7 @@ mod tests { #[test] fn test_pop_f64() { - let mut v = FVector::from_vec(vec![1.1, 2.2]); + let mut v = ColFVector::from_vec(vec![1.1, 2.2]); assert_eq!(v.pop(), Some(2.2)); assert_eq!(v.pop(), Some(1.1)); assert_eq!(v.pop(), None); @@ -4774,7 +4772,7 @@ mod tests { #[test] fn test_pop_complex() { - let mut v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let mut v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); assert_eq!(v.pop(), Some(Complex::new(3.0, 4.0))); assert_eq!(v.pop(), Some(Complex::new(1.0, 2.0))); assert_eq!(v.pop(), None); @@ -4783,21 +4781,21 @@ mod tests { // --- insert --- #[test] fn test_insert() { - let mut v = FVector::from_vec(vec![1, 3]); + let mut v = ColFVector::from_vec(vec![1, 3]); v.insert(1, 2); assert_eq!(v.as_slice(), &[1, 2, 3]); } #[test] fn test_insert_f64() { - let mut v = FVector::from_vec(vec![1.1, 3.3]); + let mut v = ColFVector::from_vec(vec![1.1, 3.3]); v.insert(1, 2.2); assert_eq!(v.as_slice(), &[1.1, 2.2, 3.3]); } #[test] fn test_insert_complex() { - let mut v = FVector::from_vec(vec![Complex::new(1.0, 1.0), Complex::new(3.0, 3.0)]); + let mut v = ColFVector::from_vec(vec![Complex::new(1.0, 1.0), Complex::new(3.0, 3.0)]); v.insert(1, Complex::new(2.0, 2.0)); assert_eq!( v.as_slice(), @@ -4808,21 +4806,21 @@ mod tests { // --- remove --- #[test] fn test_remove() { - let mut v = FVector::from_vec(vec![1, 2, 3]); + let mut v = ColFVector::from_vec(vec![1, 2, 3]); assert_eq!(v.remove(1), 2); assert_eq!(v.as_slice(), &[1, 3]); } #[test] fn test_remove_f64() { - let mut v = FVector::from_vec(vec![1.1, 2.2, 3.3]); + let mut v = ColFVector::from_vec(vec![1.1, 2.2, 3.3]); assert_eq!(v.remove(1), 2.2); assert_eq!(v.as_slice(), &[1.1, 3.3]); } #[test] fn test_remove_complex() { - let mut v = FVector::from_vec(vec![ + let mut v = ColFVector::from_vec(vec![ Complex::new(1.0, 1.0), Complex::new(2.0, 2.0), Complex::new(3.0, 3.0), @@ -4834,7 +4832,7 @@ mod tests { // --- resize --- #[test] fn test_resize() { - let mut v = FVector::from_vec(vec![1, 2]); + let mut v = ColFVector::from_vec(vec![1, 2]); v.resize(4, 0); assert_eq!(v.as_slice(), &[1, 2, 0, 0]); v.resize(2, 0); @@ -4843,7 +4841,7 @@ mod tests { #[test] fn test_resize_f64() { - let mut v = FVector::from_vec(vec![1.1, 2.2]); + let mut v = ColFVector::from_vec(vec![1.1, 2.2]); v.resize(4, 0.0); assert_eq!(v.as_slice(), &[1.1, 2.2, 0.0, 0.0]); v.resize(1, 0.0); @@ -4852,7 +4850,7 @@ mod tests { #[test] fn test_resize_complex() { - let mut v = FVector::from_vec(vec![Complex::new(1.0, 1.0)]); + let mut v = ColFVector::from_vec(vec![Complex::new(1.0, 1.0)]); v.resize(3, Complex::new(0.0, 0.0)); assert_eq!( v.as_slice(), @@ -4865,7 +4863,7 @@ mod tests { // --- clear --- #[test] fn test_clear() { - let mut v = FVector::from_vec(vec![1, 2, 3]); + let mut v = ColFVector::from_vec(vec![1, 2, 3]); assert!(!v.is_empty()); v.clear(); assert!(v.is_empty()); @@ -4873,7 +4871,7 @@ mod tests { #[test] fn test_clear_f64() { - let mut v = FVector::from_vec(vec![1.1, 2.2, 3.3]); + let mut v = ColFVector::from_vec(vec![1.1, 2.2, 3.3]); assert!(!v.is_empty()); v.clear(); assert!(v.is_empty()); @@ -4881,7 +4879,7 @@ mod tests { #[test] fn test_clear_complex() { - let mut v = FVector::from_vec(vec![Complex::new(1.0, 1.0), Complex::new(2.0, 2.0)]); + let mut v = ColFVector::from_vec(vec![Complex::new(1.0, 1.0), Complex::new(2.0, 2.0)]); assert!(!v.is_empty()); v.clear(); assert!(v.is_empty()); @@ -4890,7 +4888,7 @@ mod tests { // --- get_mut --- #[test] fn test_get_mut_single() { - let mut v = FVector::from_vec(vec![10, 20, 30]); + let mut v = ColFVector::from_vec(vec![10, 20, 30]); if let Some(x) = v.get_mut(1) { *x = 99; } @@ -4899,13 +4897,13 @@ mod tests { #[test] fn test_get_mut_out_of_bounds() { - let mut v = FVector::from_vec(vec![1, 2, 3]); + let mut v = ColFVector::from_vec(vec![1, 2, 3]); assert!(v.get_mut(10).is_none()); } #[test] fn test_get_mut_range() { - let mut v = FVector::from_vec(vec![1, 2, 3, 4]); + let mut v = ColFVector::from_vec(vec![1, 2, 3, 4]); if let Some(slice) = v.get_mut(1..3) { slice[0] = 20; slice[1] = 30; @@ -4915,7 +4913,7 @@ mod tests { #[test] fn test_get_mut_full_range() { - let mut v = FVector::from_vec(vec![5, 6, 7]); + let mut v = ColFVector::from_vec(vec![5, 6, 7]); if let Some(slice) = v.get_mut(..) { for x in slice { *x *= 2; @@ -4927,7 +4925,7 @@ mod tests { // --- iter_mut --- #[test] fn test_iter_mut_i32() { - let mut v = FVector::from_vec(vec![1, 2, 3]); + let mut v = ColFVector::from_vec(vec![1, 2, 3]); for x in v.iter_mut() { *x *= 2; } @@ -4936,7 +4934,7 @@ mod tests { #[test] fn test_iter_mut_f64() { - let mut v = FVector::from_vec(vec![1.5, -2.0, 0.0]); + let mut v = ColFVector::from_vec(vec![1.5, -2.0, 0.0]); for x in v.iter_mut() { *x += 1.0; } @@ -4946,7 +4944,7 @@ mod tests { #[test] fn test_iter_mut_complex() { use num::Complex; - let mut v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + let mut v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); for x in v.iter_mut() { x.re += 1.0; x.im *= 2.0; @@ -4956,7 +4954,7 @@ mod tests { #[test] fn test_iter_mut_empty() { - let mut v = FVector::::new(); + let mut v = ColFVector::::new(); let mut count = 0; for _ in v.iter_mut() { count += 1; @@ -4967,7 +4965,7 @@ mod tests { // --- as_mut_slice --- #[test] fn test_as_mut_slice_i32() { - let mut v = FVector::from_vec(vec![1, 2, 3]); + let mut v = ColFVector::from_vec(vec![1, 2, 3]); let slice = v.as_mut_slice(); slice[0] = 10; slice[2] = 30; @@ -4976,7 +4974,7 @@ mod tests { #[test] fn test_as_mut_slice_f64() { - let mut v = FVector::from_vec(vec![1.1, 2.2, 3.3]); + let mut v = ColFVector::from_vec(vec![1.1, 2.2, 3.3]); let slice = v.as_mut_slice(); slice[1] = 9.9; assert_eq!(v.as_slice(), &[1.1, 9.9, 3.3]); @@ -4985,7 +4983,7 @@ mod tests { #[test] fn test_as_mut_slice_complex() { use num::Complex; - let mut v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let mut v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let slice = v.as_mut_slice(); slice[0].re = 10.0; slice[1].im = 40.0; @@ -4994,7 +4992,7 @@ mod tests { #[test] fn test_as_mut_slice_empty() { - let mut v = FVector::::new(); + let mut v = ColFVector::::new(); let slice = v.as_mut_slice(); assert_eq!(slice.len(), 0); } @@ -5002,7 +5000,7 @@ mod tests { // --- map --- #[test] fn test_map_i32() { - let v = FVector::from_vec(vec![1, 2, 3]); + let v = ColFVector::from_vec(vec![1, 2, 3]); let squared = v.map(|x| x * x); assert_eq!(squared.as_slice(), &[1, 4, 9]); // original unchanged @@ -5011,7 +5009,7 @@ mod tests { #[test] fn test_map_f64() { - let v: FlexVector = FVector::from_vec(vec![1.5, -2.0, 0.0]); + let v: FlexVector = ColFVector::from_vec(vec![1.5, -2.0, 0.0]); let abs = v.map(|x| x.abs()); assert_eq!(abs.as_slice(), &[1.5, 2.0, 0.0]); } @@ -5019,7 +5017,7 @@ mod tests { #[test] fn test_map_complex() { use num::Complex; - let v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); let conj = v.map(|x| x.conj()); assert_eq!(conj.as_slice(), &[Complex::new(1.0, -2.0), Complex::new(-3.0, -4.0)]); } @@ -5033,7 +5031,7 @@ mod tests { #[test] fn test_map_with_fn_pointer() { - let v = FVector::from_vec(vec![1, 2, 3]); + let v = ColFVector::from_vec(vec![1, 2, 3]); let squared = v.map(square); assert_eq!(squared.as_slice(), &[1, 4, 9]); } @@ -5041,21 +5039,21 @@ mod tests { // --- mut_map --- #[test] fn test_mut_map_i32() { - let mut v = FVector::from_vec(vec![1, 2, 3]); + let mut v = ColFVector::from_vec(vec![1, 2, 3]); v.mut_map(|x| x * 10); assert_eq!(v.as_slice(), &[10, 20, 30]); } #[test] fn test_mut_map_f64() { - let mut v = FVector::from_vec(vec![1.5, -2.0, 0.0]); + let mut v = ColFVector::from_vec(vec![1.5, -2.0, 0.0]); v.mut_map(|x| x + 1.0); assert_eq!(v.as_slice(), &[2.5, -1.0, 1.0]); } #[test] fn test_mut_map_complex() { - let mut v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + let mut v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); v.mut_map(|x| Complex::new(x.re + 1.0, x.im * 2.0)); assert_eq!(v.as_slice(), &[Complex::new(2.0, 4.0), Complex::new(-2.0, 8.0)]); } @@ -5069,7 +5067,7 @@ mod tests { #[test] fn test_mut_map_with_fn_pointer() { - let mut v = FVector::from_vec(vec![1, 2, 3]); + let mut v = ColFVector::from_vec(vec![1, 2, 3]); v.mut_map(double); assert_eq!(v.as_slice(), &[2, 4, 6]); } @@ -5077,28 +5075,28 @@ mod tests { // --- flat_map --- #[test] fn test_flat_map_i32_basic() { - let v = FVector::from_vec(vec![1, 2, 3]); + let v = ColFVector::from_vec(vec![1, 2, 3]); let flat = v.flat_map(|x| vec![x, x * 10]); assert_eq!(flat.as_slice(), &[1, 10, 2, 20, 3, 30]); } #[test] fn test_flat_map_i32_empty_inner() { - let v = FVector::from_vec(vec![1, 2, 3]); + let v = ColFVector::from_vec(vec![1, 2, 3]); let flat = v.flat_map(|x| if x % 2 == 0 { vec![] } else { vec![x] }); assert_eq!(flat.as_slice(), &[1, 3]); } #[test] fn test_flat_map_i32_option() { - let v = FVector::from_vec(vec![1, 2, 3, 4]); + let v = ColFVector::from_vec(vec![1, 2, 3, 4]); let flat = v.flat_map(|x| if x % 2 == 0 { Some(x) } else { None }); assert_eq!(flat.as_slice(), &[2, 4]); } #[test] fn test_flat_map_f64_basic() { - let v = FVector::from_vec(vec![1.0, 2.0]); + let v = ColFVector::from_vec(vec![1.0, 2.0]); let flat = v.flat_map(|x| vec![x, x + 0.5]); assert_eq!(flat.as_slice(), &[1.0, 1.5, 2.0, 2.5]); } @@ -5112,7 +5110,7 @@ mod tests { #[test] fn test_flat_map_f64_nan_infinity() { - let v = FVector::from_vec(vec![f64::NAN, f64::INFINITY, 1.0]); + let v = ColFVector::from_vec(vec![f64::NAN, f64::INFINITY, 1.0]); let flat = v.flat_map(|x| vec![x]); assert!(flat.as_slice()[0].is_nan()); assert_eq!(flat.as_slice()[1], f64::INFINITY); @@ -5122,7 +5120,7 @@ mod tests { #[test] fn test_flat_map_complex_f64_basic() { use num::Complex; - let v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let flat = v.flat_map(|z| vec![z, z.conj()]); assert_eq!( flat.as_slice(), @@ -5138,7 +5136,7 @@ mod tests { #[test] fn test_flat_map_complex_f64_empty_inner() { use num::Complex; - let v = FVector::from_vec(vec![Complex::new(0.0, 0.0), Complex::new(1.0, 1.0)]); + let v = ColFVector::from_vec(vec![Complex::new(0.0, 0.0), Complex::new(1.0, 1.0)]); let flat = v.flat_map(|z| if z == Complex::new(0.0, 0.0) { vec![] } else { vec![z] }); assert_eq!(flat.as_slice(), &[Complex::new(1.0, 1.0)]); } @@ -5146,7 +5144,7 @@ mod tests { #[test] fn test_flat_map_complex_f64_option() { use num::Complex; - let v = FVector::from_vec(vec![Complex::new(1.0, 0.0), Complex::new(0.0, 0.0)]); + let v = ColFVector::from_vec(vec![Complex::new(1.0, 0.0), Complex::new(0.0, 0.0)]); let flat = v.flat_map(|z| if z.im == 0.0 { Some(z) } else { None }); assert_eq!(flat.as_slice(), &[Complex::new(1.0, 0.0), Complex::new(0.0, 0.0)]); } @@ -5160,7 +5158,7 @@ mod tests { #[test] fn test_flat_map_all_empty_inner() { - let v = FVector::from_vec(vec![1, 2, 3]); + let v = ColFVector::from_vec(vec![1, 2, 3]); let flat = v.flat_map(|_| Vec::::new()); assert!(flat.is_empty()); } @@ -5169,35 +5167,35 @@ mod tests { #[test] fn test_filter_i32_basic() { - let v = FVector::from_vec(vec![1, 2, 3, 4, 5]); + let v = ColFVector::from_vec(vec![1, 2, 3, 4, 5]); let filtered = v.filter(|&x| x % 2 == 0); assert_eq!(filtered.as_slice(), &[2, 4]); } #[test] fn test_filter_i32_all_false() { - let v = FVector::from_vec(vec![1, 3, 5]); + let v = ColFVector::from_vec(vec![1, 3, 5]); let filtered = v.filter(|&x| x > 10); assert!(filtered.is_empty()); } #[test] fn test_filter_i32_all_true() { - let v = FVector::from_vec(vec![1, 2, 3]); + let v = ColFVector::from_vec(vec![1, 2, 3]); let filtered = v.filter(|_| true); assert_eq!(filtered.as_slice(), &[1, 2, 3]); } #[test] fn test_filter_f64_positive() { - let v = FVector::from_vec(vec![-1.5, 0.0, 2.5, 3.3]); + let v = ColFVector::from_vec(vec![-1.5, 0.0, 2.5, 3.3]); let filtered = v.filter(|&x| x > 0.0); assert_eq!(filtered.as_slice(), &[2.5, 3.3]); } #[test] fn test_filter_f64_nan() { - let v = FVector::from_vec(vec![1.0, f64::NAN, 2.0]); + let v = ColFVector::from_vec(vec![1.0, f64::NAN, 2.0]); let filtered = v.filter(|&x| x.is_nan()); assert_eq!(filtered.len(), 1); assert!(filtered[0].is_nan()); @@ -5206,7 +5204,7 @@ mod tests { #[test] fn test_filter_complex_f64_real_positive() { use num::Complex; - let v = FVector::from_vec(vec![ + let v = ColFVector::from_vec(vec![ Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0), Complex::new(0.0, 0.0), @@ -5218,7 +5216,7 @@ mod tests { #[test] fn test_filter_complex_f64_imag_nonzero() { use num::Complex; - let v = FVector::from_vec(vec![ + let v = ColFVector::from_vec(vec![ Complex::new(1.0, 0.0), Complex::new(0.0, 2.0), Complex::new(3.0, 0.0), @@ -5229,7 +5227,7 @@ mod tests { #[test] fn test_filter_empty() { - let v: FlexVector = FVector::new(); + let v: FlexVector = ColFVector::new(); let filtered = v.filter(|&x| x > 0); assert!(filtered.is_empty()); } @@ -5238,42 +5236,42 @@ mod tests { #[test] fn test_reduce_i32_sum() { - let v = FVector::from_vec(vec![1, 2, 3, 4]); + let v = ColFVector::from_vec(vec![1, 2, 3, 4]); let sum = v.reduce(|a, b| a + b); assert_eq!(sum, Some(10)); } #[test] fn test_reduce_i32_product() { - let v = FVector::from_vec(vec![2, 3, 4]); + let v = ColFVector::from_vec(vec![2, 3, 4]); let product = v.reduce(|a, b| a * b); assert_eq!(product, Some(24)); } #[test] fn test_reduce_i32_empty() { - let v: FlexVector = FVector::new(); + let v: FlexVector = ColFVector::new(); let result = v.reduce(|a, b| a + b); assert_eq!(result, None); } #[test] fn test_reduce_f64_sum() { - let v = FVector::from_vec(vec![1.5, 2.5, 3.0]); + let v = ColFVector::from_vec(vec![1.5, 2.5, 3.0]); let sum = v.reduce(|a, b| a + b); assert_eq!(sum, Some(7.0)); } #[test] fn test_reduce_f64_product() { - let v = FVector::from_vec(vec![1.5, 2.0, 3.0]); + let v = ColFVector::from_vec(vec![1.5, 2.0, 3.0]); let product = v.reduce(|a, b| a * b); assert!((product.unwrap() - 9.0).abs() < 1e-12); } #[test] fn test_reduce_f64_empty() { - let v: FlexVector = FVector::new(); + let v: FlexVector = ColFVector::new(); let result = v.reduce(|a, b| a + b); assert_eq!(result, None); } @@ -5281,7 +5279,7 @@ mod tests { #[test] fn test_reduce_complex_f64_sum() { use num::Complex; - let v = FVector::from_vec(vec![ + let v = ColFVector::from_vec(vec![ Complex::new(1.0, 2.0), Complex::new(3.0, 4.0), Complex::new(5.0, 6.0), @@ -5293,7 +5291,7 @@ mod tests { #[test] fn test_reduce_complex_f64_product() { use num::Complex; - let v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let product = v.reduce(|a, b| a * b); assert_eq!(product, Some(Complex::new(-5.0, 10.0))); // (1+2i)*(3+4i) = -5+10i } @@ -5301,7 +5299,7 @@ mod tests { #[test] fn test_reduce_complex_f64_empty() { use num::Complex; - let v: FlexVector> = FVector::new(); + let v: FlexVector> = ColFVector::new(); let result = v.reduce(|a, b| a + b); assert_eq!(result, None); } @@ -5310,42 +5308,42 @@ mod tests { #[test] fn test_fold_i32_sum() { - let v = FVector::from_vec(vec![1, 2, 3, 4]); + let v = ColFVector::from_vec(vec![1, 2, 3, 4]); let sum = v.fold(0, |acc, x| acc + x); assert_eq!(sum, 10); } #[test] fn test_fold_i32_product() { - let v = FVector::from_vec(vec![2, 3, 4]); + let v = ColFVector::from_vec(vec![2, 3, 4]); let product = v.fold(1, |acc, x| acc * x); assert_eq!(product, 24); } #[test] fn test_fold_i32_empty() { - let v: FlexVector = FVector::new(); + let v: FlexVector = ColFVector::new(); let sum = v.fold(0, |acc, x| acc + x); assert_eq!(sum, 0); } #[test] fn test_fold_f64_sum() { - let v = FVector::from_vec(vec![1.5, 2.5, 3.0]); + let v = ColFVector::from_vec(vec![1.5, 2.5, 3.0]); let sum = v.fold(0.0, |acc, x| acc + x); assert!((sum - 7.0).abs() < 1e-12); } #[test] fn test_fold_f64_product() { - let v = FVector::from_vec(vec![1.5, 2.0, 3.0]); + let v = ColFVector::from_vec(vec![1.5, 2.0, 3.0]); let product = v.fold(1.0, |acc, x| acc * x); assert!((product - 9.0).abs() < 1e-12); } #[test] fn test_fold_f64_empty() { - let v: FlexVector = FVector::new(); + let v: FlexVector = ColFVector::new(); let sum = v.fold(0.0, |acc, x| acc + x); assert_eq!(sum, 0.0); } @@ -5353,7 +5351,7 @@ mod tests { #[test] fn test_fold_complex_f64_sum() { use num::Complex; - let v = FVector::from_vec(vec![ + let v = ColFVector::from_vec(vec![ Complex::new(1.0, 2.0), Complex::new(3.0, 4.0), Complex::new(5.0, 6.0), @@ -5365,7 +5363,7 @@ mod tests { #[test] fn test_fold_complex_f64_product() { use num::Complex; - let v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let product = v.fold(Complex::new(1.0, 0.0), |acc, x| acc * x); assert_eq!(product, Complex::new(-5.0, 10.0)); // (1+0i)*(1+2i)*(3+4i) = (1+2i)*(3+4i) = -5+10i } @@ -5373,7 +5371,7 @@ mod tests { #[test] fn test_fold_complex_f64_empty() { use num::Complex; - let v: FlexVector> = FVector::new(); + let v: FlexVector> = ColFVector::new(); let sum = v.fold(Complex::new(0.0, 0.0), |acc, x| acc + x); assert_eq!(sum, Complex::new(0.0, 0.0)); } @@ -5381,7 +5379,7 @@ mod tests { // --- zip --- #[test] fn test_zip_i32() { - let v1 = FVector::from_vec(vec![1, 2, 3]); + let v1 = ColFVector::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); let zipped = v1.zip(v2); assert_eq!(zipped.as_slice(), &[(1, 4), (2, 5), (3, 6)]); @@ -5389,8 +5387,8 @@ mod tests { #[test] fn test_zip_f64() { - let v1 = FVector::from_vec(vec![1.1, 2.2, 3.3]); - let v2 = FVector::from_vec(vec![4.4, 5.5, 6.6]); + let v1 = ColFVector::from_vec(vec![1.1, 2.2, 3.3]); + let v2 = ColFVector::from_vec(vec![4.4, 5.5, 6.6]); let zipped = v1.zip(v2); assert_eq!(zipped.as_slice(), &[(1.1, 4.4), (2.2, 5.5), (3.3, 6.6)]); } @@ -5398,8 +5396,8 @@ mod tests { #[test] fn test_zip_complex_f64() { use num::Complex; - let v1 = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); - let v2 = FVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); + let v1 = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v2 = ColFVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); let zipped = v1.zip(v2); assert_eq!( zipped.as_slice(), @@ -5412,16 +5410,16 @@ mod tests { #[test] fn test_zip_different_lengths() { - let v1 = FVector::from_vec(vec![1, 2]); - let v2 = FVector::from_vec(vec![3, 4, 5]); + let v1 = ColFVector::from_vec(vec![1, 2]); + let v2 = ColFVector::from_vec(vec![3, 4, 5]); let zipped = v1.zip(v2); assert_eq!(zipped.as_slice(), &[(1, 3), (2, 4)]); } #[test] fn test_zip_empty() { - let v1: FlexVector = FVector::new(); - let v2: FlexVector = FVector::new(); + let v1: FlexVector = ColFVector::new(); + let v2: FlexVector = ColFVector::new(); let zipped = v1.zip(v2); assert!(zipped.is_empty()); } @@ -5429,16 +5427,16 @@ mod tests { // --- zip_with --- #[test] fn test_zip_with_i32() { - let v1 = FVector::from_vec(vec![1, 2, 3]); - let v2 = FVector::from_vec(vec![4, 5, 6]); + let v1 = ColFVector::from_vec(vec![1, 2, 3]); + let v2 = ColFVector::from_vec(vec![4, 5, 6]); let summed = v1.zip_with(v2, |a, b| a + b); assert_eq!(summed.as_slice(), &[5, 7, 9]); } #[test] fn test_zip_with_f64() { - let v1 = FVector::from_vec(vec![1.5, 2.5, 3.5]); - let v2 = FVector::from_vec(vec![4.5, 5.5, 6.5]); + let v1 = ColFVector::from_vec(vec![1.5, 2.5, 3.5]); + let v2 = ColFVector::from_vec(vec![4.5, 5.5, 6.5]); let prod = v1.zip_with(v2, |a, b| a * b); assert_eq!(prod.as_slice(), &[6.75, 13.75, 22.75]); } @@ -5446,16 +5444,16 @@ mod tests { #[test] fn test_zip_with_complex_f64() { use num::Complex; - let v1 = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); - let v2 = FVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); + let v1 = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v2 = ColFVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); let sum = v1.zip_with(v2, |a, b| a + b); assert_eq!(sum.as_slice(), &[Complex::new(6.0, 8.0), Complex::new(10.0, 12.0)]); } #[test] fn test_zip_with_different_lengths() { - let v1 = FVector::from_vec(vec![1, 2]); - let v2 = FVector::from_vec(vec![10, 20, 30]); + let v1 = ColFVector::from_vec(vec![1, 2]); + let v2 = ColFVector::from_vec(vec![10, 20, 30]); let zipped = v1.zip_with(v2, |a, b| a + b); assert_eq!(zipped.as_slice(), &[11, 22]); } @@ -5472,28 +5470,28 @@ mod tests { #[test] fn test_step_by_i32_basic() { - let v = FVector::from_vec(vec![1, 2, 3, 4, 5, 6]); + let v = ColFVector::from_vec(vec![1, 2, 3, 4, 5, 6]); let stepped = v.step_by(2).unwrap(); assert_eq!(stepped.as_slice(), &[1, 3, 5]); } #[test] fn test_step_by_i32_step_one() { - let v = FVector::from_vec(vec![10, 20, 30]); + let v = ColFVector::from_vec(vec![10, 20, 30]); let stepped = v.step_by(1).unwrap(); assert_eq!(stepped.as_slice(), &[10, 20, 30]); } #[test] fn test_step_by_i32_step_equals_len() { - let v = FVector::from_vec(vec![7, 8, 9]); + let v = ColFVector::from_vec(vec![7, 8, 9]); let stepped = v.step_by(3).unwrap(); assert_eq!(stepped.as_slice(), &[7]); } #[test] fn test_step_by_i32_step_greater_than_len() { - let v = FVector::from_vec(vec![1, 2, 3]); + let v = ColFVector::from_vec(vec![1, 2, 3]); let stepped = v.step_by(5).unwrap(); assert_eq!(stepped.as_slice(), &[1]); } @@ -5507,14 +5505,14 @@ mod tests { #[test] fn test_step_by_i32_zero_step() { - let v = FVector::from_vec(vec![1, 2, 3]); + let v = ColFVector::from_vec(vec![1, 2, 3]); let result = v.step_by(0); assert!(result.is_err()); } #[test] fn test_step_by_f64_basic() { - let v = FVector::from_vec(vec![1.0, 2.0, 3.0, 4.0]); + let v = ColFVector::from_vec(vec![1.0, 2.0, 3.0, 4.0]); let stepped = v.step_by(2).unwrap(); assert_eq!(stepped.as_slice(), &[1.0, 3.0]); } @@ -5528,7 +5526,7 @@ mod tests { #[test] fn test_step_by_f64_zero_step() { - let v = FVector::from_vec(vec![1.1, 2.2]); + let v = ColFVector::from_vec(vec![1.1, 2.2]); let result = v.step_by(0); assert!(result.is_err()); } @@ -5536,7 +5534,7 @@ mod tests { #[test] fn test_step_by_complex_f64_basic() { use num::Complex; - let v = FVector::from_vec(vec![ + let v = ColFVector::from_vec(vec![ Complex::new(1.0, 2.0), Complex::new(3.0, 4.0), Complex::new(5.0, 6.0), @@ -5557,7 +5555,7 @@ mod tests { #[test] fn test_step_by_complex_f64_zero_step() { use num::Complex; - let v = FVector::from_vec(vec![Complex::new(1.0, 2.0)]); + let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0)]); let result = v.step_by(0); assert!(result.is_err()); } @@ -5566,28 +5564,28 @@ mod tests { #[test] fn test_create_mask_i32_greater_than() { - let v = FVector::from_vec(vec![1, 5, 3, 7]); + let v = ColFVector::from_vec(vec![1, 5, 3, 7]); let mask = v.create_mask(|&x| x > 3); assert_eq!(mask.as_slice(), &[false, true, false, true]); } #[test] fn test_create_mask_i32_even() { - let v = FVector::from_vec(vec![2, 3, 4, 5]); + let v = ColFVector::from_vec(vec![2, 3, 4, 5]); let mask = v.create_mask(|&x| x % 2 == 0); assert_eq!(mask.as_slice(), &[true, false, true, false]); } #[test] fn test_create_mask_f64_positive() { - let v = FVector::from_vec(vec![-1.0, 0.0, 2.5, -3.3]); + let v = ColFVector::from_vec(vec![-1.0, 0.0, 2.5, -3.3]); let mask = v.create_mask(|&x| x > 0.0); assert_eq!(mask.as_slice(), &[false, false, true, false]); } #[test] fn test_create_mask_f64_nan() { - let v = FVector::from_vec(vec![1.0, f64::NAN, 2.0]); + let v = ColFVector::from_vec(vec![1.0, f64::NAN, 2.0]); let mask = v.create_mask(|&x| x.is_nan()); assert_eq!(mask.as_slice(), &[false, true, false]); } @@ -5595,7 +5593,7 @@ mod tests { #[test] fn test_create_mask_complex_f64_real_positive() { use num::Complex; - let v = FVector::from_vec(vec![ + let v = ColFVector::from_vec(vec![ Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0), Complex::new(0.0, 0.0), @@ -5607,7 +5605,7 @@ mod tests { #[test] fn test_create_mask_complex_f64_imag_nonzero() { use num::Complex; - let v = FVector::from_vec(vec![ + let v = ColFVector::from_vec(vec![ Complex::new(1.0, 0.0), Complex::new(0.0, 2.0), Complex::new(3.0, 0.0), @@ -5618,7 +5616,7 @@ mod tests { #[test] fn test_create_mask_empty() { - let v: FlexVector = FVector::new(); + let v: FlexVector = ColFVector::new(); let mask = v.create_mask(|&x| x > 0); assert!(mask.is_empty()); } @@ -5627,7 +5625,7 @@ mod tests { #[test] fn test_filter_by_mask_i32_basic() { - let v = FVector::from_vec(vec![10, 20, 30, 40]); + let v = ColFVector::from_vec(vec![10, 20, 30, 40]); let mask = vec![false, true, false, true]; let filtered = v.filter_by_mask(&mask).unwrap(); assert_eq!(filtered.as_slice(), &[20, 40]); @@ -5635,7 +5633,7 @@ mod tests { #[test] fn test_filter_by_mask_i32_all_true() { - let v = FVector::from_vec(vec![1, 2, 3]); + let v = ColFVector::from_vec(vec![1, 2, 3]); let mask = vec![true, true, true]; let filtered = v.filter_by_mask(&mask).unwrap(); assert_eq!(filtered.as_slice(), &[1, 2, 3]); @@ -5643,7 +5641,7 @@ mod tests { #[test] fn test_filter_by_mask_i32_all_false() { - let v = FVector::from_vec(vec![1, 2, 3]); + let v = ColFVector::from_vec(vec![1, 2, 3]); let mask = vec![false, false, false]; let filtered = v.filter_by_mask(&mask).unwrap(); assert!(filtered.is_empty()); @@ -5659,7 +5657,7 @@ mod tests { #[test] fn test_filter_by_mask_i32_mismatched_length() { - let v = FVector::from_vec(vec![1, 2, 3]); + let v = ColFVector::from_vec(vec![1, 2, 3]); let mask = vec![true, false]; let result = v.filter_by_mask(&mask); assert!(result.is_err()); @@ -5667,7 +5665,7 @@ mod tests { #[test] fn test_filter_by_mask_f64_basic() { - let v = FVector::from_vec(vec![1.1, 2.2, 3.3, 4.4]); + let v = ColFVector::from_vec(vec![1.1, 2.2, 3.3, 4.4]); let mask = vec![true, false, true, false]; let filtered = v.filter_by_mask(&mask).unwrap(); assert_eq!(filtered.as_slice(), &[1.1, 3.3]); @@ -5675,7 +5673,7 @@ mod tests { #[test] fn test_filter_by_mask_f64_all_true() { - let v = FVector::from_vec(vec![1.1, 2.2]); + let v = ColFVector::from_vec(vec![1.1, 2.2]); let mask = vec![true, true]; let filtered = v.filter_by_mask(&mask).unwrap(); assert_eq!(filtered.as_slice(), &[1.1, 2.2]); @@ -5683,7 +5681,7 @@ mod tests { #[test] fn test_filter_by_mask_f64_all_false() { - let v = FVector::from_vec(vec![1.1, 2.2]); + let v = ColFVector::from_vec(vec![1.1, 2.2]); let mask = vec![false, false]; let filtered = v.filter_by_mask(&mask).unwrap(); assert!(filtered.is_empty()); @@ -5699,7 +5697,7 @@ mod tests { #[test] fn test_filter_by_mask_f64_mismatched_length() { - let v = FVector::from_vec(vec![1.1, 2.2]); + let v = ColFVector::from_vec(vec![1.1, 2.2]); let mask = vec![true]; let result = v.filter_by_mask(&mask); assert!(result.is_err()); @@ -5708,7 +5706,7 @@ mod tests { #[test] fn test_filter_by_mask_complex_f64_basic() { use num::Complex; - let v = FVector::from_vec(vec![ + let v = ColFVector::from_vec(vec![ Complex::new(1.0, 2.0), Complex::new(3.0, 4.0), Complex::new(5.0, 6.0), @@ -5721,7 +5719,7 @@ mod tests { #[test] fn test_filter_by_mask_complex_f64_all_true() { use num::Complex; - let v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let mask = vec![true, true]; let filtered = v.filter_by_mask(&mask).unwrap(); assert_eq!(filtered.as_slice(), &[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); @@ -5730,7 +5728,7 @@ mod tests { #[test] fn test_filter_by_mask_complex_f64_all_false() { use num::Complex; - let v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let mask = vec![false, false]; let filtered = v.filter_by_mask(&mask).unwrap(); assert!(filtered.is_empty()); @@ -5748,7 +5746,7 @@ mod tests { #[test] fn test_filter_by_mask_complex_f64_mismatched_length() { use num::Complex; - let v = FVector::from_vec(vec![Complex::new(1.0, 2.0)]); + let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0)]); let mask = vec![true, false]; let result = v.filter_by_mask(&mask); assert!(result.is_err()); @@ -5758,38 +5756,38 @@ mod tests { #[test] fn test_broadcast_to_i32() { - let v = FVector::from_vec(vec![1, 2]); + let v = ColFVector::from_vec(vec![1, 2]); let b = v.broadcast_to(5).unwrap(); assert_eq!(b.as_slice(), &[1, 2, 1, 2, 1]); - let v = FVector::from_vec(vec![7]); + let v = ColFVector::from_vec(vec![7]); let b = v.broadcast_to(4).unwrap(); assert_eq!(b.as_slice(), &[7, 7, 7, 7]); - let v: FlexVector = FVector::from_vec(vec![]); + let v: FlexVector = ColFVector::from_vec(vec![]); let b = v.broadcast_to(0).unwrap(); assert!(b.is_empty()); - let v: FlexVector = FVector::from_vec(vec![]); + let v: FlexVector = ColFVector::from_vec(vec![]); let result = v.broadcast_to(3); assert!(result.is_err()); } #[test] fn test_broadcast_to_f64() { - let v = FVector::from_vec(vec![1.5, -2.0]); + let v = ColFVector::from_vec(vec![1.5, -2.0]); let b = v.broadcast_to(5).unwrap(); assert_eq!(b.as_slice(), &[1.5, -2.0, 1.5, -2.0, 1.5]); - let v = FVector::from_vec(vec![3.14]); + let v = ColFVector::from_vec(vec![3.14]); let b = v.broadcast_to(3).unwrap(); assert_eq!(b.as_slice(), &[3.14, 3.14, 3.14]); - let v: FlexVector = FVector::from_vec(vec![]); + let v: FlexVector = ColFVector::from_vec(vec![]); let b = v.broadcast_to(0).unwrap(); assert!(b.is_empty()); - let v: FlexVector = FVector::from_vec(vec![]); + let v: FlexVector = ColFVector::from_vec(vec![]); let result = v.broadcast_to(2); assert!(result.is_err()); } @@ -5797,7 +5795,7 @@ mod tests { #[test] fn test_broadcast_to_complex_f64() { use num::Complex; - let v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); let b = v.broadcast_to(5).unwrap(); assert_eq!( b.as_slice(), @@ -5810,18 +5808,18 @@ mod tests { ] ); - let v = FVector::from_vec(vec![Complex::new(0.0, 1.0)]); + let v = ColFVector::from_vec(vec![Complex::new(0.0, 1.0)]); let b = v.broadcast_to(3).unwrap(); assert_eq!( b.as_slice(), &[Complex::new(0.0, 1.0), Complex::new(0.0, 1.0), Complex::new(0.0, 1.0)] ); - let v: FlexVector, Column> = FVector::from_vec(vec![]); + let v: FlexVector, Column> = ColFVector::from_vec(vec![]); let b = v.broadcast_to(0).unwrap(); assert!(b.is_empty()); - let v: FlexVector, Column> = FVector::from_vec(vec![]); + let v: FlexVector, Column> = ColFVector::from_vec(vec![]); let result = v.broadcast_to(1); assert!(result.is_err()); } @@ -5945,7 +5943,7 @@ mod tests { // -- translate -- #[test] fn test_translate_i32() { - let v1 = FVector::from_vec(vec![1, 2, 3]); + let v1 = ColFVector::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); let result = v1.translate(&v2).unwrap(); assert_eq!(result.as_slice(), &[5, 7, 9]); @@ -5953,7 +5951,7 @@ mod tests { #[test] fn test_translate_f64() { - let v1 = FVector::from_vec(vec![1.5, 2.5, 3.5]); + let v1 = ColFVector::from_vec(vec![1.5, 2.5, 3.5]); let v2 = FlexVector::from_vec(vec![0.5, 1.5, 2.5]); let result = v1.translate(&v2).unwrap(); assert_eq!(result.as_slice(), &[2.0, 4.0, 6.0]); @@ -5961,7 +5959,7 @@ mod tests { #[test] fn test_translate_complex_f64() { - let v1 = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v1 = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); let result = v1.translate(&v2).unwrap(); assert_eq!(result.as_slice(), &[Complex::new(6.0, 8.0), Complex::new(10.0, 12.0)]); @@ -5969,7 +5967,7 @@ mod tests { #[test] fn test_translate_mismatched_lengths() { - let v1 = FVector::from_vec(vec![1, 2]); + let v1 = ColFVector::from_vec(vec![1, 2]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); let result = v1.translate(&v2); assert!(result.is_err()); @@ -5979,7 +5977,7 @@ mod tests { #[test] fn test_translate_into_i32() { - let v1 = FVector::from_vec(vec![1, 2, 3]); + let v1 = ColFVector::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); let mut out = [0; 3]; v1.translate_into(&v2, &mut out).unwrap(); @@ -5989,7 +5987,7 @@ mod tests { #[test] fn test_translate_into_f64() { - let v1 = FVector::from_vec(vec![1.5, 2.5, 3.5]); + let v1 = ColFVector::from_vec(vec![1.5, 2.5, 3.5]); let v2 = FlexVector::from_vec(vec![0.5, 1.5, 2.5]); let mut out = [0.0; 3]; v1.translate_into(&v2, &mut out).unwrap(); @@ -6000,7 +5998,7 @@ mod tests { #[test] fn test_translate_into_complex_f64() { use num::Complex; - let v1 = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v1 = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); let mut out = [Complex::new(0.0, 0.0); 2]; v1.translate_into(&v2, &mut out).unwrap(); @@ -6010,7 +6008,7 @@ mod tests { #[test] fn test_translate_into_mismatched_length() { - let v1 = FVector::from_vec(vec![1, 2, 3]); + let v1 = ColFVector::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); let mut out = [0; 2]; let result = v1.translate_into(&v2, &mut out); @@ -6020,7 +6018,7 @@ mod tests { // -- mut_translate -- #[test] fn test_mut_translate_i32() { - let mut v1 = FVector::from_vec(vec![1, 2, 3]); + let mut v1 = ColFVector::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); v1.mut_translate(&v2).unwrap(); assert_eq!(v1.as_slice(), &[5, 7, 9]); @@ -6028,7 +6026,7 @@ mod tests { #[test] fn test_mut_translate_f64() { - let mut v1 = FVector::from_vec(vec![1.5, 2.5, 3.5]); + let mut v1 = ColFVector::from_vec(vec![1.5, 2.5, 3.5]); let v2 = FlexVector::from_vec(vec![0.5, 1.5, 2.5]); v1.mut_translate(&v2).unwrap(); assert_eq!(v1.as_slice(), &[2.0, 4.0, 6.0]); @@ -6036,7 +6034,7 @@ mod tests { #[test] fn test_mut_translate_complex_f64() { - let mut v1 = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let mut v1 = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); v1.mut_translate(&v2).unwrap(); assert_eq!(v1.as_slice(), &[Complex::new(6.0, 8.0), Complex::new(10.0, 12.0)]); @@ -6044,7 +6042,7 @@ mod tests { #[test] fn test_mut_translate_mismatched_lengths() { - let mut v1 = FVector::from_vec(vec![1, 2]); + let mut v1 = ColFVector::from_vec(vec![1, 2]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); let result = v1.mut_translate(&v2); assert!(result.is_err()); @@ -6053,14 +6051,14 @@ mod tests { // -- scale -- #[test] fn test_scale_i32() { - let v = FVector::from_vec(vec![1, 2, 3]); + let v = ColFVector::from_vec(vec![1, 2, 3]); let scaled = v.scale(10); assert_eq!(scaled.as_slice(), &[10, 20, 30]); } #[test] fn test_scale_f64() { - let v = FVector::from_vec(vec![1.5, -2.0, 0.0]); + let v = ColFVector::from_vec(vec![1.5, -2.0, 0.0]); let scaled = v.scale(2.0); assert_eq!(scaled.as_slice(), &[3.0, -4.0, 0.0]); } @@ -6068,7 +6066,7 @@ mod tests { #[test] fn test_scale_complex_f64() { use num::Complex; - let v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); let scalar = Complex::new(2.0, 0.0); let scaled = v.scale(scalar); assert_eq!(scaled.as_slice(), &[Complex::new(2.0, 4.0), Complex::new(-6.0, 8.0)]); @@ -6077,21 +6075,21 @@ mod tests { // -- mut_scale -- #[test] fn test_mut_scale_i32() { - let mut v = FVector::from_vec(vec![1, 2, 3]); + let mut v = ColFVector::from_vec(vec![1, 2, 3]); v.mut_scale(10); assert_eq!(v.as_slice(), &[10, 20, 30]); } #[test] fn test_mut_scale_f64() { - let mut v = FVector::from_vec(vec![1.5, -2.0, 0.0]); + let mut v = ColFVector::from_vec(vec![1.5, -2.0, 0.0]); v.mut_scale(2.0); assert_eq!(v.as_slice(), &[3.0, -4.0, 0.0]); } #[test] fn test_mut_scale_complex_f64() { - let mut v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + let mut v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); let scalar = Complex::new(2.0, 0.0); v.mut_scale(scalar); assert_eq!(v.as_slice(), &[Complex::new(2.0, 4.0), Complex::new(-6.0, 8.0)]); @@ -6100,7 +6098,7 @@ mod tests { // -- negate -- #[test] fn test_negate_i32() { - let v = FVector::from_vec(vec![1, -2, 3]); + let v = ColFVector::from_vec(vec![1, -2, 3]); let neg = v.negate(); assert_eq!(neg.as_slice(), &[-1, 2, -3]); // original unchanged @@ -6109,7 +6107,7 @@ mod tests { #[test] fn test_negate_f64() { - let v = FVector::from_vec(vec![1.5, -2.5, 0.0]); + let v = ColFVector::from_vec(vec![1.5, -2.5, 0.0]); let neg = v.negate(); assert_eq!(neg.as_slice(), &[-1.5, 2.5, -0.0]); assert_eq!(v.as_slice(), &[1.5, -2.5, 0.0]); @@ -6118,7 +6116,7 @@ mod tests { #[test] fn test_negate_complex_f64() { use num::Complex; - let v = FVector::from_vec(vec![Complex::new(1.0, -2.0), Complex::new(-3.0, 4.0)]); + let v = ColFVector::from_vec(vec![Complex::new(1.0, -2.0), Complex::new(-3.0, 4.0)]); let neg = v.negate(); assert_eq!(neg.as_slice(), &[Complex::new(-1.0, 2.0), Complex::new(3.0, -4.0)]); assert_eq!(v.as_slice(), &[Complex::new(1.0, -2.0), Complex::new(-3.0, 4.0)]); @@ -6127,14 +6125,14 @@ mod tests { // -- mut_negate -- #[test] fn test_mut_negate_i32() { - let mut v = FVector::from_vec(vec![1, -2, 3]); + let mut v = ColFVector::from_vec(vec![1, -2, 3]); v.mut_negate(); assert_eq!(v.as_slice(), &[-1, 2, -3]); } #[test] fn test_mut_negate_f64() { - let mut v = FVector::from_vec(vec![1.5, -2.5, 0.0]); + let mut v = ColFVector::from_vec(vec![1.5, -2.5, 0.0]); v.mut_negate(); assert_eq!(v.as_slice(), &[-1.5, 2.5, -0.0]); } @@ -6142,7 +6140,7 @@ mod tests { #[test] fn test_mut_negate_complex_f64() { use num::Complex; - let mut v = FVector::from_vec(vec![Complex::new(1.0, -2.0), Complex::new(-3.0, 4.0)]); + let mut v = ColFVector::from_vec(vec![Complex::new(1.0, -2.0), Complex::new(-3.0, 4.0)]); v.mut_negate(); assert_eq!(v.as_slice(), &[Complex::new(-1.0, 2.0), Complex::new(3.0, -4.0)]); } @@ -6150,14 +6148,14 @@ mod tests { // -- mut_zero -- #[test] fn test_mut_zero_i32() { - let mut v = FVector::from_vec(vec![1, -2, 3]); + let mut v = ColFVector::from_vec(vec![1, -2, 3]); v.mut_zero(); assert_eq!(v.as_slice(), &[0, 0, 0]); } #[test] fn test_mut_zero_f64() { - let mut v = FVector::from_vec(vec![1.5, -2.5, 0.0]); + let mut v = ColFVector::from_vec(vec![1.5, -2.5, 0.0]); v.mut_zero(); assert_eq!(v.as_slice(), &[0.0, 0.0, 0.0]); } @@ -6165,7 +6163,7 @@ mod tests { #[test] fn test_mut_zero_complex_f64() { use num::Complex; - let mut v = FVector::from_vec(vec![Complex::new(1.0, -2.0), Complex::new(-3.0, 4.0)]); + let mut v = ColFVector::from_vec(vec![Complex::new(1.0, -2.0), Complex::new(-3.0, 4.0)]); v.mut_zero(); assert_eq!(v.as_slice(), &[Complex::new(0.0, 0.0), Complex::new(0.0, 0.0)]); } @@ -6173,7 +6171,7 @@ mod tests { // -- dot -- #[test] fn test_dot_i32() { - let v1 = FVector::from_vec(vec![1, 2, 3]); + let v1 = ColFVector::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); let dot = v1.dot(&v2).unwrap(); assert_eq!(dot, 1 * 4 + 2 * 5 + 3 * 6); // 32 @@ -6181,7 +6179,7 @@ mod tests { #[test] fn test_dot_f64() { - let v1 = FVector::from_vec(vec![1.5, 2.0, -3.0]); + let v1 = ColFVector::from_vec(vec![1.5, 2.0, -3.0]); let v2 = FlexVector::from_vec(vec![2.0, 0.5, 4.0]); let dot = v1.dot(&v2).unwrap(); assert!((dot - (1.5 * 2.0 + 2.0 * 0.5 + -3.0 * 4.0)).abs() < 1e-12); @@ -6189,7 +6187,7 @@ mod tests { #[test] fn test_dot_mismatched_lengths() { - let v1 = FVector::from_vec(vec![1, 2]); + let v1 = ColFVector::from_vec(vec![1, 2]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); let dot = v1.dot(&v2); assert!(dot.is_err()); @@ -6208,7 +6206,7 @@ mod tests { // -- dot_to_f64 -- #[test] fn test_dot_to_f64_i32() { - let v1 = FVector::from_vec(vec![1, 2, 3]); + let v1 = ColFVector::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); let dot = v1.dot_to_f64(&v2).unwrap(); assert!((dot - 32.0).abs() < 1e-12); @@ -6216,7 +6214,7 @@ mod tests { #[test] fn test_dot_to_f64_f64() { - let v1 = FVector::from_vec(vec![1.5, 2.0, -3.0]); + let v1 = ColFVector::from_vec(vec![1.5, 2.0, -3.0]); let v2 = FlexVector::from_vec(vec![2.0, 0.5, 4.0]); let dot = v1.dot_to_f64(&v2).unwrap(); assert!((dot - (1.5 * 2.0 + 2.0 * 0.5 + -3.0 * 4.0)).abs() < 1e-12); @@ -6224,7 +6222,7 @@ mod tests { #[test] fn test_dot_to_f64_mismatched_lengths() { - let v1 = FVector::from_vec(vec![1, 2]); + let v1 = ColFVector::from_vec(vec![1, 2]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); let dot = v1.dot(&v2); assert!(dot.is_err()); @@ -6235,7 +6233,7 @@ mod tests { // -- cross -- #[test] fn test_cross_i32() { - let v1 = FVector::from_vec(vec![1, 2, 3]); + let v1 = ColFVector::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); let cross = v1.cross(&v2).unwrap(); // [2*6 - 3*5, 3*4 - 1*6, 1*5 - 2*4] = [12-15, 12-6, 5-8] = [-3, 6, -3] @@ -6244,7 +6242,7 @@ mod tests { #[test] fn test_cross_f64() { - let v1 = FVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 5.0, 6.0]); let cross = v1.cross(&v2).unwrap(); assert_eq!(cross.as_slice(), &[-3.0, 6.0, -3.0]); @@ -6256,7 +6254,7 @@ mod tests { #[test] fn test_cross_wrong_length_1() { - let v1 = FVector::from_vec(vec![1, 2]); + let v1 = ColFVector::from_vec(vec![1, 2]); let v2 = FlexVector::from_vec(vec![3, 4, 5]); let result = v1.cross(&v2); assert!(result.is_err()); @@ -6264,7 +6262,7 @@ mod tests { #[test] fn test_cross_wrong_length_2() { - let v1 = FVector::from_vec(vec![1, 2, 3]); + let v1 = ColFVector::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![3, 4]); let result = v1.cross(&v2); assert!(result.is_err()); @@ -6272,7 +6270,7 @@ mod tests { #[test] fn test_cross_wrong_length_3() { - let v1 = FVector::from_vec(vec![1, 2]); + let v1 = ColFVector::from_vec(vec![1, 2]); let v2 = FlexVector::from_vec(vec![3, 4]); let result = v1.cross(&v2); assert!(result.is_err()); @@ -6282,7 +6280,7 @@ mod tests { #[test] fn test_cross_into_i32() { - let v1 = FVector::from_vec(vec![1, 2, 3]); + let v1 = ColFVector::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); let mut out = [0; 3]; v1.cross_into(&v2, &mut out).unwrap(); @@ -6291,7 +6289,7 @@ mod tests { #[test] fn test_cross_into_f64() { - let v1 = FVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 5.0, 6.0]); let mut out = [0.0; 3]; v1.cross_into(&v2, &mut out).unwrap(); @@ -6300,19 +6298,19 @@ mod tests { #[test] fn test_cross_into_wrong_length() { - let v1 = FVector::from_vec(vec![1, 2]); + let v1 = ColFVector::from_vec(vec![1, 2]); let v2 = FlexVector::from_vec(vec![3, 4, 5]); let mut out = [0; 3]; let result = v1.cross_into(&v2, &mut out); assert!(result.is_err()); - let v1 = FVector::from_vec(vec![1, 2, 3]); + let v1 = ColFVector::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![3, 4]); let mut out = [0; 3]; let result = v1.cross_into(&v2, &mut out); assert!(result.is_err()); - let v1 = FVector::from_vec(vec![1, 2, 3]); + let v1 = ColFVector::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![3, 4, 5]); let mut out = [0; 2]; let result = v1.cross_into(&v2, &mut out); @@ -6322,14 +6320,14 @@ mod tests { // -- sum -- #[test] fn test_sum_i32() { - let v = FVector::from_vec(vec![1, 2, 3, 4]); + let v = ColFVector::from_vec(vec![1, 2, 3, 4]); let s = v.sum(); assert_eq!(s, 10); } #[test] fn test_sum_f64() { - let v = FVector::from_vec(vec![1.5, -2.5, 3.0]); + let v = ColFVector::from_vec(vec![1.5, -2.5, 3.0]); let s = v.sum(); assert!((s - 2.0).abs() < 1e-12); } @@ -6337,7 +6335,7 @@ mod tests { #[test] fn test_sum_complex_f64() { use num::Complex; - let v = FVector::from_vec(vec![ + let v = ColFVector::from_vec(vec![ Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0), Complex::new(5.0, -6.0), @@ -6349,14 +6347,14 @@ mod tests { // -- product -- #[test] fn test_product_i32() { - let v = FVector::from_vec(vec![2, 3, 4]); + let v = ColFVector::from_vec(vec![2, 3, 4]); let p = v.product(); assert_eq!(p, 24); } #[test] fn test_product_f64() { - let v = FVector::from_vec(vec![1.5, -2.0, 3.0]); + let v = ColFVector::from_vec(vec![1.5, -2.0, 3.0]); let p = v.product(); assert!((p - (1.5 * -2.0 * 3.0)).abs() < 1e-12); } @@ -6364,7 +6362,7 @@ mod tests { #[test] fn test_product_complex_f64() { use num::Complex; - let v = FVector::from_vec(vec![ + let v = ColFVector::from_vec(vec![ Complex::new(1.0, 2.0), Complex::new(3.0, -1.0), Complex::new(2.0, 0.5), @@ -6377,31 +6375,31 @@ mod tests { // -- minimum -- #[test] fn test_minimum_i32() { - let v = FVector::from_vec(vec![3, 1, 4, 2]); + let v = ColFVector::from_vec(vec![3, 1, 4, 2]); assert_eq!(v.minimum(), Some(1)); } #[test] fn test_minimum_f64_basic() { - let v = FVector::from_vec(vec![1.5, -2.0, 3.0]); + let v = ColFVector::from_vec(vec![1.5, -2.0, 3.0]); assert_eq!(v.minimum(), Some(-2.0)); } #[test] fn test_minimum_f64_all_positive() { - let v = FVector::from_vec(vec![2.0, 4.0, 1.0, 3.0]); + let v = ColFVector::from_vec(vec![2.0, 4.0, 1.0, 3.0]); assert_eq!(v.minimum(), Some(1.0)); } #[test] fn test_minimum_f64_all_negative() { - let v = FVector::from_vec(vec![-1.0, -2.0, -3.0]); + let v = ColFVector::from_vec(vec![-1.0, -2.0, -3.0]); assert_eq!(v.minimum(), Some(-3.0)); } #[test] fn test_minimum_f64_single_element() { - let v = FVector::from_vec(vec![42.0]); + let v = ColFVector::from_vec(vec![42.0]); assert_eq!(v.minimum(), Some(42.0)); } @@ -6413,7 +6411,7 @@ mod tests { #[test] fn test_minimum_f64_with_nan() { - let v = FVector::from_vec(vec![1.0, f64::NAN, 2.0]); + let v = ColFVector::from_vec(vec![1.0, f64::NAN, 2.0]); // The result is not guaranteed to be meaningful if NaN is present, // but it should return Some value (could be NaN or a number). assert!(v.minimum().is_some()); @@ -6429,7 +6427,7 @@ mod tests { #[test] fn test_elementwise_min_i32() { - let v1 = FVector::from_vec(vec![1, 5, 3, 7]); + let v1 = ColFVector::from_vec(vec![1, 5, 3, 7]); let v2 = FlexVector::from_vec(vec![2, 4, 6, 0]); let min = v1.elementwise_min(&v2).unwrap(); assert_eq!(min.as_slice(), &[1, 4, 3, 0]); @@ -6437,7 +6435,7 @@ mod tests { #[test] fn test_elementwise_min_f64() { - let v1 = FVector::from_vec(vec![1.5, -2.0, 3.0]); + let v1 = ColFVector::from_vec(vec![1.5, -2.0, 3.0]); let v2 = FlexVector::from_vec(vec![2.5, -3.0, 2.0]); let min = v1.elementwise_min(&v2).unwrap(); assert_eq!(min.as_slice(), &[1.5, -3.0, 2.0]); @@ -6453,7 +6451,7 @@ mod tests { #[test] fn test_elementwise_min_mismatched_length() { - let v1 = FVector::from_vec(vec![1, 2]); + let v1 = ColFVector::from_vec(vec![1, 2]); let v2 = FlexVector::from_vec(vec![3, 4, 5]); let result = v1.elementwise_min(&v2); assert!(result.is_err()); @@ -6461,7 +6459,7 @@ mod tests { #[test] fn test_elementwise_min_f64_with_nan() { - let v1 = FVector::from_vec(vec![1.0, f64::NAN, 3.0]); + let v1 = ColFVector::from_vec(vec![1.0, f64::NAN, 3.0]); let v2 = FlexVector::from_vec(vec![2.0, 2.0, f64::NAN]); let min = v1.elementwise_min(&v2).unwrap(); assert_eq!(min[0], 1.0); // min(1.0, 2.0) = 1.0 @@ -6471,7 +6469,7 @@ mod tests { #[test] fn test_elementwise_min_f64_both_nan() { - let v1 = FVector::from_vec(vec![f64::NAN]); + let v1 = ColFVector::from_vec(vec![f64::NAN]); let v2 = FlexVector::from_vec(vec![f64::NAN]); let min = v1.elementwise_min(&v2).unwrap(); assert!(min[0].is_nan()); @@ -6481,7 +6479,7 @@ mod tests { #[test] fn test_elementwise_min_into_i32() { - let v1 = FVector::from_vec(vec![1, 5, 3, 7]); + let v1 = ColFVector::from_vec(vec![1, 5, 3, 7]); let v2 = FlexVector::from_vec(vec![2, 4, 6, 0]); let mut out = [0; 4]; v1.elementwise_min_into(&v2, &mut out).unwrap(); @@ -6490,7 +6488,7 @@ mod tests { #[test] fn test_elementwise_min_into_f64() { - let v1 = FVector::from_vec(vec![1.5, -2.0, 3.0]); + let v1 = ColFVector::from_vec(vec![1.5, -2.0, 3.0]); let v2 = FlexVector::from_vec(vec![2.5, -3.0, 2.0]); let mut out = [0.0; 3]; v1.elementwise_min_into(&v2, &mut out).unwrap(); @@ -6499,13 +6497,13 @@ mod tests { #[test] fn test_elementwise_min_into_mismatched_length() { - let v1 = FVector::from_vec(vec![1, 2, 3]); + let v1 = ColFVector::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![4, 5]); let mut out = [0; 3]; let result = v1.elementwise_min_into(&v2, &mut out); assert!(result.is_err()); - let v1 = FVector::from_vec(vec![1, 2, 3]); + let v1 = ColFVector::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); let mut out = [0; 2]; let result = v1.elementwise_min_into(&v2, &mut out); @@ -6515,31 +6513,31 @@ mod tests { // -- maximum -- #[test] fn test_maximum_i32() { - let v = FVector::from_vec(vec![3, 1, 4, 2]); + let v = ColFVector::from_vec(vec![3, 1, 4, 2]); assert_eq!(v.maximum(), Some(4)); } #[test] fn test_maximum_f64_basic() { - let v = FVector::from_vec(vec![1.5, -2.0, 3.0]); + let v = ColFVector::from_vec(vec![1.5, -2.0, 3.0]); assert_eq!(v.maximum(), Some(3.0)); } #[test] fn test_maximum_f64_all_positive() { - let v = FVector::from_vec(vec![2.0, 4.0, 1.0, 3.0]); + let v = ColFVector::from_vec(vec![2.0, 4.0, 1.0, 3.0]); assert_eq!(v.maximum(), Some(4.0)); } #[test] fn test_maximum_f64_all_negative() { - let v = FVector::from_vec(vec![-1.0, -2.0, -3.0]); + let v = ColFVector::from_vec(vec![-1.0, -2.0, -3.0]); assert_eq!(v.maximum(), Some(-1.0)); } #[test] fn test_maximum_f64_single_element() { - let v = FVector::from_vec(vec![42.0]); + let v = ColFVector::from_vec(vec![42.0]); assert_eq!(v.maximum(), Some(42.0)); } @@ -6551,7 +6549,7 @@ mod tests { #[test] fn test_maximum_f64_with_nan() { - let v = FVector::from_vec(vec![1.0, f64::NAN, 2.0]); + let v = ColFVector::from_vec(vec![1.0, f64::NAN, 2.0]); // The result is not guaranteed to be meaningful if NaN is present, // but it should return Some value (could be NaN or a number). assert!(v.maximum().is_some()); @@ -6561,7 +6559,7 @@ mod tests { #[test] fn test_elementwise_max_i32() { - let v1 = FVector::from_vec(vec![1, 5, 3, 7]); + let v1 = ColFVector::from_vec(vec![1, 5, 3, 7]); let v2 = FlexVector::from_vec(vec![2, 4, 6, 0]); let max = v1.elementwise_max(&v2).unwrap(); assert_eq!(max.as_slice(), &[2, 5, 6, 7]); @@ -6569,7 +6567,7 @@ mod tests { #[test] fn test_elementwise_max_f64() { - let v1 = FVector::from_vec(vec![1.5, -2.0, 3.0]); + let v1 = ColFVector::from_vec(vec![1.5, -2.0, 3.0]); let v2 = FlexVector::from_vec(vec![2.5, -3.0, 2.0]); let max = v1.elementwise_max(&v2).unwrap(); assert_eq!(max.as_slice(), &[2.5, -2.0, 3.0]); @@ -6585,7 +6583,7 @@ mod tests { #[test] fn test_elementwise_max_mismatched_length() { - let v1 = FVector::from_vec(vec![1, 2, 3]); + let v1 = ColFVector::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![4, 5]); let result = v1.elementwise_max(&v2); assert!(result.is_err()); @@ -6593,7 +6591,7 @@ mod tests { #[test] fn test_elementwise_max_f64_with_nan() { - let v1 = FVector::from_vec(vec![1.0, f64::NAN, 3.0]); + let v1 = ColFVector::from_vec(vec![1.0, f64::NAN, 3.0]); let v2 = FlexVector::from_vec(vec![2.0, 2.0, f64::NAN]); let max = v1.elementwise_max(&v2).unwrap(); assert_eq!(max[0], 2.0); // max(1.0, 2.0) = 2.0 @@ -6603,7 +6601,7 @@ mod tests { #[test] fn test_elementwise_max_f64_both_nan() { - let v1 = FVector::from_vec(vec![f64::NAN]); + let v1 = ColFVector::from_vec(vec![f64::NAN]); let v2 = FlexVector::from_vec(vec![f64::NAN]); let max = v1.elementwise_max(&v2).unwrap(); assert!(max[0].is_nan()); @@ -6613,7 +6611,7 @@ mod tests { #[test] fn test_elementwise_max_into_i32() { - let v1 = FVector::from_vec(vec![1, 5, 3, 7]); + let v1 = ColFVector::from_vec(vec![1, 5, 3, 7]); let v2 = FlexVector::from_vec(vec![2, 4, 6, 0]); let mut out = [0; 4]; v1.elementwise_max_into(&v2, &mut out).unwrap(); @@ -6622,7 +6620,7 @@ mod tests { #[test] fn test_elementwise_max_into_f64() { - let v1 = FVector::from_vec(vec![1.5, -2.0, 3.0]); + let v1 = ColFVector::from_vec(vec![1.5, -2.0, 3.0]); let v2 = FlexVector::from_vec(vec![2.5, -3.0, 2.0]); let mut out = [0.0; 3]; v1.elementwise_max_into(&v2, &mut out).unwrap(); @@ -6631,13 +6629,13 @@ mod tests { #[test] fn test_elementwise_max_into_mismatched_length() { - let v1 = FVector::from_vec(vec![1, 2, 3]); + let v1 = ColFVector::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![4, 5]); let mut out = [0; 3]; let result = v1.elementwise_max_into(&v2, &mut out); assert!(result.is_err()); - let v1 = FVector::from_vec(vec![1, 2, 3]); + let v1 = ColFVector::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); let mut out = [0; 2]; let result = v1.elementwise_max_into(&v2, &mut out); @@ -6648,21 +6646,21 @@ mod tests { #[test] fn test_elementwise_clamp_i32_basic() { - let v = FVector::from_vec(vec![-5, 0, 5, 10, 15]); + let v = ColFVector::from_vec(vec![-5, 0, 5, 10, 15]); let clamped = v.elementwise_clamp(0, 10); assert_eq!(clamped.as_slice(), &[0, 0, 5, 10, 10]); } #[test] fn test_elementwise_clamp_i32_all_below() { - let v = FVector::from_vec(vec![-3, -2, -1]); + let v = ColFVector::from_vec(vec![-3, -2, -1]); let clamped = v.elementwise_clamp(0, 5); assert_eq!(clamped.as_slice(), &[0, 0, 0]); } #[test] fn test_elementwise_clamp_i32_all_above() { - let v = FVector::from_vec(vec![11, 12, 13]); + let v = ColFVector::from_vec(vec![11, 12, 13]); let clamped = v.elementwise_clamp(0, 10); assert_eq!(clamped.as_slice(), &[10, 10, 10]); } @@ -6676,28 +6674,28 @@ mod tests { #[test] fn test_elementwise_clamp_f64_basic() { - let v = FVector::from_vec(vec![-2.5, 0.0, 3.5, 7.2, 12.0]); + let v = ColFVector::from_vec(vec![-2.5, 0.0, 3.5, 7.2, 12.0]); let clamped = v.elementwise_clamp(0.0, 10.0); assert_eq!(clamped.as_slice(), &[0.0, 0.0, 3.5, 7.2, 10.0]); } #[test] fn test_elementwise_clamp_f64_all_below() { - let v = FVector::from_vec(vec![-1.1, -2.2]); + let v = ColFVector::from_vec(vec![-1.1, -2.2]); let clamped = v.elementwise_clamp(0.0, 5.0); assert_eq!(clamped.as_slice(), &[0.0, 0.0]); } #[test] fn test_elementwise_clamp_f64_all_above() { - let v = FVector::from_vec(vec![11.1, 12.2]); + let v = ColFVector::from_vec(vec![11.1, 12.2]); let clamped = v.elementwise_clamp(0.0, 10.0); assert_eq!(clamped.as_slice(), &[10.0, 10.0]); } #[test] fn test_elementwise_clamp_f64_with_nan() { - let v = FVector::from_vec(vec![1.0, f64::NAN, 5.0]); + let v = ColFVector::from_vec(vec![1.0, f64::NAN, 5.0]); let clamped = v.elementwise_clamp(0.0, 4.0); assert_eq!(clamped[0], 1.0); assert!(clamped[1].is_nan()); @@ -6715,7 +6713,7 @@ mod tests { #[test] fn test_elementwise_clamp_into_i32_basic() { - let v = FVector::from_vec(vec![-5, 0, 5, 10, 15]); + let v = ColFVector::from_vec(vec![-5, 0, 5, 10, 15]); let mut out = [0; 5]; v.elementwise_clamp_into(0, 10, &mut out).unwrap(); assert_eq!(out, [0, 0, 5, 10, 10]); @@ -6723,7 +6721,7 @@ mod tests { #[test] fn test_elementwise_clamp_into_i32_all_below() { - let v = FVector::from_vec(vec![-3, -2, -1]); + let v = ColFVector::from_vec(vec![-3, -2, -1]); let mut out = [0; 3]; v.elementwise_clamp_into(0, 5, &mut out).unwrap(); assert_eq!(out, [0, 0, 0]); @@ -6731,7 +6729,7 @@ mod tests { #[test] fn test_elementwise_clamp_into_i32_all_above() { - let v = FVector::from_vec(vec![11, 12, 13]); + let v = ColFVector::from_vec(vec![11, 12, 13]); let mut out = [0; 3]; v.elementwise_clamp_into(0, 10, &mut out).unwrap(); assert_eq!(out, [10, 10, 10]); @@ -6747,7 +6745,7 @@ mod tests { #[test] fn test_elementwise_clamp_into_f64_basic() { - let v = FVector::from_vec(vec![-2.5, 0.0, 3.5, 7.2, 12.0]); + let v = ColFVector::from_vec(vec![-2.5, 0.0, 3.5, 7.2, 12.0]); let mut out = [0.0; 5]; v.elementwise_clamp_into(0.0, 10.0, &mut out).unwrap(); assert_eq!(out, [0.0, 0.0, 3.5, 7.2, 10.0]); @@ -6755,7 +6753,7 @@ mod tests { #[test] fn test_elementwise_clamp_into_f64_all_below() { - let v = FVector::from_vec(vec![-1.1, -2.2]); + let v = ColFVector::from_vec(vec![-1.1, -2.2]); let mut out = [0.0; 2]; v.elementwise_clamp_into(0.0, 5.0, &mut out).unwrap(); assert_eq!(out, [0.0, 0.0]); @@ -6763,7 +6761,7 @@ mod tests { #[test] fn test_elementwise_clamp_into_f64_all_above() { - let v = FVector::from_vec(vec![11.1, 12.2]); + let v = ColFVector::from_vec(vec![11.1, 12.2]); let mut out = [0.0; 2]; v.elementwise_clamp_into(0.0, 10.0, &mut out).unwrap(); assert_eq!(out, [10.0, 10.0]); @@ -6771,7 +6769,7 @@ mod tests { #[test] fn test_elementwise_clamp_into_f64_with_nan() { - let v = FVector::from_vec(vec![1.0, f64::NAN, 5.0]); + let v = ColFVector::from_vec(vec![1.0, f64::NAN, 5.0]); let mut out = [0.0; 3]; v.elementwise_clamp_into(0.0, 4.0, &mut out).unwrap(); assert_eq!(out[0], 1.0); @@ -6790,14 +6788,14 @@ mod tests { // -- l1_norm -- #[test] fn test_l1_norm_i32() { - let v = FVector::from_vec(vec![1, -2, 3]); + let v = ColFVector::from_vec(vec![1, -2, 3]); let norm = v.l1_norm(); assert_eq!(norm, 6); } #[test] fn test_l1_norm_f64() { - let v = FVector::from_vec(vec![1.5, -2.5, 3.0]); + let v = ColFVector::from_vec(vec![1.5, -2.5, 3.0]); let norm = v.l1_norm(); assert!((norm - 7.0).abs() < 1e-12); } @@ -6807,14 +6805,14 @@ mod tests { // -- linf_norm -- #[test] fn test_linf_norm_i32() { - let v = FVector::from_vec(vec![1, -5, 3, 2]); + let v = ColFVector::from_vec(vec![1, -5, 3, 2]); let norm = v.linf_norm(); assert_eq!(norm, 5); } #[test] fn test_linf_norm_f64() { - let v = FVector::from_vec(vec![1.5, -2.5, 3.0, -7.2]); + let v = ColFVector::from_vec(vec![1.5, -2.5, 3.0, -7.2]); let norm = v.linf_norm(); assert!((norm - 7.2).abs() < 1e-12); } @@ -6828,7 +6826,7 @@ mod tests { // -- normalize -- #[test] fn test_normalize_f64() { - let v = FVector::from_vec(vec![3.0, 4.0]); + let v = ColFVector::from_vec(vec![3.0, 4.0]); let normalized = v.normalize().unwrap(); // The norm is 5.0, so the normalized vector should be [0.6, 0.8] assert!((normalized.as_slice()[0] - 0.6).abs() < 1e-12); @@ -6837,14 +6835,14 @@ mod tests { #[test] fn test_normalize_f64_zero_vector() { - let v = FVector::from_vec(vec![0.0, 0.0]); + let v = ColFVector::from_vec(vec![0.0, 0.0]); let result = v.normalize(); assert!(result.is_err()); } #[test] fn test_normalize_f64_negative_values() { - let v = FVector::from_vec(vec![-3.0, -4.0]); + let v = ColFVector::from_vec(vec![-3.0, -4.0]); let normalized = v.normalize().unwrap(); // The norm is 5.0, so the normalized vector should be [-0.6, -0.8] assert!((normalized.as_slice()[0] + 0.6).abs() < 1e-12); @@ -6865,7 +6863,7 @@ mod tests { #[test] fn test_normalize_into_f64_zero_vector() { - let v = FVector::from_vec(vec![0.0, 0.0]); + let v = ColFVector::from_vec(vec![0.0, 0.0]); let mut out = [0.0; 2]; let result = v.normalize_into(&mut out); assert!(result.is_err()); @@ -6873,7 +6871,7 @@ mod tests { #[test] fn test_normalize_into_f64_negative_values() { - let v = FVector::from_vec(vec![-3.0, -4.0]); + let v = ColFVector::from_vec(vec![-3.0, -4.0]); let mut out = [0.0; 2]; let _ = v.normalize_into(&mut out).unwrap(); // The norm is 5.0, so the normalized vector should be [-0.6, -0.8] @@ -6884,7 +6882,7 @@ mod tests { // -- mut_normalize -- #[test] fn test_mut_normalize_f64() { - let mut v = FVector::from_vec(vec![3.0, 4.0]); + let mut v = ColFVector::from_vec(vec![3.0, 4.0]); v.mut_normalize().unwrap(); // The norm is 5.0, so the normalized vector should be [0.6, 0.8] assert!((v.as_slice()[0] - 0.6).abs() < 1e-12); @@ -6893,14 +6891,14 @@ mod tests { #[test] fn test_mut_normalize_f64_zero_vector() { - let mut v = FVector::from_vec(vec![0.0, 0.0]); + let mut v = ColFVector::from_vec(vec![0.0, 0.0]); let result = v.mut_normalize(); assert!(result.is_err()); } #[test] fn test_mut_normalize_f64_negative_values() { - let mut v = FVector::from_vec(vec![-3.0, -4.0]); + let mut v = ColFVector::from_vec(vec![-3.0, -4.0]); v.mut_normalize().unwrap(); // The norm is 5.0, so the normalized vector should be [-0.6, -0.8] assert!((v.as_slice()[0] + 0.6).abs() < 1e-12); @@ -6910,7 +6908,7 @@ mod tests { // -- normalize_to -- #[test] fn test_normalize_to_f64() { - let v = FVector::from_vec(vec![3.0, 4.0]); + let v = ColFVector::from_vec(vec![3.0, 4.0]); let normalized = v.normalize_to(10.0).unwrap(); // The original norm is 5.0, so the normalized vector should be [6.0, 8.0] assert!((normalized.as_slice()[0] - 6.0).abs() < 1e-12); @@ -6919,14 +6917,14 @@ mod tests { #[test] fn test_normalize_to_f64_zero_vector() { - let v = FVector::from_vec(vec![0.0, 0.0]); + let v = ColFVector::from_vec(vec![0.0, 0.0]); let result = v.normalize_to(1.0); assert!(result.is_err()); } #[test] fn test_normalize_to_f64_negative_values() { - let v = FVector::from_vec(vec![-3.0, -4.0]); + let v = ColFVector::from_vec(vec![-3.0, -4.0]); let normalized = v.normalize_to(5.0).unwrap(); // The original norm is 5.0, so the normalized vector should be [-3.0, -4.0] assert!((normalized.as_slice()[0] + 3.0).abs() < 1e-12); @@ -6972,7 +6970,7 @@ mod tests { // -- mut_normalize_to -- #[test] fn test_mut_normalize_to_f64() { - let mut v = FVector::from_vec(vec![3.0, 4.0]); + let mut v = ColFVector::from_vec(vec![3.0, 4.0]); v.mut_normalize_to(10.0).unwrap(); // The original norm is 5.0, so the normalized vector should be [6.0, 8.0] assert!((v.as_slice()[0] - 6.0).abs() < 1e-12); @@ -6981,14 +6979,14 @@ mod tests { #[test] fn test_mut_normalize_to_f64_zero_vector() { - let mut v = FVector::from_vec(vec![0.0, 0.0]); + let mut v = ColFVector::from_vec(vec![0.0, 0.0]); let result = v.mut_normalize_to(1.0); assert!(result.is_err()); } #[test] fn test_mut_normalize_to_f64_negative_values() { - let mut v = FVector::from_vec(vec![-3.0, -4.0]); + let mut v = ColFVector::from_vec(vec![-3.0, -4.0]); v.mut_normalize_to(5.0).unwrap(); // The original norm is 5.0, so the normalized vector should be [-3.0, -4.0] assert!((v.as_slice()[0] + 3.0).abs() < 1e-12); @@ -6998,7 +6996,7 @@ mod tests { // -- lerp -- #[test] fn test_lerp_f64_weight_zero() { - let v1 = FVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 5.0, 6.0]); let result = v1.lerp(&v2, 0.0).unwrap(); // Should be equal to v1 @@ -7009,7 +7007,7 @@ mod tests { #[test] fn test_lerp_f64_weight_one() { - let v1 = FVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 5.0, 6.0]); let result = v1.lerp(&v2, 1.0).unwrap(); // Should be equal to v2 @@ -7020,7 +7018,7 @@ mod tests { #[test] fn test_lerp_f64_weight_half() { - let v1 = FVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 5.0, 6.0]); let result = v1.lerp(&v2, 0.5).unwrap(); // Should be the midpoint @@ -7031,7 +7029,7 @@ mod tests { #[test] fn test_lerp_f64_weight_out_of_bounds() { - let v1 = FVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 5.0, 6.0]); let result_low = v1.lerp(&v2, -0.1); let result_high = v1.lerp(&v2, 1.1); @@ -7122,7 +7120,7 @@ mod tests { // -- mut_lerp -- #[test] fn test_mut_lerp_f64_weight_zero() { - let mut v1 = FVector::from_vec(vec![1.0, 2.0, 3.0]); + let mut v1 = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 5.0, 6.0]); v1.mut_lerp(&v2, 0.0).unwrap(); // Should be equal to original v1 @@ -7133,7 +7131,7 @@ mod tests { #[test] fn test_mut_lerp_f64_weight_one() { - let mut v1 = FVector::from_vec(vec![1.0, 2.0, 3.0]); + let mut v1 = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 5.0, 6.0]); v1.mut_lerp(&v2, 1.0).unwrap(); // Should be equal to v2 @@ -7144,7 +7142,7 @@ mod tests { #[test] fn test_mut_lerp_f64_weight_half() { - let mut v1 = FVector::from_vec(vec![1.0, 2.0, 3.0]); + let mut v1 = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 5.0, 6.0]); v1.mut_lerp(&v2, 0.5).unwrap(); // Should be the midpoint @@ -7155,7 +7153,7 @@ mod tests { #[test] fn test_mut_lerp_f64_weight_out_of_bounds() { - let mut v1 = FVector::from_vec(vec![1.0, 2.0, 3.0]); + let mut v1 = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 5.0, 6.0]); let result_low = v1.mut_lerp(&v2, -0.1); let result_high = v1.mut_lerp(&v2, 1.1); @@ -7166,7 +7164,7 @@ mod tests { // -- midpoint -- #[test] fn test_midpoint_f64() { - let v1 = FVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 5.0, 6.0]); let midpoint = v1.midpoint(&v2).unwrap(); // Should be the average of each element @@ -7177,7 +7175,7 @@ mod tests { #[test] fn test_midpoint_f64_negative_values() { - let v1 = FVector::from_vec(vec![-1.0, -2.0, -3.0]); + let v1 = ColFVector::from_vec(vec![-1.0, -2.0, -3.0]); let v2 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); let midpoint = v1.midpoint(&v2).unwrap(); // Should be [0.0, 0.0, 0.0] @@ -7188,7 +7186,7 @@ mod tests { #[test] fn test_midpoint_f64_mismatched_length() { - let v1 = FVector::from_vec(vec![1.0, 2.0]); + let v1 = ColFVector::from_vec(vec![1.0, 2.0]); let v2 = FlexVector::from_vec(vec![3.0, 4.0, 5.0]); let result = v1.midpoint(&v2); assert!(result.is_err()); @@ -7248,7 +7246,7 @@ mod tests { // -- distance -- #[test] fn test_distance_f64_basic() { - let v1 = FVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 6.0, 8.0]); let dist = v1.distance(&v2).unwrap(); // sqrt((1-4)^2 + (2-6)^2 + (3-8)^2) = sqrt(9 + 16 + 25) = sqrt(50) @@ -7257,7 +7255,7 @@ mod tests { #[test] fn test_distance_f64_zero() { - let v1 = FVector::from_vec(vec![0.0, 0.0, 0.0]); + let v1 = ColFVector::from_vec(vec![0.0, 0.0, 0.0]); let v2 = FlexVector::from_vec(vec![0.0, 0.0, 0.0]); let dist = v1.distance(&v2).unwrap(); assert_eq!(dist, 0.0); @@ -7265,7 +7263,7 @@ mod tests { #[test] fn test_distance_f64_negative_values() { - let v1 = FVector::from_vec(vec![-1.0, -2.0, -3.0]); + let v1 = ColFVector::from_vec(vec![-1.0, -2.0, -3.0]); let v2 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); let dist = v1.distance(&v2).unwrap(); // sqrt(((-1)-1)^2 + ((-2)-2)^2 + ((-3)-3)^2) = sqrt(4 + 16 + 36) = sqrt(56) @@ -7274,7 +7272,7 @@ mod tests { #[test] fn test_distance_f64_mismatched_length() { - let v1 = FVector::from_vec(vec![1.0, 2.0]); + let v1 = ColFVector::from_vec(vec![1.0, 2.0]); let v2 = FlexVector::from_vec(vec![3.0, 4.0, 5.0]); let result = v1.distance(&v2); assert!(result.is_err()); @@ -7283,7 +7281,7 @@ mod tests { // -- manhattan_distance -- #[test] fn test_manhattan_distance_f64_basic() { - let v1 = FVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 6.0, 8.0]); let dist = v1.manhattan_distance(&v2).unwrap(); // |1-4| + |2-6| + |3-8| = 3 + 4 + 5 = 12 @@ -7292,7 +7290,7 @@ mod tests { #[test] fn test_manhattan_distance_f64_zero() { - let v1 = FVector::from_vec(vec![0.0, 0.0, 0.0]); + let v1 = ColFVector::from_vec(vec![0.0, 0.0, 0.0]); let v2 = FlexVector::from_vec(vec![0.0, 0.0, 0.0]); let dist = v1.manhattan_distance(&v2).unwrap(); assert_eq!(dist, 0.0); @@ -7300,7 +7298,7 @@ mod tests { #[test] fn test_manhattan_distance_f64_negative_values() { - let v1 = FVector::from_vec(vec![-1.0, -2.0, -3.0]); + let v1 = ColFVector::from_vec(vec![-1.0, -2.0, -3.0]); let v2 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); let dist = v1.manhattan_distance(&v2).unwrap(); // |(-1)-1| + |(-2)-2| + |(-3)-3| = 2 + 4 + 6 = 12 @@ -7309,7 +7307,7 @@ mod tests { #[test] fn test_manhattan_distance_f64_mismatched_length() { - let v1 = FVector::from_vec(vec![1.0, 2.0]); + let v1 = ColFVector::from_vec(vec![1.0, 2.0]); let v2 = FlexVector::from_vec(vec![3.0, 4.0, 5.0]); let result = v1.manhattan_distance(&v2); assert!(result.is_err()); @@ -7318,7 +7316,7 @@ mod tests { // -- chebyshev_distance -- #[test] fn test_chebyshev_distance_f64_basic() { - let v1 = FVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 6.0, 8.0]); let dist = v1.chebyshev_distance(&v2).unwrap(); // max(|1-4|, |2-6|, |3-8|) = max(3, 4, 5) = 5 @@ -7327,7 +7325,7 @@ mod tests { #[test] fn test_chebyshev_distance_f64_zero() { - let v1 = FVector::from_vec(vec![0.0, 0.0, 0.0]); + let v1 = ColFVector::from_vec(vec![0.0, 0.0, 0.0]); let v2 = FlexVector::from_vec(vec![0.0, 0.0, 0.0]); let dist = v1.chebyshev_distance(&v2).unwrap(); assert_eq!(dist, 0.0); @@ -7335,7 +7333,7 @@ mod tests { #[test] fn test_chebyshev_distance_f64_negative_values() { - let v1 = FVector::from_vec(vec![-1.0, -2.0, -3.0]); + let v1 = ColFVector::from_vec(vec![-1.0, -2.0, -3.0]); let v2 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); let dist = v1.chebyshev_distance(&v2).unwrap(); // max(|-1-1|, |-2-2|, |-3-3|) = max(2, 4, 6) = 6 @@ -7344,7 +7342,7 @@ mod tests { #[test] fn test_chebyshev_distance_f64_mismatched_length() { - let v1 = FVector::from_vec(vec![1.0, 2.0]); + let v1 = ColFVector::from_vec(vec![1.0, 2.0]); let v2 = FlexVector::from_vec(vec![3.0, 4.0, 5.0]); let result = v1.chebyshev_distance(&v2); assert!(result.is_err()); @@ -7353,7 +7351,7 @@ mod tests { // -- minkowski_distance -- #[test] fn test_minkowski_distance_f64_basic() { - let v1 = FVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 6.0, 8.0]); let dist = v1.minkowski_distance(&v2, 3.0).unwrap(); // ((|1-4|^3 + |2-6|^3 + |3-8|^3))^(1/3) = (27 + 64 + 125)^(1/3) = (216)^(1/3) = 6 @@ -7362,7 +7360,7 @@ mod tests { #[test] fn test_minkowski_distance_f64_p1() { - let v1 = FVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 6.0, 8.0]); let dist = v1.minkowski_distance(&v2, 1.0).unwrap(); // Should match manhattan distance: 3 + 4 + 5 = 12 @@ -7371,7 +7369,7 @@ mod tests { #[test] fn test_minkowski_distance_f64_p2() { - let v1 = FVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 6.0, 8.0]); let dist = v1.minkowski_distance(&v2, 2.0).unwrap(); // Should match euclidean distance: sqrt(9 + 16 + 25) = sqrt(50) @@ -7388,7 +7386,7 @@ mod tests { #[test] fn test_minkowski_distance_f64_identical() { - let v1 = FVector::from_vec(vec![1.23, 4.56, 7.89]); + let v1 = ColFVector::from_vec(vec![1.23, 4.56, 7.89]); let v2 = FlexVector::from_vec(vec![1.23, 4.56, 7.89]); let dist = v1.minkowski_distance(&v2, 2.0).unwrap(); assert_eq!(dist, 0.0); @@ -7396,7 +7394,7 @@ mod tests { #[test] fn test_minkowski_distance_f64_partial() { - let v1 = FVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 6.0]); let result = v1.minkowski_distance(&v2, 2.0); assert!(result.is_err()); @@ -7404,7 +7402,7 @@ mod tests { #[test] fn test_minkowski_distance_f64_invalid_p() { - let v1 = FVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 6.0, 8.0]); let result = v1.minkowski_distance(&v2, 0.5); assert!(result.is_err()); @@ -7413,7 +7411,7 @@ mod tests { // -- norm -- #[test] fn test_norm_f64_basic() { - let v = FVector::from_vec(vec![3.0, 4.0]); + let v = ColFVector::from_vec(vec![3.0, 4.0]); let norm = v.norm(); // sqrt(3^2 + 4^2) = 5 assert!((norm - 5.0).abs() < 1e-12); @@ -7421,21 +7419,21 @@ mod tests { #[test] fn test_norm_f64_zero() { - let v = FVector::from_vec(vec![0.0, 0.0, 0.0]); + let v = ColFVector::from_vec(vec![0.0, 0.0, 0.0]); let norm = v.norm(); assert_eq!(norm, 0.0); } #[test] fn test_norm_f64_single_element() { - let v = FVector::from_vec(vec![7.0]); + let v = ColFVector::from_vec(vec![7.0]); let norm = v.norm(); assert_eq!(norm, 7.0); } #[test] fn test_norm_f64_negative_values() { - let v = FVector::from_vec(vec![-3.0, -4.0]); + let v = ColFVector::from_vec(vec![-3.0, -4.0]); let norm = v.norm(); // sqrt((-3)^2 + (-4)^2) = 5 assert!((norm - 5.0).abs() < 1e-12); @@ -7444,7 +7442,7 @@ mod tests { // -- magnitude -- #[test] fn test_magnitude_f64_basic() { - let v = FVector::from_vec(vec![3.0, 4.0]); + let v = ColFVector::from_vec(vec![3.0, 4.0]); let mag = v.magnitude(); // sqrt(3^2 + 4^2) = 5 assert!((mag - 5.0).abs() < 1e-12); @@ -7452,21 +7450,21 @@ mod tests { #[test] fn test_magnitude_f64_zero() { - let v = FVector::from_vec(vec![0.0, 0.0, 0.0]); + let v = ColFVector::from_vec(vec![0.0, 0.0, 0.0]); let mag = v.magnitude(); assert_eq!(mag, 0.0); } #[test] fn test_magnitude_f64_single_element() { - let v = FVector::from_vec(vec![7.0]); + let v = ColFVector::from_vec(vec![7.0]); let mag = v.magnitude(); assert_eq!(mag, 7.0); } #[test] fn test_magnitude_f64_negative_values() { - let v = FVector::from_vec(vec![-3.0, -4.0]); + let v = ColFVector::from_vec(vec![-3.0, -4.0]); let mag = v.magnitude(); // sqrt((-3)^2 + (-4)^2) = 5 assert!((mag - 5.0).abs() < 1e-12); @@ -7475,7 +7473,7 @@ mod tests { // -- lp_norm -- #[test] fn test_lp_norm_f64_p1() { - let v = FVector::from_vec(vec![1.0, -2.0, 3.0]); + let v = ColFVector::from_vec(vec![1.0, -2.0, 3.0]); let norm = v.lp_norm(1.0).unwrap(); // L1 norm: |1| + |−2| + |3| = 1 + 2 + 3 = 6 assert!((norm - 6.0).abs() < 1e-12); @@ -7483,7 +7481,7 @@ mod tests { #[test] fn test_lp_norm_f64_p2() { - let v = FVector::from_vec(vec![3.0, 4.0]); + let v = ColFVector::from_vec(vec![3.0, 4.0]); let norm = v.lp_norm(2.0).unwrap(); // L2 norm: sqrt(3^2 + 4^2) = 5 assert!((norm - 5.0).abs() < 1e-12); @@ -7491,7 +7489,7 @@ mod tests { #[test] fn test_lp_norm_f64_p3() { - let v = FVector::from_vec(vec![1.0, 2.0, 3.0]); + let v = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); let norm = v.lp_norm(3.0).unwrap(); // (|1|^3 + |2|^3 + |3|^3)^(1/3) = (1 + 8 + 27)^(1/3) = 36^(1/3) assert!((norm - 36f64.powf(1.0 / 3.0)).abs() < 1e-12); @@ -7499,14 +7497,14 @@ mod tests { #[test] fn test_lp_norm_f64_zero() { - let v = FVector::from_vec(vec![0.0, 0.0, 0.0]); + let v = ColFVector::from_vec(vec![0.0, 0.0, 0.0]); let norm = v.lp_norm(2.0).unwrap(); assert_eq!(norm, 0.0); } #[test] fn test_lp_norm_f64_invalid_p() { - let v = FVector::from_vec(vec![1.0, 2.0, 3.0]); + let v = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); let result = v.lp_norm(0.5); assert!(result.is_err()); } @@ -7514,7 +7512,7 @@ mod tests { // -- angle_with -- #[test] fn test_angle_with_f64_orthogonal() { - let v1 = FVector::from_vec(vec![1.0, 0.0]); + let v1 = ColFVector::from_vec(vec![1.0, 0.0]); let v2 = FlexVector::from_vec(vec![0.0, 1.0]); let angle = v1.angle_with(&v2).unwrap(); // Orthogonal vectors: angle should be pi/2 @@ -7523,7 +7521,7 @@ mod tests { #[test] fn test_angle_with_f64_parallel() { - let v1 = FVector::from_vec(vec![1.0, 2.0]); + let v1 = ColFVector::from_vec(vec![1.0, 2.0]); let v2 = FlexVector::from_vec(vec![2.0, 4.0]); let angle = v1.angle_with(&v2).unwrap(); // Parallel vectors: angle should be 0 (allow for floating-point error) @@ -7532,7 +7530,7 @@ mod tests { #[test] fn test_angle_with_f64_opposite() { - let v1 = FVector::from_vec(vec![1.0, 0.0]); + let v1 = ColFVector::from_vec(vec![1.0, 0.0]); let v2 = FlexVector::from_vec(vec![-1.0, 0.0]); let angle = v1.angle_with(&v2).unwrap(); // Opposite vectors: angle should be pi @@ -7541,7 +7539,7 @@ mod tests { #[test] fn test_angle_with_f64_identical() { - let v1 = FVector::from_vec(vec![3.0, 4.0]); + let v1 = ColFVector::from_vec(vec![3.0, 4.0]); let v2 = FlexVector::from_vec(vec![3.0, 4.0]); let angle = v1.angle_with(&v2).unwrap(); // Identical vectors: angle should be 0 @@ -7550,7 +7548,7 @@ mod tests { #[test] fn test_angle_with_f64_arbitrary() { - let v1 = FVector::from_vec(vec![1.0, 2.0]); + let v1 = ColFVector::from_vec(vec![1.0, 2.0]); let v2 = FlexVector::from_vec(vec![2.0, 1.0]); let angle = v1.angle_with(&v2).unwrap(); // Check that the angle is between 0 and pi @@ -7559,7 +7557,7 @@ mod tests { #[test] fn test_angle_with_f64_zero_vector() { - let v1 = FVector::from_vec(vec![0.0, 0.0]); + let v1 = ColFVector::from_vec(vec![0.0, 0.0]); let v2 = FlexVector::from_vec(vec![1.0, 2.0]); let result = v1.angle_with(&v2); assert!(result.is_err()); @@ -7567,7 +7565,7 @@ mod tests { #[test] fn test_angle_with_f64_mismatched_length() { - let v1 = FVector::from_vec(vec![1.0, 2.0]); + let v1 = ColFVector::from_vec(vec![1.0, 2.0]); let v2 = FlexVector::from_vec(vec![3.0, 4.0, 5.0]); let result = v1.angle_with(&v2); assert!(result.is_err()); @@ -7576,7 +7574,7 @@ mod tests { // -- project_onto -- #[test] fn test_project_onto_f64_basic() { - let v1 = FVector::from_vec(vec![3.0, 4.0]); + let v1 = ColFVector::from_vec(vec![3.0, 4.0]); let v2 = FlexVector::from_vec(vec![1.0, 0.0]); let proj = v1.project_onto(&v2).unwrap(); // Projection of [3,4] onto [1,0] is [3,0] @@ -7586,7 +7584,7 @@ mod tests { #[test] fn test_project_onto_f64_parallel() { - let v1 = FVector::from_vec(vec![2.0, 4.0]); + let v1 = ColFVector::from_vec(vec![2.0, 4.0]); let v2 = FlexVector::from_vec(vec![1.0, 2.0]); let proj = v1.project_onto(&v2).unwrap(); // v1 is parallel to v2, so projection should be v1 itself @@ -7596,7 +7594,7 @@ mod tests { #[test] fn test_project_onto_f64_orthogonal() { - let v1 = FVector::from_vec(vec![0.0, 1.0]); + let v1 = ColFVector::from_vec(vec![0.0, 1.0]); let v2 = FlexVector::from_vec(vec![1.0, 0.0]); let proj = v1.project_onto(&v2).unwrap(); // Orthogonal vectors: projection should be [0,0] @@ -7606,7 +7604,7 @@ mod tests { #[test] fn test_project_onto_f64_identical() { - let v1 = FVector::from_vec(vec![5.0, 5.0]); + let v1 = ColFVector::from_vec(vec![5.0, 5.0]); let v2 = FlexVector::from_vec(vec![5.0, 5.0]); let proj = v1.project_onto(&v2).unwrap(); // Should be v1 itself @@ -7616,7 +7614,7 @@ mod tests { #[test] fn test_project_onto_f64_zero_vector() { - let v1 = FVector::from_vec(vec![1.0, 2.0]); + let v1 = ColFVector::from_vec(vec![1.0, 2.0]); let v2 = FlexVector::from_vec(vec![0.0, 0.0]); let result = v1.project_onto(&v2); assert!(result.is_err()); @@ -7624,7 +7622,7 @@ mod tests { #[test] fn test_project_onto_f64_mismatched_length() { - let v1 = FVector::from_vec(vec![1.0, 2.0]); + let v1 = ColFVector::from_vec(vec![1.0, 2.0]); let v2 = FlexVector::from_vec(vec![3.0, 4.0, 5.0]); let result = v1.project_onto(&v2); assert!(result.is_err()); @@ -7711,7 +7709,7 @@ mod tests { // --- cosine_similarity --- #[test] fn test_cosine_similarity_f64_parallel() { - let v1 = FVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![2.0, 4.0, 6.0]); let cos_sim = v1.cosine_similarity(&v2).unwrap(); assert!((cos_sim - 1.0).abs() < 1e-10); @@ -7719,7 +7717,7 @@ mod tests { #[test] fn test_cosine_similarity_f64_orthogonal() { - let v1 = FVector::from_vec(vec![1.0, 0.0]); + let v1 = ColFVector::from_vec(vec![1.0, 0.0]); let v2 = FlexVector::from_vec(vec![0.0, 1.0]); let cos_sim = v1.cosine_similarity(&v2).unwrap(); assert!((cos_sim - 0.0).abs() < 1e-10); @@ -7727,7 +7725,7 @@ mod tests { #[test] fn test_cosine_similarity_f64_opposite() { - let v1 = FVector::from_vec(vec![1.0, 0.0]); + let v1 = ColFVector::from_vec(vec![1.0, 0.0]); let v2 = FlexVector::from_vec(vec![-1.0, 0.0]); let cos_sim = v1.cosine_similarity(&v2).unwrap(); assert!((cos_sim + 1.0).abs() < 1e-10); @@ -7735,7 +7733,7 @@ mod tests { #[test] fn test_cosine_similarity_f64_zero_vector() { - let v1 = FVector::from_vec(vec![0.0, 0.0]); + let v1 = ColFVector::from_vec(vec![0.0, 0.0]); let v2 = FlexVector::from_vec(vec![1.0, 2.0]); let cos_sim = v1.cosine_similarity(&v2); assert!(cos_sim.is_err()); @@ -7750,7 +7748,7 @@ mod tests { // -- normalize -- #[test] fn test_normalize_complex_f64_basic() { - let v = FVector::from_vec(vec![Complex::new(3.0, 4.0)]); + let v = ColFVector::from_vec(vec![Complex::new(3.0, 4.0)]); let normalized = v.normalize().unwrap(); // The norm is 5.0, so the normalized vector should be [0.6 + 0.8i] assert!((normalized.as_slice()[0].re - 0.6).abs() < 1e-12); @@ -7759,7 +7757,7 @@ mod tests { #[test] fn test_normalize_complex_f64_multiple_elements() { - let v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let norm = ((1.0 * 1.0 + 2.0 * 2.0) + (3.0 * 3.0 + 4.0 * 4.0)).sqrt(); let normalized = v.normalize().unwrap(); assert!((normalized.as_slice()[0].re - 1.0 / norm).abs() < 1e-12); @@ -7770,7 +7768,7 @@ mod tests { #[test] fn test_normalize_complex_f64_zero_vector() { - let v = FVector::from_vec(vec![Complex::new(0.0, 0.0)]); + let v = ColFVector::from_vec(vec![Complex::new(0.0, 0.0)]); let result = v.normalize(); assert!(result.is_err()); } @@ -8908,28 +8906,28 @@ mod tests { #[test] fn test_neg() { - let v = FVector::from_vec(vec![1, -2, 3]); + let v = ColFVector::from_vec(vec![1, -2, 3]); let neg_v = -v; assert_eq!(neg_v.as_slice(), &[-1, 2, -3]); } #[test] fn test_neg_f64() { - let v = FVector::from_vec(vec![1.5, -2.5, 0.0]); + let v = ColFVector::from_vec(vec![1.5, -2.5, 0.0]); let neg_v = -v; assert_eq!(neg_v.as_slice(), &[-1.5, 2.5, -0.0]); } #[test] fn test_neg_complex() { - let v = FVector::from_vec(vec![Complex::new(1.0, -2.0), Complex::new(-3.0, 4.0)]); + let v = ColFVector::from_vec(vec![Complex::new(1.0, -2.0), Complex::new(-3.0, 4.0)]); let neg_v = -v; assert_eq!(neg_v.as_slice(), &[Complex::new(-1.0, 2.0), Complex::new(3.0, -4.0)]); } #[test] fn test_neg_nan() { - let v = FVector::from_vec(vec![f64::NAN, -f64::NAN]); + let v = ColFVector::from_vec(vec![f64::NAN, -f64::NAN]); let neg_v = -v; // Negating NaN is still NaN, but sign bit may flip assert!(neg_v.as_slice()[0].is_nan()); @@ -8941,14 +8939,14 @@ mod tests { #[test] fn test_neg_infinity() { - let v = FVector::from_vec(vec![f64::INFINITY, f64::NEG_INFINITY]); + let v = ColFVector::from_vec(vec![f64::INFINITY, f64::NEG_INFINITY]); let neg_v = -v; assert_eq!(neg_v.as_slice(), &[-f64::INFINITY, f64::INFINITY]); } #[test] fn test_add() { - let v1 = FVector::from_vec(vec![1, 2, 3]); + let v1 = ColFVector::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); let sum = v1 + v2; assert_eq!(sum.as_slice(), &[5, 7, 9]); @@ -8956,7 +8954,7 @@ mod tests { #[test] fn test_add_f64() { - let v1 = FVector::from_vec(vec![1.0, 2.5]); + let v1 = ColFVector::from_vec(vec![1.0, 2.5]); let v2 = FlexVector::from_vec(vec![3.0, 4.5]); let sum = v1 + v2; assert_eq!(sum.as_slice(), &[4.0, 7.0]); @@ -8964,7 +8962,7 @@ mod tests { #[test] fn test_add_complex() { - let v1 = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v1 = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); let sum = v1 + v2; assert_eq!(sum.as_slice(), &[Complex::new(6.0, 8.0), Complex::new(10.0, 12.0)]); @@ -8972,7 +8970,7 @@ mod tests { #[test] fn test_add_nan_infinity() { - let v1 = FVector::from_vec(vec![f64::NAN, f64::INFINITY, 1.0]); + let v1 = ColFVector::from_vec(vec![f64::NAN, f64::INFINITY, 1.0]); let v2 = FlexVector::from_vec(vec![1.0, 2.0, f64::INFINITY]); let sum = v1 + v2; assert!(sum.as_slice()[0].is_nan()); @@ -8983,14 +8981,14 @@ mod tests { #[test] #[should_panic(expected = "Vector length mismatch")] fn test_add_panic_on_mismatched_length() { - let v1 = FVector::from_vec(vec![1, 2]); + let v1 = ColFVector::from_vec(vec![1, 2]); let v2 = FlexVector::from_vec(vec![1, 2, 3]); let _ = v1 + v2; } #[test] fn test_sub() { - let v1 = FVector::from_vec(vec![10, 20, 30]); + let v1 = ColFVector::from_vec(vec![10, 20, 30]); let v2 = FlexVector::from_vec(vec![1, 2, 3]); let diff = v1 - v2; assert_eq!(diff.as_slice(), &[9, 18, 27]); @@ -8998,7 +8996,7 @@ mod tests { #[test] fn test_sub_f64() { - let v1 = FVector::from_vec(vec![5.5, 2.0]); + let v1 = ColFVector::from_vec(vec![5.5, 2.0]); let v2 = FlexVector::from_vec(vec![1.5, 1.0]); let diff = v1 - v2; assert_eq!(diff.as_slice(), &[4.0, 1.0]); @@ -9006,7 +9004,7 @@ mod tests { #[test] fn test_sub_complex() { - let v1 = FVector::from_vec(vec![Complex::new(5.0, 7.0), Complex::new(3.0, 4.0)]); + let v1 = ColFVector::from_vec(vec![Complex::new(5.0, 7.0), Complex::new(3.0, 4.0)]); let v2 = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(2.0, 1.0)]); let diff = v1 - v2; assert_eq!(diff.as_slice(), &[Complex::new(4.0, 5.0), Complex::new(1.0, 3.0)]); @@ -9014,7 +9012,7 @@ mod tests { #[test] fn test_sub_nan_infinity() { - let v1 = FVector::from_vec(vec![f64::NAN, f64::INFINITY, 5.0]); + let v1 = ColFVector::from_vec(vec![f64::NAN, f64::INFINITY, 5.0]); let v2 = FlexVector::from_vec(vec![2.0, f64::INFINITY, f64::NAN]); let diff = v1 - v2; assert!(diff.as_slice()[0].is_nan()); @@ -9025,14 +9023,14 @@ mod tests { #[test] #[should_panic(expected = "Vector length mismatch")] fn test_sub_panic_on_mismatched_length() { - let v1 = FVector::from_vec(vec![1, 2]); + let v1 = ColFVector::from_vec(vec![1, 2]); let v2 = FlexVector::from_vec(vec![1, 2, 3]); let _ = v1 - v2; } #[test] fn test_mul() { - let v1 = FVector::from_vec(vec![2, 3, 4]); + let v1 = ColFVector::from_vec(vec![2, 3, 4]); let v2 = FlexVector::from_vec(vec![5, 6, 7]); let prod = v1 * v2; assert_eq!(prod.as_slice(), &[10, 18, 28]); @@ -9040,7 +9038,7 @@ mod tests { #[test] fn test_mul_f64() { - let v1 = FVector::from_vec(vec![1.5, 2.0]); + let v1 = ColFVector::from_vec(vec![1.5, 2.0]); let v2 = FlexVector::from_vec(vec![2.0, 3.0]); let prod = v1 * v2; assert_eq!(prod.as_slice(), &[3.0, 6.0]); @@ -9048,7 +9046,7 @@ mod tests { #[test] fn test_mul_complex() { - let v1 = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v1 = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); let prod = v1 * v2; assert_eq!( @@ -9062,7 +9060,7 @@ mod tests { #[test] fn test_mul_nan_infinity() { - let v1 = FVector::from_vec(vec![f64::NAN, f64::INFINITY, 2.0]); + let v1 = ColFVector::from_vec(vec![f64::NAN, f64::INFINITY, 2.0]); let v2 = FlexVector::from_vec(vec![3.0, 2.0, f64::INFINITY]); let prod = v1 * v2; assert!(prod.as_slice()[0].is_nan()); @@ -9073,14 +9071,14 @@ mod tests { #[test] #[should_panic(expected = "Vector length mismatch")] fn test_mul_panic_on_mismatched_length() { - let v1 = FVector::from_vec(vec![1, 2]); + let v1 = ColFVector::from_vec(vec![1, 2]); let v2 = FlexVector::from_vec(vec![1, 2, 3]); let _ = v1 * v2; } #[test] fn test_elem_div_f32() { - let v1 = FVector::from_vec(vec![2.0f32, 4.0, 8.0]); + let v1 = ColFVector::from_vec(vec![2.0f32, 4.0, 8.0]); let v2 = FlexVector::from_vec(vec![1.0f32, 2.0, 4.0]); let result = v1 / v2; assert_eq!(result.as_slice(), &[2.0, 2.0, 2.0]); @@ -9088,7 +9086,7 @@ mod tests { #[test] fn test_elem_div_f64() { - let v1 = FVector::from_vec(vec![2.0f64, 4.0, 8.0]); + let v1 = ColFVector::from_vec(vec![2.0f64, 4.0, 8.0]); let v2 = FlexVector::from_vec(vec![1.0f64, 2.0, 4.0]); let result = v1 / v2; assert_eq!(result.as_slice(), &[2.0, 2.0, 2.0]); @@ -9096,7 +9094,7 @@ mod tests { #[test] fn test_elem_div_complex_f32() { - let v1 = FVector::from_vec(vec![Complex::new(2.0f32, 2.0), Complex::new(4.0, 0.0)]); + let v1 = ColFVector::from_vec(vec![Complex::new(2.0f32, 2.0), Complex::new(4.0, 0.0)]); let v2 = FlexVector::from_vec(vec![Complex::new(1.0f32, 1.0), Complex::new(2.0, 0.0)]); let result = v1 / v2; assert!((result[0] - Complex::new(2.0, 0.0)).norm() < 1e-6); @@ -9105,7 +9103,7 @@ mod tests { #[test] fn test_elem_div_complex_f64() { - let v1 = FVector::from_vec(vec![Complex::new(2.0f64, 2.0), Complex::new(4.0, 0.0)]); + let v1 = ColFVector::from_vec(vec![Complex::new(2.0f64, 2.0), Complex::new(4.0, 0.0)]); let v2 = FlexVector::from_vec(vec![Complex::new(1.0f64, 1.0), Complex::new(2.0, 0.0)]); let result = v1 / v2; assert!((result[0] - Complex::new(2.0, 0.0)).norm() < 1e-12); @@ -9115,14 +9113,14 @@ mod tests { #[test] #[should_panic(expected = "Vector length mismatch")] fn test_elem_div_panic_on_mismatched_length() { - let v1 = FVector::from_vec(vec![1.0f64, 2.0]); + let v1 = ColFVector::from_vec(vec![1.0f64, 2.0]); let v2 = FlexVector::from_vec(vec![1.0f64, 2.0, 3.0]); let _ = v1 / v2; } #[test] fn test_add_assign() { - let mut v1 = FVector::from_vec(vec![1, 2, 3]); + let mut v1 = ColFVector::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); v1 += v2; assert_eq!(v1.as_slice(), &[5, 7, 9]); @@ -9130,7 +9128,7 @@ mod tests { #[test] fn test_add_assign_f64() { - let mut v1 = FVector::from_vec(vec![1.0, 2.5]); + let mut v1 = ColFVector::from_vec(vec![1.0, 2.5]); let v2 = FlexVector::from_vec(vec![3.0, 4.5]); v1 += v2; assert_eq!(v1.as_slice(), &[4.0, 7.0]); @@ -9138,7 +9136,7 @@ mod tests { #[test] fn test_add_assign_complex() { - let mut v1 = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let mut v1 = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); v1 += v2; assert_eq!(v1.as_slice(), &[Complex::new(6.0, 8.0), Complex::new(10.0, 12.0)]); @@ -9147,14 +9145,14 @@ mod tests { #[test] #[should_panic(expected = "Vector length mismatch")] fn test_add_assign_panic_on_mismatched_length() { - let mut v1 = FVector::from_vec(vec![1, 2]); + let mut v1 = ColFVector::from_vec(vec![1, 2]); let v2 = FlexVector::from_vec(vec![1, 2, 3]); v1 += v2; } #[test] fn test_sub_assign() { - let mut v1 = FVector::from_vec(vec![10, 20, 30]); + let mut v1 = ColFVector::from_vec(vec![10, 20, 30]); let v2 = FlexVector::from_vec(vec![1, 2, 3]); v1 -= v2; assert_eq!(v1.as_slice(), &[9, 18, 27]); @@ -9162,7 +9160,7 @@ mod tests { #[test] fn test_sub_assign_f64() { - let mut v1 = FVector::from_vec(vec![5.5, 2.0]); + let mut v1 = ColFVector::from_vec(vec![5.5, 2.0]); let v2 = FlexVector::from_vec(vec![1.5, 1.0]); v1 -= v2; assert_eq!(v1.as_slice(), &[4.0, 1.0]); @@ -9170,7 +9168,7 @@ mod tests { #[test] fn test_sub_assign_complex() { - let mut v1 = FVector::from_vec(vec![Complex::new(5.0, 7.0), Complex::new(3.0, 4.0)]); + let mut v1 = ColFVector::from_vec(vec![Complex::new(5.0, 7.0), Complex::new(3.0, 4.0)]); let v2 = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(2.0, 1.0)]); v1 -= v2; assert_eq!(v1.as_slice(), &[Complex::new(4.0, 5.0), Complex::new(1.0, 3.0)]); @@ -9179,14 +9177,14 @@ mod tests { #[test] #[should_panic(expected = "Vector length mismatch")] fn test_sub_assign_panic_on_mismatched_length() { - let mut v1 = FVector::from_vec(vec![1, 2]); + let mut v1 = ColFVector::from_vec(vec![1, 2]); let v2 = FlexVector::from_vec(vec![1, 2, 3]); v1 -= v2; } #[test] fn test_mul_assign() { - let mut v1 = FVector::from_vec(vec![2, 3, 4]); + let mut v1 = ColFVector::from_vec(vec![2, 3, 4]); let v2 = FlexVector::from_vec(vec![5, 6, 7]); v1 *= v2; assert_eq!(v1.as_slice(), &[10, 18, 28]); @@ -9194,7 +9192,7 @@ mod tests { #[test] fn test_mul_assign_f64() { - let mut v1 = FVector::from_vec(vec![1.5, 2.0]); + let mut v1 = ColFVector::from_vec(vec![1.5, 2.0]); let v2 = FlexVector::from_vec(vec![2.0, 3.0]); v1 *= v2; assert_eq!(v1.as_slice(), &[3.0, 6.0]); @@ -9202,7 +9200,7 @@ mod tests { #[test] fn test_mul_assign_complex() { - let mut v1 = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let mut v1 = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); v1 *= v2; assert_eq!(v1.as_slice(), &[Complex::new(-7.0, 16.0), Complex::new(-11.0, 52.0)]); @@ -9211,14 +9209,14 @@ mod tests { #[test] #[should_panic(expected = "Vector length mismatch")] fn test_mul_assign_panic_on_mismatched_length() { - let mut v1 = FVector::from_vec(vec![1, 2]); + let mut v1 = ColFVector::from_vec(vec![1, 2]); let v2 = FlexVector::from_vec(vec![1, 2, 3]); v1 *= v2; } #[test] fn test_elem_div_assign_f32() { - let mut v1 = FVector::from_vec(vec![2.0f32, 4.0, 8.0]); + let mut v1 = ColFVector::from_vec(vec![2.0f32, 4.0, 8.0]); let v2 = FlexVector::from_vec(vec![1.0f32, 2.0, 4.0]); v1 /= v2; assert_eq!(v1.as_slice(), &[2.0, 2.0, 2.0]); @@ -9226,7 +9224,7 @@ mod tests { #[test] fn test_elem_div_assign_f64() { - let mut v1 = FVector::from_vec(vec![2.0f64, 4.0, 8.0]); + let mut v1 = ColFVector::from_vec(vec![2.0f64, 4.0, 8.0]); let v2 = FlexVector::from_vec(vec![1.0f64, 2.0, 4.0]); v1 /= v2; assert_eq!(v1.as_slice(), &[2.0, 2.0, 2.0]); @@ -9234,7 +9232,7 @@ mod tests { #[test] fn test_elem_div_assign_complex_f32() { - let mut v1 = FVector::from_vec(vec![Complex::new(2.0f32, 2.0), Complex::new(4.0, 0.0)]); + let mut v1 = ColFVector::from_vec(vec![Complex::new(2.0f32, 2.0), Complex::new(4.0, 0.0)]); let v2 = FlexVector::from_vec(vec![Complex::new(1.0f32, 1.0), Complex::new(2.0, 0.0)]); v1 /= v2; assert!((v1[0] - Complex::new(2.0, 0.0)).norm() < 1e-6); @@ -9243,7 +9241,7 @@ mod tests { #[test] fn test_elem_div_assign_complex_f64() { - let mut v1 = FVector::from_vec(vec![Complex::new(2.0f64, 2.0), Complex::new(4.0, 0.0)]); + let mut v1 = ColFVector::from_vec(vec![Complex::new(2.0f64, 2.0), Complex::new(4.0, 0.0)]); let v2 = FlexVector::from_vec(vec![Complex::new(1.0f64, 1.0), Complex::new(2.0, 0.0)]); v1 /= v2; assert!((v1[0] - Complex::new(2.0, 0.0)).norm() < 1e-12); @@ -9253,105 +9251,105 @@ mod tests { #[test] #[should_panic(expected = "Vector length mismatch")] fn test_elem_div_assign_panic_on_mismatched_length() { - let mut v1 = FVector::from_vec(vec![1.0f64, 2.0]); + let mut v1 = ColFVector::from_vec(vec![1.0f64, 2.0]); let v2 = FlexVector::from_vec(vec![1.0f64, 2.0, 3.0]); v1 /= v2; } #[test] fn test_scalar_add() { - let v = FVector::from_vec(vec![1, 2, 3]); + let v = ColFVector::from_vec(vec![1, 2, 3]); let sum = v.clone() + 10; assert_eq!(sum.as_slice(), &[11, 12, 13]); } #[test] fn test_scalar_add_f64() { - let v = FVector::from_vec(vec![1.5, -2.0, 0.0]); + let v = ColFVector::from_vec(vec![1.5, -2.0, 0.0]); let sum = v.clone() + 2.0; assert_eq!(sum.as_slice(), &[3.5, 0.0, 2.0]); } #[test] fn test_scalar_add_complex_f64() { - let v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); let sum = v.clone() + Complex::new(2.0, 1.0); assert_eq!(sum.as_slice(), &[Complex::new(3.0, 3.0), Complex::new(-1.0, 5.0)]); } #[test] fn test_scalar_add_assign() { - let mut v = FVector::from_vec(vec![1, 2, 3]); + let mut v = ColFVector::from_vec(vec![1, 2, 3]); v += 10; assert_eq!(v.as_slice(), &[11, 12, 13]); } #[test] fn test_scalar_add_assign_f64() { - let mut v = FVector::from_vec(vec![1.5, -2.0, 0.0]); + let mut v = ColFVector::from_vec(vec![1.5, -2.0, 0.0]); v += 2.0; assert_eq!(v.as_slice(), &[3.5, 0.0, 2.0]); } #[test] fn test_scalar_add_assign_complex_f64() { - let mut v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + let mut v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); v += Complex::new(2.0, 1.0); assert_eq!(v.as_slice(), &[Complex::new(3.0, 3.0), Complex::new(-1.0, 5.0)]); } #[test] fn test_scalar_sub() { - let v = FVector::from_vec(vec![10, 20, 30]); + let v = ColFVector::from_vec(vec![10, 20, 30]); let diff = v.clone() - 5; assert_eq!(diff.as_slice(), &[5, 15, 25]); } #[test] fn test_scalar_sub_f64() { - let v = FVector::from_vec(vec![1.5, -2.0, 0.0]); + let v = ColFVector::from_vec(vec![1.5, -2.0, 0.0]); let diff = v.clone() - 2.0; assert_eq!(diff.as_slice(), &[-0.5, -4.0, -2.0]); } #[test] fn test_scalar_sub_complex_f64() { - let v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); let diff = v.clone() - Complex::new(2.0, 1.0); assert_eq!(diff.as_slice(), &[Complex::new(-1.0, 1.0), Complex::new(-5.0, 3.0)]); } #[test] fn test_scalar_sub_assign() { - let mut v = FVector::from_vec(vec![10, 20, 30]); + let mut v = ColFVector::from_vec(vec![10, 20, 30]); v -= 5; assert_eq!(v.as_slice(), &[5, 15, 25]); } #[test] fn test_scalar_sub_assign_f64() { - let mut v = FVector::from_vec(vec![1.5, -2.0, 0.0]); + let mut v = ColFVector::from_vec(vec![1.5, -2.0, 0.0]); v -= 2.0; assert_eq!(v.as_slice(), &[-0.5, -4.0, -2.0]); } #[test] fn test_scalar_sub_assign_complex_f64() { - let mut v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + let mut v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); v -= Complex::new(2.0, 1.0); assert_eq!(v.as_slice(), &[Complex::new(-1.0, 1.0), Complex::new(-5.0, 3.0)]); } #[test] fn test_scalar_mul() { - let v = FVector::from_vec(vec![2, -3, 4]); + let v = ColFVector::from_vec(vec![2, -3, 4]); let prod = v.clone() * 3; assert_eq!(prod.as_slice(), &[6, -9, 12]); } #[test] fn test_scalar_mul_f64() { - let v = FVector::from_vec(vec![1.5, -2.0, 0.0]); + let v = ColFVector::from_vec(vec![1.5, -2.0, 0.0]); let prod = v.clone() * 2.0; assert_eq!(prod.as_slice(), &[3.0, -4.0, 0.0]); } @@ -9359,7 +9357,7 @@ mod tests { #[test] fn test_scalar_mul_complex() { use num::Complex; - let v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); let scalar = Complex::new(2.0, 0.0); let prod = v.clone() * scalar; assert_eq!(prod.as_slice(), &[Complex::new(2.0, 4.0), Complex::new(-6.0, 8.0)]); @@ -9367,14 +9365,14 @@ mod tests { #[test] fn test_scalar_mul_assign() { - let mut v = FVector::from_vec(vec![2, -3, 4]); + let mut v = ColFVector::from_vec(vec![2, -3, 4]); v *= 3; assert_eq!(v.as_slice(), &[6, -9, 12]); } #[test] fn test_scalar_mul_assign_f64() { - let mut v = FVector::from_vec(vec![1.5, -2.0, 0.0]); + let mut v = ColFVector::from_vec(vec![1.5, -2.0, 0.0]); v *= 2.0; assert_eq!(v.as_slice(), &[3.0, -4.0, 0.0]); } @@ -9382,7 +9380,7 @@ mod tests { #[test] fn test_scalar_mul_assign_complex() { use num::Complex; - let mut v = FVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + let mut v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); let scalar = Complex::new(2.0, 0.0); v *= scalar; assert_eq!(v.as_slice(), &[Complex::new(2.0, 4.0), Complex::new(-6.0, 8.0)]); @@ -9434,7 +9432,7 @@ mod tests { fn test_scalar_div_assign_complex_by_complex() { use num::Complex; let mut v: FlexVector> = - FVector::from_vec(vec![Complex::new(2.0, 4.0), Complex::new(-6.0, 8.0)]); + ColFVector::from_vec(vec![Complex::new(2.0, 4.0), Complex::new(-6.0, 8.0)]); let divisor = Complex::new(2.0, 0.0); v /= divisor; assert_eq!(v.as_slice(), &[Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); @@ -9442,7 +9440,7 @@ mod tests { #[test] fn test_scalar_div_nan_infinity() { - let v = FVector::from_vec(vec![f64::NAN, f64::INFINITY, 4.0]); + let v = ColFVector::from_vec(vec![f64::NAN, f64::INFINITY, 4.0]); let result: FlexVector = v.clone() / 2.0; assert!(result.as_slice()[0].is_nan()); assert_eq!(result.as_slice()[1], f64::INFINITY); @@ -9451,7 +9449,7 @@ mod tests { #[test] fn test_scalar_div_assign_nan_infinity() { - let mut v: FlexVector = FVector::from_vec(vec![f64::NAN, f64::INFINITY, 4.0]); + let mut v: FlexVector = ColFVector::from_vec(vec![f64::NAN, f64::INFINITY, 4.0]); v /= 2.0; assert!(v.as_slice()[0].is_nan()); assert_eq!(v.as_slice()[1], f64::INFINITY); @@ -9462,7 +9460,7 @@ mod tests { #[test] fn test_add_overflow_i32() { - let v1 = FVector::from_vec(vec![i32::MAX]); + let v1 = ColFVector::from_vec(vec![i32::MAX]); let v2 = FlexVector::from_vec(vec![1]); if cfg!(debug_assertions) { // In debug mode, should panic on overflow @@ -9479,7 +9477,7 @@ mod tests { #[test] fn test_mul_overflow_i32() { - let v1 = FVector::from_vec(vec![i32::MAX]); + let v1 = ColFVector::from_vec(vec![i32::MAX]); let v2 = FlexVector::from_vec(vec![2]); if cfg!(debug_assertions) { // In debug mode, should panic on overflow @@ -9496,7 +9494,7 @@ mod tests { #[test] fn test_divide_by_zero_f64() { - let v: FlexVector = FVector::from_vec(vec![1.0, -2.0, 0.0]); + let v: FlexVector = ColFVector::from_vec(vec![1.0, -2.0, 0.0]); let result = v.clone() / 0.0; assert_eq!(result.as_slice()[0], f64::INFINITY); assert_eq!(result.as_slice()[1], f64::NEG_INFINITY); @@ -9505,7 +9503,7 @@ mod tests { #[test] fn test_neg_zero_f64() { - let v = FVector::from_vec(vec![0.0, -0.0]); + let v = ColFVector::from_vec(vec![0.0, -0.0]); let neg_v = -v; assert_eq!(neg_v.as_slice()[0], -0.0); assert_eq!(neg_v.as_slice()[1], 0.0); @@ -9514,7 +9512,7 @@ mod tests { #[test] fn test_complex_div_by_zero() { use num::Complex; - let v: FlexVector> = FVector::from_vec(vec![Complex::new(1.0, 1.0)]); + let v: FlexVector> = ColFVector::from_vec(vec![Complex::new(1.0, 1.0)]); let result = v.clone() / Complex::new(0.0, 0.0); assert!(result.as_slice()[0].re.is_nan()); assert!(result.as_slice()[0].im.is_nan()); From 13ffea2ab8e3f8753d8b76c82f94c41329cd88db Mon Sep 17 00:00:00 2001 From: Chris Simpkins Date: Sat, 7 Jun 2025 14:35:20 -0400 Subject: [PATCH 82/82] refactor public type alias names for FlexVector, VectorSlice, VectorSliceMut --- src/types/flexvector.rs | 1178 +++++++++++++++++++------------------- src/types/vectorslice.rs | 20 +- 2 files changed, 599 insertions(+), 599 deletions(-) diff --git a/src/types/flexvector.rs b/src/types/flexvector.rs index c168246..8454153 100644 --- a/src/types/flexvector.rs +++ b/src/types/flexvector.rs @@ -49,9 +49,9 @@ pub struct FlexVector { } /// ... -pub type ColFVector = FlexVector; +pub type ColFVec = FlexVector; /// ... -pub type RowFVector = FlexVector; +pub type RowFVec = FlexVector; // ================================ // @@ -2054,19 +2054,19 @@ mod tests { #[test] fn test_from_fn_i32() { - let v = ColFVector::from_fn(5, |i| i as i32 * 2); + let v = ColFVec::from_fn(5, |i| i as i32 * 2); assert_eq!(v.as_slice(), &[0, 2, 4, 6, 8]); } #[test] fn test_from_fn_f64() { - let v = ColFVector::from_fn(4, |i| (i as f64).powi(2)); + let v = ColFVec::from_fn(4, |i| (i as f64).powi(2)); assert_eq!(v.as_slice(), &[0.0, 1.0, 4.0, 9.0]); } #[test] fn test_from_fn_complex() { - let v = ColFVector::from_fn(3, |i| Complex::new(i as f64, -(i as f64))); + let v = ColFVec::from_fn(3, |i| Complex::new(i as f64, -(i as f64))); assert_eq!( v.as_slice(), &[Complex::new(0.0, 0.0), Complex::new(1.0, -1.0), Complex::new(2.0, -2.0)] @@ -2075,13 +2075,13 @@ mod tests { #[test] fn test_from_fn_zero_length() { - let v: FlexVector = ColFVector::from_fn(0, |_| 42); + let v: FlexVector = ColFVec::from_fn(0, |_| 42); assert!(v.is_empty()); } #[test] fn test_from_fn_with_fn_pointer() { - let v = ColFVector::from_fn(4, square_usize); + let v = ColFVec::from_fn(4, square_usize); assert_eq!(v.as_slice(), &[0, 1, 4, 9]); } @@ -2116,14 +2116,14 @@ mod tests { #[test] fn test_repeat_pattern_i32_basic() { let pattern = [1, 2, 3]; - let v = ColFVector::repeat_pattern(&pattern, 8).unwrap(); + let v = ColFVec::repeat_pattern(&pattern, 8).unwrap(); assert_eq!(v.as_slice(), &[1, 2, 3, 1, 2, 3, 1, 2]); } #[test] fn test_repeat_pattern_i32_exact_multiple() { let pattern = [4, 5]; - let v = ColFVector::repeat_pattern(&pattern, 6).unwrap(); + let v = ColFVec::repeat_pattern(&pattern, 6).unwrap(); assert_eq!(v.as_slice(), &[4, 5, 4, 5, 4, 5]); } @@ -2137,35 +2137,35 @@ mod tests { #[test] fn test_repeat_pattern_i32_pattern_empty_len_zero() { let pattern: [i32; 0] = []; - let v = ColFVector::repeat_pattern(&pattern, 0).unwrap(); + let v = ColFVec::repeat_pattern(&pattern, 0).unwrap(); assert!(v.is_empty()); } #[test] fn test_repeat_pattern_i32_pattern_empty_len_nonzero() { let pattern: [i32; 0] = []; - let result = ColFVector::repeat_pattern(&pattern, 3); + let result = ColFVec::repeat_pattern(&pattern, 3); assert!(result.is_err()); } #[test] fn test_repeat_pattern_f64_basic() { let pattern = [1.5, 2.5]; - let v = ColFVector::repeat_pattern(&pattern, 5).unwrap(); + let v = ColFVec::repeat_pattern(&pattern, 5).unwrap(); assert_eq!(v.as_slice(), &[1.5, 2.5, 1.5, 2.5, 1.5]); } #[test] fn test_repeat_pattern_f64_empty_pattern_len_zero() { let pattern: [f64; 0] = []; - let v = ColFVector::repeat_pattern(&pattern, 0).unwrap(); + let v = ColFVec::repeat_pattern(&pattern, 0).unwrap(); assert!(v.is_empty()); } #[test] fn test_repeat_pattern_f64_empty_pattern_len_nonzero() { let pattern: [f64; 0] = []; - let result = ColFVector::repeat_pattern(&pattern, 2); + let result = ColFVec::repeat_pattern(&pattern, 2); assert!(result.is_err()); } @@ -2173,7 +2173,7 @@ mod tests { fn test_repeat_pattern_complex_f64_basic() { use num::Complex; let pattern = [Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]; - let v = ColFVector::repeat_pattern(&pattern, 5).unwrap(); + let v = ColFVec::repeat_pattern(&pattern, 5).unwrap(); assert_eq!( v.as_slice(), &[ @@ -2190,7 +2190,7 @@ mod tests { fn test_repeat_pattern_complex_f64_empty_pattern_len_zero() { use num::Complex; let pattern: [Complex; 0] = []; - let v = ColFVector::repeat_pattern(&pattern, 0).unwrap(); + let v = ColFVec::repeat_pattern(&pattern, 0).unwrap(); assert!(v.is_empty()); } @@ -2198,7 +2198,7 @@ mod tests { fn test_repeat_pattern_complex_f64_empty_pattern_len_nonzero() { use num::Complex; let pattern: [Complex; 0] = []; - let result = ColFVector::repeat_pattern(&pattern, 1); + let result = ColFVec::repeat_pattern(&pattern, 1); assert!(result.is_err()); } @@ -2305,7 +2305,7 @@ mod tests { let a = 1; let b = 2; let c = 3; - let refs = ColFVector::from_vec(vec![&a, &b, &c]); + let refs = ColFVec::from_vec(vec![&a, &b, &c]); let owned = refs.cloned(); assert_eq!(owned.as_slice(), &[1, 2, 3]); } @@ -2322,30 +2322,30 @@ mod tests { use num::Complex; let a = Complex::new(1.0, 2.0); let b = Complex::new(3.0, 4.0); - let refs = ColFVector::from_vec(vec![&a, &b]); + let refs = ColFVec::from_vec(vec![&a, &b]); let owned = refs.cloned(); assert_eq!(owned.as_slice(), &[a, b]); } #[test] fn test_flatten_flexvector_of_flexvector() { - let row1 = ColFVector::from_vec(vec![1, 2]); - let row2 = ColFVector::from_vec(vec![3, 4, 5]); - let nested = ColFVector::from_vec(vec![row1, row2]); + let row1 = ColFVec::from_vec(vec![1, 2]); + let row2 = ColFVec::from_vec(vec![3, 4, 5]); + let nested = ColFVec::from_vec(vec![row1, row2]); let flat = nested.flatten(); assert_eq!(flat.as_slice(), &[1, 2, 3, 4, 5]); } #[test] fn test_flatten_flexvector_of_vec() { - let nested = ColFVector::from_vec(vec![vec![10, 20], vec![30]]); + let nested = ColFVec::from_vec(vec![vec![10, 20], vec![30]]); let flat = nested.flatten(); assert_eq!(flat.as_slice(), &[10, 20, 30]); } #[test] fn test_flatten_flexvector_of_array() { - let nested = ColFVector::from_vec(vec![[1, 2], [3, 4]]); + let nested = ColFVec::from_vec(vec![[1, 2], [3, 4]]); let flat = nested.flatten(); assert_eq!(flat.as_slice(), &[1, 2, 3, 4]); } @@ -2354,7 +2354,7 @@ mod tests { fn test_flatten_flexvector_of_slice_refs() { let a = [7, 8]; let b = [9]; - let nested = ColFVector::from_vec(vec![&a[..], &b[..]]); + let nested = ColFVec::from_vec(vec![&a[..], &b[..]]); let flat = nested.flatten(); assert_eq!(flat.as_slice(), &[&7, &8, &9]); } @@ -2368,7 +2368,7 @@ mod tests { #[test] fn test_flatten_with_empty_inner() { - let nested = ColFVector::from_vec(vec![vec![], vec![1, 2], vec![]]); + let nested = ColFVec::from_vec(vec![vec![], vec![1, 2], vec![]]); let flat = nested.flatten(); assert_eq!(flat.as_slice(), &[1, 2]); } @@ -2377,7 +2377,7 @@ mod tests { fn test_flatten_cloned_flexvector_of_slice_refs() { let a = [8, 9]; let b = [10]; - let nested = ColFVector::from_vec(vec![&a[..], &b[..]]); + let nested = ColFVec::from_vec(vec![&a[..], &b[..]]); let flat = nested.flatten_cloned(); let expected: Vec = vec![8, 9, 10]; assert_eq!(flat.as_slice(), expected.as_slice()); @@ -2388,7 +2388,7 @@ mod tests { fn test_flatten_cloned_flexvector_of_refs() { let x = 42; let y = 43; - let nested = ColFVector::from_vec(vec![vec![&x, &y], vec![&x]]); + let nested = ColFVec::from_vec(vec![vec![&x, &y], vec![&x]]); let flat = nested.flatten_cloned(); assert_eq!(flat.as_slice(), &[42, 43, 42]); let _: &[i32] = flat.as_slice(); // type check: &[i32] @@ -2403,7 +2403,7 @@ mod tests { #[test] fn test_flatten_cloned_with_empty_inner() { - let nested = ColFVector::from_vec(vec![vec![], vec![&1, &2], vec![]]); + let nested = ColFVec::from_vec(vec![vec![], vec![&1, &2], vec![]]); let flat = nested.flatten_cloned(); assert_eq!(flat.as_slice(), &[1, 2]); } @@ -2663,7 +2663,7 @@ mod tests { // ================================ #[test] fn test_deref_access_slice_methods_i32() { - let v = ColFVector::from_vec(vec![3, 1, 2]); + let v = ColFVec::from_vec(vec![3, 1, 2]); // Use sort (not implemented in FlexVector directly) let mut sorted = v.clone(); sorted.sort(); @@ -2674,7 +2674,7 @@ mod tests { #[test] fn test_deref_access_slice_methods_f64() { - let v = ColFVector::from_vec(vec![3.5, 1.5, 2.5]); + let v = ColFVec::from_vec(vec![3.5, 1.5, 2.5]); // Use sort_by let mut sorted = v.clone(); sorted.sort_by(|a, b| a.partial_cmp(b).unwrap()); @@ -2687,7 +2687,7 @@ mod tests { #[test] fn test_deref_access_slice_methods_complex() { use num::Complex; - let v = ColFVector::from_vec(vec![ + let v = ColFVec::from_vec(vec![ Complex::new(1.0, 2.0), Complex::new(3.0, 4.0), Complex::new(5.0, 6.0), @@ -2707,7 +2707,7 @@ mod tests { #[test] fn test_deref_mut_i32() { - let mut v = ColFVector::from_vec(vec![10, 20, 30]); + let mut v = ColFVec::from_vec(vec![10, 20, 30]); // Mutate via indexing v[1] = 99; assert_eq!(v.as_slice(), &[10, 99, 30]); @@ -2718,7 +2718,7 @@ mod tests { #[test] fn test_deref_mut_f64() { - let mut v = ColFVector::from_vec(vec![1.5, 2.5, 3.5]); + let mut v = ColFVec::from_vec(vec![1.5, 2.5, 3.5]); // Mutate via indexing v[0] = -1.5; assert_eq!(v.as_slice(), &[-1.5, 2.5, 3.5]); @@ -2730,7 +2730,7 @@ mod tests { #[test] fn test_deref_mut_complex_f64() { use num::Complex; - let mut v = ColFVector::from_vec(vec![ + let mut v = ColFVec::from_vec(vec![ Complex::new(1.0, 2.0), Complex::new(3.0, 4.0), Complex::new(5.0, 6.0), @@ -2753,14 +2753,14 @@ mod tests { // ================================ #[test] fn test_asref_i32() { - let v = ColFVector::from_vec(vec![1, 2, 3]); + let v = ColFVec::from_vec(vec![1, 2, 3]); let slice: &[i32] = v.as_ref(); assert_eq!(slice, &[1, 2, 3]); } #[test] fn test_asref_f64() { - let v = ColFVector::from_vec(vec![1.1, 2.2, 3.3]); + let v = ColFVec::from_vec(vec![1.1, 2.2, 3.3]); let slice: &[f64] = v.as_ref(); assert_eq!(slice, &[1.1, 2.2, 3.3]); } @@ -2768,14 +2768,14 @@ mod tests { #[test] fn test_asref_complex_f64() { use num::Complex; - let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = ColFVec::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let slice: &[Complex] = v.as_ref(); assert_eq!(slice, &[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); } #[test] fn test_asmut_i32() { - let mut v = ColFVector::from_vec(vec![1, 2, 3]); + let mut v = ColFVec::from_vec(vec![1, 2, 3]); let slice: &mut [i32] = v.as_mut(); slice[0] = 10; slice[2] = 30; @@ -2784,7 +2784,7 @@ mod tests { #[test] fn test_asmut_f64() { - let mut v = ColFVector::from_vec(vec![1.1, 2.2, 3.3]); + let mut v = ColFVec::from_vec(vec![1.1, 2.2, 3.3]); let slice: &mut [f64] = v.as_mut(); slice[1] = 9.9; assert_eq!(v.as_slice(), &[1.1, 9.9, 3.3]); @@ -2793,7 +2793,7 @@ mod tests { #[test] fn test_asmut_complex_f64() { use num::Complex; - let mut v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let mut v = ColFVec::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let slice: &mut [Complex] = v.as_mut(); slice[0].re = 10.0; slice[1].im = 40.0; @@ -2809,7 +2809,7 @@ mod tests { #[test] fn test_borrow_i32() { use std::borrow::Borrow; - let v = ColFVector::from_vec(vec![1, 2, 3]); + let v = ColFVec::from_vec(vec![1, 2, 3]); let slice: &[i32] = v.borrow(); assert_eq!(slice, &[1, 2, 3]); } @@ -2817,7 +2817,7 @@ mod tests { #[test] fn test_borrow_f64() { use std::borrow::Borrow; - let v = ColFVector::from_vec(vec![1.1, 2.2, 3.3]); + let v = ColFVec::from_vec(vec![1.1, 2.2, 3.3]); let slice: &[f64] = v.borrow(); assert_eq!(slice, &[1.1, 2.2, 3.3]); } @@ -2826,7 +2826,7 @@ mod tests { fn test_borrow_complex_f64() { use num::Complex; use std::borrow::Borrow; - let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = ColFVec::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let slice: &[Complex] = v.borrow(); assert_eq!(slice, &[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); } @@ -2834,7 +2834,7 @@ mod tests { #[test] fn test_borrow_mut_i32() { use std::borrow::BorrowMut; - let mut v = ColFVector::from_vec(vec![1, 2, 3]); + let mut v = ColFVec::from_vec(vec![1, 2, 3]); { let slice: &mut [i32] = v.borrow_mut(); slice[0] = 10; @@ -2846,7 +2846,7 @@ mod tests { #[test] fn test_borrow_mut_f64() { use std::borrow::BorrowMut; - let mut v = ColFVector::from_vec(vec![1.1, 2.2, 3.3]); + let mut v = ColFVec::from_vec(vec![1.1, 2.2, 3.3]); { let slice: &mut [f64] = v.borrow_mut(); slice[1] = 9.9; @@ -2858,7 +2858,7 @@ mod tests { fn test_borrow_mut_complex_f64() { use num::Complex; use std::borrow::BorrowMut; - let mut v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let mut v = ColFVec::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); { let slice: &mut [Complex] = v.borrow_mut(); slice[0].re = 10.0; @@ -2874,14 +2874,14 @@ mod tests { // ================================ #[test] fn test_into_iter_i32() { - let v = ColFVector::from_vec(vec![1, 2, 3]); + let v = ColFVec::from_vec(vec![1, 2, 3]); let collected: Vec<_> = v.into_iter().collect(); assert_eq!(collected, vec![1, 2, 3]); } #[test] fn test_into_iter_f64() { - let v = ColFVector::from_vec(vec![1.1, 2.2, 3.3]); + let v = ColFVec::from_vec(vec![1.1, 2.2, 3.3]); let collected: Vec<_> = v.into_iter().collect(); assert_eq!(collected, vec![1.1, 2.2, 3.3]); } @@ -2889,21 +2889,21 @@ mod tests { #[test] fn test_into_iter_complex_f64() { use num::Complex; - let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = ColFVec::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let collected: Vec<_> = v.into_iter().collect(); assert_eq!(collected, vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); } #[test] fn test_iter_ref_i32() { - let v = ColFVector::from_vec(vec![10, 20, 30]); + let v = ColFVec::from_vec(vec![10, 20, 30]); let collected: Vec<_> = (&v).into_iter().copied().collect(); assert_eq!(collected, vec![10, 20, 30]); } #[test] fn test_iter_ref_f64() { - let v = ColFVector::from_vec(vec![1.5, 2.5, 3.5]); + let v = ColFVec::from_vec(vec![1.5, 2.5, 3.5]); let collected: Vec<_> = (&v).into_iter().copied().collect(); assert_eq!(collected, vec![1.5, 2.5, 3.5]); } @@ -2911,14 +2911,14 @@ mod tests { #[test] fn test_iter_ref_complex_f64() { use num::Complex; - let v = ColFVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); + let v = ColFVec::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); let collected: Vec<_> = (&v).into_iter().cloned().collect(); assert_eq!(collected, vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); } #[test] fn test_iter_mutable_i32() { - let mut v = ColFVector::from_vec(vec![1, 2, 3]); + let mut v = ColFVec::from_vec(vec![1, 2, 3]); for x in &mut v { *x *= 10; } @@ -2927,7 +2927,7 @@ mod tests { #[test] fn test_iter_mutable_f64() { - let mut v = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); + let mut v = ColFVec::from_vec(vec![1.0, 2.0, 3.0]); for x in &mut v { *x += 0.5; } @@ -2937,7 +2937,7 @@ mod tests { #[test] fn test_iter_mutable_complex_f64() { use num::Complex; - let mut v = ColFVector::from_vec(vec![Complex::new(1.0, 1.0), Complex::new(2.0, 2.0)]); + let mut v = ColFVec::from_vec(vec![Complex::new(1.0, 1.0), Complex::new(2.0, 2.0)]); for x in &mut v { x.re *= 2.0; x.im *= 3.0; @@ -2952,18 +2952,18 @@ mod tests { // ================================ #[test] fn test_partial_eq_i32() { - let v1 = ColFVector::from_vec(vec![1, 2, 3]); - let v2 = ColFVector::from_vec(vec![1, 2, 3]); - let v3 = ColFVector::from_vec(vec![3, 2, 1]); + let v1 = ColFVec::from_vec(vec![1, 2, 3]); + let v2 = ColFVec::from_vec(vec![1, 2, 3]); + let v3 = ColFVec::from_vec(vec![3, 2, 1]); assert_eq!(v1, v2); assert_ne!(v1, v3); } #[test] fn test_partial_eq_f64() { - let v1 = ColFVector::from_vec(vec![1.1, 2.2, 3.3]); - let v2 = ColFVector::from_vec(vec![1.1, 2.2, 3.3]); - let v3 = ColFVector::from_vec(vec![3.3, 2.2, 1.1]); + let v1 = ColFVec::from_vec(vec![1.1, 2.2, 3.3]); + let v2 = ColFVec::from_vec(vec![1.1, 2.2, 3.3]); + let v3 = ColFVec::from_vec(vec![3.3, 2.2, 1.1]); assert_eq!(v1, v2); assert_ne!(v1, v3); } @@ -2971,9 +2971,9 @@ mod tests { #[test] fn test_partial_eq_complex_f64() { use num::Complex; - let v1 = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); - let v2 = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); - let v3 = ColFVector::from_vec(vec![Complex::new(4.0, 3.0), Complex::new(2.0, 1.0)]); + let v1 = ColFVec::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v2 = ColFVec::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v3 = ColFVec::from_vec(vec![Complex::new(4.0, 3.0), Complex::new(2.0, 1.0)]); assert_eq!(v1, v2); assert_ne!(v1, v3); } @@ -2987,28 +2987,28 @@ mod tests { #[test] fn test_partial_eq_different_lengths() { - let v1 = ColFVector::from_vec(vec![1, 2]); - let v2 = ColFVector::from_vec(vec![1, 2, 3]); + let v1 = ColFVec::from_vec(vec![1, 2]); + let v2 = ColFVec::from_vec(vec![1, 2, 3]); assert_ne!(v1, v2); } #[test] fn test_partial_eq_f64_nan() { - let v1 = ColFVector::from_vec(vec![f64::NAN, 1.0]); - let v2 = ColFVector::from_vec(vec![f64::NAN, 1.0]); + let v1 = ColFVec::from_vec(vec![f64::NAN, 1.0]); + let v2 = ColFVec::from_vec(vec![f64::NAN, 1.0]); // NaN != NaN, so these should not be equal assert_ne!(v1, v2); - let v3 = ColFVector::from_vec(vec![f64::NAN, 1.0]); - let v4 = ColFVector::from_vec(vec![f64::NAN, 2.0]); + let v3 = ColFVec::from_vec(vec![f64::NAN, 1.0]); + let v4 = ColFVec::from_vec(vec![f64::NAN, 2.0]); assert_ne!(v3, v4); } #[test] fn test_partial_eq_f64_zero_negzero() { - let v1 = ColFVector::from_vec(vec![0.0, -0.0]); - let v2 = ColFVector::from_vec(vec![0.0, -0.0]); - let v3 = ColFVector::from_vec(vec![-0.0, 0.0]); + let v1 = ColFVec::from_vec(vec![0.0, -0.0]); + let v2 = ColFVec::from_vec(vec![0.0, -0.0]); + let v3 = ColFVec::from_vec(vec![-0.0, 0.0]); // 0.0 == -0.0 in Rust assert_eq!(v1, v2); assert_eq!(v1, v3); @@ -3016,9 +3016,9 @@ mod tests { #[test] fn test_partial_eq_f64_infinity() { - let v1 = ColFVector::from_vec(vec![f64::INFINITY, f64::NEG_INFINITY]); - let v2 = ColFVector::from_vec(vec![f64::INFINITY, f64::NEG_INFINITY]); - let v3 = ColFVector::from_vec(vec![f64::NEG_INFINITY, f64::INFINITY]); + let v1 = ColFVec::from_vec(vec![f64::INFINITY, f64::NEG_INFINITY]); + let v2 = ColFVec::from_vec(vec![f64::INFINITY, f64::NEG_INFINITY]); + let v3 = ColFVec::from_vec(vec![f64::NEG_INFINITY, f64::INFINITY]); assert_eq!(v1, v2); assert_ne!(v1, v3); } @@ -3027,21 +3027,21 @@ mod tests { fn test_partial_eq_complex_nan() { use num::Complex; let nan = f64::NAN; - let v1 = ColFVector::from_vec(vec![Complex::new(nan, 1.0)]); - let v2 = ColFVector::from_vec(vec![Complex::new(nan, 1.0)]); + let v1 = ColFVec::from_vec(vec![Complex::new(nan, 1.0)]); + let v2 = ColFVec::from_vec(vec![Complex::new(nan, 1.0)]); // Complex::new(NaN, 1.0) != Complex::new(NaN, 1.0) assert_ne!(v1, v2); - let v3 = ColFVector::from_vec(vec![Complex::new(1.0, nan)]); - let v4 = ColFVector::from_vec(vec![Complex::new(1.0, nan)]); + let v3 = ColFVec::from_vec(vec![Complex::new(1.0, nan)]); + let v4 = ColFVec::from_vec(vec![Complex::new(1.0, nan)]); assert_ne!(v3, v4); } #[test] fn test_partial_eq_complex_zero_negzero() { use num::Complex; - let v1 = ColFVector::from_vec(vec![Complex::new(0.0, -0.0)]); - let v2 = ColFVector::from_vec(vec![Complex::new(-0.0, 0.0)]); + let v1 = ColFVec::from_vec(vec![Complex::new(0.0, -0.0)]); + let v2 = ColFVec::from_vec(vec![Complex::new(-0.0, 0.0)]); // 0.0 == -0.0 for both real and imaginary parts assert_eq!(v1, v2); } @@ -3049,10 +3049,10 @@ mod tests { #[test] fn test_partial_eq_complex_infinity() { use num::Complex; - let v1 = ColFVector::from_vec(vec![Complex::new(f64::INFINITY, 1.0)]); - let v2 = ColFVector::from_vec(vec![Complex::new(f64::INFINITY, 1.0)]); - let v3 = ColFVector::from_vec(vec![Complex::new(1.0, f64::INFINITY)]); - let v4 = ColFVector::from_vec(vec![Complex::new(1.0, f64::INFINITY)]); + let v1 = ColFVec::from_vec(vec![Complex::new(f64::INFINITY, 1.0)]); + let v2 = ColFVec::from_vec(vec![Complex::new(f64::INFINITY, 1.0)]); + let v3 = ColFVec::from_vec(vec![Complex::new(1.0, f64::INFINITY)]); + let v4 = ColFVec::from_vec(vec![Complex::new(1.0, f64::INFINITY)]); assert_eq!(v1, v2); assert_eq!(v3, v4); assert_ne!(v1, v3); @@ -3060,23 +3060,23 @@ mod tests { #[test] fn test_eq_trait_i32() { - let v1 = ColFVector::from_vec(vec![5, 6, 7]); - let v2 = ColFVector::from_vec(vec![5, 6, 7]); + let v1 = ColFVec::from_vec(vec![5, 6, 7]); + let v2 = ColFVec::from_vec(vec![5, 6, 7]); assert!(v1.eq(&v2)); } #[test] fn test_eq_trait_f64() { - let v1 = ColFVector::from_vec(vec![0.0, -0.0]); - let v2 = ColFVector::from_vec(vec![0.0, -0.0]); + let v1 = ColFVec::from_vec(vec![0.0, -0.0]); + let v2 = ColFVec::from_vec(vec![0.0, -0.0]); assert!(v1.eq(&v2)); } #[test] fn test_eq_trait_complex_f64() { use num::Complex; - let v1 = ColFVector::from_vec(vec![Complex::new(0.0, 1.0)]); - let v2 = ColFVector::from_vec(vec![Complex::new(0.0, 1.0)]); + let v1 = ColFVec::from_vec(vec![Complex::new(0.0, 1.0)]); + let v2 = ColFVec::from_vec(vec![Complex::new(0.0, 1.0)]); assert!(v1.eq(&v2)); } @@ -3087,9 +3087,9 @@ mod tests { // ================================ #[test] fn test_partial_ord_i32() { - let v1 = ColFVector::from_vec(vec![1, 2, 3]); - let v2 = ColFVector::from_vec(vec![1, 2, 4]); - let v3 = ColFVector::from_vec(vec![1, 2, 3]); + let v1 = ColFVec::from_vec(vec![1, 2, 3]); + let v2 = ColFVec::from_vec(vec![1, 2, 4]); + let v3 = ColFVec::from_vec(vec![1, 2, 3]); assert!(v1 < v2); assert!(v2 > v1); assert!(v1 <= v3); @@ -3101,9 +3101,9 @@ mod tests { #[test] fn test_ord_i32() { - let v1 = ColFVector::from_vec(vec![1, 2, 3]); - let v2 = ColFVector::from_vec(vec![1, 2, 4]); - let v3 = ColFVector::from_vec(vec![1, 2, 3]); + let v1 = ColFVec::from_vec(vec![1, 2, 3]); + let v2 = ColFVec::from_vec(vec![1, 2, 4]); + let v3 = ColFVec::from_vec(vec![1, 2, 3]); assert_eq!(v1.cmp(&v2), std::cmp::Ordering::Less); assert_eq!(v2.cmp(&v1), std::cmp::Ordering::Greater); assert_eq!(v1.cmp(&v3), std::cmp::Ordering::Equal); @@ -3111,9 +3111,9 @@ mod tests { #[test] fn test_partial_ord_f64() { - let v1 = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); - let v2 = ColFVector::from_vec(vec![1.0, 2.0, 4.0]); - let v3 = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = ColFVec::from_vec(vec![1.0, 2.0, 3.0]); + let v2 = ColFVec::from_vec(vec![1.0, 2.0, 4.0]); + let v3 = ColFVec::from_vec(vec![1.0, 2.0, 3.0]); assert_eq!(v1.partial_cmp(&v2), Some(std::cmp::Ordering::Less)); assert_eq!(v2.partial_cmp(&v1), Some(std::cmp::Ordering::Greater)); assert_eq!(v1.partial_cmp(&v3), Some(std::cmp::Ordering::Equal)); @@ -3121,8 +3121,8 @@ mod tests { #[test] fn test_partial_ord_f64_nan() { - let v1 = ColFVector::from_vec(vec![1.0, f64::NAN]); - let v2 = ColFVector::from_vec(vec![1.0, 2.0]); + let v1 = ColFVec::from_vec(vec![1.0, f64::NAN]); + let v2 = ColFVec::from_vec(vec![1.0, 2.0]); // Comparison with NaN yields None assert_eq!(v1.partial_cmp(&v2), None); assert_eq!(v2.partial_cmp(&v1), None); @@ -3130,10 +3130,10 @@ mod tests { #[test] fn test_partial_ord_f64_infinity() { - let v1 = ColFVector::from_vec(vec![1.0, f64::INFINITY]); - let v2 = ColFVector::from_vec(vec![1.0, f64::NEG_INFINITY]); - let v3 = ColFVector::from_vec(vec![1.0, f64::INFINITY]); - let v4 = ColFVector::from_vec(vec![1.0, 1.0]); + let v1 = ColFVec::from_vec(vec![1.0, f64::INFINITY]); + let v2 = ColFVec::from_vec(vec![1.0, f64::NEG_INFINITY]); + let v3 = ColFVec::from_vec(vec![1.0, f64::INFINITY]); + let v4 = ColFVec::from_vec(vec![1.0, 1.0]); // INFINITY > NEG_INFINITY assert_eq!(v1.partial_cmp(&v2), Some(std::cmp::Ordering::Greater)); assert_eq!(v2.partial_cmp(&v1), Some(std::cmp::Ordering::Less)); @@ -3151,9 +3151,9 @@ mod tests { // ================================ #[test] fn test_hash_i32() { - let v1 = ColFVector::from_vec(vec![1, 2, 3]); - let v2 = ColFVector::from_vec(vec![1, 2, 3]); - let v3 = ColFVector::from_vec(vec![3, 2, 1]); + let v1 = ColFVec::from_vec(vec![1, 2, 3]); + let v2 = ColFVec::from_vec(vec![1, 2, 3]); + let v3 = ColFVec::from_vec(vec![3, 2, 1]); let mut hasher1 = DefaultHasher::new(); v1.hash(&mut hasher1); @@ -3174,9 +3174,9 @@ mod tests { #[test] fn test_hash_complex_i32() { use num::Complex; - let v1 = ColFVector::from_vec(vec![Complex::new(1, 2), Complex::new(3, 4)]); - let v2 = ColFVector::from_vec(vec![Complex::new(1, 2), Complex::new(3, 4)]); - let v3 = ColFVector::from_vec(vec![Complex::new(4, 3), Complex::new(2, 1)]); + let v1 = ColFVec::from_vec(vec![Complex::new(1, 2), Complex::new(3, 4)]); + let v2 = ColFVec::from_vec(vec![Complex::new(1, 2), Complex::new(3, 4)]); + let v3 = ColFVec::from_vec(vec![Complex::new(4, 3), Complex::new(2, 1)]); let mut hasher1 = DefaultHasher::new(); v1.hash(&mut hasher1); @@ -3252,7 +3252,7 @@ mod tests { #[test] fn test_from_slice_i32() { let slice: &[i32] = &[4, 5, 6]; - let fv = ColFVector::from(slice); + let fv = ColFVec::from(slice); let fv_col: FlexVector = slice.into(); let fv_row: FlexVector = slice.into(); assert_eq!(fv.as_slice(), slice); @@ -3263,7 +3263,7 @@ mod tests { #[test] fn test_from_slice_f64() { let slice: &[f64] = &[4.4, 5.5, 6.6]; - let fv = ColFVector::from(slice); + let fv = ColFVec::from(slice); let fv_col: FlexVector = slice.into(); let fv_row: FlexVector = slice.into(); assert_eq!(fv.as_slice(), slice); @@ -3275,7 +3275,7 @@ mod tests { fn test_from_slice_complex_f64() { use num::Complex; let slice: &[Complex] = &[Complex::new(7.0, 8.0), Complex::new(9.0, 10.0)]; - let fv = ColFVector::from(slice); + let fv = ColFVec::from(slice); let fv_col: FlexVector, Column> = slice.into(); let fv_row: FlexVector, Row> = slice.into(); assert_eq!(fv.as_slice(), slice); @@ -3574,7 +3574,7 @@ mod tests { #[test] fn test_from_vectorslice_i32() { - let v = ColFVector::from_vec(vec![1, 2, 3, 4]); + let v = ColFVec::from_vec(vec![1, 2, 3, 4]); let vslice = v.as_vslice(1..3); let fv: FlexVector = FlexVector::from(vslice); assert_eq!(fv.as_slice(), &[2, 3]); @@ -3582,7 +3582,7 @@ mod tests { #[test] fn test_from_vectorslice_f64() { - let v = ColFVector::from_vec(vec![1.1, 2.2, 3.3, 4.4]); + let v = ColFVec::from_vec(vec![1.1, 2.2, 3.3, 4.4]); let vslice = v.as_vslice(0..2); let fv: FlexVector = FlexVector::from(vslice); assert_eq!(fv.as_slice(), &[1.1, 2.2]); @@ -3591,7 +3591,7 @@ mod tests { #[test] fn test_from_vectorslice_complex_f64() { use num::Complex; - let v = ColFVector::from_vec(vec![ + let v = ColFVec::from_vec(vec![ Complex::new(1.0, 2.0), Complex::new(3.0, 4.0), Complex::new(5.0, 6.0), @@ -3603,7 +3603,7 @@ mod tests { #[test] fn test_from_vectorslicemut_i32() { - let mut v = ColFVector::from_vec(vec![10, 20, 30, 40]); + let mut v = ColFVec::from_vec(vec![10, 20, 30, 40]); { let vslice_mut = v.as_mut_vslice(2..4); let fv: FlexVector = FlexVector::from(vslice_mut); @@ -3613,7 +3613,7 @@ mod tests { #[test] fn test_from_vectorslicemut_f64() { - let mut v = ColFVector::from_vec(vec![1.5, 2.5, 3.5]); + let mut v = ColFVec::from_vec(vec![1.5, 2.5, 3.5]); { let vslice_mut = v.as_mut_vslice(0..2); let fv: FlexVector = FlexVector::from(vslice_mut); @@ -3624,7 +3624,7 @@ mod tests { #[test] fn test_from_vectorslicemut_complex_f64() { use num::Complex; - let mut v = ColFVector::from_vec(vec![ + let mut v = ColFVec::from_vec(vec![ Complex::new(7.0, 8.0), Complex::new(9.0, 10.0), Complex::new(11.0, 12.0), @@ -3706,7 +3706,7 @@ mod tests { #[test] fn test_index_usize() { - let v = ColFVector::from_vec(vec![10, 20, 30, 40]); + let v = ColFVec::from_vec(vec![10, 20, 30, 40]); assert_eq!(v[0], 10); assert_eq!(v[1], 20); assert_eq!(v[3], 40); @@ -3715,13 +3715,13 @@ mod tests { #[test] #[should_panic] fn test_index_usize_out_of_bounds() { - let v = ColFVector::from_vec(vec![1, 2, 3]); + let v = ColFVec::from_vec(vec![1, 2, 3]); let _ = v[10]; } #[test] fn test_index_range() { - let v = ColFVector::from_vec(vec![1, 2, 3, 4, 5]); + let v = ColFVec::from_vec(vec![1, 2, 3, 4, 5]); assert_eq!(&v[1..4], &[2, 3, 4]); assert_eq!(&v[0..2], &[1, 2]); assert_eq!(&v[2..5], &[3, 4, 5]); @@ -3729,34 +3729,34 @@ mod tests { #[test] fn test_index_range_from() { - let v = ColFVector::from_vec(vec![1, 2, 3, 4, 5]); + let v = ColFVec::from_vec(vec![1, 2, 3, 4, 5]); assert_eq!(&v[2..], &[3, 4, 5]); assert_eq!(&v[0..], &[1, 2, 3, 4, 5]); } #[test] fn test_index_range_to() { - let v = ColFVector::from_vec(vec![1, 2, 3, 4, 5]); + let v = ColFVec::from_vec(vec![1, 2, 3, 4, 5]); assert_eq!(&v[..3], &[1, 2, 3]); assert_eq!(&v[..1], &[1]); } #[test] fn test_index_range_full() { - let v = ColFVector::from_vec(vec![1, 2, 3]); + let v = ColFVec::from_vec(vec![1, 2, 3]); assert_eq!(&v[..], &[1, 2, 3]); } #[test] fn test_index_range_inclusive() { - let v = ColFVector::from_vec(vec![1, 2, 3, 4, 5]); + let v = ColFVec::from_vec(vec![1, 2, 3, 4, 5]); assert_eq!(&v[1..=3], &[2, 3, 4]); assert_eq!(&v[0..=4], &[1, 2, 3, 4, 5]); } #[test] fn test_index_range_to_inclusive() { - let v = ColFVector::from_vec(vec![1, 2, 3, 4, 5]); + let v = ColFVec::from_vec(vec![1, 2, 3, 4, 5]); assert_eq!(&v[..=2], &[1, 2, 3]); assert_eq!(&v[..=0], &[1]); } @@ -3764,21 +3764,21 @@ mod tests { #[test] #[should_panic] fn test_index_range_out_of_bounds() { - let v = ColFVector::from_vec(vec![1, 2, 3]); + let v = ColFVec::from_vec(vec![1, 2, 3]); let _ = &v[2..5]; } #[test] #[should_panic] fn test_index_range_inclusive_out_of_bounds() { - let v = ColFVector::from_vec(vec![1, 2, 3]); + let v = ColFVec::from_vec(vec![1, 2, 3]); let _ = &v[1..=5]; } #[test] #[should_panic] fn test_index_range_to_inclusive_out_of_bounds() { - let v = ColFVector::from_vec(vec![1, 2, 3]); + let v = ColFVec::from_vec(vec![1, 2, 3]); let _ = &v[..=5]; } @@ -3790,7 +3790,7 @@ mod tests { #[test] fn test_index_mut_usize() { - let mut v = ColFVector::from_vec(vec![1, 2, 3]); + let mut v = ColFVec::from_vec(vec![1, 2, 3]); v[0] = 10; v[2] = 30; assert_eq!(v.as_slice(), &[10, 2, 30]); @@ -3799,48 +3799,48 @@ mod tests { #[test] #[should_panic] fn test_index_mut_usize_out_of_bounds() { - let mut v = ColFVector::from_vec(vec![1, 2, 3]); + let mut v = ColFVec::from_vec(vec![1, 2, 3]); v[10] = 99; } #[test] fn test_index_mut_range() { - let mut v = ColFVector::from_vec(vec![1, 2, 3, 4, 5]); + let mut v = ColFVec::from_vec(vec![1, 2, 3, 4, 5]); v[1..4].copy_from_slice(&[20, 30, 40]); assert_eq!(v.as_slice(), &[1, 20, 30, 40, 5]); } #[test] fn test_index_mut_range_from() { - let mut v = ColFVector::from_vec(vec![1, 2, 3, 4, 5]); + let mut v = ColFVec::from_vec(vec![1, 2, 3, 4, 5]); v[2..].copy_from_slice(&[99, 100, 101]); assert_eq!(v.as_slice(), &[1, 2, 99, 100, 101]); } #[test] fn test_index_mut_range_to() { - let mut v = ColFVector::from_vec(vec![1, 2, 3, 4, 5]); + let mut v = ColFVec::from_vec(vec![1, 2, 3, 4, 5]); v[..3].copy_from_slice(&[7, 8, 9]); assert_eq!(v.as_slice(), &[7, 8, 9, 4, 5]); } #[test] fn test_index_mut_range_full() { - let mut v = ColFVector::from_vec(vec![1, 2, 3]); + let mut v = ColFVec::from_vec(vec![1, 2, 3]); v[..].copy_from_slice(&[10, 20, 30]); assert_eq!(v.as_slice(), &[10, 20, 30]); } #[test] fn test_index_mut_range_inclusive() { - let mut v = ColFVector::from_vec(vec![1, 2, 3, 4, 5]); + let mut v = ColFVec::from_vec(vec![1, 2, 3, 4, 5]); v[1..=3].copy_from_slice(&[21, 31, 41]); assert_eq!(v.as_slice(), &[1, 21, 31, 41, 5]); } #[test] fn test_index_mut_range_to_inclusive() { - let mut v = ColFVector::from_vec(vec![1, 2, 3, 4, 5]); + let mut v = ColFVec::from_vec(vec![1, 2, 3, 4, 5]); v[..=2].copy_from_slice(&[100, 200, 300]); assert_eq!(v.as_slice(), &[100, 200, 300, 4, 5]); } @@ -3848,21 +3848,21 @@ mod tests { #[test] #[should_panic] fn test_index_mut_range_out_of_bounds() { - let mut v = ColFVector::from_vec(vec![1, 2, 3]); + let mut v = ColFVec::from_vec(vec![1, 2, 3]); v[2..5].copy_from_slice(&[9, 9, 9]); } #[test] #[should_panic] fn test_index_mut_range_inclusive_out_of_bounds() { - let mut v = ColFVector::from_vec(vec![1, 2, 3]); + let mut v = ColFVec::from_vec(vec![1, 2, 3]); v[1..=5].copy_from_slice(&[9, 9, 9, 9, 9]); } #[test] #[should_panic] fn test_index_mut_range_to_inclusive_out_of_bounds() { - let mut v = ColFVector::from_vec(vec![1, 2, 3]); + let mut v = ColFVec::from_vec(vec![1, 2, 3]); v[..=5].copy_from_slice(&[9, 9, 9, 9, 9, 9]); } @@ -3873,14 +3873,14 @@ mod tests { // ================================ #[test] fn test_extend_i32() { - let mut v = ColFVector::from_vec(vec![1, 2]); + let mut v = ColFVec::from_vec(vec![1, 2]); v.extend(vec![3, 4]); assert_eq!(v.as_slice(), &[1, 2, 3, 4]); } #[test] fn test_extend_f64() { - let mut v = ColFVector::from_vec(vec![1.1, 2.2]); + let mut v = ColFVec::from_vec(vec![1.1, 2.2]); v.extend(vec![3.3, 4.4]); assert_eq!(v.as_slice(), &[1.1, 2.2, 3.3, 4.4]); } @@ -3888,7 +3888,7 @@ mod tests { #[test] fn test_extend_complex_f64() { use num::Complex; - let mut v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0)]); + let mut v = ColFVec::from_vec(vec![Complex::new(1.0, 2.0)]); v.extend(vec![Complex::new(3.0, 4.0), Complex::new(5.0, 6.0)]); assert_eq!( v.as_slice(), @@ -3905,7 +3905,7 @@ mod tests { #[test] fn test_extend_with_empty() { - let mut v = ColFVector::from_vec(vec![1, 2]); + let mut v = ColFVec::from_vec(vec![1, 2]); v.extend(Vec::::new()); assert_eq!(v.as_slice(), &[1, 2]); } @@ -3918,21 +3918,21 @@ mod tests { // --- as_slice --- #[test] fn test_as_slice() { - let v = ColFVector::from_vec(vec![1, 2, 3]); + let v = ColFVec::from_vec(vec![1, 2, 3]); let slice = v.as_slice(); assert_eq!(slice, &[1, 2, 3]); } #[test] fn test_as_slice_f64() { - let v = ColFVector::from_vec(vec![1.1, 2.2, 3.3]); + let v = ColFVec::from_vec(vec![1.1, 2.2, 3.3]); let slice = v.as_slice(); assert_eq!(slice, &[1.1, 2.2, 3.3]); } #[test] fn test_as_slice_complex() { - let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = ColFVec::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let slice = v.as_slice(); assert_eq!(slice, &[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0),]); } @@ -3940,7 +3940,7 @@ mod tests { // --- len --- #[test] fn test_len() { - let v = ColFVector::from_vec(vec![10, 20, 30, 40]); + let v = ColFVec::from_vec(vec![10, 20, 30, 40]); assert_eq!(v.len(), 4); let empty = FlexVector::::new(); assert_eq!(empty.len(), 0); @@ -3948,7 +3948,7 @@ mod tests { #[test] fn test_len_f64() { - let v = ColFVector::from_vec(vec![10.0, 20.0]); + let v = ColFVec::from_vec(vec![10.0, 20.0]); assert_eq!(v.len(), 2); let empty = FlexVector::::new(); assert_eq!(empty.len(), 0); @@ -3956,7 +3956,7 @@ mod tests { #[test] fn test_len_complex() { - let v = ColFVector::from_vec(vec![Complex::new(1.0, 0.0)]); + let v = ColFVec::from_vec(vec![Complex::new(1.0, 0.0)]); assert_eq!(v.len(), 1); let empty = FlexVector::>::new(); assert_eq!(empty.len(), 0); @@ -3965,7 +3965,7 @@ mod tests { // --- is_empty --- #[test] fn test_is_empty() { - let v = ColFVector::from_vec(vec![1]); + let v = ColFVec::from_vec(vec![1]); assert!(!v.is_empty()); let empty = FlexVector::::new(); assert!(empty.is_empty()); @@ -3973,7 +3973,7 @@ mod tests { #[test] fn test_is_empty_f64() { - let v = ColFVector::from_vec(vec![1.0]); + let v = ColFVec::from_vec(vec![1.0]); assert!(!v.is_empty()); let empty = FlexVector::::new(); assert!(empty.is_empty()); @@ -3981,7 +3981,7 @@ mod tests { #[test] fn test_is_empty_complex() { - let v = ColFVector::from_vec(vec![Complex::new(0.0, 1.0)]); + let v = ColFVec::from_vec(vec![Complex::new(0.0, 1.0)]); assert!(!v.is_empty()); let empty = FlexVector::>::new(); assert!(empty.is_empty()); @@ -3990,7 +3990,7 @@ mod tests { // --- get --- #[test] fn test_get() { - let v = ColFVector::from_vec(vec![10, 20, 30]); + let v = ColFVec::from_vec(vec![10, 20, 30]); assert_eq!(v.get(0), Some(&10)); assert_eq!(v.get(2), Some(&30)); assert_eq!(v.get(3), None); @@ -3998,7 +3998,7 @@ mod tests { #[test] fn test_get_f64() { - let v = ColFVector::from_vec(vec![10.5, 20.5, 30.5]); + let v = ColFVec::from_vec(vec![10.5, 20.5, 30.5]); assert_eq!(v.get(0), Some(&10.5)); assert_eq!(v.get(2), Some(&30.5)); assert_eq!(v.get(3), None); @@ -4006,7 +4006,7 @@ mod tests { #[test] fn test_get_complex() { - let v = ColFVector::from_vec(vec![Complex::new(1.0, 1.0), Complex::new(2.0, 2.0)]); + let v = ColFVec::from_vec(vec![Complex::new(1.0, 1.0), Complex::new(2.0, 2.0)]); assert_eq!(v.get(0), Some(&Complex::new(1.0, 1.0))); assert_eq!(v.get(1), Some(&Complex::new(2.0, 2.0))); assert_eq!(v.get(2), None); @@ -4015,7 +4015,7 @@ mod tests { // --- first --- #[test] fn test_first() { - let v = ColFVector::from_vec(vec![5, 6, 7]); + let v = ColFVec::from_vec(vec![5, 6, 7]); assert_eq!(v.first(), Some(&5)); let empty = FlexVector::::new(); assert_eq!(empty.first(), None); @@ -4023,7 +4023,7 @@ mod tests { #[test] fn test_first_f64() { - let v = ColFVector::from_vec(vec![5.5, 6.5]); + let v = ColFVec::from_vec(vec![5.5, 6.5]); assert_eq!(v.first(), Some(&5.5)); let empty = FlexVector::::new(); assert_eq!(empty.first(), None); @@ -4031,7 +4031,7 @@ mod tests { #[test] fn test_first_complex() { - let v = ColFVector::from_vec(vec![Complex::new(7.0, 8.0), Complex::new(9.0, 10.0)]); + let v = ColFVec::from_vec(vec![Complex::new(7.0, 8.0), Complex::new(9.0, 10.0)]); assert_eq!(v.first(), Some(&Complex::new(7.0, 8.0))); let empty = FlexVector::>::new(); assert_eq!(empty.first(), None); @@ -4040,7 +4040,7 @@ mod tests { // --- last --- #[test] fn test_last() { - let v = ColFVector::from_vec(vec![1, 2, 3]); + let v = ColFVec::from_vec(vec![1, 2, 3]); assert_eq!(v.last(), Some(&3)); let empty = FlexVector::::new(); assert_eq!(empty.last(), None); @@ -4048,7 +4048,7 @@ mod tests { #[test] fn test_last_f64() { - let v = ColFVector::from_vec(vec![1.5, 2.5, 3.5]); + let v = ColFVec::from_vec(vec![1.5, 2.5, 3.5]); assert_eq!(v.last(), Some(&3.5)); let empty = FlexVector::::new(); assert_eq!(empty.last(), None); @@ -4056,7 +4056,7 @@ mod tests { #[test] fn test_last_complex() { - let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = ColFVec::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); assert_eq!(v.last(), Some(&Complex::new(3.0, 4.0))); let empty = FlexVector::>::new(); assert_eq!(empty.last(), None); @@ -4065,7 +4065,7 @@ mod tests { // --- iter --- #[test] fn test_iter() { - let v = ColFVector::from_vec(vec![1, 2, 3]); + let v = ColFVec::from_vec(vec![1, 2, 3]); let mut iter = v.iter(); assert_eq!(iter.next(), Some(&1)); assert_eq!(iter.next(), Some(&2)); @@ -4075,14 +4075,14 @@ mod tests { #[test] fn test_iter_f64() { - let v = ColFVector::from_vec(vec![1.1, 2.2, 3.3]); + let v = ColFVec::from_vec(vec![1.1, 2.2, 3.3]); let collected: Vec<_> = v.iter().copied().collect(); assert_eq!(collected, vec![1.1, 2.2, 3.3]); } #[test] fn test_iter_complex() { - let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = ColFVec::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let collected: Vec<_> = v.iter().cloned().collect(); assert_eq!(collected, vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0),]); } @@ -4090,21 +4090,21 @@ mod tests { // --- iter_rev --- #[test] fn test_iter_rev() { - let v = ColFVector::from_vec(vec![1, 2, 3]); + let v = ColFVec::from_vec(vec![1, 2, 3]); let collected: Vec<_> = v.iter_rev().copied().collect(); assert_eq!(collected, vec![3, 2, 1]); } #[test] fn test_iter_rev_f64() { - let v = ColFVector::from_vec(vec![1.1, 2.2, 3.3]); + let v = ColFVec::from_vec(vec![1.1, 2.2, 3.3]); let collected: Vec<_> = v.iter_rev().copied().collect(); assert_eq!(collected, vec![3.3, 2.2, 1.1]); } #[test] fn test_iter_rev_complex() { - let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = ColFVec::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let collected: Vec<_> = v.iter_rev().cloned().collect(); assert_eq!(collected, vec![Complex::new(3.0, 4.0), Complex::new(1.0, 2.0),]); } @@ -4112,21 +4112,21 @@ mod tests { // --- enumerate --- #[test] fn test_enumerate() { - let v = ColFVector::from_vec(vec![10, 20, 30]); + let v = ColFVec::from_vec(vec![10, 20, 30]); let pairs: Vec<_> = v.enumerate().collect(); assert_eq!(pairs, vec![(0, &10), (1, &20), (2, &30)]); } #[test] fn test_enumerate_f64() { - let v = ColFVector::from_vec(vec![1.5, 2.5]); + let v = ColFVec::from_vec(vec![1.5, 2.5]); let pairs: Vec<_> = v.enumerate().collect(); assert_eq!(pairs, vec![(0, &1.5), (1, &2.5)]); } #[test] fn test_enumerate_complex() { - let v = ColFVector::from_vec(vec![Complex::new(0.0, 1.0), Complex::new(2.0, 3.0)]); + let v = ColFVec::from_vec(vec![Complex::new(0.0, 1.0), Complex::new(2.0, 3.0)]); let pairs: Vec<_> = v.enumerate().collect(); assert_eq!(pairs, vec![(0, &Complex::new(0.0, 1.0)), (1, &Complex::new(2.0, 3.0)),]); } @@ -4134,21 +4134,21 @@ mod tests { // --- to_vec --- #[test] fn test_to_vec() { - let v = ColFVector::from_vec(vec![1, 2, 3]); + let v = ColFVec::from_vec(vec![1, 2, 3]); let vec_copy = v.to_vec(); assert_eq!(vec_copy, vec![1, 2, 3]); } #[test] fn test_to_vec_f64() { - let v = ColFVector::from_vec(vec![1.5, 2.5]); + let v = ColFVec::from_vec(vec![1.5, 2.5]); let vec_copy = v.to_vec(); assert_eq!(vec_copy, vec![1.5, 2.5]); } #[test] fn test_to_vec_complex() { - let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = ColFVec::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let vec_copy = v.to_vec(); assert_eq!(vec_copy, vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0),]); } @@ -4221,7 +4221,7 @@ mod tests { // --- pretty --- #[test] fn test_pretty() { - let v = ColFVector::from_vec(vec![1, 2, 3]); + let v = ColFVec::from_vec(vec![1, 2, 3]); let pretty = v.pretty(); assert!(pretty.contains("1")); assert!(pretty.contains("2")); @@ -4231,7 +4231,7 @@ mod tests { #[test] fn test_pretty_f64() { - let v = ColFVector::from_vec(vec![1.5, 2.5]); + let v = ColFVec::from_vec(vec![1.5, 2.5]); let pretty = v.pretty(); assert!(pretty.contains("1.5")); assert!(pretty.contains("2.5")); @@ -4240,7 +4240,7 @@ mod tests { #[test] fn test_pretty_complex() { - let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = ColFVec::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let pretty = v.pretty(); assert!(pretty.contains("1.0")); assert!(pretty.contains("2.0")); @@ -4252,21 +4252,21 @@ mod tests { // --- contains --- #[test] fn test_contains() { - let v = ColFVector::from_vec(vec![1, 2, 3]); + let v = ColFVec::from_vec(vec![1, 2, 3]); assert!(v.contains(&2)); assert!(!v.contains(&4)); } #[test] fn test_contains_f64() { - let v = ColFVector::from_vec(vec![1.1, 2.2, 3.3]); + let v = ColFVec::from_vec(vec![1.1, 2.2, 3.3]); assert!(v.contains(&2.2)); assert!(!v.contains(&4.4)); } #[test] fn test_contains_complex() { - let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = ColFVec::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); assert!(v.contains(&Complex::new(1.0, 2.0))); assert!(!v.contains(&Complex::new(0.0, 0.0))); } @@ -4274,21 +4274,21 @@ mod tests { // --- starts_with --- #[test] fn test_starts_with() { - let v = ColFVector::from_vec(vec![1, 2, 3]); + let v = ColFVec::from_vec(vec![1, 2, 3]); assert!(v.starts_with(&[1, 2])); assert!(!v.starts_with(&[2, 3])); } #[test] fn test_starts_with_f64() { - let v = ColFVector::from_vec(vec![1.1, 2.2, 3.3]); + let v = ColFVec::from_vec(vec![1.1, 2.2, 3.3]); assert!(v.starts_with(&[1.1, 2.2])); assert!(!v.starts_with(&[2.2, 3.3])); } #[test] fn test_starts_with_complex() { - let v = ColFVector::from_vec(vec![ + let v = ColFVec::from_vec(vec![ Complex::new(1.0, 2.0), Complex::new(3.0, 4.0), Complex::new(5.0, 6.0), @@ -4300,21 +4300,21 @@ mod tests { // --- ends_with --- #[test] fn test_ends_with() { - let v = ColFVector::from_vec(vec![1, 2, 3]); + let v = ColFVec::from_vec(vec![1, 2, 3]); assert!(v.ends_with(&[2, 3])); assert!(!v.ends_with(&[1, 2])); } #[test] fn test_ends_with_f64() { - let v = ColFVector::from_vec(vec![1.1, 2.2, 3.3]); + let v = ColFVec::from_vec(vec![1.1, 2.2, 3.3]); assert!(v.ends_with(&[2.2, 3.3])); assert!(!v.ends_with(&[1.1, 2.2])); } #[test] fn test_ends_with_complex() { - let v = ColFVector::from_vec(vec![ + let v = ColFVec::from_vec(vec![ Complex::new(1.0, 2.0), Complex::new(3.0, 4.0), Complex::new(5.0, 6.0), @@ -4326,21 +4326,21 @@ mod tests { // --- position --- #[test] fn test_position() { - let v = ColFVector::from_vec(vec![10, 20, 30]); + let v = ColFVec::from_vec(vec![10, 20, 30]); assert_eq!(v.position(|&x| x == 20), Some(1)); assert_eq!(v.position(|&x| x == 99), None); } #[test] fn test_position_f64() { - let v = ColFVector::from_vec(vec![10.0, 20.0, 30.0]); + let v = ColFVec::from_vec(vec![10.0, 20.0, 30.0]); assert_eq!(v.position(|&x| x == 20.0), Some(1)); assert_eq!(v.position(|&x| x == 99.0), None); } #[test] fn test_position_complex() { - let v = ColFVector::from_vec(vec![ + let v = ColFVec::from_vec(vec![ Complex::new(1.0, 1.0), Complex::new(2.0, 2.0), Complex::new(3.0, 3.0), @@ -4352,21 +4352,21 @@ mod tests { // --- rposition --- #[test] fn test_rposition() { - let v = ColFVector::from_vec(vec![1, 2, 3, 2]); + let v = ColFVec::from_vec(vec![1, 2, 3, 2]); assert_eq!(v.rposition(|&x| x == 2), Some(3)); assert_eq!(v.rposition(|&x| x == 99), None); } #[test] fn test_rposition_f64() { - let v = ColFVector::from_vec(vec![1.0, 2.0, 3.0, 2.0]); + let v = ColFVec::from_vec(vec![1.0, 2.0, 3.0, 2.0]); assert_eq!(v.rposition(|&x| x == 2.0), Some(3)); assert_eq!(v.rposition(|&x| x == 99.0), None); } #[test] fn test_rposition_complex() { - let v = ColFVector::from_vec(vec![ + let v = ColFVec::from_vec(vec![ Complex::new(1.0, 1.0), Complex::new(2.0, 2.0), Complex::new(3.0, 3.0), @@ -4379,21 +4379,21 @@ mod tests { // --- windows --- #[test] fn test_windows() { - let v = ColFVector::from_vec(vec![1, 2, 3, 4]); + let v = ColFVec::from_vec(vec![1, 2, 3, 4]); let windows: Vec<_> = v.windows(2).collect(); assert_eq!(windows, vec![&[1, 2][..], &[2, 3][..], &[3, 4][..]]); } #[test] fn test_windows_f64() { - let v = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); + let v = ColFVec::from_vec(vec![1.0, 2.0, 3.0]); let windows: Vec<_> = v.windows(2).collect(); assert_eq!(windows, vec![&[1.0, 2.0][..], &[2.0, 3.0][..]]); } #[test] fn test_windows_complex() { - let v = ColFVector::from_vec(vec![ + let v = ColFVec::from_vec(vec![ Complex::new(1.0, 0.0), Complex::new(2.0, 0.0), Complex::new(3.0, 0.0), @@ -4411,21 +4411,21 @@ mod tests { // --- chunks --- #[test] fn test_chunks() { - let v = ColFVector::from_vec(vec![1, 2, 3, 4, 5]); + let v = ColFVec::from_vec(vec![1, 2, 3, 4, 5]); let chunks: Vec<_> = v.chunks(2).collect(); assert_eq!(chunks, vec![&[1, 2][..], &[3, 4][..], &[5][..]]); } #[test] fn test_chunks_f64() { - let v = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); + let v = ColFVec::from_vec(vec![1.0, 2.0, 3.0]); let chunks: Vec<_> = v.chunks(2).collect(); assert_eq!(chunks, vec![&[1.0, 2.0][..], &[3.0][..]]); } #[test] fn test_chunks_complex() { - let v = ColFVector::from_vec(vec![ + let v = ColFVec::from_vec(vec![ Complex::new(1.0, 0.0), Complex::new(2.0, 0.0), Complex::new(3.0, 0.0), @@ -4443,7 +4443,7 @@ mod tests { // --- split_at --- #[test] fn test_split_at() { - let v = ColFVector::from_vec(vec![1, 2, 3, 4]); + let v = ColFVec::from_vec(vec![1, 2, 3, 4]); let (left, right) = v.split_at(2); assert_eq!(left, &[1, 2]); assert_eq!(right, &[3, 4]); @@ -4451,7 +4451,7 @@ mod tests { #[test] fn test_split_at_f64() { - let v = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); + let v = ColFVec::from_vec(vec![1.0, 2.0, 3.0]); let (left, right) = v.split_at(1); assert_eq!(left, &[1.0]); assert_eq!(right, &[2.0, 3.0]); @@ -4459,7 +4459,7 @@ mod tests { #[test] fn test_split_at_complex() { - let v = ColFVector::from_vec(vec![Complex::new(1.0, 0.0), Complex::new(2.0, 0.0)]); + let v = ColFVec::from_vec(vec![Complex::new(1.0, 0.0), Complex::new(2.0, 0.0)]); let (left, right) = v.split_at(1); assert_eq!(left, &[Complex::new(1.0, 0.0)]); assert_eq!(right, &[Complex::new(2.0, 0.0)]); @@ -4468,21 +4468,21 @@ mod tests { // --- split --- #[test] fn test_split() { - let v = ColFVector::from_vec(vec![1, 2, 0, 3, 0, 4]); + let v = ColFVec::from_vec(vec![1, 2, 0, 3, 0, 4]); let splits: Vec<_> = v.split(|&x| x == 0).collect(); assert_eq!(splits, vec![&[1, 2][..], &[3][..], &[4][..]]); } #[test] fn test_split_f64() { - let v = ColFVector::from_vec(vec![1.0, 0.0, 2.0, 0.0, 3.0]); + let v = ColFVec::from_vec(vec![1.0, 0.0, 2.0, 0.0, 3.0]); let splits: Vec<_> = v.split(|&x| x == 0.0).collect(); assert_eq!(splits, vec![&[1.0][..], &[2.0][..], &[3.0][..]]); } #[test] fn test_split_complex() { - let v = ColFVector::from_vec(vec![ + let v = ColFVec::from_vec(vec![ Complex::new(1.0, 0.0), Complex::new(0.0, 0.0), Complex::new(2.0, 0.0), @@ -4494,21 +4494,21 @@ mod tests { // --- splitn --- #[test] fn test_splitn() { - let v = ColFVector::from_vec(vec![1, 0, 2, 0, 3]); + let v = ColFVec::from_vec(vec![1, 0, 2, 0, 3]); let splits: Vec<_> = v.splitn(2, |&x| x == 0).collect(); assert_eq!(splits, vec![&[1][..], &[2, 0, 3][..]]); } #[test] fn test_splitn_f64() { - let v = ColFVector::from_vec(vec![1.0, 0.0, 2.0, 0.0, 3.0]); + let v = ColFVec::from_vec(vec![1.0, 0.0, 2.0, 0.0, 3.0]); let splits: Vec<_> = v.splitn(2, |&x| x == 0.0).collect(); assert_eq!(splits, vec![&[1.0][..], &[2.0, 0.0, 3.0][..]]); } #[test] fn test_splitn_complex() { - let v = ColFVector::from_vec(vec![ + let v = ColFVec::from_vec(vec![ Complex::new(1.0, 0.0), Complex::new(0.0, 0.0), Complex::new(2.0, 0.0), @@ -4528,21 +4528,21 @@ mod tests { // --- rsplit --- #[test] fn test_rsplit() { - let v = ColFVector::from_vec(vec![1, 0, 2, 0, 3]); + let v = ColFVec::from_vec(vec![1, 0, 2, 0, 3]); let splits: Vec<_> = v.rsplit(|&x| x == 0).collect(); assert_eq!(splits, vec![&[3][..], &[2][..], &[1][..]]); } #[test] fn test_rsplit_f64() { - let v = ColFVector::from_vec(vec![1.0, 0.0, 2.0, 0.0, 3.0]); + let v = ColFVec::from_vec(vec![1.0, 0.0, 2.0, 0.0, 3.0]); let splits: Vec<_> = v.rsplit(|&x| x == 0.0).collect(); assert_eq!(splits, vec![&[3.0][..], &[2.0][..], &[1.0][..]]); } #[test] fn test_rsplit_complex() { - let v = ColFVector::from_vec(vec![ + let v = ColFVec::from_vec(vec![ Complex::new(1.0, 0.0), Complex::new(0.0, 0.0), Complex::new(2.0, 0.0), @@ -4563,21 +4563,21 @@ mod tests { // --- rsplitn --- #[test] fn test_rsplitn() { - let v = ColFVector::from_vec(vec![1, 0, 2, 0, 3]); + let v = ColFVec::from_vec(vec![1, 0, 2, 0, 3]); let splits: Vec<_> = v.rsplitn(2, |&x| x == 0).collect(); assert_eq!(splits, vec![&[3][..], &[1, 0, 2][..]]); } #[test] fn test_rsplitn_f64() { - let v = ColFVector::from_vec(vec![1.0, 0.0, 2.0, 0.0, 3.0]); + let v = ColFVec::from_vec(vec![1.0, 0.0, 2.0, 0.0, 3.0]); let splits: Vec<_> = v.rsplitn(2, |&x| x == 0.0).collect(); assert_eq!(splits, vec![&[3.0][..], &[1.0, 0.0, 2.0][..]]); } #[test] fn test_rsplitn_complex() { - let v = ColFVector::from_vec(vec![ + let v = ColFVec::from_vec(vec![ Complex::new(1.0, 0.0), Complex::new(0.0, 0.0), Complex::new(2.0, 0.0), @@ -4731,7 +4731,7 @@ mod tests { // --- push --- #[test] fn test_push() { - let mut v = ColFVector::new(); + let mut v = ColFVec::new(); v.push(1); v.push(2); assert_eq!(v.as_slice(), &[1, 2]); @@ -4739,7 +4739,7 @@ mod tests { #[test] fn test_push_f64() { - let mut v = ColFVector::new(); + let mut v = ColFVec::new(); v.push(1.1); v.push(2.2); assert_eq!(v.as_slice(), &[1.1, 2.2]); @@ -4747,7 +4747,7 @@ mod tests { #[test] fn test_push_complex() { - let mut v = ColFVector::new(); + let mut v = ColFVec::new(); v.push(Complex::new(1.0, 2.0)); v.push(Complex::new(3.0, 4.0)); assert_eq!(v.as_slice(), &[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); @@ -4756,7 +4756,7 @@ mod tests { // --- pop --- #[test] fn test_pop() { - let mut v = ColFVector::from_vec(vec![1, 2]); + let mut v = ColFVec::from_vec(vec![1, 2]); assert_eq!(v.pop(), Some(2)); assert_eq!(v.pop(), Some(1)); assert_eq!(v.pop(), None); @@ -4764,7 +4764,7 @@ mod tests { #[test] fn test_pop_f64() { - let mut v = ColFVector::from_vec(vec![1.1, 2.2]); + let mut v = ColFVec::from_vec(vec![1.1, 2.2]); assert_eq!(v.pop(), Some(2.2)); assert_eq!(v.pop(), Some(1.1)); assert_eq!(v.pop(), None); @@ -4772,7 +4772,7 @@ mod tests { #[test] fn test_pop_complex() { - let mut v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let mut v = ColFVec::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); assert_eq!(v.pop(), Some(Complex::new(3.0, 4.0))); assert_eq!(v.pop(), Some(Complex::new(1.0, 2.0))); assert_eq!(v.pop(), None); @@ -4781,21 +4781,21 @@ mod tests { // --- insert --- #[test] fn test_insert() { - let mut v = ColFVector::from_vec(vec![1, 3]); + let mut v = ColFVec::from_vec(vec![1, 3]); v.insert(1, 2); assert_eq!(v.as_slice(), &[1, 2, 3]); } #[test] fn test_insert_f64() { - let mut v = ColFVector::from_vec(vec![1.1, 3.3]); + let mut v = ColFVec::from_vec(vec![1.1, 3.3]); v.insert(1, 2.2); assert_eq!(v.as_slice(), &[1.1, 2.2, 3.3]); } #[test] fn test_insert_complex() { - let mut v = ColFVector::from_vec(vec![Complex::new(1.0, 1.0), Complex::new(3.0, 3.0)]); + let mut v = ColFVec::from_vec(vec![Complex::new(1.0, 1.0), Complex::new(3.0, 3.0)]); v.insert(1, Complex::new(2.0, 2.0)); assert_eq!( v.as_slice(), @@ -4806,21 +4806,21 @@ mod tests { // --- remove --- #[test] fn test_remove() { - let mut v = ColFVector::from_vec(vec![1, 2, 3]); + let mut v = ColFVec::from_vec(vec![1, 2, 3]); assert_eq!(v.remove(1), 2); assert_eq!(v.as_slice(), &[1, 3]); } #[test] fn test_remove_f64() { - let mut v = ColFVector::from_vec(vec![1.1, 2.2, 3.3]); + let mut v = ColFVec::from_vec(vec![1.1, 2.2, 3.3]); assert_eq!(v.remove(1), 2.2); assert_eq!(v.as_slice(), &[1.1, 3.3]); } #[test] fn test_remove_complex() { - let mut v = ColFVector::from_vec(vec![ + let mut v = ColFVec::from_vec(vec![ Complex::new(1.0, 1.0), Complex::new(2.0, 2.0), Complex::new(3.0, 3.0), @@ -4832,7 +4832,7 @@ mod tests { // --- resize --- #[test] fn test_resize() { - let mut v = ColFVector::from_vec(vec![1, 2]); + let mut v = ColFVec::from_vec(vec![1, 2]); v.resize(4, 0); assert_eq!(v.as_slice(), &[1, 2, 0, 0]); v.resize(2, 0); @@ -4841,7 +4841,7 @@ mod tests { #[test] fn test_resize_f64() { - let mut v = ColFVector::from_vec(vec![1.1, 2.2]); + let mut v = ColFVec::from_vec(vec![1.1, 2.2]); v.resize(4, 0.0); assert_eq!(v.as_slice(), &[1.1, 2.2, 0.0, 0.0]); v.resize(1, 0.0); @@ -4850,7 +4850,7 @@ mod tests { #[test] fn test_resize_complex() { - let mut v = ColFVector::from_vec(vec![Complex::new(1.0, 1.0)]); + let mut v = ColFVec::from_vec(vec![Complex::new(1.0, 1.0)]); v.resize(3, Complex::new(0.0, 0.0)); assert_eq!( v.as_slice(), @@ -4863,7 +4863,7 @@ mod tests { // --- clear --- #[test] fn test_clear() { - let mut v = ColFVector::from_vec(vec![1, 2, 3]); + let mut v = ColFVec::from_vec(vec![1, 2, 3]); assert!(!v.is_empty()); v.clear(); assert!(v.is_empty()); @@ -4871,7 +4871,7 @@ mod tests { #[test] fn test_clear_f64() { - let mut v = ColFVector::from_vec(vec![1.1, 2.2, 3.3]); + let mut v = ColFVec::from_vec(vec![1.1, 2.2, 3.3]); assert!(!v.is_empty()); v.clear(); assert!(v.is_empty()); @@ -4879,7 +4879,7 @@ mod tests { #[test] fn test_clear_complex() { - let mut v = ColFVector::from_vec(vec![Complex::new(1.0, 1.0), Complex::new(2.0, 2.0)]); + let mut v = ColFVec::from_vec(vec![Complex::new(1.0, 1.0), Complex::new(2.0, 2.0)]); assert!(!v.is_empty()); v.clear(); assert!(v.is_empty()); @@ -4888,7 +4888,7 @@ mod tests { // --- get_mut --- #[test] fn test_get_mut_single() { - let mut v = ColFVector::from_vec(vec![10, 20, 30]); + let mut v = ColFVec::from_vec(vec![10, 20, 30]); if let Some(x) = v.get_mut(1) { *x = 99; } @@ -4897,13 +4897,13 @@ mod tests { #[test] fn test_get_mut_out_of_bounds() { - let mut v = ColFVector::from_vec(vec![1, 2, 3]); + let mut v = ColFVec::from_vec(vec![1, 2, 3]); assert!(v.get_mut(10).is_none()); } #[test] fn test_get_mut_range() { - let mut v = ColFVector::from_vec(vec![1, 2, 3, 4]); + let mut v = ColFVec::from_vec(vec![1, 2, 3, 4]); if let Some(slice) = v.get_mut(1..3) { slice[0] = 20; slice[1] = 30; @@ -4913,7 +4913,7 @@ mod tests { #[test] fn test_get_mut_full_range() { - let mut v = ColFVector::from_vec(vec![5, 6, 7]); + let mut v = ColFVec::from_vec(vec![5, 6, 7]); if let Some(slice) = v.get_mut(..) { for x in slice { *x *= 2; @@ -4925,7 +4925,7 @@ mod tests { // --- iter_mut --- #[test] fn test_iter_mut_i32() { - let mut v = ColFVector::from_vec(vec![1, 2, 3]); + let mut v = ColFVec::from_vec(vec![1, 2, 3]); for x in v.iter_mut() { *x *= 2; } @@ -4934,7 +4934,7 @@ mod tests { #[test] fn test_iter_mut_f64() { - let mut v = ColFVector::from_vec(vec![1.5, -2.0, 0.0]); + let mut v = ColFVec::from_vec(vec![1.5, -2.0, 0.0]); for x in v.iter_mut() { *x += 1.0; } @@ -4944,7 +4944,7 @@ mod tests { #[test] fn test_iter_mut_complex() { use num::Complex; - let mut v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + let mut v = ColFVec::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); for x in v.iter_mut() { x.re += 1.0; x.im *= 2.0; @@ -4954,7 +4954,7 @@ mod tests { #[test] fn test_iter_mut_empty() { - let mut v = ColFVector::::new(); + let mut v = ColFVec::::new(); let mut count = 0; for _ in v.iter_mut() { count += 1; @@ -4965,7 +4965,7 @@ mod tests { // --- as_mut_slice --- #[test] fn test_as_mut_slice_i32() { - let mut v = ColFVector::from_vec(vec![1, 2, 3]); + let mut v = ColFVec::from_vec(vec![1, 2, 3]); let slice = v.as_mut_slice(); slice[0] = 10; slice[2] = 30; @@ -4974,7 +4974,7 @@ mod tests { #[test] fn test_as_mut_slice_f64() { - let mut v = ColFVector::from_vec(vec![1.1, 2.2, 3.3]); + let mut v = ColFVec::from_vec(vec![1.1, 2.2, 3.3]); let slice = v.as_mut_slice(); slice[1] = 9.9; assert_eq!(v.as_slice(), &[1.1, 9.9, 3.3]); @@ -4983,7 +4983,7 @@ mod tests { #[test] fn test_as_mut_slice_complex() { use num::Complex; - let mut v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let mut v = ColFVec::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let slice = v.as_mut_slice(); slice[0].re = 10.0; slice[1].im = 40.0; @@ -4992,7 +4992,7 @@ mod tests { #[test] fn test_as_mut_slice_empty() { - let mut v = ColFVector::::new(); + let mut v = ColFVec::::new(); let slice = v.as_mut_slice(); assert_eq!(slice.len(), 0); } @@ -5000,7 +5000,7 @@ mod tests { // --- map --- #[test] fn test_map_i32() { - let v = ColFVector::from_vec(vec![1, 2, 3]); + let v = ColFVec::from_vec(vec![1, 2, 3]); let squared = v.map(|x| x * x); assert_eq!(squared.as_slice(), &[1, 4, 9]); // original unchanged @@ -5009,7 +5009,7 @@ mod tests { #[test] fn test_map_f64() { - let v: FlexVector = ColFVector::from_vec(vec![1.5, -2.0, 0.0]); + let v: FlexVector = ColFVec::from_vec(vec![1.5, -2.0, 0.0]); let abs = v.map(|x| x.abs()); assert_eq!(abs.as_slice(), &[1.5, 2.0, 0.0]); } @@ -5017,7 +5017,7 @@ mod tests { #[test] fn test_map_complex() { use num::Complex; - let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + let v = ColFVec::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); let conj = v.map(|x| x.conj()); assert_eq!(conj.as_slice(), &[Complex::new(1.0, -2.0), Complex::new(-3.0, -4.0)]); } @@ -5031,7 +5031,7 @@ mod tests { #[test] fn test_map_with_fn_pointer() { - let v = ColFVector::from_vec(vec![1, 2, 3]); + let v = ColFVec::from_vec(vec![1, 2, 3]); let squared = v.map(square); assert_eq!(squared.as_slice(), &[1, 4, 9]); } @@ -5039,21 +5039,21 @@ mod tests { // --- mut_map --- #[test] fn test_mut_map_i32() { - let mut v = ColFVector::from_vec(vec![1, 2, 3]); + let mut v = ColFVec::from_vec(vec![1, 2, 3]); v.mut_map(|x| x * 10); assert_eq!(v.as_slice(), &[10, 20, 30]); } #[test] fn test_mut_map_f64() { - let mut v = ColFVector::from_vec(vec![1.5, -2.0, 0.0]); + let mut v = ColFVec::from_vec(vec![1.5, -2.0, 0.0]); v.mut_map(|x| x + 1.0); assert_eq!(v.as_slice(), &[2.5, -1.0, 1.0]); } #[test] fn test_mut_map_complex() { - let mut v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + let mut v = ColFVec::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); v.mut_map(|x| Complex::new(x.re + 1.0, x.im * 2.0)); assert_eq!(v.as_slice(), &[Complex::new(2.0, 4.0), Complex::new(-2.0, 8.0)]); } @@ -5067,7 +5067,7 @@ mod tests { #[test] fn test_mut_map_with_fn_pointer() { - let mut v = ColFVector::from_vec(vec![1, 2, 3]); + let mut v = ColFVec::from_vec(vec![1, 2, 3]); v.mut_map(double); assert_eq!(v.as_slice(), &[2, 4, 6]); } @@ -5075,28 +5075,28 @@ mod tests { // --- flat_map --- #[test] fn test_flat_map_i32_basic() { - let v = ColFVector::from_vec(vec![1, 2, 3]); + let v = ColFVec::from_vec(vec![1, 2, 3]); let flat = v.flat_map(|x| vec![x, x * 10]); assert_eq!(flat.as_slice(), &[1, 10, 2, 20, 3, 30]); } #[test] fn test_flat_map_i32_empty_inner() { - let v = ColFVector::from_vec(vec![1, 2, 3]); + let v = ColFVec::from_vec(vec![1, 2, 3]); let flat = v.flat_map(|x| if x % 2 == 0 { vec![] } else { vec![x] }); assert_eq!(flat.as_slice(), &[1, 3]); } #[test] fn test_flat_map_i32_option() { - let v = ColFVector::from_vec(vec![1, 2, 3, 4]); + let v = ColFVec::from_vec(vec![1, 2, 3, 4]); let flat = v.flat_map(|x| if x % 2 == 0 { Some(x) } else { None }); assert_eq!(flat.as_slice(), &[2, 4]); } #[test] fn test_flat_map_f64_basic() { - let v = ColFVector::from_vec(vec![1.0, 2.0]); + let v = ColFVec::from_vec(vec![1.0, 2.0]); let flat = v.flat_map(|x| vec![x, x + 0.5]); assert_eq!(flat.as_slice(), &[1.0, 1.5, 2.0, 2.5]); } @@ -5110,7 +5110,7 @@ mod tests { #[test] fn test_flat_map_f64_nan_infinity() { - let v = ColFVector::from_vec(vec![f64::NAN, f64::INFINITY, 1.0]); + let v = ColFVec::from_vec(vec![f64::NAN, f64::INFINITY, 1.0]); let flat = v.flat_map(|x| vec![x]); assert!(flat.as_slice()[0].is_nan()); assert_eq!(flat.as_slice()[1], f64::INFINITY); @@ -5120,7 +5120,7 @@ mod tests { #[test] fn test_flat_map_complex_f64_basic() { use num::Complex; - let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = ColFVec::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let flat = v.flat_map(|z| vec![z, z.conj()]); assert_eq!( flat.as_slice(), @@ -5136,7 +5136,7 @@ mod tests { #[test] fn test_flat_map_complex_f64_empty_inner() { use num::Complex; - let v = ColFVector::from_vec(vec![Complex::new(0.0, 0.0), Complex::new(1.0, 1.0)]); + let v = ColFVec::from_vec(vec![Complex::new(0.0, 0.0), Complex::new(1.0, 1.0)]); let flat = v.flat_map(|z| if z == Complex::new(0.0, 0.0) { vec![] } else { vec![z] }); assert_eq!(flat.as_slice(), &[Complex::new(1.0, 1.0)]); } @@ -5144,7 +5144,7 @@ mod tests { #[test] fn test_flat_map_complex_f64_option() { use num::Complex; - let v = ColFVector::from_vec(vec![Complex::new(1.0, 0.0), Complex::new(0.0, 0.0)]); + let v = ColFVec::from_vec(vec![Complex::new(1.0, 0.0), Complex::new(0.0, 0.0)]); let flat = v.flat_map(|z| if z.im == 0.0 { Some(z) } else { None }); assert_eq!(flat.as_slice(), &[Complex::new(1.0, 0.0), Complex::new(0.0, 0.0)]); } @@ -5158,7 +5158,7 @@ mod tests { #[test] fn test_flat_map_all_empty_inner() { - let v = ColFVector::from_vec(vec![1, 2, 3]); + let v = ColFVec::from_vec(vec![1, 2, 3]); let flat = v.flat_map(|_| Vec::::new()); assert!(flat.is_empty()); } @@ -5167,35 +5167,35 @@ mod tests { #[test] fn test_filter_i32_basic() { - let v = ColFVector::from_vec(vec![1, 2, 3, 4, 5]); + let v = ColFVec::from_vec(vec![1, 2, 3, 4, 5]); let filtered = v.filter(|&x| x % 2 == 0); assert_eq!(filtered.as_slice(), &[2, 4]); } #[test] fn test_filter_i32_all_false() { - let v = ColFVector::from_vec(vec![1, 3, 5]); + let v = ColFVec::from_vec(vec![1, 3, 5]); let filtered = v.filter(|&x| x > 10); assert!(filtered.is_empty()); } #[test] fn test_filter_i32_all_true() { - let v = ColFVector::from_vec(vec![1, 2, 3]); + let v = ColFVec::from_vec(vec![1, 2, 3]); let filtered = v.filter(|_| true); assert_eq!(filtered.as_slice(), &[1, 2, 3]); } #[test] fn test_filter_f64_positive() { - let v = ColFVector::from_vec(vec![-1.5, 0.0, 2.5, 3.3]); + let v = ColFVec::from_vec(vec![-1.5, 0.0, 2.5, 3.3]); let filtered = v.filter(|&x| x > 0.0); assert_eq!(filtered.as_slice(), &[2.5, 3.3]); } #[test] fn test_filter_f64_nan() { - let v = ColFVector::from_vec(vec![1.0, f64::NAN, 2.0]); + let v = ColFVec::from_vec(vec![1.0, f64::NAN, 2.0]); let filtered = v.filter(|&x| x.is_nan()); assert_eq!(filtered.len(), 1); assert!(filtered[0].is_nan()); @@ -5204,7 +5204,7 @@ mod tests { #[test] fn test_filter_complex_f64_real_positive() { use num::Complex; - let v = ColFVector::from_vec(vec![ + let v = ColFVec::from_vec(vec![ Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0), Complex::new(0.0, 0.0), @@ -5216,7 +5216,7 @@ mod tests { #[test] fn test_filter_complex_f64_imag_nonzero() { use num::Complex; - let v = ColFVector::from_vec(vec![ + let v = ColFVec::from_vec(vec![ Complex::new(1.0, 0.0), Complex::new(0.0, 2.0), Complex::new(3.0, 0.0), @@ -5227,7 +5227,7 @@ mod tests { #[test] fn test_filter_empty() { - let v: FlexVector = ColFVector::new(); + let v: FlexVector = ColFVec::new(); let filtered = v.filter(|&x| x > 0); assert!(filtered.is_empty()); } @@ -5236,42 +5236,42 @@ mod tests { #[test] fn test_reduce_i32_sum() { - let v = ColFVector::from_vec(vec![1, 2, 3, 4]); + let v = ColFVec::from_vec(vec![1, 2, 3, 4]); let sum = v.reduce(|a, b| a + b); assert_eq!(sum, Some(10)); } #[test] fn test_reduce_i32_product() { - let v = ColFVector::from_vec(vec![2, 3, 4]); + let v = ColFVec::from_vec(vec![2, 3, 4]); let product = v.reduce(|a, b| a * b); assert_eq!(product, Some(24)); } #[test] fn test_reduce_i32_empty() { - let v: FlexVector = ColFVector::new(); + let v: FlexVector = ColFVec::new(); let result = v.reduce(|a, b| a + b); assert_eq!(result, None); } #[test] fn test_reduce_f64_sum() { - let v = ColFVector::from_vec(vec![1.5, 2.5, 3.0]); + let v = ColFVec::from_vec(vec![1.5, 2.5, 3.0]); let sum = v.reduce(|a, b| a + b); assert_eq!(sum, Some(7.0)); } #[test] fn test_reduce_f64_product() { - let v = ColFVector::from_vec(vec![1.5, 2.0, 3.0]); + let v = ColFVec::from_vec(vec![1.5, 2.0, 3.0]); let product = v.reduce(|a, b| a * b); assert!((product.unwrap() - 9.0).abs() < 1e-12); } #[test] fn test_reduce_f64_empty() { - let v: FlexVector = ColFVector::new(); + let v: FlexVector = ColFVec::new(); let result = v.reduce(|a, b| a + b); assert_eq!(result, None); } @@ -5279,7 +5279,7 @@ mod tests { #[test] fn test_reduce_complex_f64_sum() { use num::Complex; - let v = ColFVector::from_vec(vec![ + let v = ColFVec::from_vec(vec![ Complex::new(1.0, 2.0), Complex::new(3.0, 4.0), Complex::new(5.0, 6.0), @@ -5291,7 +5291,7 @@ mod tests { #[test] fn test_reduce_complex_f64_product() { use num::Complex; - let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = ColFVec::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let product = v.reduce(|a, b| a * b); assert_eq!(product, Some(Complex::new(-5.0, 10.0))); // (1+2i)*(3+4i) = -5+10i } @@ -5299,7 +5299,7 @@ mod tests { #[test] fn test_reduce_complex_f64_empty() { use num::Complex; - let v: FlexVector> = ColFVector::new(); + let v: FlexVector> = ColFVec::new(); let result = v.reduce(|a, b| a + b); assert_eq!(result, None); } @@ -5308,42 +5308,42 @@ mod tests { #[test] fn test_fold_i32_sum() { - let v = ColFVector::from_vec(vec![1, 2, 3, 4]); + let v = ColFVec::from_vec(vec![1, 2, 3, 4]); let sum = v.fold(0, |acc, x| acc + x); assert_eq!(sum, 10); } #[test] fn test_fold_i32_product() { - let v = ColFVector::from_vec(vec![2, 3, 4]); + let v = ColFVec::from_vec(vec![2, 3, 4]); let product = v.fold(1, |acc, x| acc * x); assert_eq!(product, 24); } #[test] fn test_fold_i32_empty() { - let v: FlexVector = ColFVector::new(); + let v: FlexVector = ColFVec::new(); let sum = v.fold(0, |acc, x| acc + x); assert_eq!(sum, 0); } #[test] fn test_fold_f64_sum() { - let v = ColFVector::from_vec(vec![1.5, 2.5, 3.0]); + let v = ColFVec::from_vec(vec![1.5, 2.5, 3.0]); let sum = v.fold(0.0, |acc, x| acc + x); assert!((sum - 7.0).abs() < 1e-12); } #[test] fn test_fold_f64_product() { - let v = ColFVector::from_vec(vec![1.5, 2.0, 3.0]); + let v = ColFVec::from_vec(vec![1.5, 2.0, 3.0]); let product = v.fold(1.0, |acc, x| acc * x); assert!((product - 9.0).abs() < 1e-12); } #[test] fn test_fold_f64_empty() { - let v: FlexVector = ColFVector::new(); + let v: FlexVector = ColFVec::new(); let sum = v.fold(0.0, |acc, x| acc + x); assert_eq!(sum, 0.0); } @@ -5351,7 +5351,7 @@ mod tests { #[test] fn test_fold_complex_f64_sum() { use num::Complex; - let v = ColFVector::from_vec(vec![ + let v = ColFVec::from_vec(vec![ Complex::new(1.0, 2.0), Complex::new(3.0, 4.0), Complex::new(5.0, 6.0), @@ -5363,7 +5363,7 @@ mod tests { #[test] fn test_fold_complex_f64_product() { use num::Complex; - let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = ColFVec::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let product = v.fold(Complex::new(1.0, 0.0), |acc, x| acc * x); assert_eq!(product, Complex::new(-5.0, 10.0)); // (1+0i)*(1+2i)*(3+4i) = (1+2i)*(3+4i) = -5+10i } @@ -5371,7 +5371,7 @@ mod tests { #[test] fn test_fold_complex_f64_empty() { use num::Complex; - let v: FlexVector> = ColFVector::new(); + let v: FlexVector> = ColFVec::new(); let sum = v.fold(Complex::new(0.0, 0.0), |acc, x| acc + x); assert_eq!(sum, Complex::new(0.0, 0.0)); } @@ -5379,7 +5379,7 @@ mod tests { // --- zip --- #[test] fn test_zip_i32() { - let v1 = ColFVector::from_vec(vec![1, 2, 3]); + let v1 = ColFVec::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); let zipped = v1.zip(v2); assert_eq!(zipped.as_slice(), &[(1, 4), (2, 5), (3, 6)]); @@ -5387,8 +5387,8 @@ mod tests { #[test] fn test_zip_f64() { - let v1 = ColFVector::from_vec(vec![1.1, 2.2, 3.3]); - let v2 = ColFVector::from_vec(vec![4.4, 5.5, 6.6]); + let v1 = ColFVec::from_vec(vec![1.1, 2.2, 3.3]); + let v2 = ColFVec::from_vec(vec![4.4, 5.5, 6.6]); let zipped = v1.zip(v2); assert_eq!(zipped.as_slice(), &[(1.1, 4.4), (2.2, 5.5), (3.3, 6.6)]); } @@ -5396,8 +5396,8 @@ mod tests { #[test] fn test_zip_complex_f64() { use num::Complex; - let v1 = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); - let v2 = ColFVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); + let v1 = ColFVec::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v2 = ColFVec::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); let zipped = v1.zip(v2); assert_eq!( zipped.as_slice(), @@ -5410,16 +5410,16 @@ mod tests { #[test] fn test_zip_different_lengths() { - let v1 = ColFVector::from_vec(vec![1, 2]); - let v2 = ColFVector::from_vec(vec![3, 4, 5]); + let v1 = ColFVec::from_vec(vec![1, 2]); + let v2 = ColFVec::from_vec(vec![3, 4, 5]); let zipped = v1.zip(v2); assert_eq!(zipped.as_slice(), &[(1, 3), (2, 4)]); } #[test] fn test_zip_empty() { - let v1: FlexVector = ColFVector::new(); - let v2: FlexVector = ColFVector::new(); + let v1: FlexVector = ColFVec::new(); + let v2: FlexVector = ColFVec::new(); let zipped = v1.zip(v2); assert!(zipped.is_empty()); } @@ -5427,16 +5427,16 @@ mod tests { // --- zip_with --- #[test] fn test_zip_with_i32() { - let v1 = ColFVector::from_vec(vec![1, 2, 3]); - let v2 = ColFVector::from_vec(vec![4, 5, 6]); + let v1 = ColFVec::from_vec(vec![1, 2, 3]); + let v2 = ColFVec::from_vec(vec![4, 5, 6]); let summed = v1.zip_with(v2, |a, b| a + b); assert_eq!(summed.as_slice(), &[5, 7, 9]); } #[test] fn test_zip_with_f64() { - let v1 = ColFVector::from_vec(vec![1.5, 2.5, 3.5]); - let v2 = ColFVector::from_vec(vec![4.5, 5.5, 6.5]); + let v1 = ColFVec::from_vec(vec![1.5, 2.5, 3.5]); + let v2 = ColFVec::from_vec(vec![4.5, 5.5, 6.5]); let prod = v1.zip_with(v2, |a, b| a * b); assert_eq!(prod.as_slice(), &[6.75, 13.75, 22.75]); } @@ -5444,16 +5444,16 @@ mod tests { #[test] fn test_zip_with_complex_f64() { use num::Complex; - let v1 = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); - let v2 = ColFVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); + let v1 = ColFVec::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v2 = ColFVec::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); let sum = v1.zip_with(v2, |a, b| a + b); assert_eq!(sum.as_slice(), &[Complex::new(6.0, 8.0), Complex::new(10.0, 12.0)]); } #[test] fn test_zip_with_different_lengths() { - let v1 = ColFVector::from_vec(vec![1, 2]); - let v2 = ColFVector::from_vec(vec![10, 20, 30]); + let v1 = ColFVec::from_vec(vec![1, 2]); + let v2 = ColFVec::from_vec(vec![10, 20, 30]); let zipped = v1.zip_with(v2, |a, b| a + b); assert_eq!(zipped.as_slice(), &[11, 22]); } @@ -5470,28 +5470,28 @@ mod tests { #[test] fn test_step_by_i32_basic() { - let v = ColFVector::from_vec(vec![1, 2, 3, 4, 5, 6]); + let v = ColFVec::from_vec(vec![1, 2, 3, 4, 5, 6]); let stepped = v.step_by(2).unwrap(); assert_eq!(stepped.as_slice(), &[1, 3, 5]); } #[test] fn test_step_by_i32_step_one() { - let v = ColFVector::from_vec(vec![10, 20, 30]); + let v = ColFVec::from_vec(vec![10, 20, 30]); let stepped = v.step_by(1).unwrap(); assert_eq!(stepped.as_slice(), &[10, 20, 30]); } #[test] fn test_step_by_i32_step_equals_len() { - let v = ColFVector::from_vec(vec![7, 8, 9]); + let v = ColFVec::from_vec(vec![7, 8, 9]); let stepped = v.step_by(3).unwrap(); assert_eq!(stepped.as_slice(), &[7]); } #[test] fn test_step_by_i32_step_greater_than_len() { - let v = ColFVector::from_vec(vec![1, 2, 3]); + let v = ColFVec::from_vec(vec![1, 2, 3]); let stepped = v.step_by(5).unwrap(); assert_eq!(stepped.as_slice(), &[1]); } @@ -5505,14 +5505,14 @@ mod tests { #[test] fn test_step_by_i32_zero_step() { - let v = ColFVector::from_vec(vec![1, 2, 3]); + let v = ColFVec::from_vec(vec![1, 2, 3]); let result = v.step_by(0); assert!(result.is_err()); } #[test] fn test_step_by_f64_basic() { - let v = ColFVector::from_vec(vec![1.0, 2.0, 3.0, 4.0]); + let v = ColFVec::from_vec(vec![1.0, 2.0, 3.0, 4.0]); let stepped = v.step_by(2).unwrap(); assert_eq!(stepped.as_slice(), &[1.0, 3.0]); } @@ -5526,7 +5526,7 @@ mod tests { #[test] fn test_step_by_f64_zero_step() { - let v = ColFVector::from_vec(vec![1.1, 2.2]); + let v = ColFVec::from_vec(vec![1.1, 2.2]); let result = v.step_by(0); assert!(result.is_err()); } @@ -5534,7 +5534,7 @@ mod tests { #[test] fn test_step_by_complex_f64_basic() { use num::Complex; - let v = ColFVector::from_vec(vec![ + let v = ColFVec::from_vec(vec![ Complex::new(1.0, 2.0), Complex::new(3.0, 4.0), Complex::new(5.0, 6.0), @@ -5555,7 +5555,7 @@ mod tests { #[test] fn test_step_by_complex_f64_zero_step() { use num::Complex; - let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0)]); + let v = ColFVec::from_vec(vec![Complex::new(1.0, 2.0)]); let result = v.step_by(0); assert!(result.is_err()); } @@ -5564,28 +5564,28 @@ mod tests { #[test] fn test_create_mask_i32_greater_than() { - let v = ColFVector::from_vec(vec![1, 5, 3, 7]); + let v = ColFVec::from_vec(vec![1, 5, 3, 7]); let mask = v.create_mask(|&x| x > 3); assert_eq!(mask.as_slice(), &[false, true, false, true]); } #[test] fn test_create_mask_i32_even() { - let v = ColFVector::from_vec(vec![2, 3, 4, 5]); + let v = ColFVec::from_vec(vec![2, 3, 4, 5]); let mask = v.create_mask(|&x| x % 2 == 0); assert_eq!(mask.as_slice(), &[true, false, true, false]); } #[test] fn test_create_mask_f64_positive() { - let v = ColFVector::from_vec(vec![-1.0, 0.0, 2.5, -3.3]); + let v = ColFVec::from_vec(vec![-1.0, 0.0, 2.5, -3.3]); let mask = v.create_mask(|&x| x > 0.0); assert_eq!(mask.as_slice(), &[false, false, true, false]); } #[test] fn test_create_mask_f64_nan() { - let v = ColFVector::from_vec(vec![1.0, f64::NAN, 2.0]); + let v = ColFVec::from_vec(vec![1.0, f64::NAN, 2.0]); let mask = v.create_mask(|&x| x.is_nan()); assert_eq!(mask.as_slice(), &[false, true, false]); } @@ -5593,7 +5593,7 @@ mod tests { #[test] fn test_create_mask_complex_f64_real_positive() { use num::Complex; - let v = ColFVector::from_vec(vec![ + let v = ColFVec::from_vec(vec![ Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0), Complex::new(0.0, 0.0), @@ -5605,7 +5605,7 @@ mod tests { #[test] fn test_create_mask_complex_f64_imag_nonzero() { use num::Complex; - let v = ColFVector::from_vec(vec![ + let v = ColFVec::from_vec(vec![ Complex::new(1.0, 0.0), Complex::new(0.0, 2.0), Complex::new(3.0, 0.0), @@ -5616,7 +5616,7 @@ mod tests { #[test] fn test_create_mask_empty() { - let v: FlexVector = ColFVector::new(); + let v: FlexVector = ColFVec::new(); let mask = v.create_mask(|&x| x > 0); assert!(mask.is_empty()); } @@ -5625,7 +5625,7 @@ mod tests { #[test] fn test_filter_by_mask_i32_basic() { - let v = ColFVector::from_vec(vec![10, 20, 30, 40]); + let v = ColFVec::from_vec(vec![10, 20, 30, 40]); let mask = vec![false, true, false, true]; let filtered = v.filter_by_mask(&mask).unwrap(); assert_eq!(filtered.as_slice(), &[20, 40]); @@ -5633,7 +5633,7 @@ mod tests { #[test] fn test_filter_by_mask_i32_all_true() { - let v = ColFVector::from_vec(vec![1, 2, 3]); + let v = ColFVec::from_vec(vec![1, 2, 3]); let mask = vec![true, true, true]; let filtered = v.filter_by_mask(&mask).unwrap(); assert_eq!(filtered.as_slice(), &[1, 2, 3]); @@ -5641,7 +5641,7 @@ mod tests { #[test] fn test_filter_by_mask_i32_all_false() { - let v = ColFVector::from_vec(vec![1, 2, 3]); + let v = ColFVec::from_vec(vec![1, 2, 3]); let mask = vec![false, false, false]; let filtered = v.filter_by_mask(&mask).unwrap(); assert!(filtered.is_empty()); @@ -5657,7 +5657,7 @@ mod tests { #[test] fn test_filter_by_mask_i32_mismatched_length() { - let v = ColFVector::from_vec(vec![1, 2, 3]); + let v = ColFVec::from_vec(vec![1, 2, 3]); let mask = vec![true, false]; let result = v.filter_by_mask(&mask); assert!(result.is_err()); @@ -5665,7 +5665,7 @@ mod tests { #[test] fn test_filter_by_mask_f64_basic() { - let v = ColFVector::from_vec(vec![1.1, 2.2, 3.3, 4.4]); + let v = ColFVec::from_vec(vec![1.1, 2.2, 3.3, 4.4]); let mask = vec![true, false, true, false]; let filtered = v.filter_by_mask(&mask).unwrap(); assert_eq!(filtered.as_slice(), &[1.1, 3.3]); @@ -5673,7 +5673,7 @@ mod tests { #[test] fn test_filter_by_mask_f64_all_true() { - let v = ColFVector::from_vec(vec![1.1, 2.2]); + let v = ColFVec::from_vec(vec![1.1, 2.2]); let mask = vec![true, true]; let filtered = v.filter_by_mask(&mask).unwrap(); assert_eq!(filtered.as_slice(), &[1.1, 2.2]); @@ -5681,7 +5681,7 @@ mod tests { #[test] fn test_filter_by_mask_f64_all_false() { - let v = ColFVector::from_vec(vec![1.1, 2.2]); + let v = ColFVec::from_vec(vec![1.1, 2.2]); let mask = vec![false, false]; let filtered = v.filter_by_mask(&mask).unwrap(); assert!(filtered.is_empty()); @@ -5697,7 +5697,7 @@ mod tests { #[test] fn test_filter_by_mask_f64_mismatched_length() { - let v = ColFVector::from_vec(vec![1.1, 2.2]); + let v = ColFVec::from_vec(vec![1.1, 2.2]); let mask = vec![true]; let result = v.filter_by_mask(&mask); assert!(result.is_err()); @@ -5706,7 +5706,7 @@ mod tests { #[test] fn test_filter_by_mask_complex_f64_basic() { use num::Complex; - let v = ColFVector::from_vec(vec![ + let v = ColFVec::from_vec(vec![ Complex::new(1.0, 2.0), Complex::new(3.0, 4.0), Complex::new(5.0, 6.0), @@ -5719,7 +5719,7 @@ mod tests { #[test] fn test_filter_by_mask_complex_f64_all_true() { use num::Complex; - let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = ColFVec::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let mask = vec![true, true]; let filtered = v.filter_by_mask(&mask).unwrap(); assert_eq!(filtered.as_slice(), &[Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); @@ -5728,7 +5728,7 @@ mod tests { #[test] fn test_filter_by_mask_complex_f64_all_false() { use num::Complex; - let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = ColFVec::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let mask = vec![false, false]; let filtered = v.filter_by_mask(&mask).unwrap(); assert!(filtered.is_empty()); @@ -5746,7 +5746,7 @@ mod tests { #[test] fn test_filter_by_mask_complex_f64_mismatched_length() { use num::Complex; - let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0)]); + let v = ColFVec::from_vec(vec![Complex::new(1.0, 2.0)]); let mask = vec![true, false]; let result = v.filter_by_mask(&mask); assert!(result.is_err()); @@ -5756,38 +5756,38 @@ mod tests { #[test] fn test_broadcast_to_i32() { - let v = ColFVector::from_vec(vec![1, 2]); + let v = ColFVec::from_vec(vec![1, 2]); let b = v.broadcast_to(5).unwrap(); assert_eq!(b.as_slice(), &[1, 2, 1, 2, 1]); - let v = ColFVector::from_vec(vec![7]); + let v = ColFVec::from_vec(vec![7]); let b = v.broadcast_to(4).unwrap(); assert_eq!(b.as_slice(), &[7, 7, 7, 7]); - let v: FlexVector = ColFVector::from_vec(vec![]); + let v: FlexVector = ColFVec::from_vec(vec![]); let b = v.broadcast_to(0).unwrap(); assert!(b.is_empty()); - let v: FlexVector = ColFVector::from_vec(vec![]); + let v: FlexVector = ColFVec::from_vec(vec![]); let result = v.broadcast_to(3); assert!(result.is_err()); } #[test] fn test_broadcast_to_f64() { - let v = ColFVector::from_vec(vec![1.5, -2.0]); + let v = ColFVec::from_vec(vec![1.5, -2.0]); let b = v.broadcast_to(5).unwrap(); assert_eq!(b.as_slice(), &[1.5, -2.0, 1.5, -2.0, 1.5]); - let v = ColFVector::from_vec(vec![3.14]); + let v = ColFVec::from_vec(vec![3.14]); let b = v.broadcast_to(3).unwrap(); assert_eq!(b.as_slice(), &[3.14, 3.14, 3.14]); - let v: FlexVector = ColFVector::from_vec(vec![]); + let v: FlexVector = ColFVec::from_vec(vec![]); let b = v.broadcast_to(0).unwrap(); assert!(b.is_empty()); - let v: FlexVector = ColFVector::from_vec(vec![]); + let v: FlexVector = ColFVec::from_vec(vec![]); let result = v.broadcast_to(2); assert!(result.is_err()); } @@ -5795,7 +5795,7 @@ mod tests { #[test] fn test_broadcast_to_complex_f64() { use num::Complex; - let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + let v = ColFVec::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); let b = v.broadcast_to(5).unwrap(); assert_eq!( b.as_slice(), @@ -5808,18 +5808,18 @@ mod tests { ] ); - let v = ColFVector::from_vec(vec![Complex::new(0.0, 1.0)]); + let v = ColFVec::from_vec(vec![Complex::new(0.0, 1.0)]); let b = v.broadcast_to(3).unwrap(); assert_eq!( b.as_slice(), &[Complex::new(0.0, 1.0), Complex::new(0.0, 1.0), Complex::new(0.0, 1.0)] ); - let v: FlexVector, Column> = ColFVector::from_vec(vec![]); + let v: FlexVector, Column> = ColFVec::from_vec(vec![]); let b = v.broadcast_to(0).unwrap(); assert!(b.is_empty()); - let v: FlexVector, Column> = ColFVector::from_vec(vec![]); + let v: FlexVector, Column> = ColFVec::from_vec(vec![]); let result = v.broadcast_to(1); assert!(result.is_err()); } @@ -5943,7 +5943,7 @@ mod tests { // -- translate -- #[test] fn test_translate_i32() { - let v1 = ColFVector::from_vec(vec![1, 2, 3]); + let v1 = ColFVec::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); let result = v1.translate(&v2).unwrap(); assert_eq!(result.as_slice(), &[5, 7, 9]); @@ -5951,7 +5951,7 @@ mod tests { #[test] fn test_translate_f64() { - let v1 = ColFVector::from_vec(vec![1.5, 2.5, 3.5]); + let v1 = ColFVec::from_vec(vec![1.5, 2.5, 3.5]); let v2 = FlexVector::from_vec(vec![0.5, 1.5, 2.5]); let result = v1.translate(&v2).unwrap(); assert_eq!(result.as_slice(), &[2.0, 4.0, 6.0]); @@ -5959,7 +5959,7 @@ mod tests { #[test] fn test_translate_complex_f64() { - let v1 = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v1 = ColFVec::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); let result = v1.translate(&v2).unwrap(); assert_eq!(result.as_slice(), &[Complex::new(6.0, 8.0), Complex::new(10.0, 12.0)]); @@ -5967,7 +5967,7 @@ mod tests { #[test] fn test_translate_mismatched_lengths() { - let v1 = ColFVector::from_vec(vec![1, 2]); + let v1 = ColFVec::from_vec(vec![1, 2]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); let result = v1.translate(&v2); assert!(result.is_err()); @@ -5977,7 +5977,7 @@ mod tests { #[test] fn test_translate_into_i32() { - let v1 = ColFVector::from_vec(vec![1, 2, 3]); + let v1 = ColFVec::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); let mut out = [0; 3]; v1.translate_into(&v2, &mut out).unwrap(); @@ -5987,7 +5987,7 @@ mod tests { #[test] fn test_translate_into_f64() { - let v1 = ColFVector::from_vec(vec![1.5, 2.5, 3.5]); + let v1 = ColFVec::from_vec(vec![1.5, 2.5, 3.5]); let v2 = FlexVector::from_vec(vec![0.5, 1.5, 2.5]); let mut out = [0.0; 3]; v1.translate_into(&v2, &mut out).unwrap(); @@ -5998,7 +5998,7 @@ mod tests { #[test] fn test_translate_into_complex_f64() { use num::Complex; - let v1 = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v1 = ColFVec::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); let mut out = [Complex::new(0.0, 0.0); 2]; v1.translate_into(&v2, &mut out).unwrap(); @@ -6008,7 +6008,7 @@ mod tests { #[test] fn test_translate_into_mismatched_length() { - let v1 = ColFVector::from_vec(vec![1, 2, 3]); + let v1 = ColFVec::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); let mut out = [0; 2]; let result = v1.translate_into(&v2, &mut out); @@ -6018,7 +6018,7 @@ mod tests { // -- mut_translate -- #[test] fn test_mut_translate_i32() { - let mut v1 = ColFVector::from_vec(vec![1, 2, 3]); + let mut v1 = ColFVec::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); v1.mut_translate(&v2).unwrap(); assert_eq!(v1.as_slice(), &[5, 7, 9]); @@ -6026,7 +6026,7 @@ mod tests { #[test] fn test_mut_translate_f64() { - let mut v1 = ColFVector::from_vec(vec![1.5, 2.5, 3.5]); + let mut v1 = ColFVec::from_vec(vec![1.5, 2.5, 3.5]); let v2 = FlexVector::from_vec(vec![0.5, 1.5, 2.5]); v1.mut_translate(&v2).unwrap(); assert_eq!(v1.as_slice(), &[2.0, 4.0, 6.0]); @@ -6034,7 +6034,7 @@ mod tests { #[test] fn test_mut_translate_complex_f64() { - let mut v1 = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let mut v1 = ColFVec::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); v1.mut_translate(&v2).unwrap(); assert_eq!(v1.as_slice(), &[Complex::new(6.0, 8.0), Complex::new(10.0, 12.0)]); @@ -6042,7 +6042,7 @@ mod tests { #[test] fn test_mut_translate_mismatched_lengths() { - let mut v1 = ColFVector::from_vec(vec![1, 2]); + let mut v1 = ColFVec::from_vec(vec![1, 2]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); let result = v1.mut_translate(&v2); assert!(result.is_err()); @@ -6051,14 +6051,14 @@ mod tests { // -- scale -- #[test] fn test_scale_i32() { - let v = ColFVector::from_vec(vec![1, 2, 3]); + let v = ColFVec::from_vec(vec![1, 2, 3]); let scaled = v.scale(10); assert_eq!(scaled.as_slice(), &[10, 20, 30]); } #[test] fn test_scale_f64() { - let v = ColFVector::from_vec(vec![1.5, -2.0, 0.0]); + let v = ColFVec::from_vec(vec![1.5, -2.0, 0.0]); let scaled = v.scale(2.0); assert_eq!(scaled.as_slice(), &[3.0, -4.0, 0.0]); } @@ -6066,7 +6066,7 @@ mod tests { #[test] fn test_scale_complex_f64() { use num::Complex; - let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + let v = ColFVec::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); let scalar = Complex::new(2.0, 0.0); let scaled = v.scale(scalar); assert_eq!(scaled.as_slice(), &[Complex::new(2.0, 4.0), Complex::new(-6.0, 8.0)]); @@ -6075,21 +6075,21 @@ mod tests { // -- mut_scale -- #[test] fn test_mut_scale_i32() { - let mut v = ColFVector::from_vec(vec![1, 2, 3]); + let mut v = ColFVec::from_vec(vec![1, 2, 3]); v.mut_scale(10); assert_eq!(v.as_slice(), &[10, 20, 30]); } #[test] fn test_mut_scale_f64() { - let mut v = ColFVector::from_vec(vec![1.5, -2.0, 0.0]); + let mut v = ColFVec::from_vec(vec![1.5, -2.0, 0.0]); v.mut_scale(2.0); assert_eq!(v.as_slice(), &[3.0, -4.0, 0.0]); } #[test] fn test_mut_scale_complex_f64() { - let mut v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + let mut v = ColFVec::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); let scalar = Complex::new(2.0, 0.0); v.mut_scale(scalar); assert_eq!(v.as_slice(), &[Complex::new(2.0, 4.0), Complex::new(-6.0, 8.0)]); @@ -6098,7 +6098,7 @@ mod tests { // -- negate -- #[test] fn test_negate_i32() { - let v = ColFVector::from_vec(vec![1, -2, 3]); + let v = ColFVec::from_vec(vec![1, -2, 3]); let neg = v.negate(); assert_eq!(neg.as_slice(), &[-1, 2, -3]); // original unchanged @@ -6107,7 +6107,7 @@ mod tests { #[test] fn test_negate_f64() { - let v = ColFVector::from_vec(vec![1.5, -2.5, 0.0]); + let v = ColFVec::from_vec(vec![1.5, -2.5, 0.0]); let neg = v.negate(); assert_eq!(neg.as_slice(), &[-1.5, 2.5, -0.0]); assert_eq!(v.as_slice(), &[1.5, -2.5, 0.0]); @@ -6116,7 +6116,7 @@ mod tests { #[test] fn test_negate_complex_f64() { use num::Complex; - let v = ColFVector::from_vec(vec![Complex::new(1.0, -2.0), Complex::new(-3.0, 4.0)]); + let v = ColFVec::from_vec(vec![Complex::new(1.0, -2.0), Complex::new(-3.0, 4.0)]); let neg = v.negate(); assert_eq!(neg.as_slice(), &[Complex::new(-1.0, 2.0), Complex::new(3.0, -4.0)]); assert_eq!(v.as_slice(), &[Complex::new(1.0, -2.0), Complex::new(-3.0, 4.0)]); @@ -6125,14 +6125,14 @@ mod tests { // -- mut_negate -- #[test] fn test_mut_negate_i32() { - let mut v = ColFVector::from_vec(vec![1, -2, 3]); + let mut v = ColFVec::from_vec(vec![1, -2, 3]); v.mut_negate(); assert_eq!(v.as_slice(), &[-1, 2, -3]); } #[test] fn test_mut_negate_f64() { - let mut v = ColFVector::from_vec(vec![1.5, -2.5, 0.0]); + let mut v = ColFVec::from_vec(vec![1.5, -2.5, 0.0]); v.mut_negate(); assert_eq!(v.as_slice(), &[-1.5, 2.5, -0.0]); } @@ -6140,7 +6140,7 @@ mod tests { #[test] fn test_mut_negate_complex_f64() { use num::Complex; - let mut v = ColFVector::from_vec(vec![Complex::new(1.0, -2.0), Complex::new(-3.0, 4.0)]); + let mut v = ColFVec::from_vec(vec![Complex::new(1.0, -2.0), Complex::new(-3.0, 4.0)]); v.mut_negate(); assert_eq!(v.as_slice(), &[Complex::new(-1.0, 2.0), Complex::new(3.0, -4.0)]); } @@ -6148,14 +6148,14 @@ mod tests { // -- mut_zero -- #[test] fn test_mut_zero_i32() { - let mut v = ColFVector::from_vec(vec![1, -2, 3]); + let mut v = ColFVec::from_vec(vec![1, -2, 3]); v.mut_zero(); assert_eq!(v.as_slice(), &[0, 0, 0]); } #[test] fn test_mut_zero_f64() { - let mut v = ColFVector::from_vec(vec![1.5, -2.5, 0.0]); + let mut v = ColFVec::from_vec(vec![1.5, -2.5, 0.0]); v.mut_zero(); assert_eq!(v.as_slice(), &[0.0, 0.0, 0.0]); } @@ -6163,7 +6163,7 @@ mod tests { #[test] fn test_mut_zero_complex_f64() { use num::Complex; - let mut v = ColFVector::from_vec(vec![Complex::new(1.0, -2.0), Complex::new(-3.0, 4.0)]); + let mut v = ColFVec::from_vec(vec![Complex::new(1.0, -2.0), Complex::new(-3.0, 4.0)]); v.mut_zero(); assert_eq!(v.as_slice(), &[Complex::new(0.0, 0.0), Complex::new(0.0, 0.0)]); } @@ -6171,7 +6171,7 @@ mod tests { // -- dot -- #[test] fn test_dot_i32() { - let v1 = ColFVector::from_vec(vec![1, 2, 3]); + let v1 = ColFVec::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); let dot = v1.dot(&v2).unwrap(); assert_eq!(dot, 1 * 4 + 2 * 5 + 3 * 6); // 32 @@ -6179,7 +6179,7 @@ mod tests { #[test] fn test_dot_f64() { - let v1 = ColFVector::from_vec(vec![1.5, 2.0, -3.0]); + let v1 = ColFVec::from_vec(vec![1.5, 2.0, -3.0]); let v2 = FlexVector::from_vec(vec![2.0, 0.5, 4.0]); let dot = v1.dot(&v2).unwrap(); assert!((dot - (1.5 * 2.0 + 2.0 * 0.5 + -3.0 * 4.0)).abs() < 1e-12); @@ -6187,7 +6187,7 @@ mod tests { #[test] fn test_dot_mismatched_lengths() { - let v1 = ColFVector::from_vec(vec![1, 2]); + let v1 = ColFVec::from_vec(vec![1, 2]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); let dot = v1.dot(&v2); assert!(dot.is_err()); @@ -6206,7 +6206,7 @@ mod tests { // -- dot_to_f64 -- #[test] fn test_dot_to_f64_i32() { - let v1 = ColFVector::from_vec(vec![1, 2, 3]); + let v1 = ColFVec::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); let dot = v1.dot_to_f64(&v2).unwrap(); assert!((dot - 32.0).abs() < 1e-12); @@ -6214,7 +6214,7 @@ mod tests { #[test] fn test_dot_to_f64_f64() { - let v1 = ColFVector::from_vec(vec![1.5, 2.0, -3.0]); + let v1 = ColFVec::from_vec(vec![1.5, 2.0, -3.0]); let v2 = FlexVector::from_vec(vec![2.0, 0.5, 4.0]); let dot = v1.dot_to_f64(&v2).unwrap(); assert!((dot - (1.5 * 2.0 + 2.0 * 0.5 + -3.0 * 4.0)).abs() < 1e-12); @@ -6222,7 +6222,7 @@ mod tests { #[test] fn test_dot_to_f64_mismatched_lengths() { - let v1 = ColFVector::from_vec(vec![1, 2]); + let v1 = ColFVec::from_vec(vec![1, 2]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); let dot = v1.dot(&v2); assert!(dot.is_err()); @@ -6233,7 +6233,7 @@ mod tests { // -- cross -- #[test] fn test_cross_i32() { - let v1 = ColFVector::from_vec(vec![1, 2, 3]); + let v1 = ColFVec::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); let cross = v1.cross(&v2).unwrap(); // [2*6 - 3*5, 3*4 - 1*6, 1*5 - 2*4] = [12-15, 12-6, 5-8] = [-3, 6, -3] @@ -6242,7 +6242,7 @@ mod tests { #[test] fn test_cross_f64() { - let v1 = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = ColFVec::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 5.0, 6.0]); let cross = v1.cross(&v2).unwrap(); assert_eq!(cross.as_slice(), &[-3.0, 6.0, -3.0]); @@ -6254,7 +6254,7 @@ mod tests { #[test] fn test_cross_wrong_length_1() { - let v1 = ColFVector::from_vec(vec![1, 2]); + let v1 = ColFVec::from_vec(vec![1, 2]); let v2 = FlexVector::from_vec(vec![3, 4, 5]); let result = v1.cross(&v2); assert!(result.is_err()); @@ -6262,7 +6262,7 @@ mod tests { #[test] fn test_cross_wrong_length_2() { - let v1 = ColFVector::from_vec(vec![1, 2, 3]); + let v1 = ColFVec::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![3, 4]); let result = v1.cross(&v2); assert!(result.is_err()); @@ -6270,7 +6270,7 @@ mod tests { #[test] fn test_cross_wrong_length_3() { - let v1 = ColFVector::from_vec(vec![1, 2]); + let v1 = ColFVec::from_vec(vec![1, 2]); let v2 = FlexVector::from_vec(vec![3, 4]); let result = v1.cross(&v2); assert!(result.is_err()); @@ -6280,7 +6280,7 @@ mod tests { #[test] fn test_cross_into_i32() { - let v1 = ColFVector::from_vec(vec![1, 2, 3]); + let v1 = ColFVec::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); let mut out = [0; 3]; v1.cross_into(&v2, &mut out).unwrap(); @@ -6289,7 +6289,7 @@ mod tests { #[test] fn test_cross_into_f64() { - let v1 = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = ColFVec::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 5.0, 6.0]); let mut out = [0.0; 3]; v1.cross_into(&v2, &mut out).unwrap(); @@ -6298,19 +6298,19 @@ mod tests { #[test] fn test_cross_into_wrong_length() { - let v1 = ColFVector::from_vec(vec![1, 2]); + let v1 = ColFVec::from_vec(vec![1, 2]); let v2 = FlexVector::from_vec(vec![3, 4, 5]); let mut out = [0; 3]; let result = v1.cross_into(&v2, &mut out); assert!(result.is_err()); - let v1 = ColFVector::from_vec(vec![1, 2, 3]); + let v1 = ColFVec::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![3, 4]); let mut out = [0; 3]; let result = v1.cross_into(&v2, &mut out); assert!(result.is_err()); - let v1 = ColFVector::from_vec(vec![1, 2, 3]); + let v1 = ColFVec::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![3, 4, 5]); let mut out = [0; 2]; let result = v1.cross_into(&v2, &mut out); @@ -6320,14 +6320,14 @@ mod tests { // -- sum -- #[test] fn test_sum_i32() { - let v = ColFVector::from_vec(vec![1, 2, 3, 4]); + let v = ColFVec::from_vec(vec![1, 2, 3, 4]); let s = v.sum(); assert_eq!(s, 10); } #[test] fn test_sum_f64() { - let v = ColFVector::from_vec(vec![1.5, -2.5, 3.0]); + let v = ColFVec::from_vec(vec![1.5, -2.5, 3.0]); let s = v.sum(); assert!((s - 2.0).abs() < 1e-12); } @@ -6335,7 +6335,7 @@ mod tests { #[test] fn test_sum_complex_f64() { use num::Complex; - let v = ColFVector::from_vec(vec![ + let v = ColFVec::from_vec(vec![ Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0), Complex::new(5.0, -6.0), @@ -6347,14 +6347,14 @@ mod tests { // -- product -- #[test] fn test_product_i32() { - let v = ColFVector::from_vec(vec![2, 3, 4]); + let v = ColFVec::from_vec(vec![2, 3, 4]); let p = v.product(); assert_eq!(p, 24); } #[test] fn test_product_f64() { - let v = ColFVector::from_vec(vec![1.5, -2.0, 3.0]); + let v = ColFVec::from_vec(vec![1.5, -2.0, 3.0]); let p = v.product(); assert!((p - (1.5 * -2.0 * 3.0)).abs() < 1e-12); } @@ -6362,7 +6362,7 @@ mod tests { #[test] fn test_product_complex_f64() { use num::Complex; - let v = ColFVector::from_vec(vec![ + let v = ColFVec::from_vec(vec![ Complex::new(1.0, 2.0), Complex::new(3.0, -1.0), Complex::new(2.0, 0.5), @@ -6375,31 +6375,31 @@ mod tests { // -- minimum -- #[test] fn test_minimum_i32() { - let v = ColFVector::from_vec(vec![3, 1, 4, 2]); + let v = ColFVec::from_vec(vec![3, 1, 4, 2]); assert_eq!(v.minimum(), Some(1)); } #[test] fn test_minimum_f64_basic() { - let v = ColFVector::from_vec(vec![1.5, -2.0, 3.0]); + let v = ColFVec::from_vec(vec![1.5, -2.0, 3.0]); assert_eq!(v.minimum(), Some(-2.0)); } #[test] fn test_minimum_f64_all_positive() { - let v = ColFVector::from_vec(vec![2.0, 4.0, 1.0, 3.0]); + let v = ColFVec::from_vec(vec![2.0, 4.0, 1.0, 3.0]); assert_eq!(v.minimum(), Some(1.0)); } #[test] fn test_minimum_f64_all_negative() { - let v = ColFVector::from_vec(vec![-1.0, -2.0, -3.0]); + let v = ColFVec::from_vec(vec![-1.0, -2.0, -3.0]); assert_eq!(v.minimum(), Some(-3.0)); } #[test] fn test_minimum_f64_single_element() { - let v = ColFVector::from_vec(vec![42.0]); + let v = ColFVec::from_vec(vec![42.0]); assert_eq!(v.minimum(), Some(42.0)); } @@ -6411,7 +6411,7 @@ mod tests { #[test] fn test_minimum_f64_with_nan() { - let v = ColFVector::from_vec(vec![1.0, f64::NAN, 2.0]); + let v = ColFVec::from_vec(vec![1.0, f64::NAN, 2.0]); // The result is not guaranteed to be meaningful if NaN is present, // but it should return Some value (could be NaN or a number). assert!(v.minimum().is_some()); @@ -6427,7 +6427,7 @@ mod tests { #[test] fn test_elementwise_min_i32() { - let v1 = ColFVector::from_vec(vec![1, 5, 3, 7]); + let v1 = ColFVec::from_vec(vec![1, 5, 3, 7]); let v2 = FlexVector::from_vec(vec![2, 4, 6, 0]); let min = v1.elementwise_min(&v2).unwrap(); assert_eq!(min.as_slice(), &[1, 4, 3, 0]); @@ -6435,7 +6435,7 @@ mod tests { #[test] fn test_elementwise_min_f64() { - let v1 = ColFVector::from_vec(vec![1.5, -2.0, 3.0]); + let v1 = ColFVec::from_vec(vec![1.5, -2.0, 3.0]); let v2 = FlexVector::from_vec(vec![2.5, -3.0, 2.0]); let min = v1.elementwise_min(&v2).unwrap(); assert_eq!(min.as_slice(), &[1.5, -3.0, 2.0]); @@ -6451,7 +6451,7 @@ mod tests { #[test] fn test_elementwise_min_mismatched_length() { - let v1 = ColFVector::from_vec(vec![1, 2]); + let v1 = ColFVec::from_vec(vec![1, 2]); let v2 = FlexVector::from_vec(vec![3, 4, 5]); let result = v1.elementwise_min(&v2); assert!(result.is_err()); @@ -6459,7 +6459,7 @@ mod tests { #[test] fn test_elementwise_min_f64_with_nan() { - let v1 = ColFVector::from_vec(vec![1.0, f64::NAN, 3.0]); + let v1 = ColFVec::from_vec(vec![1.0, f64::NAN, 3.0]); let v2 = FlexVector::from_vec(vec![2.0, 2.0, f64::NAN]); let min = v1.elementwise_min(&v2).unwrap(); assert_eq!(min[0], 1.0); // min(1.0, 2.0) = 1.0 @@ -6469,7 +6469,7 @@ mod tests { #[test] fn test_elementwise_min_f64_both_nan() { - let v1 = ColFVector::from_vec(vec![f64::NAN]); + let v1 = ColFVec::from_vec(vec![f64::NAN]); let v2 = FlexVector::from_vec(vec![f64::NAN]); let min = v1.elementwise_min(&v2).unwrap(); assert!(min[0].is_nan()); @@ -6479,7 +6479,7 @@ mod tests { #[test] fn test_elementwise_min_into_i32() { - let v1 = ColFVector::from_vec(vec![1, 5, 3, 7]); + let v1 = ColFVec::from_vec(vec![1, 5, 3, 7]); let v2 = FlexVector::from_vec(vec![2, 4, 6, 0]); let mut out = [0; 4]; v1.elementwise_min_into(&v2, &mut out).unwrap(); @@ -6488,7 +6488,7 @@ mod tests { #[test] fn test_elementwise_min_into_f64() { - let v1 = ColFVector::from_vec(vec![1.5, -2.0, 3.0]); + let v1 = ColFVec::from_vec(vec![1.5, -2.0, 3.0]); let v2 = FlexVector::from_vec(vec![2.5, -3.0, 2.0]); let mut out = [0.0; 3]; v1.elementwise_min_into(&v2, &mut out).unwrap(); @@ -6497,13 +6497,13 @@ mod tests { #[test] fn test_elementwise_min_into_mismatched_length() { - let v1 = ColFVector::from_vec(vec![1, 2, 3]); + let v1 = ColFVec::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![4, 5]); let mut out = [0; 3]; let result = v1.elementwise_min_into(&v2, &mut out); assert!(result.is_err()); - let v1 = ColFVector::from_vec(vec![1, 2, 3]); + let v1 = ColFVec::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); let mut out = [0; 2]; let result = v1.elementwise_min_into(&v2, &mut out); @@ -6513,31 +6513,31 @@ mod tests { // -- maximum -- #[test] fn test_maximum_i32() { - let v = ColFVector::from_vec(vec![3, 1, 4, 2]); + let v = ColFVec::from_vec(vec![3, 1, 4, 2]); assert_eq!(v.maximum(), Some(4)); } #[test] fn test_maximum_f64_basic() { - let v = ColFVector::from_vec(vec![1.5, -2.0, 3.0]); + let v = ColFVec::from_vec(vec![1.5, -2.0, 3.0]); assert_eq!(v.maximum(), Some(3.0)); } #[test] fn test_maximum_f64_all_positive() { - let v = ColFVector::from_vec(vec![2.0, 4.0, 1.0, 3.0]); + let v = ColFVec::from_vec(vec![2.0, 4.0, 1.0, 3.0]); assert_eq!(v.maximum(), Some(4.0)); } #[test] fn test_maximum_f64_all_negative() { - let v = ColFVector::from_vec(vec![-1.0, -2.0, -3.0]); + let v = ColFVec::from_vec(vec![-1.0, -2.0, -3.0]); assert_eq!(v.maximum(), Some(-1.0)); } #[test] fn test_maximum_f64_single_element() { - let v = ColFVector::from_vec(vec![42.0]); + let v = ColFVec::from_vec(vec![42.0]); assert_eq!(v.maximum(), Some(42.0)); } @@ -6549,7 +6549,7 @@ mod tests { #[test] fn test_maximum_f64_with_nan() { - let v = ColFVector::from_vec(vec![1.0, f64::NAN, 2.0]); + let v = ColFVec::from_vec(vec![1.0, f64::NAN, 2.0]); // The result is not guaranteed to be meaningful if NaN is present, // but it should return Some value (could be NaN or a number). assert!(v.maximum().is_some()); @@ -6559,7 +6559,7 @@ mod tests { #[test] fn test_elementwise_max_i32() { - let v1 = ColFVector::from_vec(vec![1, 5, 3, 7]); + let v1 = ColFVec::from_vec(vec![1, 5, 3, 7]); let v2 = FlexVector::from_vec(vec![2, 4, 6, 0]); let max = v1.elementwise_max(&v2).unwrap(); assert_eq!(max.as_slice(), &[2, 5, 6, 7]); @@ -6567,7 +6567,7 @@ mod tests { #[test] fn test_elementwise_max_f64() { - let v1 = ColFVector::from_vec(vec![1.5, -2.0, 3.0]); + let v1 = ColFVec::from_vec(vec![1.5, -2.0, 3.0]); let v2 = FlexVector::from_vec(vec![2.5, -3.0, 2.0]); let max = v1.elementwise_max(&v2).unwrap(); assert_eq!(max.as_slice(), &[2.5, -2.0, 3.0]); @@ -6583,7 +6583,7 @@ mod tests { #[test] fn test_elementwise_max_mismatched_length() { - let v1 = ColFVector::from_vec(vec![1, 2, 3]); + let v1 = ColFVec::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![4, 5]); let result = v1.elementwise_max(&v2); assert!(result.is_err()); @@ -6591,7 +6591,7 @@ mod tests { #[test] fn test_elementwise_max_f64_with_nan() { - let v1 = ColFVector::from_vec(vec![1.0, f64::NAN, 3.0]); + let v1 = ColFVec::from_vec(vec![1.0, f64::NAN, 3.0]); let v2 = FlexVector::from_vec(vec![2.0, 2.0, f64::NAN]); let max = v1.elementwise_max(&v2).unwrap(); assert_eq!(max[0], 2.0); // max(1.0, 2.0) = 2.0 @@ -6601,7 +6601,7 @@ mod tests { #[test] fn test_elementwise_max_f64_both_nan() { - let v1 = ColFVector::from_vec(vec![f64::NAN]); + let v1 = ColFVec::from_vec(vec![f64::NAN]); let v2 = FlexVector::from_vec(vec![f64::NAN]); let max = v1.elementwise_max(&v2).unwrap(); assert!(max[0].is_nan()); @@ -6611,7 +6611,7 @@ mod tests { #[test] fn test_elementwise_max_into_i32() { - let v1 = ColFVector::from_vec(vec![1, 5, 3, 7]); + let v1 = ColFVec::from_vec(vec![1, 5, 3, 7]); let v2 = FlexVector::from_vec(vec![2, 4, 6, 0]); let mut out = [0; 4]; v1.elementwise_max_into(&v2, &mut out).unwrap(); @@ -6620,7 +6620,7 @@ mod tests { #[test] fn test_elementwise_max_into_f64() { - let v1 = ColFVector::from_vec(vec![1.5, -2.0, 3.0]); + let v1 = ColFVec::from_vec(vec![1.5, -2.0, 3.0]); let v2 = FlexVector::from_vec(vec![2.5, -3.0, 2.0]); let mut out = [0.0; 3]; v1.elementwise_max_into(&v2, &mut out).unwrap(); @@ -6629,13 +6629,13 @@ mod tests { #[test] fn test_elementwise_max_into_mismatched_length() { - let v1 = ColFVector::from_vec(vec![1, 2, 3]); + let v1 = ColFVec::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![4, 5]); let mut out = [0; 3]; let result = v1.elementwise_max_into(&v2, &mut out); assert!(result.is_err()); - let v1 = ColFVector::from_vec(vec![1, 2, 3]); + let v1 = ColFVec::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); let mut out = [0; 2]; let result = v1.elementwise_max_into(&v2, &mut out); @@ -6646,21 +6646,21 @@ mod tests { #[test] fn test_elementwise_clamp_i32_basic() { - let v = ColFVector::from_vec(vec![-5, 0, 5, 10, 15]); + let v = ColFVec::from_vec(vec![-5, 0, 5, 10, 15]); let clamped = v.elementwise_clamp(0, 10); assert_eq!(clamped.as_slice(), &[0, 0, 5, 10, 10]); } #[test] fn test_elementwise_clamp_i32_all_below() { - let v = ColFVector::from_vec(vec![-3, -2, -1]); + let v = ColFVec::from_vec(vec![-3, -2, -1]); let clamped = v.elementwise_clamp(0, 5); assert_eq!(clamped.as_slice(), &[0, 0, 0]); } #[test] fn test_elementwise_clamp_i32_all_above() { - let v = ColFVector::from_vec(vec![11, 12, 13]); + let v = ColFVec::from_vec(vec![11, 12, 13]); let clamped = v.elementwise_clamp(0, 10); assert_eq!(clamped.as_slice(), &[10, 10, 10]); } @@ -6674,28 +6674,28 @@ mod tests { #[test] fn test_elementwise_clamp_f64_basic() { - let v = ColFVector::from_vec(vec![-2.5, 0.0, 3.5, 7.2, 12.0]); + let v = ColFVec::from_vec(vec![-2.5, 0.0, 3.5, 7.2, 12.0]); let clamped = v.elementwise_clamp(0.0, 10.0); assert_eq!(clamped.as_slice(), &[0.0, 0.0, 3.5, 7.2, 10.0]); } #[test] fn test_elementwise_clamp_f64_all_below() { - let v = ColFVector::from_vec(vec![-1.1, -2.2]); + let v = ColFVec::from_vec(vec![-1.1, -2.2]); let clamped = v.elementwise_clamp(0.0, 5.0); assert_eq!(clamped.as_slice(), &[0.0, 0.0]); } #[test] fn test_elementwise_clamp_f64_all_above() { - let v = ColFVector::from_vec(vec![11.1, 12.2]); + let v = ColFVec::from_vec(vec![11.1, 12.2]); let clamped = v.elementwise_clamp(0.0, 10.0); assert_eq!(clamped.as_slice(), &[10.0, 10.0]); } #[test] fn test_elementwise_clamp_f64_with_nan() { - let v = ColFVector::from_vec(vec![1.0, f64::NAN, 5.0]); + let v = ColFVec::from_vec(vec![1.0, f64::NAN, 5.0]); let clamped = v.elementwise_clamp(0.0, 4.0); assert_eq!(clamped[0], 1.0); assert!(clamped[1].is_nan()); @@ -6713,7 +6713,7 @@ mod tests { #[test] fn test_elementwise_clamp_into_i32_basic() { - let v = ColFVector::from_vec(vec![-5, 0, 5, 10, 15]); + let v = ColFVec::from_vec(vec![-5, 0, 5, 10, 15]); let mut out = [0; 5]; v.elementwise_clamp_into(0, 10, &mut out).unwrap(); assert_eq!(out, [0, 0, 5, 10, 10]); @@ -6721,7 +6721,7 @@ mod tests { #[test] fn test_elementwise_clamp_into_i32_all_below() { - let v = ColFVector::from_vec(vec![-3, -2, -1]); + let v = ColFVec::from_vec(vec![-3, -2, -1]); let mut out = [0; 3]; v.elementwise_clamp_into(0, 5, &mut out).unwrap(); assert_eq!(out, [0, 0, 0]); @@ -6729,7 +6729,7 @@ mod tests { #[test] fn test_elementwise_clamp_into_i32_all_above() { - let v = ColFVector::from_vec(vec![11, 12, 13]); + let v = ColFVec::from_vec(vec![11, 12, 13]); let mut out = [0; 3]; v.elementwise_clamp_into(0, 10, &mut out).unwrap(); assert_eq!(out, [10, 10, 10]); @@ -6745,7 +6745,7 @@ mod tests { #[test] fn test_elementwise_clamp_into_f64_basic() { - let v = ColFVector::from_vec(vec![-2.5, 0.0, 3.5, 7.2, 12.0]); + let v = ColFVec::from_vec(vec![-2.5, 0.0, 3.5, 7.2, 12.0]); let mut out = [0.0; 5]; v.elementwise_clamp_into(0.0, 10.0, &mut out).unwrap(); assert_eq!(out, [0.0, 0.0, 3.5, 7.2, 10.0]); @@ -6753,7 +6753,7 @@ mod tests { #[test] fn test_elementwise_clamp_into_f64_all_below() { - let v = ColFVector::from_vec(vec![-1.1, -2.2]); + let v = ColFVec::from_vec(vec![-1.1, -2.2]); let mut out = [0.0; 2]; v.elementwise_clamp_into(0.0, 5.0, &mut out).unwrap(); assert_eq!(out, [0.0, 0.0]); @@ -6761,7 +6761,7 @@ mod tests { #[test] fn test_elementwise_clamp_into_f64_all_above() { - let v = ColFVector::from_vec(vec![11.1, 12.2]); + let v = ColFVec::from_vec(vec![11.1, 12.2]); let mut out = [0.0; 2]; v.elementwise_clamp_into(0.0, 10.0, &mut out).unwrap(); assert_eq!(out, [10.0, 10.0]); @@ -6769,7 +6769,7 @@ mod tests { #[test] fn test_elementwise_clamp_into_f64_with_nan() { - let v = ColFVector::from_vec(vec![1.0, f64::NAN, 5.0]); + let v = ColFVec::from_vec(vec![1.0, f64::NAN, 5.0]); let mut out = [0.0; 3]; v.elementwise_clamp_into(0.0, 4.0, &mut out).unwrap(); assert_eq!(out[0], 1.0); @@ -6788,14 +6788,14 @@ mod tests { // -- l1_norm -- #[test] fn test_l1_norm_i32() { - let v = ColFVector::from_vec(vec![1, -2, 3]); + let v = ColFVec::from_vec(vec![1, -2, 3]); let norm = v.l1_norm(); assert_eq!(norm, 6); } #[test] fn test_l1_norm_f64() { - let v = ColFVector::from_vec(vec![1.5, -2.5, 3.0]); + let v = ColFVec::from_vec(vec![1.5, -2.5, 3.0]); let norm = v.l1_norm(); assert!((norm - 7.0).abs() < 1e-12); } @@ -6805,14 +6805,14 @@ mod tests { // -- linf_norm -- #[test] fn test_linf_norm_i32() { - let v = ColFVector::from_vec(vec![1, -5, 3, 2]); + let v = ColFVec::from_vec(vec![1, -5, 3, 2]); let norm = v.linf_norm(); assert_eq!(norm, 5); } #[test] fn test_linf_norm_f64() { - let v = ColFVector::from_vec(vec![1.5, -2.5, 3.0, -7.2]); + let v = ColFVec::from_vec(vec![1.5, -2.5, 3.0, -7.2]); let norm = v.linf_norm(); assert!((norm - 7.2).abs() < 1e-12); } @@ -6826,7 +6826,7 @@ mod tests { // -- normalize -- #[test] fn test_normalize_f64() { - let v = ColFVector::from_vec(vec![3.0, 4.0]); + let v = ColFVec::from_vec(vec![3.0, 4.0]); let normalized = v.normalize().unwrap(); // The norm is 5.0, so the normalized vector should be [0.6, 0.8] assert!((normalized.as_slice()[0] - 0.6).abs() < 1e-12); @@ -6835,14 +6835,14 @@ mod tests { #[test] fn test_normalize_f64_zero_vector() { - let v = ColFVector::from_vec(vec![0.0, 0.0]); + let v = ColFVec::from_vec(vec![0.0, 0.0]); let result = v.normalize(); assert!(result.is_err()); } #[test] fn test_normalize_f64_negative_values() { - let v = ColFVector::from_vec(vec![-3.0, -4.0]); + let v = ColFVec::from_vec(vec![-3.0, -4.0]); let normalized = v.normalize().unwrap(); // The norm is 5.0, so the normalized vector should be [-0.6, -0.8] assert!((normalized.as_slice()[0] + 0.6).abs() < 1e-12); @@ -6863,7 +6863,7 @@ mod tests { #[test] fn test_normalize_into_f64_zero_vector() { - let v = ColFVector::from_vec(vec![0.0, 0.0]); + let v = ColFVec::from_vec(vec![0.0, 0.0]); let mut out = [0.0; 2]; let result = v.normalize_into(&mut out); assert!(result.is_err()); @@ -6871,7 +6871,7 @@ mod tests { #[test] fn test_normalize_into_f64_negative_values() { - let v = ColFVector::from_vec(vec![-3.0, -4.0]); + let v = ColFVec::from_vec(vec![-3.0, -4.0]); let mut out = [0.0; 2]; let _ = v.normalize_into(&mut out).unwrap(); // The norm is 5.0, so the normalized vector should be [-0.6, -0.8] @@ -6882,7 +6882,7 @@ mod tests { // -- mut_normalize -- #[test] fn test_mut_normalize_f64() { - let mut v = ColFVector::from_vec(vec![3.0, 4.0]); + let mut v = ColFVec::from_vec(vec![3.0, 4.0]); v.mut_normalize().unwrap(); // The norm is 5.0, so the normalized vector should be [0.6, 0.8] assert!((v.as_slice()[0] - 0.6).abs() < 1e-12); @@ -6891,14 +6891,14 @@ mod tests { #[test] fn test_mut_normalize_f64_zero_vector() { - let mut v = ColFVector::from_vec(vec![0.0, 0.0]); + let mut v = ColFVec::from_vec(vec![0.0, 0.0]); let result = v.mut_normalize(); assert!(result.is_err()); } #[test] fn test_mut_normalize_f64_negative_values() { - let mut v = ColFVector::from_vec(vec![-3.0, -4.0]); + let mut v = ColFVec::from_vec(vec![-3.0, -4.0]); v.mut_normalize().unwrap(); // The norm is 5.0, so the normalized vector should be [-0.6, -0.8] assert!((v.as_slice()[0] + 0.6).abs() < 1e-12); @@ -6908,7 +6908,7 @@ mod tests { // -- normalize_to -- #[test] fn test_normalize_to_f64() { - let v = ColFVector::from_vec(vec![3.0, 4.0]); + let v = ColFVec::from_vec(vec![3.0, 4.0]); let normalized = v.normalize_to(10.0).unwrap(); // The original norm is 5.0, so the normalized vector should be [6.0, 8.0] assert!((normalized.as_slice()[0] - 6.0).abs() < 1e-12); @@ -6917,14 +6917,14 @@ mod tests { #[test] fn test_normalize_to_f64_zero_vector() { - let v = ColFVector::from_vec(vec![0.0, 0.0]); + let v = ColFVec::from_vec(vec![0.0, 0.0]); let result = v.normalize_to(1.0); assert!(result.is_err()); } #[test] fn test_normalize_to_f64_negative_values() { - let v = ColFVector::from_vec(vec![-3.0, -4.0]); + let v = ColFVec::from_vec(vec![-3.0, -4.0]); let normalized = v.normalize_to(5.0).unwrap(); // The original norm is 5.0, so the normalized vector should be [-3.0, -4.0] assert!((normalized.as_slice()[0] + 3.0).abs() < 1e-12); @@ -6970,7 +6970,7 @@ mod tests { // -- mut_normalize_to -- #[test] fn test_mut_normalize_to_f64() { - let mut v = ColFVector::from_vec(vec![3.0, 4.0]); + let mut v = ColFVec::from_vec(vec![3.0, 4.0]); v.mut_normalize_to(10.0).unwrap(); // The original norm is 5.0, so the normalized vector should be [6.0, 8.0] assert!((v.as_slice()[0] - 6.0).abs() < 1e-12); @@ -6979,14 +6979,14 @@ mod tests { #[test] fn test_mut_normalize_to_f64_zero_vector() { - let mut v = ColFVector::from_vec(vec![0.0, 0.0]); + let mut v = ColFVec::from_vec(vec![0.0, 0.0]); let result = v.mut_normalize_to(1.0); assert!(result.is_err()); } #[test] fn test_mut_normalize_to_f64_negative_values() { - let mut v = ColFVector::from_vec(vec![-3.0, -4.0]); + let mut v = ColFVec::from_vec(vec![-3.0, -4.0]); v.mut_normalize_to(5.0).unwrap(); // The original norm is 5.0, so the normalized vector should be [-3.0, -4.0] assert!((v.as_slice()[0] + 3.0).abs() < 1e-12); @@ -6996,7 +6996,7 @@ mod tests { // -- lerp -- #[test] fn test_lerp_f64_weight_zero() { - let v1 = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = ColFVec::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 5.0, 6.0]); let result = v1.lerp(&v2, 0.0).unwrap(); // Should be equal to v1 @@ -7007,7 +7007,7 @@ mod tests { #[test] fn test_lerp_f64_weight_one() { - let v1 = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = ColFVec::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 5.0, 6.0]); let result = v1.lerp(&v2, 1.0).unwrap(); // Should be equal to v2 @@ -7018,7 +7018,7 @@ mod tests { #[test] fn test_lerp_f64_weight_half() { - let v1 = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = ColFVec::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 5.0, 6.0]); let result = v1.lerp(&v2, 0.5).unwrap(); // Should be the midpoint @@ -7029,7 +7029,7 @@ mod tests { #[test] fn test_lerp_f64_weight_out_of_bounds() { - let v1 = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = ColFVec::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 5.0, 6.0]); let result_low = v1.lerp(&v2, -0.1); let result_high = v1.lerp(&v2, 1.1); @@ -7120,7 +7120,7 @@ mod tests { // -- mut_lerp -- #[test] fn test_mut_lerp_f64_weight_zero() { - let mut v1 = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); + let mut v1 = ColFVec::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 5.0, 6.0]); v1.mut_lerp(&v2, 0.0).unwrap(); // Should be equal to original v1 @@ -7131,7 +7131,7 @@ mod tests { #[test] fn test_mut_lerp_f64_weight_one() { - let mut v1 = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); + let mut v1 = ColFVec::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 5.0, 6.0]); v1.mut_lerp(&v2, 1.0).unwrap(); // Should be equal to v2 @@ -7142,7 +7142,7 @@ mod tests { #[test] fn test_mut_lerp_f64_weight_half() { - let mut v1 = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); + let mut v1 = ColFVec::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 5.0, 6.0]); v1.mut_lerp(&v2, 0.5).unwrap(); // Should be the midpoint @@ -7153,7 +7153,7 @@ mod tests { #[test] fn test_mut_lerp_f64_weight_out_of_bounds() { - let mut v1 = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); + let mut v1 = ColFVec::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 5.0, 6.0]); let result_low = v1.mut_lerp(&v2, -0.1); let result_high = v1.mut_lerp(&v2, 1.1); @@ -7164,7 +7164,7 @@ mod tests { // -- midpoint -- #[test] fn test_midpoint_f64() { - let v1 = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = ColFVec::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 5.0, 6.0]); let midpoint = v1.midpoint(&v2).unwrap(); // Should be the average of each element @@ -7175,7 +7175,7 @@ mod tests { #[test] fn test_midpoint_f64_negative_values() { - let v1 = ColFVector::from_vec(vec![-1.0, -2.0, -3.0]); + let v1 = ColFVec::from_vec(vec![-1.0, -2.0, -3.0]); let v2 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); let midpoint = v1.midpoint(&v2).unwrap(); // Should be [0.0, 0.0, 0.0] @@ -7186,7 +7186,7 @@ mod tests { #[test] fn test_midpoint_f64_mismatched_length() { - let v1 = ColFVector::from_vec(vec![1.0, 2.0]); + let v1 = ColFVec::from_vec(vec![1.0, 2.0]); let v2 = FlexVector::from_vec(vec![3.0, 4.0, 5.0]); let result = v1.midpoint(&v2); assert!(result.is_err()); @@ -7246,7 +7246,7 @@ mod tests { // -- distance -- #[test] fn test_distance_f64_basic() { - let v1 = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = ColFVec::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 6.0, 8.0]); let dist = v1.distance(&v2).unwrap(); // sqrt((1-4)^2 + (2-6)^2 + (3-8)^2) = sqrt(9 + 16 + 25) = sqrt(50) @@ -7255,7 +7255,7 @@ mod tests { #[test] fn test_distance_f64_zero() { - let v1 = ColFVector::from_vec(vec![0.0, 0.0, 0.0]); + let v1 = ColFVec::from_vec(vec![0.0, 0.0, 0.0]); let v2 = FlexVector::from_vec(vec![0.0, 0.0, 0.0]); let dist = v1.distance(&v2).unwrap(); assert_eq!(dist, 0.0); @@ -7263,7 +7263,7 @@ mod tests { #[test] fn test_distance_f64_negative_values() { - let v1 = ColFVector::from_vec(vec![-1.0, -2.0, -3.0]); + let v1 = ColFVec::from_vec(vec![-1.0, -2.0, -3.0]); let v2 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); let dist = v1.distance(&v2).unwrap(); // sqrt(((-1)-1)^2 + ((-2)-2)^2 + ((-3)-3)^2) = sqrt(4 + 16 + 36) = sqrt(56) @@ -7272,7 +7272,7 @@ mod tests { #[test] fn test_distance_f64_mismatched_length() { - let v1 = ColFVector::from_vec(vec![1.0, 2.0]); + let v1 = ColFVec::from_vec(vec![1.0, 2.0]); let v2 = FlexVector::from_vec(vec![3.0, 4.0, 5.0]); let result = v1.distance(&v2); assert!(result.is_err()); @@ -7281,7 +7281,7 @@ mod tests { // -- manhattan_distance -- #[test] fn test_manhattan_distance_f64_basic() { - let v1 = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = ColFVec::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 6.0, 8.0]); let dist = v1.manhattan_distance(&v2).unwrap(); // |1-4| + |2-6| + |3-8| = 3 + 4 + 5 = 12 @@ -7290,7 +7290,7 @@ mod tests { #[test] fn test_manhattan_distance_f64_zero() { - let v1 = ColFVector::from_vec(vec![0.0, 0.0, 0.0]); + let v1 = ColFVec::from_vec(vec![0.0, 0.0, 0.0]); let v2 = FlexVector::from_vec(vec![0.0, 0.0, 0.0]); let dist = v1.manhattan_distance(&v2).unwrap(); assert_eq!(dist, 0.0); @@ -7298,7 +7298,7 @@ mod tests { #[test] fn test_manhattan_distance_f64_negative_values() { - let v1 = ColFVector::from_vec(vec![-1.0, -2.0, -3.0]); + let v1 = ColFVec::from_vec(vec![-1.0, -2.0, -3.0]); let v2 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); let dist = v1.manhattan_distance(&v2).unwrap(); // |(-1)-1| + |(-2)-2| + |(-3)-3| = 2 + 4 + 6 = 12 @@ -7307,7 +7307,7 @@ mod tests { #[test] fn test_manhattan_distance_f64_mismatched_length() { - let v1 = ColFVector::from_vec(vec![1.0, 2.0]); + let v1 = ColFVec::from_vec(vec![1.0, 2.0]); let v2 = FlexVector::from_vec(vec![3.0, 4.0, 5.0]); let result = v1.manhattan_distance(&v2); assert!(result.is_err()); @@ -7316,7 +7316,7 @@ mod tests { // -- chebyshev_distance -- #[test] fn test_chebyshev_distance_f64_basic() { - let v1 = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = ColFVec::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 6.0, 8.0]); let dist = v1.chebyshev_distance(&v2).unwrap(); // max(|1-4|, |2-6|, |3-8|) = max(3, 4, 5) = 5 @@ -7325,7 +7325,7 @@ mod tests { #[test] fn test_chebyshev_distance_f64_zero() { - let v1 = ColFVector::from_vec(vec![0.0, 0.0, 0.0]); + let v1 = ColFVec::from_vec(vec![0.0, 0.0, 0.0]); let v2 = FlexVector::from_vec(vec![0.0, 0.0, 0.0]); let dist = v1.chebyshev_distance(&v2).unwrap(); assert_eq!(dist, 0.0); @@ -7333,7 +7333,7 @@ mod tests { #[test] fn test_chebyshev_distance_f64_negative_values() { - let v1 = ColFVector::from_vec(vec![-1.0, -2.0, -3.0]); + let v1 = ColFVec::from_vec(vec![-1.0, -2.0, -3.0]); let v2 = FlexVector::from_vec(vec![1.0, 2.0, 3.0]); let dist = v1.chebyshev_distance(&v2).unwrap(); // max(|-1-1|, |-2-2|, |-3-3|) = max(2, 4, 6) = 6 @@ -7342,7 +7342,7 @@ mod tests { #[test] fn test_chebyshev_distance_f64_mismatched_length() { - let v1 = ColFVector::from_vec(vec![1.0, 2.0]); + let v1 = ColFVec::from_vec(vec![1.0, 2.0]); let v2 = FlexVector::from_vec(vec![3.0, 4.0, 5.0]); let result = v1.chebyshev_distance(&v2); assert!(result.is_err()); @@ -7351,7 +7351,7 @@ mod tests { // -- minkowski_distance -- #[test] fn test_minkowski_distance_f64_basic() { - let v1 = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = ColFVec::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 6.0, 8.0]); let dist = v1.minkowski_distance(&v2, 3.0).unwrap(); // ((|1-4|^3 + |2-6|^3 + |3-8|^3))^(1/3) = (27 + 64 + 125)^(1/3) = (216)^(1/3) = 6 @@ -7360,7 +7360,7 @@ mod tests { #[test] fn test_minkowski_distance_f64_p1() { - let v1 = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = ColFVec::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 6.0, 8.0]); let dist = v1.minkowski_distance(&v2, 1.0).unwrap(); // Should match manhattan distance: 3 + 4 + 5 = 12 @@ -7369,7 +7369,7 @@ mod tests { #[test] fn test_minkowski_distance_f64_p2() { - let v1 = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = ColFVec::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 6.0, 8.0]); let dist = v1.minkowski_distance(&v2, 2.0).unwrap(); // Should match euclidean distance: sqrt(9 + 16 + 25) = sqrt(50) @@ -7386,7 +7386,7 @@ mod tests { #[test] fn test_minkowski_distance_f64_identical() { - let v1 = ColFVector::from_vec(vec![1.23, 4.56, 7.89]); + let v1 = ColFVec::from_vec(vec![1.23, 4.56, 7.89]); let v2 = FlexVector::from_vec(vec![1.23, 4.56, 7.89]); let dist = v1.minkowski_distance(&v2, 2.0).unwrap(); assert_eq!(dist, 0.0); @@ -7394,7 +7394,7 @@ mod tests { #[test] fn test_minkowski_distance_f64_partial() { - let v1 = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = ColFVec::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 6.0]); let result = v1.minkowski_distance(&v2, 2.0); assert!(result.is_err()); @@ -7402,7 +7402,7 @@ mod tests { #[test] fn test_minkowski_distance_f64_invalid_p() { - let v1 = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = ColFVec::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![4.0, 6.0, 8.0]); let result = v1.minkowski_distance(&v2, 0.5); assert!(result.is_err()); @@ -7411,7 +7411,7 @@ mod tests { // -- norm -- #[test] fn test_norm_f64_basic() { - let v = ColFVector::from_vec(vec![3.0, 4.0]); + let v = ColFVec::from_vec(vec![3.0, 4.0]); let norm = v.norm(); // sqrt(3^2 + 4^2) = 5 assert!((norm - 5.0).abs() < 1e-12); @@ -7419,21 +7419,21 @@ mod tests { #[test] fn test_norm_f64_zero() { - let v = ColFVector::from_vec(vec![0.0, 0.0, 0.0]); + let v = ColFVec::from_vec(vec![0.0, 0.0, 0.0]); let norm = v.norm(); assert_eq!(norm, 0.0); } #[test] fn test_norm_f64_single_element() { - let v = ColFVector::from_vec(vec![7.0]); + let v = ColFVec::from_vec(vec![7.0]); let norm = v.norm(); assert_eq!(norm, 7.0); } #[test] fn test_norm_f64_negative_values() { - let v = ColFVector::from_vec(vec![-3.0, -4.0]); + let v = ColFVec::from_vec(vec![-3.0, -4.0]); let norm = v.norm(); // sqrt((-3)^2 + (-4)^2) = 5 assert!((norm - 5.0).abs() < 1e-12); @@ -7442,7 +7442,7 @@ mod tests { // -- magnitude -- #[test] fn test_magnitude_f64_basic() { - let v = ColFVector::from_vec(vec![3.0, 4.0]); + let v = ColFVec::from_vec(vec![3.0, 4.0]); let mag = v.magnitude(); // sqrt(3^2 + 4^2) = 5 assert!((mag - 5.0).abs() < 1e-12); @@ -7450,21 +7450,21 @@ mod tests { #[test] fn test_magnitude_f64_zero() { - let v = ColFVector::from_vec(vec![0.0, 0.0, 0.0]); + let v = ColFVec::from_vec(vec![0.0, 0.0, 0.0]); let mag = v.magnitude(); assert_eq!(mag, 0.0); } #[test] fn test_magnitude_f64_single_element() { - let v = ColFVector::from_vec(vec![7.0]); + let v = ColFVec::from_vec(vec![7.0]); let mag = v.magnitude(); assert_eq!(mag, 7.0); } #[test] fn test_magnitude_f64_negative_values() { - let v = ColFVector::from_vec(vec![-3.0, -4.0]); + let v = ColFVec::from_vec(vec![-3.0, -4.0]); let mag = v.magnitude(); // sqrt((-3)^2 + (-4)^2) = 5 assert!((mag - 5.0).abs() < 1e-12); @@ -7473,7 +7473,7 @@ mod tests { // -- lp_norm -- #[test] fn test_lp_norm_f64_p1() { - let v = ColFVector::from_vec(vec![1.0, -2.0, 3.0]); + let v = ColFVec::from_vec(vec![1.0, -2.0, 3.0]); let norm = v.lp_norm(1.0).unwrap(); // L1 norm: |1| + |−2| + |3| = 1 + 2 + 3 = 6 assert!((norm - 6.0).abs() < 1e-12); @@ -7481,7 +7481,7 @@ mod tests { #[test] fn test_lp_norm_f64_p2() { - let v = ColFVector::from_vec(vec![3.0, 4.0]); + let v = ColFVec::from_vec(vec![3.0, 4.0]); let norm = v.lp_norm(2.0).unwrap(); // L2 norm: sqrt(3^2 + 4^2) = 5 assert!((norm - 5.0).abs() < 1e-12); @@ -7489,7 +7489,7 @@ mod tests { #[test] fn test_lp_norm_f64_p3() { - let v = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); + let v = ColFVec::from_vec(vec![1.0, 2.0, 3.0]); let norm = v.lp_norm(3.0).unwrap(); // (|1|^3 + |2|^3 + |3|^3)^(1/3) = (1 + 8 + 27)^(1/3) = 36^(1/3) assert!((norm - 36f64.powf(1.0 / 3.0)).abs() < 1e-12); @@ -7497,14 +7497,14 @@ mod tests { #[test] fn test_lp_norm_f64_zero() { - let v = ColFVector::from_vec(vec![0.0, 0.0, 0.0]); + let v = ColFVec::from_vec(vec![0.0, 0.0, 0.0]); let norm = v.lp_norm(2.0).unwrap(); assert_eq!(norm, 0.0); } #[test] fn test_lp_norm_f64_invalid_p() { - let v = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); + let v = ColFVec::from_vec(vec![1.0, 2.0, 3.0]); let result = v.lp_norm(0.5); assert!(result.is_err()); } @@ -7512,7 +7512,7 @@ mod tests { // -- angle_with -- #[test] fn test_angle_with_f64_orthogonal() { - let v1 = ColFVector::from_vec(vec![1.0, 0.0]); + let v1 = ColFVec::from_vec(vec![1.0, 0.0]); let v2 = FlexVector::from_vec(vec![0.0, 1.0]); let angle = v1.angle_with(&v2).unwrap(); // Orthogonal vectors: angle should be pi/2 @@ -7521,7 +7521,7 @@ mod tests { #[test] fn test_angle_with_f64_parallel() { - let v1 = ColFVector::from_vec(vec![1.0, 2.0]); + let v1 = ColFVec::from_vec(vec![1.0, 2.0]); let v2 = FlexVector::from_vec(vec![2.0, 4.0]); let angle = v1.angle_with(&v2).unwrap(); // Parallel vectors: angle should be 0 (allow for floating-point error) @@ -7530,7 +7530,7 @@ mod tests { #[test] fn test_angle_with_f64_opposite() { - let v1 = ColFVector::from_vec(vec![1.0, 0.0]); + let v1 = ColFVec::from_vec(vec![1.0, 0.0]); let v2 = FlexVector::from_vec(vec![-1.0, 0.0]); let angle = v1.angle_with(&v2).unwrap(); // Opposite vectors: angle should be pi @@ -7539,7 +7539,7 @@ mod tests { #[test] fn test_angle_with_f64_identical() { - let v1 = ColFVector::from_vec(vec![3.0, 4.0]); + let v1 = ColFVec::from_vec(vec![3.0, 4.0]); let v2 = FlexVector::from_vec(vec![3.0, 4.0]); let angle = v1.angle_with(&v2).unwrap(); // Identical vectors: angle should be 0 @@ -7548,7 +7548,7 @@ mod tests { #[test] fn test_angle_with_f64_arbitrary() { - let v1 = ColFVector::from_vec(vec![1.0, 2.0]); + let v1 = ColFVec::from_vec(vec![1.0, 2.0]); let v2 = FlexVector::from_vec(vec![2.0, 1.0]); let angle = v1.angle_with(&v2).unwrap(); // Check that the angle is between 0 and pi @@ -7557,7 +7557,7 @@ mod tests { #[test] fn test_angle_with_f64_zero_vector() { - let v1 = ColFVector::from_vec(vec![0.0, 0.0]); + let v1 = ColFVec::from_vec(vec![0.0, 0.0]); let v2 = FlexVector::from_vec(vec![1.0, 2.0]); let result = v1.angle_with(&v2); assert!(result.is_err()); @@ -7565,7 +7565,7 @@ mod tests { #[test] fn test_angle_with_f64_mismatched_length() { - let v1 = ColFVector::from_vec(vec![1.0, 2.0]); + let v1 = ColFVec::from_vec(vec![1.0, 2.0]); let v2 = FlexVector::from_vec(vec![3.0, 4.0, 5.0]); let result = v1.angle_with(&v2); assert!(result.is_err()); @@ -7574,7 +7574,7 @@ mod tests { // -- project_onto -- #[test] fn test_project_onto_f64_basic() { - let v1 = ColFVector::from_vec(vec![3.0, 4.0]); + let v1 = ColFVec::from_vec(vec![3.0, 4.0]); let v2 = FlexVector::from_vec(vec![1.0, 0.0]); let proj = v1.project_onto(&v2).unwrap(); // Projection of [3,4] onto [1,0] is [3,0] @@ -7584,7 +7584,7 @@ mod tests { #[test] fn test_project_onto_f64_parallel() { - let v1 = ColFVector::from_vec(vec![2.0, 4.0]); + let v1 = ColFVec::from_vec(vec![2.0, 4.0]); let v2 = FlexVector::from_vec(vec![1.0, 2.0]); let proj = v1.project_onto(&v2).unwrap(); // v1 is parallel to v2, so projection should be v1 itself @@ -7594,7 +7594,7 @@ mod tests { #[test] fn test_project_onto_f64_orthogonal() { - let v1 = ColFVector::from_vec(vec![0.0, 1.0]); + let v1 = ColFVec::from_vec(vec![0.0, 1.0]); let v2 = FlexVector::from_vec(vec![1.0, 0.0]); let proj = v1.project_onto(&v2).unwrap(); // Orthogonal vectors: projection should be [0,0] @@ -7604,7 +7604,7 @@ mod tests { #[test] fn test_project_onto_f64_identical() { - let v1 = ColFVector::from_vec(vec![5.0, 5.0]); + let v1 = ColFVec::from_vec(vec![5.0, 5.0]); let v2 = FlexVector::from_vec(vec![5.0, 5.0]); let proj = v1.project_onto(&v2).unwrap(); // Should be v1 itself @@ -7614,7 +7614,7 @@ mod tests { #[test] fn test_project_onto_f64_zero_vector() { - let v1 = ColFVector::from_vec(vec![1.0, 2.0]); + let v1 = ColFVec::from_vec(vec![1.0, 2.0]); let v2 = FlexVector::from_vec(vec![0.0, 0.0]); let result = v1.project_onto(&v2); assert!(result.is_err()); @@ -7622,7 +7622,7 @@ mod tests { #[test] fn test_project_onto_f64_mismatched_length() { - let v1 = ColFVector::from_vec(vec![1.0, 2.0]); + let v1 = ColFVec::from_vec(vec![1.0, 2.0]); let v2 = FlexVector::from_vec(vec![3.0, 4.0, 5.0]); let result = v1.project_onto(&v2); assert!(result.is_err()); @@ -7709,7 +7709,7 @@ mod tests { // --- cosine_similarity --- #[test] fn test_cosine_similarity_f64_parallel() { - let v1 = ColFVector::from_vec(vec![1.0, 2.0, 3.0]); + let v1 = ColFVec::from_vec(vec![1.0, 2.0, 3.0]); let v2 = FlexVector::from_vec(vec![2.0, 4.0, 6.0]); let cos_sim = v1.cosine_similarity(&v2).unwrap(); assert!((cos_sim - 1.0).abs() < 1e-10); @@ -7717,7 +7717,7 @@ mod tests { #[test] fn test_cosine_similarity_f64_orthogonal() { - let v1 = ColFVector::from_vec(vec![1.0, 0.0]); + let v1 = ColFVec::from_vec(vec![1.0, 0.0]); let v2 = FlexVector::from_vec(vec![0.0, 1.0]); let cos_sim = v1.cosine_similarity(&v2).unwrap(); assert!((cos_sim - 0.0).abs() < 1e-10); @@ -7725,7 +7725,7 @@ mod tests { #[test] fn test_cosine_similarity_f64_opposite() { - let v1 = ColFVector::from_vec(vec![1.0, 0.0]); + let v1 = ColFVec::from_vec(vec![1.0, 0.0]); let v2 = FlexVector::from_vec(vec![-1.0, 0.0]); let cos_sim = v1.cosine_similarity(&v2).unwrap(); assert!((cos_sim + 1.0).abs() < 1e-10); @@ -7733,7 +7733,7 @@ mod tests { #[test] fn test_cosine_similarity_f64_zero_vector() { - let v1 = ColFVector::from_vec(vec![0.0, 0.0]); + let v1 = ColFVec::from_vec(vec![0.0, 0.0]); let v2 = FlexVector::from_vec(vec![1.0, 2.0]); let cos_sim = v1.cosine_similarity(&v2); assert!(cos_sim.is_err()); @@ -7748,7 +7748,7 @@ mod tests { // -- normalize -- #[test] fn test_normalize_complex_f64_basic() { - let v = ColFVector::from_vec(vec![Complex::new(3.0, 4.0)]); + let v = ColFVec::from_vec(vec![Complex::new(3.0, 4.0)]); let normalized = v.normalize().unwrap(); // The norm is 5.0, so the normalized vector should be [0.6 + 0.8i] assert!((normalized.as_slice()[0].re - 0.6).abs() < 1e-12); @@ -7757,7 +7757,7 @@ mod tests { #[test] fn test_normalize_complex_f64_multiple_elements() { - let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v = ColFVec::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let norm = ((1.0 * 1.0 + 2.0 * 2.0) + (3.0 * 3.0 + 4.0 * 4.0)).sqrt(); let normalized = v.normalize().unwrap(); assert!((normalized.as_slice()[0].re - 1.0 / norm).abs() < 1e-12); @@ -7768,7 +7768,7 @@ mod tests { #[test] fn test_normalize_complex_f64_zero_vector() { - let v = ColFVector::from_vec(vec![Complex::new(0.0, 0.0)]); + let v = ColFVec::from_vec(vec![Complex::new(0.0, 0.0)]); let result = v.normalize(); assert!(result.is_err()); } @@ -8906,28 +8906,28 @@ mod tests { #[test] fn test_neg() { - let v = ColFVector::from_vec(vec![1, -2, 3]); + let v = ColFVec::from_vec(vec![1, -2, 3]); let neg_v = -v; assert_eq!(neg_v.as_slice(), &[-1, 2, -3]); } #[test] fn test_neg_f64() { - let v = ColFVector::from_vec(vec![1.5, -2.5, 0.0]); + let v = ColFVec::from_vec(vec![1.5, -2.5, 0.0]); let neg_v = -v; assert_eq!(neg_v.as_slice(), &[-1.5, 2.5, -0.0]); } #[test] fn test_neg_complex() { - let v = ColFVector::from_vec(vec![Complex::new(1.0, -2.0), Complex::new(-3.0, 4.0)]); + let v = ColFVec::from_vec(vec![Complex::new(1.0, -2.0), Complex::new(-3.0, 4.0)]); let neg_v = -v; assert_eq!(neg_v.as_slice(), &[Complex::new(-1.0, 2.0), Complex::new(3.0, -4.0)]); } #[test] fn test_neg_nan() { - let v = ColFVector::from_vec(vec![f64::NAN, -f64::NAN]); + let v = ColFVec::from_vec(vec![f64::NAN, -f64::NAN]); let neg_v = -v; // Negating NaN is still NaN, but sign bit may flip assert!(neg_v.as_slice()[0].is_nan()); @@ -8939,14 +8939,14 @@ mod tests { #[test] fn test_neg_infinity() { - let v = ColFVector::from_vec(vec![f64::INFINITY, f64::NEG_INFINITY]); + let v = ColFVec::from_vec(vec![f64::INFINITY, f64::NEG_INFINITY]); let neg_v = -v; assert_eq!(neg_v.as_slice(), &[-f64::INFINITY, f64::INFINITY]); } #[test] fn test_add() { - let v1 = ColFVector::from_vec(vec![1, 2, 3]); + let v1 = ColFVec::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); let sum = v1 + v2; assert_eq!(sum.as_slice(), &[5, 7, 9]); @@ -8954,7 +8954,7 @@ mod tests { #[test] fn test_add_f64() { - let v1 = ColFVector::from_vec(vec![1.0, 2.5]); + let v1 = ColFVec::from_vec(vec![1.0, 2.5]); let v2 = FlexVector::from_vec(vec![3.0, 4.5]); let sum = v1 + v2; assert_eq!(sum.as_slice(), &[4.0, 7.0]); @@ -8962,7 +8962,7 @@ mod tests { #[test] fn test_add_complex() { - let v1 = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v1 = ColFVec::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); let sum = v1 + v2; assert_eq!(sum.as_slice(), &[Complex::new(6.0, 8.0), Complex::new(10.0, 12.0)]); @@ -8970,7 +8970,7 @@ mod tests { #[test] fn test_add_nan_infinity() { - let v1 = ColFVector::from_vec(vec![f64::NAN, f64::INFINITY, 1.0]); + let v1 = ColFVec::from_vec(vec![f64::NAN, f64::INFINITY, 1.0]); let v2 = FlexVector::from_vec(vec![1.0, 2.0, f64::INFINITY]); let sum = v1 + v2; assert!(sum.as_slice()[0].is_nan()); @@ -8981,14 +8981,14 @@ mod tests { #[test] #[should_panic(expected = "Vector length mismatch")] fn test_add_panic_on_mismatched_length() { - let v1 = ColFVector::from_vec(vec![1, 2]); + let v1 = ColFVec::from_vec(vec![1, 2]); let v2 = FlexVector::from_vec(vec![1, 2, 3]); let _ = v1 + v2; } #[test] fn test_sub() { - let v1 = ColFVector::from_vec(vec![10, 20, 30]); + let v1 = ColFVec::from_vec(vec![10, 20, 30]); let v2 = FlexVector::from_vec(vec![1, 2, 3]); let diff = v1 - v2; assert_eq!(diff.as_slice(), &[9, 18, 27]); @@ -8996,7 +8996,7 @@ mod tests { #[test] fn test_sub_f64() { - let v1 = ColFVector::from_vec(vec![5.5, 2.0]); + let v1 = ColFVec::from_vec(vec![5.5, 2.0]); let v2 = FlexVector::from_vec(vec![1.5, 1.0]); let diff = v1 - v2; assert_eq!(diff.as_slice(), &[4.0, 1.0]); @@ -9004,7 +9004,7 @@ mod tests { #[test] fn test_sub_complex() { - let v1 = ColFVector::from_vec(vec![Complex::new(5.0, 7.0), Complex::new(3.0, 4.0)]); + let v1 = ColFVec::from_vec(vec![Complex::new(5.0, 7.0), Complex::new(3.0, 4.0)]); let v2 = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(2.0, 1.0)]); let diff = v1 - v2; assert_eq!(diff.as_slice(), &[Complex::new(4.0, 5.0), Complex::new(1.0, 3.0)]); @@ -9012,7 +9012,7 @@ mod tests { #[test] fn test_sub_nan_infinity() { - let v1 = ColFVector::from_vec(vec![f64::NAN, f64::INFINITY, 5.0]); + let v1 = ColFVec::from_vec(vec![f64::NAN, f64::INFINITY, 5.0]); let v2 = FlexVector::from_vec(vec![2.0, f64::INFINITY, f64::NAN]); let diff = v1 - v2; assert!(diff.as_slice()[0].is_nan()); @@ -9023,14 +9023,14 @@ mod tests { #[test] #[should_panic(expected = "Vector length mismatch")] fn test_sub_panic_on_mismatched_length() { - let v1 = ColFVector::from_vec(vec![1, 2]); + let v1 = ColFVec::from_vec(vec![1, 2]); let v2 = FlexVector::from_vec(vec![1, 2, 3]); let _ = v1 - v2; } #[test] fn test_mul() { - let v1 = ColFVector::from_vec(vec![2, 3, 4]); + let v1 = ColFVec::from_vec(vec![2, 3, 4]); let v2 = FlexVector::from_vec(vec![5, 6, 7]); let prod = v1 * v2; assert_eq!(prod.as_slice(), &[10, 18, 28]); @@ -9038,7 +9038,7 @@ mod tests { #[test] fn test_mul_f64() { - let v1 = ColFVector::from_vec(vec![1.5, 2.0]); + let v1 = ColFVec::from_vec(vec![1.5, 2.0]); let v2 = FlexVector::from_vec(vec![2.0, 3.0]); let prod = v1 * v2; assert_eq!(prod.as_slice(), &[3.0, 6.0]); @@ -9046,7 +9046,7 @@ mod tests { #[test] fn test_mul_complex() { - let v1 = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let v1 = ColFVec::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); let prod = v1 * v2; assert_eq!( @@ -9060,7 +9060,7 @@ mod tests { #[test] fn test_mul_nan_infinity() { - let v1 = ColFVector::from_vec(vec![f64::NAN, f64::INFINITY, 2.0]); + let v1 = ColFVec::from_vec(vec![f64::NAN, f64::INFINITY, 2.0]); let v2 = FlexVector::from_vec(vec![3.0, 2.0, f64::INFINITY]); let prod = v1 * v2; assert!(prod.as_slice()[0].is_nan()); @@ -9071,14 +9071,14 @@ mod tests { #[test] #[should_panic(expected = "Vector length mismatch")] fn test_mul_panic_on_mismatched_length() { - let v1 = ColFVector::from_vec(vec![1, 2]); + let v1 = ColFVec::from_vec(vec![1, 2]); let v2 = FlexVector::from_vec(vec![1, 2, 3]); let _ = v1 * v2; } #[test] fn test_elem_div_f32() { - let v1 = ColFVector::from_vec(vec![2.0f32, 4.0, 8.0]); + let v1 = ColFVec::from_vec(vec![2.0f32, 4.0, 8.0]); let v2 = FlexVector::from_vec(vec![1.0f32, 2.0, 4.0]); let result = v1 / v2; assert_eq!(result.as_slice(), &[2.0, 2.0, 2.0]); @@ -9086,7 +9086,7 @@ mod tests { #[test] fn test_elem_div_f64() { - let v1 = ColFVector::from_vec(vec![2.0f64, 4.0, 8.0]); + let v1 = ColFVec::from_vec(vec![2.0f64, 4.0, 8.0]); let v2 = FlexVector::from_vec(vec![1.0f64, 2.0, 4.0]); let result = v1 / v2; assert_eq!(result.as_slice(), &[2.0, 2.0, 2.0]); @@ -9094,7 +9094,7 @@ mod tests { #[test] fn test_elem_div_complex_f32() { - let v1 = ColFVector::from_vec(vec![Complex::new(2.0f32, 2.0), Complex::new(4.0, 0.0)]); + let v1 = ColFVec::from_vec(vec![Complex::new(2.0f32, 2.0), Complex::new(4.0, 0.0)]); let v2 = FlexVector::from_vec(vec![Complex::new(1.0f32, 1.0), Complex::new(2.0, 0.0)]); let result = v1 / v2; assert!((result[0] - Complex::new(2.0, 0.0)).norm() < 1e-6); @@ -9103,7 +9103,7 @@ mod tests { #[test] fn test_elem_div_complex_f64() { - let v1 = ColFVector::from_vec(vec![Complex::new(2.0f64, 2.0), Complex::new(4.0, 0.0)]); + let v1 = ColFVec::from_vec(vec![Complex::new(2.0f64, 2.0), Complex::new(4.0, 0.0)]); let v2 = FlexVector::from_vec(vec![Complex::new(1.0f64, 1.0), Complex::new(2.0, 0.0)]); let result = v1 / v2; assert!((result[0] - Complex::new(2.0, 0.0)).norm() < 1e-12); @@ -9113,14 +9113,14 @@ mod tests { #[test] #[should_panic(expected = "Vector length mismatch")] fn test_elem_div_panic_on_mismatched_length() { - let v1 = ColFVector::from_vec(vec![1.0f64, 2.0]); + let v1 = ColFVec::from_vec(vec![1.0f64, 2.0]); let v2 = FlexVector::from_vec(vec![1.0f64, 2.0, 3.0]); let _ = v1 / v2; } #[test] fn test_add_assign() { - let mut v1 = ColFVector::from_vec(vec![1, 2, 3]); + let mut v1 = ColFVec::from_vec(vec![1, 2, 3]); let v2 = FlexVector::from_vec(vec![4, 5, 6]); v1 += v2; assert_eq!(v1.as_slice(), &[5, 7, 9]); @@ -9128,7 +9128,7 @@ mod tests { #[test] fn test_add_assign_f64() { - let mut v1 = ColFVector::from_vec(vec![1.0, 2.5]); + let mut v1 = ColFVec::from_vec(vec![1.0, 2.5]); let v2 = FlexVector::from_vec(vec![3.0, 4.5]); v1 += v2; assert_eq!(v1.as_slice(), &[4.0, 7.0]); @@ -9136,7 +9136,7 @@ mod tests { #[test] fn test_add_assign_complex() { - let mut v1 = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let mut v1 = ColFVec::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); v1 += v2; assert_eq!(v1.as_slice(), &[Complex::new(6.0, 8.0), Complex::new(10.0, 12.0)]); @@ -9145,14 +9145,14 @@ mod tests { #[test] #[should_panic(expected = "Vector length mismatch")] fn test_add_assign_panic_on_mismatched_length() { - let mut v1 = ColFVector::from_vec(vec![1, 2]); + let mut v1 = ColFVec::from_vec(vec![1, 2]); let v2 = FlexVector::from_vec(vec![1, 2, 3]); v1 += v2; } #[test] fn test_sub_assign() { - let mut v1 = ColFVector::from_vec(vec![10, 20, 30]); + let mut v1 = ColFVec::from_vec(vec![10, 20, 30]); let v2 = FlexVector::from_vec(vec![1, 2, 3]); v1 -= v2; assert_eq!(v1.as_slice(), &[9, 18, 27]); @@ -9160,7 +9160,7 @@ mod tests { #[test] fn test_sub_assign_f64() { - let mut v1 = ColFVector::from_vec(vec![5.5, 2.0]); + let mut v1 = ColFVec::from_vec(vec![5.5, 2.0]); let v2 = FlexVector::from_vec(vec![1.5, 1.0]); v1 -= v2; assert_eq!(v1.as_slice(), &[4.0, 1.0]); @@ -9168,7 +9168,7 @@ mod tests { #[test] fn test_sub_assign_complex() { - let mut v1 = ColFVector::from_vec(vec![Complex::new(5.0, 7.0), Complex::new(3.0, 4.0)]); + let mut v1 = ColFVec::from_vec(vec![Complex::new(5.0, 7.0), Complex::new(3.0, 4.0)]); let v2 = FlexVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(2.0, 1.0)]); v1 -= v2; assert_eq!(v1.as_slice(), &[Complex::new(4.0, 5.0), Complex::new(1.0, 3.0)]); @@ -9177,14 +9177,14 @@ mod tests { #[test] #[should_panic(expected = "Vector length mismatch")] fn test_sub_assign_panic_on_mismatched_length() { - let mut v1 = ColFVector::from_vec(vec![1, 2]); + let mut v1 = ColFVec::from_vec(vec![1, 2]); let v2 = FlexVector::from_vec(vec![1, 2, 3]); v1 -= v2; } #[test] fn test_mul_assign() { - let mut v1 = ColFVector::from_vec(vec![2, 3, 4]); + let mut v1 = ColFVec::from_vec(vec![2, 3, 4]); let v2 = FlexVector::from_vec(vec![5, 6, 7]); v1 *= v2; assert_eq!(v1.as_slice(), &[10, 18, 28]); @@ -9192,7 +9192,7 @@ mod tests { #[test] fn test_mul_assign_f64() { - let mut v1 = ColFVector::from_vec(vec![1.5, 2.0]); + let mut v1 = ColFVec::from_vec(vec![1.5, 2.0]); let v2 = FlexVector::from_vec(vec![2.0, 3.0]); v1 *= v2; assert_eq!(v1.as_slice(), &[3.0, 6.0]); @@ -9200,7 +9200,7 @@ mod tests { #[test] fn test_mul_assign_complex() { - let mut v1 = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); + let mut v1 = ColFVec::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(3.0, 4.0)]); let v2 = FlexVector::from_vec(vec![Complex::new(5.0, 6.0), Complex::new(7.0, 8.0)]); v1 *= v2; assert_eq!(v1.as_slice(), &[Complex::new(-7.0, 16.0), Complex::new(-11.0, 52.0)]); @@ -9209,14 +9209,14 @@ mod tests { #[test] #[should_panic(expected = "Vector length mismatch")] fn test_mul_assign_panic_on_mismatched_length() { - let mut v1 = ColFVector::from_vec(vec![1, 2]); + let mut v1 = ColFVec::from_vec(vec![1, 2]); let v2 = FlexVector::from_vec(vec![1, 2, 3]); v1 *= v2; } #[test] fn test_elem_div_assign_f32() { - let mut v1 = ColFVector::from_vec(vec![2.0f32, 4.0, 8.0]); + let mut v1 = ColFVec::from_vec(vec![2.0f32, 4.0, 8.0]); let v2 = FlexVector::from_vec(vec![1.0f32, 2.0, 4.0]); v1 /= v2; assert_eq!(v1.as_slice(), &[2.0, 2.0, 2.0]); @@ -9224,7 +9224,7 @@ mod tests { #[test] fn test_elem_div_assign_f64() { - let mut v1 = ColFVector::from_vec(vec![2.0f64, 4.0, 8.0]); + let mut v1 = ColFVec::from_vec(vec![2.0f64, 4.0, 8.0]); let v2 = FlexVector::from_vec(vec![1.0f64, 2.0, 4.0]); v1 /= v2; assert_eq!(v1.as_slice(), &[2.0, 2.0, 2.0]); @@ -9232,7 +9232,7 @@ mod tests { #[test] fn test_elem_div_assign_complex_f32() { - let mut v1 = ColFVector::from_vec(vec![Complex::new(2.0f32, 2.0), Complex::new(4.0, 0.0)]); + let mut v1 = ColFVec::from_vec(vec![Complex::new(2.0f32, 2.0), Complex::new(4.0, 0.0)]); let v2 = FlexVector::from_vec(vec![Complex::new(1.0f32, 1.0), Complex::new(2.0, 0.0)]); v1 /= v2; assert!((v1[0] - Complex::new(2.0, 0.0)).norm() < 1e-6); @@ -9241,7 +9241,7 @@ mod tests { #[test] fn test_elem_div_assign_complex_f64() { - let mut v1 = ColFVector::from_vec(vec![Complex::new(2.0f64, 2.0), Complex::new(4.0, 0.0)]); + let mut v1 = ColFVec::from_vec(vec![Complex::new(2.0f64, 2.0), Complex::new(4.0, 0.0)]); let v2 = FlexVector::from_vec(vec![Complex::new(1.0f64, 1.0), Complex::new(2.0, 0.0)]); v1 /= v2; assert!((v1[0] - Complex::new(2.0, 0.0)).norm() < 1e-12); @@ -9251,105 +9251,105 @@ mod tests { #[test] #[should_panic(expected = "Vector length mismatch")] fn test_elem_div_assign_panic_on_mismatched_length() { - let mut v1 = ColFVector::from_vec(vec![1.0f64, 2.0]); + let mut v1 = ColFVec::from_vec(vec![1.0f64, 2.0]); let v2 = FlexVector::from_vec(vec![1.0f64, 2.0, 3.0]); v1 /= v2; } #[test] fn test_scalar_add() { - let v = ColFVector::from_vec(vec![1, 2, 3]); + let v = ColFVec::from_vec(vec![1, 2, 3]); let sum = v.clone() + 10; assert_eq!(sum.as_slice(), &[11, 12, 13]); } #[test] fn test_scalar_add_f64() { - let v = ColFVector::from_vec(vec![1.5, -2.0, 0.0]); + let v = ColFVec::from_vec(vec![1.5, -2.0, 0.0]); let sum = v.clone() + 2.0; assert_eq!(sum.as_slice(), &[3.5, 0.0, 2.0]); } #[test] fn test_scalar_add_complex_f64() { - let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + let v = ColFVec::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); let sum = v.clone() + Complex::new(2.0, 1.0); assert_eq!(sum.as_slice(), &[Complex::new(3.0, 3.0), Complex::new(-1.0, 5.0)]); } #[test] fn test_scalar_add_assign() { - let mut v = ColFVector::from_vec(vec![1, 2, 3]); + let mut v = ColFVec::from_vec(vec![1, 2, 3]); v += 10; assert_eq!(v.as_slice(), &[11, 12, 13]); } #[test] fn test_scalar_add_assign_f64() { - let mut v = ColFVector::from_vec(vec![1.5, -2.0, 0.0]); + let mut v = ColFVec::from_vec(vec![1.5, -2.0, 0.0]); v += 2.0; assert_eq!(v.as_slice(), &[3.5, 0.0, 2.0]); } #[test] fn test_scalar_add_assign_complex_f64() { - let mut v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + let mut v = ColFVec::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); v += Complex::new(2.0, 1.0); assert_eq!(v.as_slice(), &[Complex::new(3.0, 3.0), Complex::new(-1.0, 5.0)]); } #[test] fn test_scalar_sub() { - let v = ColFVector::from_vec(vec![10, 20, 30]); + let v = ColFVec::from_vec(vec![10, 20, 30]); let diff = v.clone() - 5; assert_eq!(diff.as_slice(), &[5, 15, 25]); } #[test] fn test_scalar_sub_f64() { - let v = ColFVector::from_vec(vec![1.5, -2.0, 0.0]); + let v = ColFVec::from_vec(vec![1.5, -2.0, 0.0]); let diff = v.clone() - 2.0; assert_eq!(diff.as_slice(), &[-0.5, -4.0, -2.0]); } #[test] fn test_scalar_sub_complex_f64() { - let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + let v = ColFVec::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); let diff = v.clone() - Complex::new(2.0, 1.0); assert_eq!(diff.as_slice(), &[Complex::new(-1.0, 1.0), Complex::new(-5.0, 3.0)]); } #[test] fn test_scalar_sub_assign() { - let mut v = ColFVector::from_vec(vec![10, 20, 30]); + let mut v = ColFVec::from_vec(vec![10, 20, 30]); v -= 5; assert_eq!(v.as_slice(), &[5, 15, 25]); } #[test] fn test_scalar_sub_assign_f64() { - let mut v = ColFVector::from_vec(vec![1.5, -2.0, 0.0]); + let mut v = ColFVec::from_vec(vec![1.5, -2.0, 0.0]); v -= 2.0; assert_eq!(v.as_slice(), &[-0.5, -4.0, -2.0]); } #[test] fn test_scalar_sub_assign_complex_f64() { - let mut v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + let mut v = ColFVec::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); v -= Complex::new(2.0, 1.0); assert_eq!(v.as_slice(), &[Complex::new(-1.0, 1.0), Complex::new(-5.0, 3.0)]); } #[test] fn test_scalar_mul() { - let v = ColFVector::from_vec(vec![2, -3, 4]); + let v = ColFVec::from_vec(vec![2, -3, 4]); let prod = v.clone() * 3; assert_eq!(prod.as_slice(), &[6, -9, 12]); } #[test] fn test_scalar_mul_f64() { - let v = ColFVector::from_vec(vec![1.5, -2.0, 0.0]); + let v = ColFVec::from_vec(vec![1.5, -2.0, 0.0]); let prod = v.clone() * 2.0; assert_eq!(prod.as_slice(), &[3.0, -4.0, 0.0]); } @@ -9357,7 +9357,7 @@ mod tests { #[test] fn test_scalar_mul_complex() { use num::Complex; - let v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + let v = ColFVec::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); let scalar = Complex::new(2.0, 0.0); let prod = v.clone() * scalar; assert_eq!(prod.as_slice(), &[Complex::new(2.0, 4.0), Complex::new(-6.0, 8.0)]); @@ -9365,14 +9365,14 @@ mod tests { #[test] fn test_scalar_mul_assign() { - let mut v = ColFVector::from_vec(vec![2, -3, 4]); + let mut v = ColFVec::from_vec(vec![2, -3, 4]); v *= 3; assert_eq!(v.as_slice(), &[6, -9, 12]); } #[test] fn test_scalar_mul_assign_f64() { - let mut v = ColFVector::from_vec(vec![1.5, -2.0, 0.0]); + let mut v = ColFVec::from_vec(vec![1.5, -2.0, 0.0]); v *= 2.0; assert_eq!(v.as_slice(), &[3.0, -4.0, 0.0]); } @@ -9380,7 +9380,7 @@ mod tests { #[test] fn test_scalar_mul_assign_complex() { use num::Complex; - let mut v = ColFVector::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); + let mut v = ColFVec::from_vec(vec![Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); let scalar = Complex::new(2.0, 0.0); v *= scalar; assert_eq!(v.as_slice(), &[Complex::new(2.0, 4.0), Complex::new(-6.0, 8.0)]); @@ -9432,7 +9432,7 @@ mod tests { fn test_scalar_div_assign_complex_by_complex() { use num::Complex; let mut v: FlexVector> = - ColFVector::from_vec(vec![Complex::new(2.0, 4.0), Complex::new(-6.0, 8.0)]); + ColFVec::from_vec(vec![Complex::new(2.0, 4.0), Complex::new(-6.0, 8.0)]); let divisor = Complex::new(2.0, 0.0); v /= divisor; assert_eq!(v.as_slice(), &[Complex::new(1.0, 2.0), Complex::new(-3.0, 4.0)]); @@ -9440,7 +9440,7 @@ mod tests { #[test] fn test_scalar_div_nan_infinity() { - let v = ColFVector::from_vec(vec![f64::NAN, f64::INFINITY, 4.0]); + let v = ColFVec::from_vec(vec![f64::NAN, f64::INFINITY, 4.0]); let result: FlexVector = v.clone() / 2.0; assert!(result.as_slice()[0].is_nan()); assert_eq!(result.as_slice()[1], f64::INFINITY); @@ -9449,7 +9449,7 @@ mod tests { #[test] fn test_scalar_div_assign_nan_infinity() { - let mut v: FlexVector = ColFVector::from_vec(vec![f64::NAN, f64::INFINITY, 4.0]); + let mut v: FlexVector = ColFVec::from_vec(vec![f64::NAN, f64::INFINITY, 4.0]); v /= 2.0; assert!(v.as_slice()[0].is_nan()); assert_eq!(v.as_slice()[1], f64::INFINITY); @@ -9460,7 +9460,7 @@ mod tests { #[test] fn test_add_overflow_i32() { - let v1 = ColFVector::from_vec(vec![i32::MAX]); + let v1 = ColFVec::from_vec(vec![i32::MAX]); let v2 = FlexVector::from_vec(vec![1]); if cfg!(debug_assertions) { // In debug mode, should panic on overflow @@ -9477,7 +9477,7 @@ mod tests { #[test] fn test_mul_overflow_i32() { - let v1 = ColFVector::from_vec(vec![i32::MAX]); + let v1 = ColFVec::from_vec(vec![i32::MAX]); let v2 = FlexVector::from_vec(vec![2]); if cfg!(debug_assertions) { // In debug mode, should panic on overflow @@ -9494,7 +9494,7 @@ mod tests { #[test] fn test_divide_by_zero_f64() { - let v: FlexVector = ColFVector::from_vec(vec![1.0, -2.0, 0.0]); + let v: FlexVector = ColFVec::from_vec(vec![1.0, -2.0, 0.0]); let result = v.clone() / 0.0; assert_eq!(result.as_slice()[0], f64::INFINITY); assert_eq!(result.as_slice()[1], f64::NEG_INFINITY); @@ -9503,7 +9503,7 @@ mod tests { #[test] fn test_neg_zero_f64() { - let v = ColFVector::from_vec(vec![0.0, -0.0]); + let v = ColFVec::from_vec(vec![0.0, -0.0]); let neg_v = -v; assert_eq!(neg_v.as_slice()[0], -0.0); assert_eq!(neg_v.as_slice()[1], 0.0); @@ -9512,7 +9512,7 @@ mod tests { #[test] fn test_complex_div_by_zero() { use num::Complex; - let v: FlexVector> = ColFVector::from_vec(vec![Complex::new(1.0, 1.0)]); + let v: FlexVector> = ColFVec::from_vec(vec![Complex::new(1.0, 1.0)]); let result = v.clone() / Complex::new(0.0, 0.0); assert!(result.as_slice()[0].re.is_nan()); assert!(result.as_slice()[0].im.is_nan()); diff --git a/src/types/vectorslice.rs b/src/types/vectorslice.rs index b1ffa3d..46f5639 100644 --- a/src/types/vectorslice.rs +++ b/src/types/vectorslice.rs @@ -33,11 +33,6 @@ use num::{Complex, Zero}; // ================================ // ///////////////////////////////// -/// ... -pub type ColVS<'a, T> = VectorSlice<'a, T, Column>; -/// ... -pub type RowVS<'a, T> = VectorSlice<'a, T, Row>; - /// ... #[derive(Clone, PartialEq, Eq)] pub struct VectorSlice<'a, T, O = Column> { @@ -46,6 +41,11 @@ pub struct VectorSlice<'a, T, O = Column> { _orientation: PhantomData, } +/// ... +pub type ColVecSli<'a, T> = VectorSlice<'a, T, Column>; +/// ... +pub type RowVecSli<'a, T> = VectorSlice<'a, T, Row>; + // ================================ // // Constructors @@ -817,11 +817,6 @@ impl<'a, T, O> VectorSlice<'a, T, O> { // ================================ // ///////////////////////////////// -/// ... -pub type ColVSMut<'a, T> = VectorSliceMut<'a, T, Column>; -/// ... -pub type RowVSMut<'a, T> = VectorSliceMut<'a, T, Row>; - /// ... #[derive(PartialEq, Eq)] pub struct VectorSliceMut<'a, T, O = Column> { @@ -830,6 +825,11 @@ pub struct VectorSliceMut<'a, T, O = Column> { _orientation: PhantomData, } +/// ... +pub type ColVecSliMut<'a, T> = VectorSliceMut<'a, T, Column>; +/// ... +pub type RowVecSliMut<'a, T> = VectorSliceMut<'a, T, Row>; + // ================================ // // Constructors