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 diff --git a/Cargo.toml b/Cargo.toml index c3d15b9..9604567 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 = [ @@ -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 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 diff --git a/src/errors.rs b/src/errors.rs index b11e5cc..62f5a4e 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -1,11 +1,15 @@ //! 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 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 /// from [`slice`] data. TryFromSliceError(String), @@ -14,6 +18,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 +28,12 @@ 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}") + } VectorError::TryFromVecError(s) => { write!(f, "VectorError::TryFromVecError: {s}") } @@ -31,6 +43,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/lib.rs b/src/lib.rs index f92d617..f96f7b7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -893,6 +893,29 @@ #![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; + +/// The vectora prelude: import this module to bring all core traits, types, and macros into scope. +/// +/// This prelude includes: +/// - Core traits +/// - Main types +/// - Main macros +pub mod prelude { + // Traits + pub use crate::types::traits::{ + Transposable, VectorBase, VectorBaseMut, VectorHasOrientation, VectorOps, VectorOpsComplex, + VectorOpsComplexMut, VectorOpsFloat, VectorOpsFloatMut, VectorOpsMut, + }; + // Types + 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/macros.rs b/src/macros.rs new file mode 100644 index 0000000..efccd1d --- /dev/null +++ b/src/macros.rs @@ -0,0 +1,1448 @@ +//! Macros. + +/// Creates a [`Vector`] with the given elements or repeated value. +/// +/// - `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 +/// +/// ``` +/// use vectora::vector; +/// use num::Complex; +/// +/// 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 { + ($elem:expr; $n:expr) => ( + $crate::types::vector::Vector::from([$elem; $n]) + ); + ($($x:expr),+ $(,)?) => ( + $crate::types::vector::Vector::from([$($x),+]) + ); +} + +/// Converts a collection (like a `Vec` or slice) into a [`Vector`] of matching length, returning a `Result`. +/// +/// - `try_vector!(data)` tries to create a Vector from a runtime collection, returning an error if the length does not match. +/// +/// # Examples +/// +/// ``` +/// use vectora::prelude::*; +/// use num::Complex; +/// +/// 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(); +/// ``` +/// +/// /// Error example: +/// +/// ``` +/// 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()); +#[macro_export] +macro_rules! try_vector { + ($elem:expr) => { + $crate::types::vector::Vector::try_from($elem) + }; +} + +/// Creates a [`FlexVector`] with the list of given elements or repeated value, optionally specifying +/// vector row or column orientation. +/// +/// - `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::*; +/// +/// 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! 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]) + }; + // 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),+]) + }; +} + +/// Creates a [`FlexVector`] from a collection (slice, Vec, or Cow), optionally specifying row or column orientation. +/// +/// - `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: Result, _> = try_fv_iter!(data.iter().map(|s| s.parse::())); +/// assert!(fv.is_err()); +/// +/// let data = vec!["1", "2", "3"]; +/// 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_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) + }; +} + +/// ... +#[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 + std::ops::Neg, + { + type Output = Self; + #[inline] + fn $method(self) -> Self { + let elements = self.elements.into_iter().map(|a| $op a).collect(); + Self { elements, _orientation: PhantomData } + } + } + }; +} + +/// ... +#[macro_export] +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 + 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 elements = self.elements.into_iter() + .zip(rhs.elements) + .map(|(a, b)| a $op b) + .collect(); + Self { elements, _orientation: PhantomData } + } + } + }; + // 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; + #[inline] + fn $method(self, rhs: Self) -> Self { + let elements = self.elements.into_iter() + .zip(rhs.elements) + .map(|(a, b)| a $op b) + .collect(); + Self { elements, _orientation: PhantomData } + } + } + }; +} + +/// ... +#[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; + #[inline] + fn div(self, rhs: Self) -> Self { + assert_eq!(self.len(), rhs.len(), "Vector length mismatch"); + let elements = + self.elements.into_iter().zip(rhs.elements).map(|(a, b)| a / b).collect(); + Self { elements, _orientation: PhantomData } + } + } + // f64 + 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 elements = + self.elements.into_iter().zip(rhs.elements).map(|(a, b)| a / b).collect(); + Self { elements, _orientation: PhantomData } + } + } + // Complex + 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 elements = + self.elements.into_iter().zip(rhs.elements).map(|(a, b)| a / b).collect(); + Self { elements, _orientation: PhantomData } + } + } + // Complex + 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 elements = + self.elements.into_iter().zip(rhs.elements).map(|(a, b)| a / b).collect(); + Self { elements, _orientation: PhantomData } + } + } + }; + // Without length check (for Vector) + (no_check_len, $VectorType:ident) => { + // f32 + impl std::ops::Div for $VectorType { + type Output = Self; + #[inline] + fn div(self, rhs: Self) -> Self { + let elements = + self.elements.into_iter().zip(rhs.elements).map(|(a, b)| a / b).collect(); + Self { elements, _orientation: PhantomData } + } + } + // f64 + impl std::ops::Div for $VectorType { + type Output = Self; + #[inline] + fn div(self, rhs: Self) -> Self { + let elements = + self.elements.into_iter().zip(rhs.elements).map(|(a, b)| a / b).collect(); + Self { elements, _orientation: PhantomData } + } + } + // Complex + impl std::ops::Div for $VectorType, O> { + type Output = Self; + #[inline] + fn div(self, rhs: Self) -> Self { + let elements = + self.elements.into_iter().zip(rhs.elements).map(|(a, b)| a / b).collect(); + Self { elements, _orientation: PhantomData } + } + } + // Complex + impl std::ops::Div for $VectorType, O> { + type Output = Self; + #[inline] + fn div(self, rhs: Self) -> Self { + let elements = + self.elements.into_iter().zip(rhs.elements).map(|(a, b)| a / b).collect(); + Self { elements, _orientation: PhantomData } + } + } + }; +} + +/// ... +#[macro_export] +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 + 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.elements.iter_mut().zip(rhs.elements) { + *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, + { + #[inline] + fn $method(&mut self, rhs: Self) { + for (a, b) in self.elements.iter_mut().zip(rhs.elements) { + *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 { + #[inline] + fn div_assign(&mut self, rhs: Self) { + assert_eq!(self.len(), rhs.len(), "Vector length mismatch"); + for (a, b) in self.elements.iter_mut().zip(rhs.elements) { + *a /= b; + } + } + } + // f64 + 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.elements.iter_mut().zip(rhs.elements) { + *a /= b; + } + } + } + // Complex + 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.elements.iter_mut().zip(rhs.elements) { + *a /= b; + } + } + } + // Complex + 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.elements.iter_mut().zip(rhs.elements) { + *a /= b; + } + } + } + }; + // Without length check (for Vector) + (no_check_len, $VectorType:ident) => { + // f32 + impl std::ops::DivAssign for $VectorType { + #[inline] + fn div_assign(&mut self, rhs: Self) { + for (a, b) in self.elements.iter_mut().zip(rhs.elements) { + *a /= b; + } + } + } + // f64 + impl std::ops::DivAssign for $VectorType { + #[inline] + fn div_assign(&mut self, rhs: Self) { + for (a, b) in self.elements.iter_mut().zip(rhs.elements) { + *a /= b; + } + } + } + // Complex + impl std::ops::DivAssign for $VectorType, O> { + #[inline] + fn div_assign(&mut self, rhs: Self) { + for (a, b) in self.elements.iter_mut().zip(rhs.elements) { + *a /= b; + } + } + } + // Complex + impl std::ops::DivAssign for $VectorType, O> { + #[inline] + fn div_assign(&mut self, rhs: Self) { + for (a, b) in self.elements.iter_mut().zip(rhs.elements) { + *a /= 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, + { + type Output = Self; + #[inline] + fn $method(self, rhs: T) -> Self { + let elements = self.elements.into_iter().map(|a| a $op rhs.clone()).collect(); + Self { elements, _orientation: PhantomData } + } + } + }; +} + +/// ... +#[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, + { + #[inline] + fn $method(&mut self, rhs: T) { + for a in &mut self.elements { + *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; + #[inline] + fn div(self, rhs: f32) -> Self { + let elements = self.elements.into_iter().map(|a| a / rhs).collect(); + Self { elements, _orientation: PhantomData } + } + } + // For f64 + impl std::ops::Div for $VectorType { + type Output = Self; + #[inline] + fn div(self, rhs: f64) -> Self { + let elements = self.elements.into_iter().map(|a| a / rhs).collect(); + Self { elements, _orientation: PhantomData } + } + } + // For Complex / f32 + impl std::ops::Div for $VectorType, O> { + type Output = Self; + #[inline] + fn div(self, rhs: f32) -> Self { + let elements = self.elements.into_iter().map(|a| a / rhs).collect(); + Self { elements, _orientation: PhantomData } + } + } + // For Complex / f64 + impl std::ops::Div for $VectorType, O> { + type Output = Self; + #[inline] + fn div(self, rhs: f64) -> Self { + let elements = self.elements.into_iter().map(|a| a / rhs).collect(); + Self { elements, _orientation: PhantomData } + } + } + // For Complex / Complex + impl std::ops::Div> for $VectorType, O> { + type Output = Self; + #[inline] + fn div(self, rhs: num::Complex) -> Self { + let elements = self.elements.into_iter().map(|a| a / rhs).collect(); + Self { elements, _orientation: PhantomData } + } + } + // For Complex / Complex + impl std::ops::Div> for $VectorType, O> { + type Output = Self; + #[inline] + fn div(self, rhs: num::Complex) -> Self { + let elements = self.elements.into_iter().map(|a| a / rhs).collect(); + Self { elements, _orientation: PhantomData } + } + } + }; +} + +/// ... +#[macro_export] +macro_rules! impl_vector_scalar_div_op_assign { + ($VectorType:ident) => { + // For f32 + impl std::ops::DivAssign for $VectorType { + #[inline] + fn div_assign(&mut self, rhs: f32) { + for a in &mut self.elements { + *a = *a / rhs; + } + } + } + // For f64 + impl std::ops::DivAssign for $VectorType { + #[inline] + fn div_assign(&mut self, rhs: f64) { + for a in &mut self.elements { + *a = *a / rhs; + } + } + } + // For Complex / f32 + impl std::ops::DivAssign for $VectorType, O> { + #[inline] + fn div_assign(&mut self, rhs: f32) { + for a in &mut self.elements { + *a = *a / rhs; + } + } + } + // For Complex / f64 + impl std::ops::DivAssign for $VectorType, O> { + #[inline] + fn div_assign(&mut self, rhs: f64) { + for a in &mut self.elements { + *a = *a / rhs; + } + } + } + // For Complex / Complex + impl std::ops::DivAssign> for $VectorType, O> { + #[inline] + fn div_assign(&mut self, rhs: num::Complex) { + for a in &mut self.elements { + *a = *a / rhs; + } + } + } + // For Complex / Complex + impl std::ops::DivAssign> for $VectorType, O> { + #[inline] + fn div_assign(&mut self, rhs: num::Complex) { + for a in &mut self.elements { + *a = *a / rhs; + } + } + } + }; +} + +#[cfg(test)] +mod tests { + use crate::types::orientation::{Column, Row}; + use crate::types::traits::VectorBase; + use crate::{FlexVector, Vector}; + #[allow(unused_imports)] + use approx::{assert_relative_eq, assert_relative_ne}; + #[allow(unused_imports)] + use num::complex::Complex; + #[allow(unused_imports)] + use pretty_assertions::{assert_eq, assert_ne}; + + #[test] + fn macro_vector_usize() { + let v1 = vector![1 as usize, 2 as usize, 3 as usize]; + let v2: Vector = vector![1, 2, 3]; + let v3: Vector = vector![1; 3]; + + assert_eq!(v1.len(), 3); + assert_eq!(v1[0], 1 as usize); + assert_eq!(v1[1], 2 as usize); + assert_eq!(v1[2], 3 as usize); + + assert_eq!(v2.len(), 3); + assert_eq!(v2[0], 1 as usize); + assert_eq!(v2[1], 2 as usize); + assert_eq!(v2[2], 3 as usize); + + assert_eq!(v3.len(), 3); + assert_eq!(v3[0], 1 as usize); + assert_eq!(v3[1], 1 as usize); + assert_eq!(v3[2], 1 as usize); + } + + #[test] + fn macro_vector_u8() { + let v1 = vector![1 as u8, 2 as u8, 3 as u8]; + let v2: Vector = vector![1, 2, 3]; + let v3: Vector = vector![1; 3]; + + assert_eq!(v1.len(), 3); + assert_eq!(v1[0], 1 as u8); + assert_eq!(v1[1], 2 as u8); + assert_eq!(v1[2], 3 as u8); + + assert_eq!(v2.len(), 3); + assert_eq!(v2[0], 1 as u8); + assert_eq!(v2[1], 2 as u8); + assert_eq!(v2[2], 3 as u8); + + assert_eq!(v3.len(), 3); + assert_eq!(v3[0], 1 as u8); + assert_eq!(v3[1], 1 as u8); + assert_eq!(v3[2], 1 as u8); + } + + #[test] + fn macro_vector_u16() { + let v1 = vector![1 as u16, 2 as u16, 3 as u16]; + let v2: Vector = vector![1, 2, 3]; + let v3: Vector = vector![1; 3]; + + assert_eq!(v1.len(), 3); + assert_eq!(v1[0], 1 as u16); + assert_eq!(v1[1], 2 as u16); + assert_eq!(v1[2], 3 as u16); + + assert_eq!(v2.len(), 3); + assert_eq!(v2[0], 1 as u16); + assert_eq!(v2[1], 2 as u16); + assert_eq!(v2[2], 3 as u16); + + assert_eq!(v3.len(), 3); + assert_eq!(v3[0], 1 as u16); + assert_eq!(v3[1], 1 as u16); + assert_eq!(v3[2], 1 as u16); + } + + #[test] + fn macro_vector_u32() { + let v1 = vector![1 as u32, 2 as u32, 3 as u32]; + let v2: Vector = vector![1, 2, 3]; + let v3: Vector = vector![1; 3]; + + assert_eq!(v1.len(), 3); + assert_eq!(v1[0], 1 as u32); + assert_eq!(v1[1], 2 as u32); + assert_eq!(v1[2], 3 as u32); + + assert_eq!(v2.len(), 3); + assert_eq!(v2[0], 1 as u32); + assert_eq!(v2[1], 2 as u32); + assert_eq!(v2[2], 3 as u32); + + assert_eq!(v3.len(), 3); + assert_eq!(v3[0], 1 as u32); + assert_eq!(v3[1], 1 as u32); + assert_eq!(v3[2], 1 as u32); + } + + #[test] + fn macro_vector_u64() { + let v1 = vector![1 as u64, 2 as u64, 3 as u64]; + let v2: Vector = vector![1, 2, 3]; + let v3: Vector = vector![1; 3]; + + assert_eq!(v1.len(), 3); + assert_eq!(v1[0], 1 as u64); + assert_eq!(v1[1], 2 as u64); + assert_eq!(v1[2], 3 as u64); + + assert_eq!(v2.len(), 3); + assert_eq!(v2[0], 1 as u64); + assert_eq!(v2[1], 2 as u64); + assert_eq!(v2[2], 3 as u64); + + assert_eq!(v3.len(), 3); + assert_eq!(v3[0], 1 as u64); + assert_eq!(v3[1], 1 as u64); + assert_eq!(v3[2], 1 as u64); + } + + #[test] + fn macro_vector_u128() { + let v1 = vector![1 as u128, 2 as u128, 3 as u128]; + let v2: Vector = vector![1, 2, 3]; + let v3: Vector = vector![1; 3]; + + assert_eq!(v1.len(), 3); + assert_eq!(v1[0], 1 as u128); + assert_eq!(v1[1], 2 as u128); + assert_eq!(v1[2], 3 as u128); + + assert_eq!(v2.len(), 3); + assert_eq!(v2[0], 1 as u128); + assert_eq!(v2[1], 2 as u128); + assert_eq!(v2[2], 3 as u128); + + assert_eq!(v3.len(), 3); + assert_eq!(v3[0], 1 as u128); + assert_eq!(v3[1], 1 as u128); + assert_eq!(v3[2], 1 as u128); + } + + #[test] + fn macro_vector_isize() { + let v1 = vector![1 as isize, 2 as isize, 3 as isize]; + let v2: Vector = vector![1, 2, 3]; + let v3: Vector = vector![1; 3]; + + assert_eq!(v1.len(), 3); + assert_eq!(v1[0], 1 as isize); + assert_eq!(v1[1], 2 as isize); + assert_eq!(v1[2], 3 as isize); + + assert_eq!(v2.len(), 3); + assert_eq!(v2[0], 1 as isize); + assert_eq!(v2[1], 2 as isize); + assert_eq!(v2[2], 3 as isize); + + assert_eq!(v3.len(), 3); + assert_eq!(v3[0], 1 as isize); + assert_eq!(v3[1], 1 as isize); + assert_eq!(v3[2], 1 as isize); + } + + #[test] + fn macro_vector_i8() { + let v1 = vector![1 as i8, 2 as i8, 3 as i8]; + let v2: Vector = vector![1, 2, 3]; + let v3: Vector = vector![1; 3]; + + assert_eq!(v1.len(), 3); + assert_eq!(v1[0], 1 as i8); + assert_eq!(v1[1], 2 as i8); + assert_eq!(v1[2], 3 as i8); + + assert_eq!(v2.len(), 3); + assert_eq!(v2[0], 1 as i8); + assert_eq!(v2[1], 2 as i8); + assert_eq!(v2[2], 3 as i8); + + assert_eq!(v3.len(), 3); + assert_eq!(v3[0], 1 as i8); + assert_eq!(v3[1], 1 as i8); + assert_eq!(v3[2], 1 as i8); + } + + #[test] + fn macro_vector_i16() { + let v1 = vector![1 as i16, 2 as i16, 3 as i16]; + let v2: Vector = vector![1, 2, 3]; + let v3: Vector = vector![1; 3]; + + assert_eq!(v1.len(), 3); + assert_eq!(v1[0], 1 as i16); + assert_eq!(v1[1], 2 as i16); + assert_eq!(v1[2], 3 as i16); + + assert_eq!(v2.len(), 3); + assert_eq!(v2[0], 1 as i16); + assert_eq!(v2[1], 2 as i16); + assert_eq!(v2[2], 3 as i16); + + assert_eq!(v3.len(), 3); + assert_eq!(v3[0], 1 as i16); + assert_eq!(v3[1], 1 as i16); + assert_eq!(v3[2], 1 as i16); + } + + #[test] + fn macro_vector_i32() { + let v1 = vector![1 as i32, 2 as i32, 3 as i32]; + let v2: Vector = vector![1, 2, 3]; + let v3: Vector = vector![1; 3]; + + assert_eq!(v1.len(), 3); + assert_eq!(v1[0], 1 as i32); + assert_eq!(v1[1], 2 as i32); + assert_eq!(v1[2], 3 as i32); + + assert_eq!(v2.len(), 3); + assert_eq!(v2[0], 1 as i32); + assert_eq!(v2[1], 2 as i32); + assert_eq!(v2[2], 3 as i32); + + assert_eq!(v3.len(), 3); + assert_eq!(v3[0], 1 as i32); + assert_eq!(v3[1], 1 as i32); + assert_eq!(v3[2], 1 as i32); + } + + #[test] + fn macro_vector_i64() { + let v1 = vector![1 as i64, 2 as i64, 3 as i64]; + let v2: Vector = vector![1, 2, 3]; + let v3: Vector = vector![1; 3]; + + assert_eq!(v1.len(), 3); + assert_eq!(v1[0], 1 as i64); + assert_eq!(v1[1], 2 as i64); + assert_eq!(v1[2], 3 as i64); + + assert_eq!(v2.len(), 3); + assert_eq!(v2[0], 1 as i64); + assert_eq!(v2[1], 2 as i64); + assert_eq!(v2[2], 3 as i64); + + assert_eq!(v3.len(), 3); + assert_eq!(v3[0], 1 as i64); + assert_eq!(v3[1], 1 as i64); + assert_eq!(v3[2], 1 as i64); + } + + #[test] + fn macro_vector_i128() { + let v1 = vector![1 as i128, 2 as i128, 3 as i128]; + let v2: Vector = vector![1, 2, 3]; + let v3: Vector = vector![1; 3]; + + assert_eq!(v1.len(), 3); + assert_eq!(v1[0], 1 as i128); + assert_eq!(v1[1], 2 as i128); + assert_eq!(v1[2], 3 as i128); + + assert_eq!(v2.len(), 3); + assert_eq!(v2[0], 1 as i128); + assert_eq!(v2[1], 2 as i128); + assert_eq!(v2[2], 3 as i128); + + assert_eq!(v3.len(), 3); + assert_eq!(v3[0], 1 as i128); + assert_eq!(v3[1], 1 as i128); + assert_eq!(v3[2], 1 as i128); + } + + #[test] + fn macro_vector_f32() { + let v1 = vector![1. as f32, 2. as f32, 3. as f32]; + let v2: Vector = vector![1., 2., 3.]; + let v3: Vector = vector![1.; 3]; + + assert_eq!(v1.len(), 3); + assert_relative_eq!(v1[0], 1.0 as f32); + assert_relative_eq!(v1[1], 2.0 as f32); + assert_relative_eq!(v1[2], 3.0 as f32); + + assert_eq!(v2.len(), 3); + assert_relative_eq!(v2[0], 1.0 as f32); + assert_relative_eq!(v2[1], 2.0 as f32); + assert_relative_eq!(v2[2], 3.0 as f32); + + assert_eq!(v3.len(), 3); + assert_relative_eq!(v3[0], 1.0 as f32); + assert_relative_eq!(v3[1], 1.0 as f32); + assert_relative_eq!(v3[2], 1.0 as f32); + } + + #[test] + fn macro_vector_f64() { + let v1 = vector![1. as f64, 2. as f64, 3. as f64]; + let v2: Vector = vector![1., 2., 3.]; + let v3: Vector = vector![1.; 3]; + + assert_eq!(v1.len(), 3); + assert_relative_eq!(v1[0], 1.0 as f64); + assert_relative_eq!(v1[1], 2.0 as f64); + assert_relative_eq!(v1[2], 3.0 as f64); + + assert_eq!(v2.len(), 3); + assert_relative_eq!(v2[0], 1.0 as f64); + assert_relative_eq!(v2[1], 2.0 as f64); + assert_relative_eq!(v2[2], 3.0 as f64); + + assert_eq!(v3.len(), 3); + assert_relative_eq!(v3[0], 1.0 as f64); + assert_relative_eq!(v3[1], 1.0 as f64); + assert_relative_eq!(v3[2], 1.0 as f64); + } + + #[test] + fn macro_vector_complex_i32() { + let v1 = vector![Complex::new(1 as i32, -2 as i32), Complex::new(-1 as i32, 2 as i32)]; + let v2: Vector, 2> = vector![Complex::new(1, -2), Complex::new(-1, 2)]; + let v3: Vector, 2> = vector![Complex::new(1, -2); 2]; + + assert_eq!(v1.len(), 2); + assert_eq!(v1[0].re, 1 as i32); + assert_eq!(v1[0].im, -2 as i32); + assert_eq!(v1[1].re, -1 as i32); + assert_eq!(v1[1].im, 2 as i32); + + assert_eq!(v2.len(), 2); + assert_eq!(v2[0].re, 1 as i32); + assert_eq!(v2[0].im, -2 as i32); + assert_eq!(v2[1].re, -1 as i32); + assert_eq!(v2[1].im, 2 as i32); + + assert_eq!(v3.len(), 2); + assert_eq!(v3[0].re, 1 as i32); + assert_eq!(v3[0].im, -2 as i32); + assert_eq!(v3[1].re, 1 as i32); + assert_eq!(v3[1].im, -2 as i32); + } + + #[test] + fn macro_vector_complex_f64() { + let v1 = vector![Complex::new(1. as f64, -2. as f64), Complex::new(-1. as f64, 2. as f64)]; + let v2: Vector, 2> = vector![Complex::new(1., -2.), Complex::new(-1., 2.)]; + let v3: Vector, 2> = vector![Complex::new(1., -2.); 2]; + + assert_eq!(v1.len(), 2); + assert_relative_eq!(v1[0].re, 1. as f64); + assert_relative_eq!(v1[0].im, -2. as f64); + assert_relative_eq!(v1[1].re, -1. as f64); + assert_relative_eq!(v1[1].im, 2. as f64); + + assert_eq!(v2.len(), 2); + assert_relative_eq!(v2[0].re, 1. as f64); + assert_relative_eq!(v2[0].im, -2. as f64); + assert_relative_eq!(v2[1].re, -1. as f64); + assert_relative_eq!(v2[1].im, 2. as f64); + + assert_eq!(v3.len(), 2); + assert_relative_eq!(v3[0].re, 1. as f64); + assert_relative_eq!(v3[0].im, -2. as f64); + assert_relative_eq!(v3[1].re, 1. as f64); + assert_relative_eq!(v3[1].im, -2. as f64); + } + + #[test] + fn macro_try_vector_i32() { + let slv = vec![1_i32, 2_i32, 3_i32]; + let v: Vector = try_vector!(slv).unwrap(); + + assert_eq!(v[0], 1_i32); + assert_eq!(v[1], 2_i32); + assert_eq!(v[2], 3_i32); + + let slv = vec![1_i32, 2_i32, 3_i32]; + let v: Vector = try_vector!(&slv).unwrap(); + + assert_eq!(v[0], 1_i32); + assert_eq!(v[1], 2_i32); + assert_eq!(v[2], 3_i32); + + let slv = vec![1_i32, 2_i32, 3_i32]; + let v: Vector = try_vector!(&slv[..]).unwrap(); + + assert_eq!(v[0], 1_i32); + assert_eq!(v[1], 2_i32); + assert_eq!(v[2], 3_i32); + } + + #[test] + fn macro_try_vector_f64() { + let slv = vec![1.0_f64, 2.0_f64, 3.0_f64]; + let v: Vector = try_vector!(slv).unwrap(); + + assert_relative_eq!(v[0], 1.0_f64); + assert_relative_eq!(v[1], 2.0_f64); + assert_relative_eq!(v[2], 3.0_f64); + + let slv = vec![1.0_f64, 2.0_f64, 3.0_f64]; + let v: Vector = try_vector!(&slv).unwrap(); + + assert_relative_eq!(v[0], 1.0_f64); + assert_relative_eq!(v[1], 2.0_f64); + assert_relative_eq!(v[2], 3.0_f64); + + let slv = vec![1.0_f64, 2.0_f64, 3.0_f64]; + let v: Vector = try_vector!(&slv[..]).unwrap(); + + assert_relative_eq!(v[0], 1.0_f64); + assert_relative_eq!(v[1], 2.0_f64); + assert_relative_eq!(v[2], 3.0_f64); + } + + #[test] + fn macro_try_vector_complex_i32() { + let slv = vec![Complex::new(1_i32, 2_i32), Complex::new(3_i32, 4_i32)]; + let v: Vector, 2> = try_vector!(slv).unwrap(); + + assert_eq!(v[0].re, 1_i32); + assert_eq!(v[0].im, 2_i32); + assert_eq!(v[1].re, 3_i32); + assert_eq!(v[1].im, 4_i32); + + let slv = vec![Complex::new(1_i32, 2_i32), Complex::new(3_i32, 4_i32)]; + let v: Vector, 2> = try_vector!(&slv).unwrap(); + + assert_eq!(v[0].re, 1_i32); + assert_eq!(v[0].im, 2_i32); + assert_eq!(v[1].re, 3_i32); + assert_eq!(v[1].im, 4_i32); + + let slv = vec![Complex::new(1_i32, 2_i32), Complex::new(3_i32, 4_i32)]; + let v: Vector, 2> = try_vector!(&slv[..]).unwrap(); + + assert_eq!(v[0].re, 1_i32); + assert_eq!(v[0].im, 2_i32); + assert_eq!(v[1].re, 3_i32); + assert_eq!(v[1].im, 4_i32); + } + + #[test] + fn macro_try_vector_complex_f64() { + let slv = vec![Complex::new(1.0_f64, 2.0_f64), Complex::new(3.0_f64, 4.0_f64)]; + let v: Vector, 2> = try_vector!(slv).unwrap(); + + assert_relative_eq!(v[0].re, 1.0_f64); + assert_relative_eq!(v[0].im, 2.0_f64); + assert_relative_eq!(v[1].re, 3.0_f64); + assert_relative_eq!(v[1].im, 4.0_f64); + + let slv = vec![Complex::new(1.0_f64, 2.0_f64), Complex::new(3.0_f64, 4.0_f64)]; + let v: Vector, 2> = try_vector!(&slv).unwrap(); + + assert_relative_eq!(v[0].re, 1.0_f64); + assert_relative_eq!(v[0].im, 2.0_f64); + assert_relative_eq!(v[1].re, 3.0_f64); + assert_relative_eq!(v[1].im, 4.0_f64); + + let slv = vec![Complex::new(1.0_f64, 2.0_f64), Complex::new(3.0_f64, 4.0_f64)]; + let v: Vector, 2> = try_vector!(&slv[..]).unwrap(); + + assert_relative_eq!(v[0].re, 1.0_f64); + assert_relative_eq!(v[0].im, 2.0_f64); + assert_relative_eq!(v[1].re, 3.0_f64); + assert_relative_eq!(v[1].im, 4.0_f64); + } + + // -- fv! macro -- + + #[test] + 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])); + } + + #[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])); + } + + #[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])); + } + + #[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 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])); + } + + #[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])); + } + + #[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) + ]) + ); + } + + #[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 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) + ]) + ); + } + + // -- fv_iter! macro -- + + #[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 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_fv_iter!(data).unwrap(); + assert_eq!(fv, FlexVector::::from_vec(vec![1, 2, 3])); + } + + #[test] + 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 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 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 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 new file mode 100644 index 0000000..8454153 --- /dev/null +++ b/src/types/flexvector.rs @@ -0,0 +1,9530 @@ +//! FlexVector type. + +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, +}; + +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::orientation::Column, types::orientation::Row, types::orientation::VectorOrientation, + 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::vectorslice::VectorSlice, + types::vectorslice::VectorSliceMut, +}; + +use crate::types::utils::{ + 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_into_impl, + normalize_to_impl, normalize_to_into_impl, project_onto_impl, project_onto_into_impl, + translate_impl, +}; + +use crate::errors::VectorError; + +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)] +pub struct FlexVector { + /// ... + pub elements: Vec, + _orientation: PhantomData, +} + +/// ... +pub type ColFVec = FlexVector; +/// ... +pub type RowFVec = FlexVector; + +// ================================ +// +// Constructors +// +// ================================ +impl FlexVector { + /// Creates a new, empty FlexVector. + #[inline] + pub fn new() -> Self { + Self { elements: Vec::new(), _orientation: PhantomData } + } + + /// Creates a new FlexVector with a pre-allocated capacity. + #[inline] + pub fn with_capacity(capacity: usize) -> Self { + Self { elements: Vec::with_capacity(capacity), _orientation: PhantomData } + } + + /// Returns a new FlexVector of the given length, filled with zeros. + #[inline] + pub fn zero(len: usize) -> Self + where + T: num::Zero + Clone, + { + Self { elements: vec![T::zero(); len], _orientation: PhantomData } + } + + /// Returns a new FlexVector of the given length, filled with ones. + #[inline] + pub fn one(len: usize) -> Self + where + T: num::One + Clone, + { + Self { elements: vec![T::one(); len], _orientation: PhantomData } + } + + /// Returns a new FlexVector of the given length, filled with the given value. + #[inline] + pub fn filled(len: usize, value: T) -> Self + where + T: Clone, + { + Self { elements: vec![value; len], _orientation: PhantomData } + } + + /// Creates a new FlexVector from a slice. + #[inline] + pub fn from_slice(slice: &[T]) -> Self + where + T: Clone, + { + Self { elements: slice.to_vec(), _orientation: PhantomData } + } + + /// Creates a FlexVector from a Vec. + #[inline] + pub fn from_vec(vec: Vec) -> Self { + Self { elements: 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. + #[inline] + pub fn try_from_iter(iter: I) -> Result + where + I: IntoIterator>, + { + 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. + /// + /// # Example + /// ``` + /// use vectora::prelude::*; + /// + /// 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, + { + 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. + /// + /// 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()); + /// ``` + #[inline] + pub fn try_from_fn(len: usize, mut f: F) -> Result + where + F: FnMut(usize) -> Result, + { + let mut elements = Vec::with_capacity(len); + for i in 0..len { + elements.push(f(i)?); + } + Ok(FlexVector { elements, _orientation: PhantomData }) + } + + /// 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 elements = pattern.iter().cycle().take(len).cloned().collect(); + Ok(FlexVector { elements, _orientation: PhantomData }) + } +} + +// ================================ +// +// Default trait impl +// +// ================================ +impl Default for FlexVector { + #[inline] + fn default() -> Self { + Self::new() + } +} + +// ================================ +// +// Display trait impl +// +// ================================ +impl std::fmt::Display for FlexVector +where + T: std::fmt::Debug, + O: 'static, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} FlexVector {:?}", &self.orientation_name(), self.elements) + } +} + +// ================================ +// +// Debug trait impl +// +// ================================ +impl std::fmt::Debug for FlexVector +where + T: std::fmt::Debug, + O: 'static, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("FlexVector") + .field("orientation", &self.orientation_name()) + .field("elements", &self.elements) + .finish() + } +} + +// ================================ +// +// FromIterator trait impl +// +// ================================ +impl FromIterator for FlexVector { + #[inline] + fn from_iter>(iter: I) -> Self { + FlexVector { elements: iter.into_iter().collect(), _orientation: PhantomData } + } +} + +// ================================ +// +// Deref/DerefMut trait impl +// +// ================================ +impl Deref for FlexVector { + type Target = [T]; + #[inline] + fn deref(&self) -> &Self::Target { + &self.elements + } +} + +impl DerefMut for FlexVector { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.elements + } +} + +// ================================ +// +// AsRef/AsMut trait impl +// +// ================================ +impl AsRef<[T]> for FlexVector { + #[inline] + fn as_ref(&self) -> &[T] { + &self.elements + } +} +impl AsMut<[T]> for FlexVector { + #[inline] + fn as_mut(&mut self) -> &mut [T] { + &mut self.elements + } +} + +// ================================ +// +// Borrow/BorrowMut trait impl +// +// ================================ +impl Borrow<[T]> for FlexVector { + #[inline] + fn borrow(&self) -> &[T] { + &self.elements + } +} +impl BorrowMut<[T]> for FlexVector { + #[inline] + fn borrow_mut(&mut self) -> &mut [T] { + &mut self.elements + } +} + +// ================================ +// +// IntoIterator trait impl +// +// ================================ +impl IntoIterator for FlexVector { + type Item = T; + type IntoIter = std::vec::IntoIter; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + self.elements.into_iter() + } +} +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.elements.iter() + } +} +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.elements.iter_mut() + } +} + +// ================================ +// +// PartialEq/Eq trait impl +// +// ================================ +impl PartialEq for FlexVector +where + T: PartialEq, +{ + #[inline] + fn eq(&self, other: &Self) -> bool { + self.elements == other.elements + } +} +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.elements.partial_cmp(&other.elements) + } +} +impl Ord for FlexVector +where + T: Ord, +{ + #[inline] + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.elements.cmp(&other.elements) + } +} + +// ================================ +// +// Hash trait impl +// +// ================================ +impl std::hash::Hash for FlexVector +where + T: std::hash::Hash, +{ + #[inline] + fn hash(&self, state: &mut H) { + self.elements.hash(state) + } +} + +// ================================ +// +// From trait impl +// +// ================================ +impl From> for FlexVector +where + T: Clone, +{ + #[inline] + fn from(vec: Vec) -> Self { + FlexVector { elements: vec, _orientation: PhantomData } + } +} + +impl From<&[T]> for FlexVector +where + T: Clone, +{ + #[inline] + fn from(slice: &[T]) -> Self { + FlexVector { elements: slice.to_vec(), _orientation: PhantomData } + } +} + +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, + [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 +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()) + } +} + +#[cfg(target_has_atomic = "ptr")] +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) + } +} + +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(fv: FlexVector) -> Self { + FlexVector { elements: fv.elements, _orientation: PhantomData } + } +} +impl From> for FlexVector { + #[inline] + fn from(fv: FlexVector) -> Self { + FlexVector { elements: fv.elements, _orientation: PhantomData } + } +} + +// ================================ +// +// Index trait impl +// +// ================================ +impl Index for FlexVector { + type Output = T; + + #[inline] + fn index(&self, idx: usize) -> &Self::Output { + &self.elements[idx] + } +} + +impl Index> for FlexVector { + type Output = [T]; + + #[inline] + fn index(&self, range: Range) -> &Self::Output { + &self.elements[range] + } +} + +impl Index> for FlexVector { + type Output = [T]; + + #[inline] + fn index(&self, range: RangeFrom) -> &Self::Output { + &self.elements[range] + } +} + +impl Index> for FlexVector { + type Output = [T]; + + #[inline] + fn index(&self, range: RangeTo) -> &Self::Output { + &self.elements[range] + } +} + +impl Index for FlexVector { + type Output = [T]; + + #[inline] + fn index(&self, range: RangeFull) -> &Self::Output { + &self.elements[range] + } +} + +impl Index> for FlexVector { + type Output = [T]; + + #[inline] + fn index(&self, range: RangeInclusive) -> &Self::Output { + &self.elements[range] + } +} + +impl Index> for FlexVector { + type Output = [T]; + + #[inline] + fn index(&self, range: RangeToInclusive) -> &Self::Output { + &self.elements[range] + } +} + +// ================================ +// +// IndexMut trait impl +// +// ================================ +impl IndexMut for FlexVector { + #[inline] + fn index_mut(&mut self, idx: usize) -> &mut Self::Output { + &mut self.elements[idx] + } +} + +impl IndexMut> for FlexVector { + #[inline] + fn index_mut(&mut self, range: Range) -> &mut Self::Output { + &mut self.elements[range] + } +} + +impl IndexMut> for FlexVector { + #[inline] + fn index_mut(&mut self, range: RangeFrom) -> &mut Self::Output { + &mut self.elements[range] + } +} + +impl IndexMut> for FlexVector { + #[inline] + fn index_mut(&mut self, range: RangeTo) -> &mut Self::Output { + &mut self.elements[range] + } +} + +impl IndexMut for FlexVector { + #[inline] + fn index_mut(&mut self, range: RangeFull) -> &mut Self::Output { + &mut self.elements[range] + } +} + +impl IndexMut> for FlexVector { + #[inline] + fn index_mut(&mut self, range: RangeInclusive) -> &mut Self::Output { + &mut self.elements[range] + } +} + +impl IndexMut> for FlexVector { + #[inline] + fn index_mut(&mut self, range: RangeToInclusive) -> &mut Self::Output { + &mut self.elements[range] + } +} + +// ================================ +// +// Extend trait impl +// +// ================================ +impl Extend for FlexVector { + #[inline] + fn extend>(&mut self, iter: I) { + self.elements.extend(iter) + } +} + +// ================================ +// +// VectorBase/VectorBaseMut trait impl +// +// ================================ +impl VectorBase for FlexVector { + /// Returns an immutable slice of the FlexVector's elements. + #[inline] + 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] { + &mut self.elements[..] + } +} + +// ================================ +// +// Transposable trait impl +// +// ================================ + +impl Transposable for FlexVector { + type Transposed = FlexVector; + + #[inline] + fn transpose(self) -> Self::Transposed { + FlexVector { elements: self.elements, _orientation: PhantomData } + } +} + +impl Transposable for FlexVector { + type Transposed = FlexVector; + + #[inline] + fn transpose(self) -> Self::Transposed { + FlexVector { elements: self.elements, _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 +where + T: Copy, +{ + type Output = Self; + + #[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 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 + 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())) + } + + /// 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 = cross_impl(a, b); + 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 + where + T: PartialOrd + Copy, + { + self.check_same_length_and_raise(other)?; + 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 + Copy, + { + 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 + #[inline] + fn elementwise_max(&self, other: &Self) -> Result + where + T: PartialOrd + Copy, + { + self.check_same_length_and_raise(other)?; + 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 + Copy, + { + 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(()) + } +} + +// ================================ +// +// VectorOpsMut trait impl +// +// ================================ +impl VectorOpsMut for FlexVector +where + T: Copy, +{ + 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(()) + } +} + +// ================================ +// +// VectorOpsFloat trait impl +// +// ================================ +impl VectorOpsFloat for FlexVector +where + T: num::Float + std::iter::Sum, +{ + type Output = Self; + + #[inline] + fn normalize(&self) -> Result + where + T: num::Float + std::ops::Div + num::Zero, + Self::Output: std::iter::FromIterator, + { + normalize_impl(self.as_slice(), self.norm()) + } + + #[inline] + fn normalize_into(&self, out: &mut [T]) -> Result<(), VectorError> + where + T: num::Float + 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 + where + 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) + } + + #[inline] + fn normalize_to_into(&self, magnitude: T, out: &mut [T]) -> Result<(), VectorError> + where + 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) + } + + #[inline] + fn lerp(&self, end: &Self, weight: T) -> Result + where + T: num::Float, + { + 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 lerp_into(&self, end: &Self, weight: T, 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(), + )); + } + 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, end: &Self) -> Result + where + T: num::Float, + { + self.lerp(end, num::cast(0.5).unwrap()) + } + + #[inline] + fn midpoint_into(&self, end: &Self, out: &mut [T]) -> Result<(), VectorError> + where + T: num::Float, + { + self.lerp_into(end, num::cast(0.5).unwrap(), out) + } + + #[inline] + fn distance(&self, other: &Self) -> Result + where + T: num::Float + 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 + 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 + 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 + 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 + 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 + 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 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 + T: num::Float + 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)) + } +} + +// ================================ +// +// VectorOpsFloatMut trait impl +// +// ================================ +impl VectorOpsFloatMut for FlexVector +where + T: num::Float + std::iter::Sum, +{ + type Output = Self; + + #[inline] + fn mut_normalize(&mut self) -> Result<(), VectorError> + where + T: num::Float + 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: num::Float + 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, + { + 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 +// +// ================================ + +impl VectorOpsComplex for FlexVector, O> +where + 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>, + { + 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 + N: num::Float, + Complex: Copy + + PartialEq + + std::ops::Div, Output = Complex> + + std::ops::Mul, Output = Complex>, + Self::Output: std::iter::FromIterator>, + { + normalize_to_impl( + self.as_slice(), + Complex::new(self.norm(), N::zero()), + Complex::new(magnitude, N::zero()), + ) + } + + #[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 + 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())) + } + + #[inline] + fn lerp(&self, end: &Self, weight: N) -> Result + 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 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 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 + N: num::Float + 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 + 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, + { + 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 + 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 + 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 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 + N: num::Float + 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)) + } +} + +impl VectorOpsComplexMut for FlexVector, O> +where + 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(); + mut_normalize_impl(self.as_mut_slice(), Complex::new(norm, N::zero())) + } + + #[inline] + 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, + { + 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, + Complex: Copy + + std::ops::Add> + + std::ops::Mul> + + std::ops::Sub>, + { + 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] + 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 { elements: self.elements.clone(), _orientation: PhantomData } + } + + /// ... + #[inline] + pub fn as_column(&self) -> FlexVector + where + T: Clone, + { + FlexVector { elements: self.elements.clone(), _orientation: PhantomData } + } + + /// Consumes self and returns a Row-oriented FlexVector. + #[inline] + pub fn into_row(self) -> FlexVector { + FlexVector { elements: self.elements, _orientation: std::marker::PhantomData } + } + + /// Consumes self and returns a Column-oriented FlexVector. + #[inline] + pub fn into_column(self) -> FlexVector { + 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.elements.push(value); + } + + /// Removes the last element and returns it, or None if empty. + #[inline] + pub fn pop(&mut self) -> Option { + 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.elements.insert(index, value); + } + + /// Removes and returns the element at position index. + #[inline] + pub fn remove(&mut self, index: usize) -> T { + self.elements.remove(index) + } + + /// Resizes the vector in-place. + #[inline] + pub fn resize(&mut self, new_len: usize, value: T) + where + T: Clone, + { + self.elements.resize(new_len, value); + } + + /// Clears the vector, removing all values. + #[inline] + pub fn clear(&mut self) { + self.elements.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]>, + { + self.elements.get_mut(index) + } + + /// Returns an iterator over mutable references to the elements. + #[inline] + pub fn iter_mut(&mut self) -> impl Iterator { + self.elements.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, + T: Clone, + { + 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. + #[inline] + pub fn mut_map(&mut self, mut f: F) + where + F: FnMut(T) -> T, + T: Clone, + { + for x in self.elements.iter_mut() { + *x = f(x.clone()); + } + } + + /// 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 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. + #[inline] + pub fn filter(&self, mut predicate: F) -> Self + where + F: FnMut(&T) -> bool, + T: Clone, + { + 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. + #[inline] + pub fn reduce(&self, mut f: F) -> Option + where + F: FnMut(T, T) -> T, + T: Clone, + { + let mut iter = self.elements.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.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 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. + #[inline] + 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 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. + /// Returns an error if step == 0. + #[inline] + pub fn step_by(&self, step: usize) -> Result + where + T: Clone, + { + if step == 0 { + return Err(VectorError::ValueError("step must be non-zero".to_string())); + } + 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. + #[inline] + pub fn create_mask(&self, predicate: F) -> FlexVector + where + F: FnMut(&T) -> bool, + { + self.elements.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 + where + T: Clone, + { + if self.len() != mask.len() { + return Err(VectorError::MismatchedLengthError( + "Mask length must match vector length".to_string(), + )); + } + let elements = self + .elements + .iter() + .zip(mask) + .filter_map(|(x, &m)| if m { Some(x.clone()) } else { None }) + .collect(); + 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 { + self.elements + } + + /// Consumes the FlexVector and returns a Box. + #[inline] + pub fn into_boxed_slice(self) -> Box<[T]> { + 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.elements) + } + + /// Consumes the FlexVector and returns a Rc. + #[inline] + pub fn into_rc_slice(self) -> std::rc::Rc<[T]> { + 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.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.elements[range]) + } + + // ================================ + // + // 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(()) + } + } +} + +impl FlexVector<&T, O> +where + T: Clone, +{ + /// Returns a FlexVector of owned values by cloning each referenced element. + #[inline] + pub fn cloned(&self) -> FlexVector { + FlexVector::from_vec(self.elements.iter().map(|&x| x.clone()).collect()) + } +} + +impl FlexVector +where + V: IntoIterator, +{ + /// Flattens a FlexVector of iterables into a single FlexVector by concatenating all elements. + #[inline] + pub fn flatten(self) -> FlexVector { + let elements = self.elements.into_iter().flatten().collect(); + FlexVector { elements, _orientation: PhantomData } + } +} + +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 { + let elements = self.elements.into_iter().flat_map(|v| v.into_iter().cloned()).collect(); + FlexVector { elements, _orientation: PhantomData } + } +} + +// ================================ +// +// Operator overload trait impl +// +// ================================ +impl_vector_unary_op!(FlexVector, Neg, neg, -); + +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!(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, 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); + +#[cfg(test)] +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}; + + // ================================ + // + // Test utility functions + // + // ================================ + fn double(x: i32) -> i32 { + x * 2 + } + + fn square(x: i32) -> i32 { + x * x + } + + fn square_usize(x: usize) -> usize { + x * x + } + + // ================================ + // + // Constructor tests + // + // ================================ + #[test] + 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); + assert_eq!(v.len(), 0); + assert!(v.elements.capacity() >= 10); + } + + #[test] + fn test_with_capacity_large() { + let v = FlexVector::::with_capacity(1000); + assert_eq!(v.len(), 0); + assert!(v.elements.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_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 = 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 = 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 = 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)] + ); + } + + #[test] + fn test_from_fn_zero_length() { + let v: FlexVector = ColFVec::from_fn(0, |_| 42); + assert!(v.is_empty()); + } + + #[test] + fn test_from_fn_with_fn_pointer() { + let v = ColFVec::from_fn(4, square_usize); + 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_repeat_pattern_i32_basic() { + let pattern = [1, 2, 3]; + 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 = ColFVec::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 = 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 = ColFVec::repeat_pattern(&pattern, 3); + assert!(result.is_err()); + } + + #[test] + fn test_repeat_pattern_f64_basic() { + let pattern = [1.5, 2.5]; + 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 = 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 = ColFVec::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 = ColFVec::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 = ColFVec::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 = ColFVec::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 = ColFVec::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 = 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 = 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 = 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 = ColFVec::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 = ColFVec::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 = ColFVec::from_vec(vec![vec![], vec![1, 2], vec![]]); + let flat = nested.flatten(); + assert_eq!(flat.as_slice(), &[1, 2]); + } + + #[test] + fn test_flatten_cloned_flexvector_of_slice_refs() { + let a = [8, 9]; + let b = [10]; + 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()); + let _: &[i32] = flat.as_slice(); // type check: &[i32] + } + + #[test] + fn test_flatten_cloned_flexvector_of_refs() { + let x = 42; + let y = 43; + 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] + } + + #[test] + fn test_flatten_cloned_empty_outer() { + let nested: FlexVector> = FlexVector::new(); + let flat = nested.flatten_cloned(); + assert!(flat.is_empty()); + } + + #[test] + fn test_flatten_cloned_with_empty_inner() { + let nested = ColFVec::from_vec(vec![vec![], vec![&1, &2], vec![]]); + let flat = nested.flatten_cloned(); + assert_eq!(flat.as_slice(), &[1, 2]); + } + + #[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_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_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_row_and_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 = 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), "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("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("elements")); + 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("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("elements")); + 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("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("elements")); + 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("elements")); + assert!(debug.contains("[]")); + } + + // ================================ + // + // 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()); + } + + // ================================ + // + // 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 + // + // ================================ + #[test] + fn test_deref_access_slice_methods_i32() { + let v = ColFVec::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 = 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()); + 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 = ColFVec::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 = ColFVec::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 = 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]); + // 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 = ColFVec::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 = 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 = 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]); + } + + #[test] + fn test_asref_complex_f64() { + use num::Complex; + 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 = ColFVec::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 = 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]); + } + + #[test] + fn test_asmut_complex_f64() { + use num::Complex; + 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; + 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 = ColFVec::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 = ColFVec::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 = 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)]); + } + + #[test] + fn test_borrow_mut_i32() { + use std::borrow::BorrowMut; + let mut v = ColFVec::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 = ColFVec::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 = 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; + 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 = 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 = 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]); + } + + #[test] + fn test_into_iter_complex_f64() { + use num::Complex; + 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 = 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 = 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]); + } + + #[test] + fn test_iter_ref_complex_f64() { + use num::Complex; + 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 = ColFVec::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 = ColFVec::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 = 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; + } + 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 = 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 = 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); + } + + #[test] + fn test_partial_eq_complex_f64() { + use num::Complex; + 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); + } + + #[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 = 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 = 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 = 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 = 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); + } + + #[test] + fn test_partial_eq_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); + } + + #[test] + fn test_partial_eq_complex_nan() { + use num::Complex; + let nan = f64::NAN; + 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 = 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 = 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); + } + + #[test] + fn test_partial_eq_complex_infinity() { + use num::Complex; + 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); + } + + #[test] + fn test_eq_trait_i32() { + 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 = 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 = 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)); + } + + // ================================ + // + // PartialOrd/Ord trait tests + // + // ================================ + #[test] + fn test_partial_ord_i32() { + 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); + 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 = 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); + } + + #[test] + fn test_partial_ord_f64() { + 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)); + } + + #[test] + fn test_partial_ord_f64_nan() { + 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); + } + + #[test] + fn test_partial_ord_f64_infinity() { + 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)); + // 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 = 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); + 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 = 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); + 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()); + 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] + 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()); + 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 = ColFVec::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 = ColFVec::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 = ColFVec::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_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 + 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_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(); + 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_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_vectorslice_i32() { + 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]); + } + + #[test] + fn test_from_vectorslice_f64() { + 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]); + } + + #[test] + fn test_from_vectorslice_complex_f64() { + use num::Complex; + let v = ColFVec::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 = ColFVec::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 = 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); + assert_eq!(fv.as_slice(), &[1.5, 2.5]); + } + } + + #[test] + fn test_from_vectorslicemut_complex_f64() { + use num::Complex; + 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), + ]); + { + 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]); + 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()); + } + + // ================================ + // + // Index trait tests + // + // ================================ + + #[test] + fn test_index_usize() { + 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); + } + + #[test] + #[should_panic] + fn test_index_usize_out_of_bounds() { + let v = ColFVec::from_vec(vec![1, 2, 3]); + let _ = v[10]; + } + + #[test] + fn test_index_range() { + 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]); + } + + #[test] + fn test_index_range_from() { + 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 = 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 = ColFVec::from_vec(vec![1, 2, 3]); + assert_eq!(&v[..], &[1, 2, 3]); + } + + #[test] + fn test_index_range_inclusive() { + 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 = ColFVec::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 = ColFVec::from_vec(vec![1, 2, 3]); + let _ = &v[2..5]; + } + + #[test] + #[should_panic] + fn test_index_range_inclusive_out_of_bounds() { + 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 = ColFVec::from_vec(vec![1, 2, 3]); + let _ = &v[..=5]; + } + + // ================================ + // + // IndexMut trait tests + // + // ================================ + + #[test] + fn test_index_mut_usize() { + let mut v = ColFVec::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 = ColFVec::from_vec(vec![1, 2, 3]); + v[10] = 99; + } + + #[test] + fn test_index_mut_range() { + 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 = 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 = 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 = 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 = 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 = 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]); + } + + #[test] + #[should_panic] + fn test_index_mut_range_out_of_bounds() { + 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 = 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 = ColFVec::from_vec(vec![1, 2, 3]); + v[..=5].copy_from_slice(&[9, 9, 9, 9, 9, 9]); + } + + // ================================ + // + // Extend trait tests + // + // ================================ + #[test] + fn test_extend_i32() { + 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 = 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]); + } + + #[test] + fn test_extend_complex_f64() { + use num::Complex; + 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(), + &[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 = ColFVec::from_vec(vec![1, 2]); + v.extend(Vec::::new()); + assert_eq!(v.as_slice(), &[1, 2]); + } + + // ================================ + // + // VectorBase trait method tests + // + // ================================ + // --- as_slice --- + #[test] + fn test_as_slice() { + 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 = 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 = 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),]); + } + + // --- len --- + #[test] + fn test_len() { + let v = ColFVec::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 = ColFVec::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 = ColFVec::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 = ColFVec::from_vec(vec![1]); + assert!(!v.is_empty()); + let empty = FlexVector::::new(); + assert!(empty.is_empty()); + } + + #[test] + fn test_is_empty_f64() { + let v = ColFVec::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 = ColFVec::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 = 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); + } + + #[test] + fn test_get_f64() { + 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); + } + + #[test] + fn test_get_complex() { + 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); + } + + // --- first --- + #[test] + fn test_first() { + let v = ColFVec::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 = ColFVec::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 = 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); + } + + // --- last --- + #[test] + fn test_last() { + let v = ColFVec::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 = 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); + } + + #[test] + fn test_last_complex() { + 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); + } + + // --- iter --- + #[test] + fn test_iter() { + 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)); + assert_eq!(iter.next(), Some(&3)); + assert_eq!(iter.next(), None); + } + + #[test] + fn test_iter_f64() { + 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 = 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),]); + } + + // --- iter_rev --- + #[test] + fn test_iter_rev() { + 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 = 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 = 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),]); + } + + // --- enumerate --- + #[test] + fn test_enumerate() { + 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 = 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 = 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)),]); + } + + // --- to_vec --- + #[test] + fn test_to_vec() { + 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 = 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 = 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),]); + } + + // -- 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] + 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 = ColFVec::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 = ColFVec::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 = 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")); + assert!(pretty.contains("3.0")); + assert!(pretty.contains("4.0")); + assert!(pretty.starts_with("[")); + } + + // --- contains --- + #[test] + fn test_contains() { + let v = ColFVec::from_vec(vec![1, 2, 3]); + assert!(v.contains(&2)); + assert!(!v.contains(&4)); + } + + #[test] + fn test_contains_f64() { + 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 = 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))); + } + + // --- starts_with --- + #[test] + fn test_starts_with() { + 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 = 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 = ColFVec::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 = 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 = 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 = ColFVec::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 = 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 = 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 = ColFVec::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 = 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 = 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 = ColFVec::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 = 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 = 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 = ColFVec::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 = 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 = 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 = ColFVec::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 = 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]); + } + + #[test] + fn test_split_at_f64() { + 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]); + } + + #[test] + fn test_split_at_complex() { + 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)]); + } + + // --- split --- + #[test] + fn test_split() { + 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 = 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 = ColFVec::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 = 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 = 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 = ColFVec::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 = 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 = 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 = ColFVec::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 = 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 = 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 = ColFVec::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)][..] + ] + ); + } + + // -- 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 = ColFVec::new(); + v.push(1); + v.push(2); + assert_eq!(v.as_slice(), &[1, 2]); + } + + #[test] + fn test_push_f64() { + let mut v = ColFVec::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 = 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)]); + } + + // --- pop --- + #[test] + fn test_pop() { + 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); + } + + #[test] + fn test_pop_f64() { + 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); + } + + #[test] + fn test_pop_complex() { + 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); + } + + // --- insert --- + #[test] + fn test_insert() { + 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 = 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 = 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(), + &[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 = 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 = 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 = ColFVec::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 = ColFVec::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 = 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); + assert_eq!(v.as_slice(), &[1.1]); + } + + #[test] + fn test_resize_complex() { + 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(), + &[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 = ColFVec::from_vec(vec![1, 2, 3]); + assert!(!v.is_empty()); + v.clear(); + assert!(v.is_empty()); + } + + #[test] + fn test_clear_f64() { + let mut v = ColFVec::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 = 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()); + } + + // --- get_mut --- + #[test] + fn test_get_mut_single() { + let mut v = ColFVec::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 = ColFVec::from_vec(vec![1, 2, 3]); + assert!(v.get_mut(10).is_none()); + } + + #[test] + fn test_get_mut_range() { + 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; + } + assert_eq!(v.as_slice(), &[1, 20, 30, 4]); + } + + #[test] + fn test_get_mut_full_range() { + let mut v = ColFVec::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 = ColFVec::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 = ColFVec::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 = 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; + } + 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 = ColFVec::::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 = ColFVec::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 = 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]); + } + + #[test] + fn test_as_mut_slice_complex() { + use num::Complex; + 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; + 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 = ColFVec::::new(); + let slice = v.as_mut_slice(); + assert_eq!(slice.len(), 0); + } + + // --- map --- + #[test] + fn test_map_i32() { + 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 + assert_eq!(v.as_slice(), &[1, 2, 3]); + } + + #[test] + fn test_map_f64() { + 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]); + } + + #[test] + fn test_map_complex() { + use num::Complex; + 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)]); + } + + #[test] + fn test_map_empty() { + let v = FlexVector::::new(); + let mapped = v.map(|x| x + 1); + assert!(mapped.is_empty()); + } + + #[test] + fn test_map_with_fn_pointer() { + let v = ColFVec::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 = 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 = 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 = 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)]); + } + + #[test] + fn test_mut_map_empty() { + let mut v = FlexVector::::new(); + v.mut_map(|x| x + 1); + assert!(v.is_empty()); + } + + #[test] + fn test_mut_map_with_fn_pointer() { + let mut v = ColFVec::from_vec(vec![1, 2, 3]); + v.mut_map(double); + assert_eq!(v.as_slice(), &[2, 4, 6]); + } + + // --- flat_map --- + #[test] + fn test_flat_map_i32_basic() { + 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 = 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 = 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 = 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]); + } + + #[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 = 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); + assert_eq!(flat.as_slice()[2], 1.0); + } + + #[test] + fn test_flat_map_complex_f64_basic() { + use num::Complex; + 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(), + &[ + 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 = 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)]); + } + + #[test] + fn test_flat_map_complex_f64_option() { + use num::Complex; + 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)]); + } + + #[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 = ColFVec::from_vec(vec![1, 2, 3]); + let flat = v.flat_map(|_| Vec::::new()); + assert!(flat.is_empty()); + } + + // -- filter -- + + #[test] + fn test_filter_i32_basic() { + 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 = 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 = 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 = 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 = 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()); + } + + #[test] + fn test_filter_complex_f64_real_positive() { + use num::Complex; + let v = ColFVec::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 = ColFVec::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 = ColFVec::new(); + let filtered = v.filter(|&x| x > 0); + assert!(filtered.is_empty()); + } + + // -- reduce -- + + #[test] + fn test_reduce_i32_sum() { + 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 = 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 = ColFVec::new(); + let result = v.reduce(|a, b| a + b); + assert_eq!(result, None); + } + + #[test] + fn test_reduce_f64_sum() { + 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 = 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 = ColFVec::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 = ColFVec::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 = 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 + } + + #[test] + fn test_reduce_complex_f64_empty() { + use num::Complex; + let v: FlexVector> = ColFVec::new(); + let result = v.reduce(|a, b| a + b); + assert_eq!(result, None); + } + + // -- fold -- + + #[test] + fn test_fold_i32_sum() { + 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 = 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 = ColFVec::new(); + let sum = v.fold(0, |acc, x| acc + x); + assert_eq!(sum, 0); + } + + #[test] + fn test_fold_f64_sum() { + 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 = 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 = ColFVec::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 = ColFVec::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 = 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 + } + + #[test] + fn test_fold_complex_f64_empty() { + use num::Complex; + 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)); + } + + // --- zip --- + #[test] + fn test_zip_i32() { + 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)]); + } + + #[test] + fn test_zip_f64() { + 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)]); + } + + #[test] + fn test_zip_complex_f64() { + use num::Complex; + 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(), + &[ + (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 = 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 = ColFVec::new(); + let v2: FlexVector = ColFVec::new(); + let zipped = v1.zip(v2); + assert!(zipped.is_empty()); + } + + // --- zip_with --- + #[test] + fn test_zip_with_i32() { + 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 = 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]); + } + + #[test] + fn test_zip_with_complex_f64() { + use num::Complex; + 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 = 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]); + } + + #[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()); + } + + // --- step_by --- + + #[test] + fn test_step_by_i32_basic() { + 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 = 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 = 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 = ColFVec::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 = 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 = 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]); + } + + #[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 = ColFVec::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 = ColFVec::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 = ColFVec::from_vec(vec![Complex::new(1.0, 2.0)]); + let result = v.step_by(0); + assert!(result.is_err()); + } + + // -- create_mask -- + + #[test] + fn test_create_mask_i32_greater_than() { + 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 = 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 = 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 = 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]); + } + + #[test] + fn test_create_mask_complex_f64_real_positive() { + use num::Complex; + let v = ColFVec::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 = ColFVec::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 = ColFVec::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 = 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]); + } + + #[test] + fn test_filter_by_mask_i32_all_true() { + 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]); + } + + #[test] + fn test_filter_by_mask_i32_all_false() { + 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()); + } + + #[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 = ColFVec::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 = 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]); + } + + #[test] + fn test_filter_by_mask_f64_all_true() { + 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]); + } + + #[test] + fn test_filter_by_mask_f64_all_false() { + 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()); + } + + #[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 = ColFVec::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 = ColFVec::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 = 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)]); + } + + #[test] + fn test_filter_by_mask_complex_f64_all_false() { + use num::Complex; + 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()); + } + + #[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 = 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()); + } + + // -- broadcast_to -- + + #[test] + fn test_broadcast_to_i32() { + 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 = ColFVec::from_vec(vec![7]); + let b = v.broadcast_to(4).unwrap(); + assert_eq!(b.as_slice(), &[7, 7, 7, 7]); + + let v: FlexVector = ColFVec::from_vec(vec![]); + let b = v.broadcast_to(0).unwrap(); + assert!(b.is_empty()); + + 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 = 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 = 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 = ColFVec::from_vec(vec![]); + let b = v.broadcast_to(0).unwrap(); + assert!(b.is_empty()); + + let v: FlexVector = ColFVec::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 = 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(), + &[ + 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 = 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> = ColFVec::from_vec(vec![]); + let b = v.broadcast_to(0).unwrap(); + assert!(b.is_empty()); + + let v: FlexVector, Column> = ColFVec::from_vec(vec![]); + let result = v.broadcast_to(1); + 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]); + } + + // -- 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 + // + // ================================ + + // -- translate -- + #[test] + fn test_translate_i32() { + 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]); + } + + #[test] + fn test_translate_f64() { + 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]); + } + + #[test] + fn test_translate_complex_f64() { + 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)]); + } + + #[test] + fn test_translate_mismatched_lengths() { + 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()); + } + + // -- translate_into -- + + #[test] + fn test_translate_into_i32() { + 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(); + assert_eq!(out, [5, 7, 9]); + assert_eq!(v1.as_slice(), &[1, 2, 3]); + } + + #[test] + fn test_translate_into_f64() { + 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(); + 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 = 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(); + 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 = 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); + assert!(result.is_err()); + } + + // -- mut_translate -- + #[test] + fn test_mut_translate_i32() { + 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]); + } + + #[test] + fn test_mut_translate_f64() { + 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]); + } + + #[test] + fn test_mut_translate_complex_f64() { + 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)]); + } + + #[test] + fn test_mut_translate_mismatched_lengths() { + 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()); + } + + // -- scale -- + #[test] + fn test_scale_i32() { + 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 = 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]); + } + + #[test] + fn test_scale_complex_f64() { + use num::Complex; + 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)]); + } + + // -- mut_scale -- + #[test] + fn test_mut_scale_i32() { + 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 = 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 = 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)]); + } + + // -- negate -- + #[test] + fn test_negate_i32() { + let v = ColFVec::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 = 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]); + } + + #[test] + fn test_negate_complex_f64() { + use num::Complex; + 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)]); + } + + // -- mut_negate -- + #[test] + fn test_mut_negate_i32() { + 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 = ColFVec::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 = 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)]); + } + + // -- mut_zero -- + #[test] + fn test_mut_zero_i32() { + 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 = ColFVec::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 = 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)]); + } + + // -- dot -- + #[test] + fn test_dot_i32() { + 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 + } + + #[test] + fn test_dot_f64() { + 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); + } + + #[test] + fn test_dot_mismatched_lengths() { + 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()); + } + + #[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 -- + #[test] + fn test_dot_to_f64_i32() { + 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); + } + + #[test] + fn test_dot_to_f64_f64() { + 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); + } + + #[test] + fn test_dot_to_f64_mismatched_lengths() { + 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()); + } + + // complex number dot_to_f64 tested in VectorOpsComplex trait impl testing section below + + // -- cross -- + #[test] + fn test_cross_i32() { + 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] + assert_eq!(cross.as_slice(), &[-3, 6, -3]); + } + + #[test] + fn test_cross_f64() { + 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]); + } + + // 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_1() { + 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()); + } + + #[test] + fn test_cross_wrong_length_2() { + 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()); + } + + #[test] + fn test_cross_wrong_length_3() { + 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()); + } + + // -- cross_into -- + + #[test] + fn test_cross_into_i32() { + 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(); + assert_eq!(out, [-3, 6, -3]); + } + + #[test] + fn test_cross_into_f64() { + 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(); + assert_eq!(out, [-3.0, 6.0, -3.0]); + } + + #[test] + fn test_cross_into_wrong_length() { + 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 = 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 = 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); + assert!(result.is_err()); + } + + // -- sum -- + #[test] + fn test_sum_i32() { + 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 = ColFVec::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 = ColFVec::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 = ColFVec::from_vec(vec![2, 3, 4]); + let p = v.product(); + assert_eq!(p, 24); + } + + #[test] + fn test_product_f64() { + 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); + } + + #[test] + fn test_product_complex_f64() { + use num::Complex; + let v = ColFVec::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 = ColFVec::from_vec(vec![3, 1, 4, 2]); + assert_eq!(v.minimum(), Some(1)); + } + + #[test] + fn test_minimum_f64_basic() { + 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 = 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 = 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 = ColFVec::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 = 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()); + } + + #[test] + fn test_minimum_empty() { + let v: FlexVector = FlexVector::new(); + assert_eq!(v.minimum(), None); + } + + // -- elementwise_min -- + + #[test] + fn test_elementwise_min_i32() { + 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]); + } + + #[test] + fn test_elementwise_min_f64() { + 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]); + } + + #[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 = 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()); + } + + #[test] + fn test_elementwise_min_f64_with_nan() { + 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 + 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 = 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()); + } + + // -- elementwise_min_into -- + + #[test] + fn test_elementwise_min_into_i32() { + 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(); + assert_eq!(out, [1, 4, 3, 0]); + } + + #[test] + fn test_elementwise_min_into_f64() { + 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(); + assert_eq!(out, [1.5, -3.0, 2.0]); + } + + #[test] + fn test_elementwise_min_into_mismatched_length() { + 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 = 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); + assert!(result.is_err()); + } + + // -- maximum -- + #[test] + fn test_maximum_i32() { + let v = ColFVec::from_vec(vec![3, 1, 4, 2]); + assert_eq!(v.maximum(), Some(4)); + } + + #[test] + fn test_maximum_f64_basic() { + 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 = 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 = 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 = ColFVec::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 = 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()); + } + + // -- elementwise_max -- + + #[test] + fn test_elementwise_max_i32() { + 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]); + } + + #[test] + fn test_elementwise_max_f64() { + 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]); + } + + #[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 = 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()); + } + + #[test] + fn test_elementwise_max_f64_with_nan() { + 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 + 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 = 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()); + } + + // -- elementwise_max_into -- + + #[test] + fn test_elementwise_max_into_i32() { + 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(); + assert_eq!(out, [2, 5, 6, 7]); + } + + #[test] + fn test_elementwise_max_into_f64() { + 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(); + assert_eq!(out, [2.5, -2.0, 3.0]); + } + + #[test] + fn test_elementwise_max_into_mismatched_length() { + 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 = 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); + assert!(result.is_err()); + } + + // -- elementwise_clamp -- + + #[test] + fn test_elementwise_clamp_i32_basic() { + 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 = 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 = ColFVec::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 = 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 = 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 = 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 = 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()); + 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()); + } + + // -- elementwise_clamp_into -- + + #[test] + fn test_elementwise_clamp_into_i32_basic() { + 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]); + } + + #[test] + fn test_elementwise_clamp_into_i32_all_below() { + 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]); + } + + #[test] + fn test_elementwise_clamp_into_i32_all_above() { + 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]); + } + + #[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 = 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]); + } + + #[test] + fn test_elementwise_clamp_into_f64_all_below() { + 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]); + } + + #[test] + fn test_elementwise_clamp_into_f64_all_above() { + 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]); + } + + #[test] + fn test_elementwise_clamp_into_f64_with_nan() { + 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); + 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() { + 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 = ColFVec::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 = 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 = ColFVec::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 = 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); + assert!((normalized.as_slice()[1] - 0.8).abs() < 1e-12); + } + + #[test] + fn test_normalize_f64_zero_vector() { + 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 = 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); + 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 = 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()); + } + + #[test] + fn test_normalize_into_f64_negative_values() { + 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] + assert!((out[0] + 0.6).abs() < 1e-12); + assert!((out[1] + 0.8).abs() < 1e-12); + } + + // -- mut_normalize -- + #[test] + fn test_mut_normalize_f64() { + 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); + assert!((v.as_slice()[1] - 0.8).abs() < 1e-12); + } + + #[test] + fn test_mut_normalize_f64_zero_vector() { + 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 = 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); + assert!((v.as_slice()[1] + 0.8).abs() < 1e-12); + } + + // -- normalize_to -- + #[test] + fn test_normalize_to_f64() { + 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); + assert!((normalized.as_slice()[1] - 8.0).abs() < 1e-12); + } + + #[test] + fn test_normalize_to_f64_zero_vector() { + 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 = 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); + 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() { + 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); + assert!((v.as_slice()[1] - 8.0).abs() < 1e-12); + } + + #[test] + fn test_mut_normalize_to_f64_zero_vector() { + 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 = 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); + assert!((v.as_slice()[1] + 4.0).abs() < 1e-12); + } + + // -- lerp -- + #[test] + fn test_lerp_f64_weight_zero() { + 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 + 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 = 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 + 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 = 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 + 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 = 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); + assert!(result_low.is_err()); + 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() { + 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 + 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 = 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 + 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 = 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 + 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 = 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); + assert!(result_low.is_err()); + assert!(result_high.is_err()); + } + + // -- midpoint -- + #[test] + fn test_midpoint_f64() { + 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 + 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 = 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] + 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 = 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()); + } + + // -- 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() { + 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) + assert!((dist - 50f64.sqrt()).abs() < 1e-12); + } + + #[test] + fn test_distance_f64_zero() { + 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); + } + + #[test] + fn test_distance_f64_negative_values() { + 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) + assert!((dist - 56f64.sqrt()).abs() < 1e-12); + } + + #[test] + fn test_distance_f64_mismatched_length() { + 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()); + } + + // -- manhattan_distance -- + #[test] + fn test_manhattan_distance_f64_basic() { + 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 + assert!((dist - 12.0).abs() < 1e-12); + } + + #[test] + fn test_manhattan_distance_f64_zero() { + 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); + } + + #[test] + fn test_manhattan_distance_f64_negative_values() { + 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 + assert!((dist - 12.0).abs() < 1e-12); + } + + #[test] + fn test_manhattan_distance_f64_mismatched_length() { + 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()); + } + + // -- chebyshev_distance -- + #[test] + fn test_chebyshev_distance_f64_basic() { + 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 + assert!((dist - 5.0).abs() < 1e-12); + } + + #[test] + fn test_chebyshev_distance_f64_zero() { + 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); + } + + #[test] + fn test_chebyshev_distance_f64_negative_values() { + 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 + assert!((dist - 6.0).abs() < 1e-12); + } + + #[test] + fn test_chebyshev_distance_f64_mismatched_length() { + 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()); + } + + // -- minkowski_distance -- + #[test] + fn test_minkowski_distance_f64_basic() { + 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 + assert!((dist - 6.0).abs() < 1e-12); + } + + #[test] + fn test_minkowski_distance_f64_p1() { + 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 + assert!((dist - 12.0).abs() < 1e-12); + } + + #[test] + fn test_minkowski_distance_f64_p2() { + 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) + 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 = 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); + } + + #[test] + fn test_minkowski_distance_f64_partial() { + 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()); + } + + #[test] + fn test_minkowski_distance_f64_invalid_p() { + 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()); + } + + // -- norm -- + #[test] + fn test_norm_f64_basic() { + 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); + } + + #[test] + fn test_norm_f64_zero() { + 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 = ColFVec::from_vec(vec![7.0]); + let norm = v.norm(); + assert_eq!(norm, 7.0); + } + + #[test] + fn test_norm_f64_negative_values() { + 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); + } + + // -- magnitude -- + #[test] + fn test_magnitude_f64_basic() { + 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); + } + + #[test] + fn test_magnitude_f64_zero() { + 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 = ColFVec::from_vec(vec![7.0]); + let mag = v.magnitude(); + assert_eq!(mag, 7.0); + } + + #[test] + fn test_magnitude_f64_negative_values() { + 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); + } + + // -- lp_norm -- + #[test] + fn test_lp_norm_f64_p1() { + 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); + } + + #[test] + fn test_lp_norm_f64_p2() { + 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); + } + + #[test] + fn test_lp_norm_f64_p3() { + 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); + } + + #[test] + fn test_lp_norm_f64_zero() { + 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 = ColFVec::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 = 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 + assert!((angle - std::f64::consts::FRAC_PI_2).abs() < 1e-12); + } + + #[test] + fn test_angle_with_f64_parallel() { + 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) + assert!(angle.abs() < 1e-7, "angle was {}", angle); + } + + #[test] + fn test_angle_with_f64_opposite() { + 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 + assert!((angle - std::f64::consts::PI).abs() < 1e-12); + } + + #[test] + fn test_angle_with_f64_identical() { + 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 + assert!(angle.abs() < 1e-12); + } + + #[test] + fn test_angle_with_f64_arbitrary() { + 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 + assert!(angle > 0.0 && angle < std::f64::consts::PI); + } + + #[test] + fn test_angle_with_f64_zero_vector() { + 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()); + } + + #[test] + fn test_angle_with_f64_mismatched_length() { + 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()); + } + + // -- project_onto -- + #[test] + fn test_project_onto_f64_basic() { + 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] + 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 = 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 + 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 = 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] + 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 = 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 + 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 = 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()); + } + + #[test] + fn test_project_onto_f64_mismatched_length() { + 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()); + } + + // -- 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() { + 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); + } + + #[test] + fn test_cosine_similarity_f64_orthogonal() { + 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); + } + + #[test] + fn test_cosine_similarity_f64_opposite() { + 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); + } + + #[test] + fn test_cosine_similarity_f64_zero_vector() { + 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()); + } + + // ================================ + // + // VectorOpsComplex trait tests + // + // ================================ + + // -- normalize -- + #[test] + fn test_normalize_complex_f64_basic() { + 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); + assert!((normalized.as_slice()[0].im - 0.8).abs() < 1e-12); + } + + #[test] + fn test_normalize_complex_f64_multiple_elements() { + 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); + 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 = ColFVec::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()); + } + + // -- 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] + 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()); + } + + // -- 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] + 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()); + } + + // -- 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] + 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()); + } + + // -- 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] + 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()); + } + + // -- 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] + 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()); + } + + // ================================ + // + // Operator overload trait tests + // + // ================================ + + #[test] + fn test_neg() { + 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 = 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 = 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 = 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()); + 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 = 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 = 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]); + } + + #[test] + fn test_add_f64() { + 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]); + } + + #[test] + fn test_add_complex() { + 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)]); + } + + #[test] + fn test_add_nan_infinity() { + 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()); + assert_eq!(sum.as_slice()[1], f64::INFINITY); + assert_eq!(sum.as_slice()[2], f64::INFINITY); + } + + #[test] + #[should_panic(expected = "Vector length mismatch")] + fn test_add_panic_on_mismatched_length() { + 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 = 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]); + } + + #[test] + fn test_sub_f64() { + 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]); + } + + #[test] + fn test_sub_complex() { + 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)]); + } + + #[test] + fn test_sub_nan_infinity() { + 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()); + assert!(diff.as_slice()[1].is_nan()); // inf - inf = NaN + assert!(diff.as_slice()[2].is_nan()); + } + + #[test] + #[should_panic(expected = "Vector length mismatch")] + fn test_sub_panic_on_mismatched_length() { + 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 = 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]); + } + + #[test] + fn test_mul_f64() { + 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]); + } + + #[test] + fn test_mul_complex() { + 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!( + 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 = 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()); + assert_eq!(prod.as_slice()[1], f64::INFINITY); + assert_eq!(prod.as_slice()[2], f64::INFINITY); + } + + #[test] + #[should_panic(expected = "Vector length mismatch")] + fn test_mul_panic_on_mismatched_length() { + 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 = 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]); + } + + #[test] + fn test_elem_div_f64() { + 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]); + } + + #[test] + fn test_elem_div_complex_f32() { + 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); + assert!((result[1] - Complex::new(2.0, 0.0)).norm() < 1e-6); + } + + #[test] + fn test_elem_div_complex_f64() { + 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); + 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 = 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 = 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]); + } + + #[test] + fn test_add_assign_f64() { + 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]); + } + + #[test] + fn test_add_assign_complex() { + 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)]); + } + + #[test] + #[should_panic(expected = "Vector length mismatch")] + fn test_add_assign_panic_on_mismatched_length() { + 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 = 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]); + } + + #[test] + fn test_sub_assign_f64() { + 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]); + } + + #[test] + fn test_sub_assign_complex() { + 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)]); + } + + #[test] + #[should_panic(expected = "Vector length mismatch")] + fn test_sub_assign_panic_on_mismatched_length() { + 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 = 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]); + } + + #[test] + fn test_mul_assign_f64() { + 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]); + } + + #[test] + fn test_mul_assign_complex() { + 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)]); + } + + #[test] + #[should_panic(expected = "Vector length mismatch")] + fn test_mul_assign_panic_on_mismatched_length() { + 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 = 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]); + } + + #[test] + fn test_elem_div_assign_f64() { + 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]); + } + + #[test] + fn test_elem_div_assign_complex_f32() { + 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); + assert!((v1[1] - Complex::new(2.0, 0.0)).norm() < 1e-6); + } + + #[test] + fn test_elem_div_assign_complex_f64() { + 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); + 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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]); + } + + #[test] + fn test_scalar_mul_complex() { + use num::Complex; + 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)]); + } + + #[test] + fn test_scalar_mul_assign() { + 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 = ColFVec::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 = 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)]); + } + + #[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> = + 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)]); + } + + #[test] + fn test_scalar_div_nan_infinity() { + 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); + assert_eq!(result.as_slice()[2], 2.0); + } + + #[test] + fn test_scalar_div_assign_nan_infinity() { + 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); + assert_eq!(v.as_slice()[2], 2.0); + } + + // additional math operator overload edge cases + + #[test] + fn test_add_overflow_i32() { + 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 + 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 = ColFVec::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 = 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); + assert!(result.as_slice()[2].is_nan()); + } + + #[test] + fn test_neg_zero_f64() { + 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); + } + + #[test] + fn test_complex_div_by_zero() { + use num::Complex; + 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()); + } + + #[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()); + } +} diff --git a/src/types/macros.rs b/src/types/macros.rs deleted file mode 100644 index 127a524..0000000 --- a/src/types/macros.rs +++ /dev/null @@ -1,647 +0,0 @@ -//! Macros. - -/// Returns a [`crate::Vector`] with scalar data contents and order as defined in -/// the numeric type arguments. -/// -/// 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; -/// ``` -/// -/// # 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 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); -/// ``` -#[macro_export] -macro_rules! vector { - ($elem:expr; $n:expr) => ( - $crate::types::vector::Vector::from([$elem; $n]) - ); - ($($x:expr),+ $(,)?) => ( - $crate::types::vector::Vector::from([$($x),+]) - ); -} - -/// 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}; -/// -/// 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()); -/// ``` -/// -/// # Examples -/// -/// ## Integer types -/// -/// ``` -/// use vectora::{try_vector, Vector}; -/// -/// 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); -/// ``` -/// -/// ## Floating point types -/// -/// ``` -/// use vectora::{try_vector, Vector}; -/// -/// 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); -/// ``` -/// -/// ## Complex number types -/// -/// ``` -/// use vectora::{try_vector, Vector}; -/// -/// use approx::assert_relative_eq; -/// 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); -/// ``` -#[macro_export] -macro_rules! try_vector { - ($elem:expr) => { - $crate::types::vector::Vector::try_from($elem) - }; -} - -#[cfg(test)] -mod tests { - use crate::Vector; - #[allow(unused_imports)] - use approx::{assert_relative_eq, assert_relative_ne}; - #[allow(unused_imports)] - use num::complex::Complex; - #[allow(unused_imports)] - use pretty_assertions::{assert_eq, assert_ne}; - - #[test] - fn macro_vector_usize() { - let v1 = vector![1 as usize, 2 as usize, 3 as usize]; - let v2: Vector = vector![1, 2, 3]; - let v3: Vector = vector![1; 3]; - - assert_eq!(v1.len(), 3); - assert_eq!(v1[0], 1 as usize); - assert_eq!(v1[1], 2 as usize); - assert_eq!(v1[2], 3 as usize); - - assert_eq!(v2.len(), 3); - assert_eq!(v2[0], 1 as usize); - assert_eq!(v2[1], 2 as usize); - assert_eq!(v2[2], 3 as usize); - - assert_eq!(v3.len(), 3); - assert_eq!(v3[0], 1 as usize); - assert_eq!(v3[1], 1 as usize); - assert_eq!(v3[2], 1 as usize); - } - - #[test] - fn macro_vector_u8() { - let v1 = vector![1 as u8, 2 as u8, 3 as u8]; - let v2: Vector = vector![1, 2, 3]; - let v3: Vector = vector![1; 3]; - - assert_eq!(v1.len(), 3); - assert_eq!(v1[0], 1 as u8); - assert_eq!(v1[1], 2 as u8); - assert_eq!(v1[2], 3 as u8); - - assert_eq!(v2.len(), 3); - assert_eq!(v2[0], 1 as u8); - assert_eq!(v2[1], 2 as u8); - assert_eq!(v2[2], 3 as u8); - - assert_eq!(v3.len(), 3); - assert_eq!(v3[0], 1 as u8); - assert_eq!(v3[1], 1 as u8); - assert_eq!(v3[2], 1 as u8); - } - - #[test] - fn macro_vector_u16() { - let v1 = vector![1 as u16, 2 as u16, 3 as u16]; - let v2: Vector = vector![1, 2, 3]; - let v3: Vector = vector![1; 3]; - - assert_eq!(v1.len(), 3); - assert_eq!(v1[0], 1 as u16); - assert_eq!(v1[1], 2 as u16); - assert_eq!(v1[2], 3 as u16); - - assert_eq!(v2.len(), 3); - assert_eq!(v2[0], 1 as u16); - assert_eq!(v2[1], 2 as u16); - assert_eq!(v2[2], 3 as u16); - - assert_eq!(v3.len(), 3); - assert_eq!(v3[0], 1 as u16); - assert_eq!(v3[1], 1 as u16); - assert_eq!(v3[2], 1 as u16); - } - - #[test] - fn macro_vector_u32() { - let v1 = vector![1 as u32, 2 as u32, 3 as u32]; - let v2: Vector = vector![1, 2, 3]; - let v3: Vector = vector![1; 3]; - - assert_eq!(v1.len(), 3); - assert_eq!(v1[0], 1 as u32); - assert_eq!(v1[1], 2 as u32); - assert_eq!(v1[2], 3 as u32); - - assert_eq!(v2.len(), 3); - assert_eq!(v2[0], 1 as u32); - assert_eq!(v2[1], 2 as u32); - assert_eq!(v2[2], 3 as u32); - - assert_eq!(v3.len(), 3); - assert_eq!(v3[0], 1 as u32); - assert_eq!(v3[1], 1 as u32); - assert_eq!(v3[2], 1 as u32); - } - - #[test] - fn macro_vector_u64() { - let v1 = vector![1 as u64, 2 as u64, 3 as u64]; - let v2: Vector = vector![1, 2, 3]; - let v3: Vector = vector![1; 3]; - - assert_eq!(v1.len(), 3); - assert_eq!(v1[0], 1 as u64); - assert_eq!(v1[1], 2 as u64); - assert_eq!(v1[2], 3 as u64); - - assert_eq!(v2.len(), 3); - assert_eq!(v2[0], 1 as u64); - assert_eq!(v2[1], 2 as u64); - assert_eq!(v2[2], 3 as u64); - - assert_eq!(v3.len(), 3); - assert_eq!(v3[0], 1 as u64); - assert_eq!(v3[1], 1 as u64); - assert_eq!(v3[2], 1 as u64); - } - - #[test] - fn macro_vector_u128() { - let v1 = vector![1 as u128, 2 as u128, 3 as u128]; - let v2: Vector = vector![1, 2, 3]; - let v3: Vector = vector![1; 3]; - - assert_eq!(v1.len(), 3); - assert_eq!(v1[0], 1 as u128); - assert_eq!(v1[1], 2 as u128); - assert_eq!(v1[2], 3 as u128); - - assert_eq!(v2.len(), 3); - assert_eq!(v2[0], 1 as u128); - assert_eq!(v2[1], 2 as u128); - assert_eq!(v2[2], 3 as u128); - - assert_eq!(v3.len(), 3); - assert_eq!(v3[0], 1 as u128); - assert_eq!(v3[1], 1 as u128); - assert_eq!(v3[2], 1 as u128); - } - - #[test] - fn macro_vector_isize() { - let v1 = vector![1 as isize, 2 as isize, 3 as isize]; - let v2: Vector = vector![1, 2, 3]; - let v3: Vector = vector![1; 3]; - - assert_eq!(v1.len(), 3); - assert_eq!(v1[0], 1 as isize); - assert_eq!(v1[1], 2 as isize); - assert_eq!(v1[2], 3 as isize); - - assert_eq!(v2.len(), 3); - assert_eq!(v2[0], 1 as isize); - assert_eq!(v2[1], 2 as isize); - assert_eq!(v2[2], 3 as isize); - - assert_eq!(v3.len(), 3); - assert_eq!(v3[0], 1 as isize); - assert_eq!(v3[1], 1 as isize); - assert_eq!(v3[2], 1 as isize); - } - - #[test] - fn macro_vector_i8() { - let v1 = vector![1 as i8, 2 as i8, 3 as i8]; - let v2: Vector = vector![1, 2, 3]; - let v3: Vector = vector![1; 3]; - - assert_eq!(v1.len(), 3); - assert_eq!(v1[0], 1 as i8); - assert_eq!(v1[1], 2 as i8); - assert_eq!(v1[2], 3 as i8); - - assert_eq!(v2.len(), 3); - assert_eq!(v2[0], 1 as i8); - assert_eq!(v2[1], 2 as i8); - assert_eq!(v2[2], 3 as i8); - - assert_eq!(v3.len(), 3); - assert_eq!(v3[0], 1 as i8); - assert_eq!(v3[1], 1 as i8); - assert_eq!(v3[2], 1 as i8); - } - - #[test] - fn macro_vector_i16() { - let v1 = vector![1 as i16, 2 as i16, 3 as i16]; - let v2: Vector = vector![1, 2, 3]; - let v3: Vector = vector![1; 3]; - - assert_eq!(v1.len(), 3); - assert_eq!(v1[0], 1 as i16); - assert_eq!(v1[1], 2 as i16); - assert_eq!(v1[2], 3 as i16); - - assert_eq!(v2.len(), 3); - assert_eq!(v2[0], 1 as i16); - assert_eq!(v2[1], 2 as i16); - assert_eq!(v2[2], 3 as i16); - - assert_eq!(v3.len(), 3); - assert_eq!(v3[0], 1 as i16); - assert_eq!(v3[1], 1 as i16); - assert_eq!(v3[2], 1 as i16); - } - - #[test] - fn macro_vector_i32() { - let v1 = vector![1 as i32, 2 as i32, 3 as i32]; - let v2: Vector = vector![1, 2, 3]; - let v3: Vector = vector![1; 3]; - - assert_eq!(v1.len(), 3); - assert_eq!(v1[0], 1 as i32); - assert_eq!(v1[1], 2 as i32); - assert_eq!(v1[2], 3 as i32); - - assert_eq!(v2.len(), 3); - assert_eq!(v2[0], 1 as i32); - assert_eq!(v2[1], 2 as i32); - assert_eq!(v2[2], 3 as i32); - - assert_eq!(v3.len(), 3); - assert_eq!(v3[0], 1 as i32); - assert_eq!(v3[1], 1 as i32); - assert_eq!(v3[2], 1 as i32); - } - - #[test] - fn macro_vector_i64() { - let v1 = vector![1 as i64, 2 as i64, 3 as i64]; - let v2: Vector = vector![1, 2, 3]; - let v3: Vector = vector![1; 3]; - - assert_eq!(v1.len(), 3); - assert_eq!(v1[0], 1 as i64); - assert_eq!(v1[1], 2 as i64); - assert_eq!(v1[2], 3 as i64); - - assert_eq!(v2.len(), 3); - assert_eq!(v2[0], 1 as i64); - assert_eq!(v2[1], 2 as i64); - assert_eq!(v2[2], 3 as i64); - - assert_eq!(v3.len(), 3); - assert_eq!(v3[0], 1 as i64); - assert_eq!(v3[1], 1 as i64); - assert_eq!(v3[2], 1 as i64); - } - - #[test] - fn macro_vector_i128() { - let v1 = vector![1 as i128, 2 as i128, 3 as i128]; - let v2: Vector = vector![1, 2, 3]; - let v3: Vector = vector![1; 3]; - - assert_eq!(v1.len(), 3); - assert_eq!(v1[0], 1 as i128); - assert_eq!(v1[1], 2 as i128); - assert_eq!(v1[2], 3 as i128); - - assert_eq!(v2.len(), 3); - assert_eq!(v2[0], 1 as i128); - assert_eq!(v2[1], 2 as i128); - assert_eq!(v2[2], 3 as i128); - - assert_eq!(v3.len(), 3); - assert_eq!(v3[0], 1 as i128); - assert_eq!(v3[1], 1 as i128); - assert_eq!(v3[2], 1 as i128); - } - - #[test] - fn macro_vector_f32() { - let v1 = vector![1. as f32, 2. as f32, 3. as f32]; - let v2: Vector = vector![1., 2., 3.]; - let v3: Vector = vector![1.; 3]; - - assert_eq!(v1.len(), 3); - assert_relative_eq!(v1[0], 1.0 as f32); - assert_relative_eq!(v1[1], 2.0 as f32); - assert_relative_eq!(v1[2], 3.0 as f32); - - assert_eq!(v2.len(), 3); - assert_relative_eq!(v2[0], 1.0 as f32); - assert_relative_eq!(v2[1], 2.0 as f32); - assert_relative_eq!(v2[2], 3.0 as f32); - - assert_eq!(v3.len(), 3); - assert_relative_eq!(v3[0], 1.0 as f32); - assert_relative_eq!(v3[1], 1.0 as f32); - assert_relative_eq!(v3[2], 1.0 as f32); - } - - #[test] - fn macro_vector_f64() { - let v1 = vector![1. as f64, 2. as f64, 3. as f64]; - let v2: Vector = vector![1., 2., 3.]; - let v3: Vector = vector![1.; 3]; - - assert_eq!(v1.len(), 3); - assert_relative_eq!(v1[0], 1.0 as f64); - assert_relative_eq!(v1[1], 2.0 as f64); - assert_relative_eq!(v1[2], 3.0 as f64); - - assert_eq!(v2.len(), 3); - assert_relative_eq!(v2[0], 1.0 as f64); - assert_relative_eq!(v2[1], 2.0 as f64); - assert_relative_eq!(v2[2], 3.0 as f64); - - assert_eq!(v3.len(), 3); - assert_relative_eq!(v3[0], 1.0 as f64); - assert_relative_eq!(v3[1], 1.0 as f64); - assert_relative_eq!(v3[2], 1.0 as f64); - } - - #[test] - fn macro_vector_complex_i32() { - let v1 = vector![Complex::new(1 as i32, -2 as i32), Complex::new(-1 as i32, 2 as i32)]; - let v2: Vector, 2> = vector![Complex::new(1, -2), Complex::new(-1, 2)]; - let v3: Vector, 2> = vector![Complex::new(1, -2); 2]; - - assert_eq!(v1.len(), 2); - assert_eq!(v1[0].re, 1 as i32); - assert_eq!(v1[0].im, -2 as i32); - assert_eq!(v1[1].re, -1 as i32); - assert_eq!(v1[1].im, 2 as i32); - - assert_eq!(v2.len(), 2); - assert_eq!(v2[0].re, 1 as i32); - assert_eq!(v2[0].im, -2 as i32); - assert_eq!(v2[1].re, -1 as i32); - assert_eq!(v2[1].im, 2 as i32); - - assert_eq!(v3.len(), 2); - assert_eq!(v3[0].re, 1 as i32); - assert_eq!(v3[0].im, -2 as i32); - assert_eq!(v3[1].re, 1 as i32); - assert_eq!(v3[1].im, -2 as i32); - } - - #[test] - fn macro_vector_complex_f64() { - let v1 = vector![Complex::new(1. as f64, -2. as f64), Complex::new(-1. as f64, 2. as f64)]; - let v2: Vector, 2> = vector![Complex::new(1., -2.), Complex::new(-1., 2.)]; - let v3: Vector, 2> = vector![Complex::new(1., -2.); 2]; - - assert_eq!(v1.len(), 2); - assert_relative_eq!(v1[0].re, 1. as f64); - assert_relative_eq!(v1[0].im, -2. as f64); - assert_relative_eq!(v1[1].re, -1. as f64); - assert_relative_eq!(v1[1].im, 2. as f64); - - assert_eq!(v2.len(), 2); - assert_relative_eq!(v2[0].re, 1. as f64); - assert_relative_eq!(v2[0].im, -2. as f64); - assert_relative_eq!(v2[1].re, -1. as f64); - assert_relative_eq!(v2[1].im, 2. as f64); - - assert_eq!(v3.len(), 2); - assert_relative_eq!(v3[0].re, 1. as f64); - assert_relative_eq!(v3[0].im, -2. as f64); - assert_relative_eq!(v3[1].re, 1. as f64); - assert_relative_eq!(v3[1].im, -2. as f64); - } - - #[test] - fn macro_try_vector_i32() { - let slv = vec![1_i32, 2_i32, 3_i32]; - let v: Vector = try_vector!(slv).unwrap(); - - assert_eq!(v[0], 1_i32); - assert_eq!(v[1], 2_i32); - assert_eq!(v[2], 3_i32); - - let slv = vec![1_i32, 2_i32, 3_i32]; - let v: Vector = try_vector!(&slv).unwrap(); - - assert_eq!(v[0], 1_i32); - assert_eq!(v[1], 2_i32); - assert_eq!(v[2], 3_i32); - - let slv = vec![1_i32, 2_i32, 3_i32]; - let v: Vector = try_vector!(&slv[..]).unwrap(); - - assert_eq!(v[0], 1_i32); - assert_eq!(v[1], 2_i32); - assert_eq!(v[2], 3_i32); - } - - #[test] - fn macro_try_vector_f64() { - let slv = vec![1.0_f64, 2.0_f64, 3.0_f64]; - let v: Vector = try_vector!(slv).unwrap(); - - assert_relative_eq!(v[0], 1.0_f64); - assert_relative_eq!(v[1], 2.0_f64); - assert_relative_eq!(v[2], 3.0_f64); - - let slv = vec![1.0_f64, 2.0_f64, 3.0_f64]; - let v: Vector = try_vector!(&slv).unwrap(); - - assert_relative_eq!(v[0], 1.0_f64); - assert_relative_eq!(v[1], 2.0_f64); - assert_relative_eq!(v[2], 3.0_f64); - - let slv = vec![1.0_f64, 2.0_f64, 3.0_f64]; - let v: Vector = try_vector!(&slv[..]).unwrap(); - - assert_relative_eq!(v[0], 1.0_f64); - assert_relative_eq!(v[1], 2.0_f64); - assert_relative_eq!(v[2], 3.0_f64); - } - - #[test] - fn macro_try_vector_complex_i32() { - let slv = vec![Complex::new(1_i32, 2_i32), Complex::new(3_i32, 4_i32)]; - let v: Vector, 2> = try_vector!(slv).unwrap(); - - assert_eq!(v[0].re, 1_i32); - assert_eq!(v[0].im, 2_i32); - assert_eq!(v[1].re, 3_i32); - assert_eq!(v[1].im, 4_i32); - - let slv = vec![Complex::new(1_i32, 2_i32), Complex::new(3_i32, 4_i32)]; - let v: Vector, 2> = try_vector!(&slv).unwrap(); - - assert_eq!(v[0].re, 1_i32); - assert_eq!(v[0].im, 2_i32); - assert_eq!(v[1].re, 3_i32); - assert_eq!(v[1].im, 4_i32); - - let slv = vec![Complex::new(1_i32, 2_i32), Complex::new(3_i32, 4_i32)]; - let v: Vector, 2> = try_vector!(&slv[..]).unwrap(); - - assert_eq!(v[0].re, 1_i32); - assert_eq!(v[0].im, 2_i32); - assert_eq!(v[1].re, 3_i32); - assert_eq!(v[1].im, 4_i32); - } - - #[test] - fn macro_try_vector_complex_f64() { - let slv = vec![Complex::new(1.0_f64, 2.0_f64), Complex::new(3.0_f64, 4.0_f64)]; - let v: Vector, 2> = try_vector!(slv).unwrap(); - - assert_relative_eq!(v[0].re, 1.0_f64); - assert_relative_eq!(v[0].im, 2.0_f64); - assert_relative_eq!(v[1].re, 3.0_f64); - assert_relative_eq!(v[1].im, 4.0_f64); - - let slv = vec![Complex::new(1.0_f64, 2.0_f64), Complex::new(3.0_f64, 4.0_f64)]; - let v: Vector, 2> = try_vector!(&slv).unwrap(); - - assert_relative_eq!(v[0].re, 1.0_f64); - assert_relative_eq!(v[0].im, 2.0_f64); - assert_relative_eq!(v[1].re, 3.0_f64); - assert_relative_eq!(v[1].im, 4.0_f64); - - let slv = vec![Complex::new(1.0_f64, 2.0_f64), Complex::new(3.0_f64, 4.0_f64)]; - let v: Vector, 2> = try_vector!(&slv[..]).unwrap(); - - assert_relative_eq!(v[0].re, 1.0_f64); - assert_relative_eq!(v[0].im, 2.0_f64); - assert_relative_eq!(v[1].re, 3.0_f64); - assert_relative_eq!(v[1].im, 4.0_f64); - } -} diff --git a/src/types/mod.rs b/src/types/mod.rs index a8c11bf..d716dbe 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -1,4 +1,11 @@ //! Library types. -pub mod macros; +pub mod flexvector; +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/orientation.rs b/src/types/orientation.rs new file mode 100644 index 0000000..4e47c49 --- /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 std::fmt; + +/// 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; + +/// ... +#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] +pub enum VectorOrientation { + /// ... + Row, + /// ... + 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 new file mode 100644 index 0000000..039c995 --- /dev/null +++ b/src/types/traits.rs @@ -0,0 +1,804 @@ +//! Traits. + +use crate::types::orientation::VectorOrientation; +use num::Complex; +use std::borrow::Cow; + +use crate::errors::VectorError; + +/// ... +pub trait VectorBase { + // --- Core accessors --- + /// ... + fn as_slice(&self) -> &[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, + { + 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]> + where + T: Clone, + { + Cow::Borrowed(self.as_slice()) + } + + /// ... + #[inline] + fn pretty(&self) -> String + where + T: std::fmt::Debug, + { + format!("{:#?}", self.as_slice()) + } + + // --- Search/containment --- + /// ... + #[inline] + fn contains(&self, x: &T) -> bool + where + T: PartialEq, + { + self.as_slice().contains(x) + } + + /// ... + #[inline] + fn starts_with(&self, needle: &[T]) -> bool + where + T: PartialEq, + { + self.as_slice().starts_with(needle) + } + + /// ... + #[inline] + fn ends_with(&self, needle: &[T]) -> bool + where + T: PartialEq, + { + self.as_slice().ends_with(needle) + } + + /// ... + #[inline] + fn position

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

(&self, predicate: P) -> Option + where + P: FnMut(&T) -> bool, + { + self.as_slice().iter().rposition(predicate) + } + + // --- 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, + { + self.as_slice().split(pred) + } + + /// ... + #[inline] + fn splitn(&self, n: usize, pred: F) -> std::slice::SplitN<'_, T, F> + where + F: FnMut(&T) -> bool, + { + self.as_slice().splitn(n, pred) + } + + /// ... + #[inline] + fn rsplit(&self, pred: F) -> std::slice::RSplit<'_, T, F> + where + F: FnMut(&T) -> bool, + { + self.as_slice().rsplit(pred) + } + + /// ... + #[inline] + 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 --- + /// ... + #[inline] + fn as_ptr(&self) -> *const T { + self.as_slice().as_ptr() + } +} + +/// ... +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. + type Transposed; + + /// Returns a new value with the opposite orientation. + fn transpose(self) -> Self::Transposed; +} + +/// ... +pub trait VectorOps: VectorBase { + /// ... + type Output; + + /// ... + fn translate(&self, other: &Self) -> Result + 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 + where + T: num::Num + Copy, + Self::Output: std::iter::FromIterator, + { + 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 + Copy, + Self::Output: std::iter::FromIterator, + { + self.as_slice().iter().map(|a| -*a).collect() + } + + /// ... + fn dot(&self, other: &Self) -> Result + where + T: num::Num + Copy + std::iter::Sum; + + /// Dot product as f64 (for integer and float types). + fn dot_to_f64(&self, other: &Self) -> Result + where + T: num::ToPrimitive; + + ///... + fn cross(&self, other: &Self) -> Result + where + 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 + where + T: num::Num + Copy + std::iter::Sum, + { + self.as_slice().iter().copied().sum() + } + + /// ... + #[inline] + fn product(&self) -> T + where + T: num::Num + Copy + std::iter::Product, + { + self.as_slice().iter().copied().product() + } + + /// ... + #[inline] + fn minimum(&self) -> Option + where + T: PartialOrd + Copy, + { + 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 + Copy; + + /// ... + fn elementwise_min_into(&self, other: &Self, out: &mut [T]) -> Result<(), VectorError> + where + T: PartialOrd + Copy; + + /// ... + #[inline] + fn maximum(&self) -> Option + where + T: PartialOrd + Copy, + { + 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 + Copy; + + /// ... + fn elementwise_max_into(&self, other: &Self, out: &mut [T]) -> Result<(), VectorError> + where + 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 + Copy, + Self::Output: std::iter::FromIterator, + { + self.as_slice() + .iter() + .map(|x| { + if *x < min { + min + } else if *x > max { + max + } else { + *x + } + }) + .collect() + } + + /// ... + #[inline] + fn elementwise_clamp_into(&self, min: T, max: T, out: &mut [T]) -> Result<(), VectorError> + where + T: PartialOrd + Copy, + { + 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 + } else if *x > max { + max + } else { + *x + }; + } + Ok(()) + } + + /// L1 norm (sum of absolute values). + #[inline] + 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). + #[inline] + 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 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 + Copy, + { + for a in self.as_mut_slice().iter_mut() { + *a = -*a; + } + } + + /// 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 { + /// ... + type Output; + + /// ... + fn normalize(&self) -> Result + where + T: num::Float + std::ops::Div, + Self::Output: std::iter::FromIterator; + + /// ... + fn normalize_into(&self, out: &mut [T]) -> Result<(), VectorError> + where + 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: 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: 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; + + /// ... + fn lerp_into(&self, end: &Self, weight: T, out: &mut [T]) -> Result<(), VectorError> + where + T: num::Float; + + /// Midpoint + 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; + + /// Euclidean distance between self and other. + fn distance(&self, other: &Self) -> Result + where + T: num::Float + std::iter::Sum; + + /// Manhattan (L1) distance between self and other. + fn manhattan_distance(&self, other: &Self) -> Result + where + T: num::Float + std::iter::Sum; + + /// Chebyshev (L∞) distance between self and other. + fn chebyshev_distance(&self, other: &Self) -> Result + where + T: num::Float + PartialOrd; + + /// Minkowski (Lp) distance between self and other. + fn minkowski_distance(&self, other: &Self, p: T) -> Result + where + T: num::Float + std::iter::Sum; + + /// Euclidean norm (magnitude) of the vector. + #[inline] + fn norm(&self) -> T + where + T: num::Float + std::iter::Sum, + { + self.as_slice().iter().map(|a| (*a).powi(2)).sum::().sqrt() + } + + /// Alias for norm (magnitude). + #[inline] + fn magnitude(&self) -> T + where + T: num::Float + std::iter::Sum, + { + self.norm() + } + + /// Lp norm (generalized Minkowski norm). + #[inline] + fn lp_norm(&self, p: T) -> Result + where + T: num::Float + 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)) + } + + /// ... + fn angle_with(&self, other: &Self) -> Result + where + 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 + 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 + T: num::Float + std::iter::Sum + std::ops::Div; +} + +/// ... +pub trait VectorOpsFloatMut: VectorBaseMut { + /// ... + type Output; + + /// ... + fn mut_normalize(&mut self) -> Result<(), VectorError> + where + T: num::Float + std::ops::Div; + + /// ... + fn mut_normalize_to(&mut self, magnitude: T) -> Result<(), VectorError> + where + 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; +} +/// ... +pub trait VectorOpsComplex: VectorBase> { + /// ... + type Output; + + /// ... + fn normalize(&self) -> Result + where + N: num::Float, + 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 + N: num::Float, + Complex: Copy + + PartialEq + + std::ops::Div, Output = Complex> + + std::ops::Mul, Output = Complex>, + Self::Output: std::iter::FromIterator>; + + /// 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> + where + 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 + 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 + 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 + 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 + PartialOrd; + + /// Minkowski (Lp) distance between self and other. + fn minkowski_distance(&self, other: &Self, p: N) -> Result + where + N: num::Float + std::iter::Sum; + + /// Euclidean norm (magnitude) of the vector (returns real). + #[inline] + fn norm(&self) -> N + where + N: num::Float + std::iter::Sum, + { + self.as_slice().iter().map(|a| a.norm_sqr()).sum::().sqrt() + } + + /// Alias for norm (magnitude). + #[inline] + fn magnitude(&self) -> N + where + N: num::Float + std::iter::Sum, + { + self.norm() + } + + /// L1 norm (sum of magnitudes). + #[inline] + fn l1_norm(&self) -> N + where + N: num::Float + std::iter::Sum, + { + self.as_slice().iter().map(|a| a.norm()).sum() + } + + /// L∞ norm (maximum magnitude). + #[inline] + fn linf_norm(&self) -> N + where + N: num::Float + PartialOrd, + { + self.as_slice().iter().map(|a| a.norm()).fold(N::zero(), |acc, x| acc.max(x)) + } + + /// Lp norm (generalized Minkowski norm for complex). + #[inline] + fn lp_norm(&self, p: N) -> Result + where + N: num::Float + 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)).sum::().powf(N::one() / p)) + } + + /// ... + fn project_onto(&self, other: &Self) -> Result + where + 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 + N: num::Float + std::iter::Sum + std::ops::Neg, + Complex: std::ops::Div>; +} + +/// ... +pub trait VectorOpsComplexMut: VectorBaseMut> { + /// ... + type Output; + + /// ... + fn mut_normalize(&mut self) -> Result<(), VectorError> + where + 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>; + + /// 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; +} +/// ... +pub trait VectorHasOrientation { + /// ... + fn orientation(&self) -> VectorOrientation; + + /// Returns the orientation name as a string. + fn orientation_name(&self) -> String { + self.orientation().to_string() + } +} diff --git a/src/types/utils.rs b/src/types/utils.rs new file mode 100644 index 0000000..1f4d527 --- /dev/null +++ b/src/types/utils.rs @@ -0,0 +1,1737 @@ +//! Core logic implementations + +use crate::errors::VectorError; + +use num::Complex; + +#[inline] +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]) +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; + } +} + +#[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() +} + +#[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 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.conj() * *y).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]] +} + +/// 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]) +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]; +} + +/// 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 +where + T: PartialOrd + Copy, +{ + a.iter() + .zip(b.iter()) + .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]) +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 }; + } +} + +/// 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 +where + T: PartialOrd + Copy, +{ + a.iter() + .zip(b.iter()) + .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]) +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 }; + } +} + +#[inline] +pub(crate) fn lerp_impl(a: &[T], b: &[T], weight: T, out: &mut [T]) +where + T: Copy + + std::ops::Add + + std::ops::Mul + + std::ops::Sub + + num::One, +{ + 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: 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) { + *a = one_minus_w * *a + weight * b; + } +} + +#[inline] +pub(crate) fn distance_impl(a: &[T], b: &[T]) -> T +where + T: num::Float + std::iter::Sum, +{ + 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 + 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 + 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 + 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 + T: num::Float, +{ + 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, +{ + 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 + T: num::Float + 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 minkowski_distance_complex_impl( + a: &[num::Complex], + b: &[num::Complex], + p: N, +) -> N +where + N: num::Float + 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 + T: num::Float + 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(b: &[T], scalar: T) -> Out +where + T: Copy + std::ops::Mul, + Out: std::iter::FromIterator, +{ + b.iter().map(|x| *x * scalar).collect() +} + +#[inline] +pub(crate) fn project_onto_into_impl(other: &[T], scalar: T, out: &mut [T]) +where + T: Copy + std::ops::Mul, +{ + 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 + 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_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 + 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], + 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()) +} + +#[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], + 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 + 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 + 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; + + use crate::FlexVector; + + 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, 0]); + } + + // -- 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); + } + + // -- 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 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 = 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: 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 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); + assert!((result.im - expected.im).abs() < 1e-12); + } + + // -- 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]); + } + + // -- 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]; + 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, 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() { + 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); + } + + // -- 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() { + 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); + } + + // -- 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() { + 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); + } + + // -- 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() { + 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); + } + + // -- 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() { + 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 b = [1.0f64, 0.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 b = [1.0f64, 2.0]; + 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 b = [1.0f64, 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 b = [5.0f64, 5.0]; + 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 b = [0.0f64, 0.0]; + 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); + } + + // -- 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() { + 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); + } + + // -- 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() { + 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); + } + + // -- 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]; + 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()); + } +} 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, diff --git a/src/types/vectorslice.rs b/src/types/vectorslice.rs new file mode 100644 index 0000000..46f5639 --- /dev/null +++ b/src/types/vectorslice.rs @@ -0,0 +1,5905 @@ +//! VectorSlice types. + +use crate::errors::VectorError; +use crate::types::flexvector::FlexVector; +use crate::types::orientation::{Column, Row, VectorOrientation}; +use crate::types::traits::{ + VectorBase, VectorBaseMut, VectorHasOrientation, VectorOps, VectorOpsComplex, VectorOpsFloat, + VectorOpsFloatMut, VectorOpsMut, +}; +use crate::types::utils::{ + 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_into_impl, + normalize_to_impl, normalize_to_into_impl, project_onto_impl, project_onto_into_impl, + translate_impl, +}; + +use std::any::TypeId; +use std::fmt; +use std::marker::PhantomData; + +use num::{Complex, Zero}; + +// ///////////////////////////////// +// ================================ +// +// VectorSlice type +// +// ================================ +// ///////////////////////////////// + +/// ... +#[derive(Clone, PartialEq, Eq)] +pub struct VectorSlice<'a, T, O = Column> { + /// ... + pub elements: &'a [T], + _orientation: PhantomData, +} + +/// ... +pub type ColVecSli<'a, T> = VectorSlice<'a, T, Column>; +/// ... +pub type RowVecSli<'a, T> = VectorSlice<'a, T, Row>; + +// ================================ +// +// Constructors +// +// ================================ + +impl<'a, T, O> VectorSlice<'a, T, O> { + /// ... + #[inline] + pub fn new(slice: &'a [T]) -> Self { + VectorSlice { elements: 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.elements + } +} + +impl<'a, T, O> AsRef<[T]> for VectorSlice<'a, T, O> { + fn as_ref(&self) -> &[T] { + self.elements + } +} + +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.elements.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.elements.iter() + } +} + +impl<'a, T, O> fmt::Display for VectorSlice<'a, T, O> +where + T: fmt::Debug, + O: 'static, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + 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: 'static, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("VectorSlice") + .field("orientation", &self.orientation_name()) + .field("elements", &self.elements) + .finish() + } +} + +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) + } +} + +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 +// +// ================================ + +impl<'a, T, O> VectorBase for VectorSlice<'a, T, O> { + #[inline] + fn as_slice(&self) -> &[T] { + self.elements + } +} + +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, +{ + 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 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 + 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 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 + Copy, + { + self.check_same_length_and_raise(other)?; + 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 + Copy, + { + 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] + fn elementwise_max(&self, other: &Self) -> Result + where + T: PartialOrd + Copy, + { + self.check_same_length_and_raise(other)?; + 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 + Copy, + { + 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(()) + } +} + +impl<'a, T, O> VectorOpsFloat for VectorSlice<'a, T, O> +where + T: num::Float + std::iter::Sum, +{ + type Output = FlexVector; + + #[inline] + fn normalize(&self) -> Result + where + T: num::Float + std::ops::Div + num::Zero, + Self::Output: std::iter::FromIterator, + { + normalize_impl(self.as_slice(), self.norm()) + } + + #[inline] + fn normalize_into(&self, out: &mut [T]) -> Result<(), VectorError> + where + T: num::Float + 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 + 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) + } + + #[inline] + fn normalize_to_into(&self, magnitude: T, out: &mut [T]) -> Result<(), VectorError> + where + 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) + } + + #[inline] + fn lerp(&self, end: &Self, weight: T) -> Result + where + T: num::Float, + { + 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 lerp_into(&self, end: &Self, weight: T, 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(), + )); + } + 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, end: &Self) -> Result + where + T: num::Float, + { + self.lerp(end, num::cast(0.5).unwrap()) + } + + #[inline] + fn midpoint_into(&self, end: &Self, out: &mut [T]) -> Result<(), VectorError> + where + T: num::Float, + { + self.lerp_into(end, num::cast(0.5).unwrap(), out) + } + + #[inline] + fn distance(&self, other: &Self) -> Result + where + T: num::Float + 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 + 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 + 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 + 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 + 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 + 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 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 + T: num::Float + 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)) + } +} + +impl<'a, N, O> VectorOpsComplex for VectorSlice<'a, Complex, O> +where + 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>, + { + 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 + N: num::Float, + 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 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 + 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, + 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 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 + N: num::Float + 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 + 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, + { + 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 + 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 + 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 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 + N: num::Float + 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 +// +// ================================ + +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 + // + // ================================ + + /// 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(()) + } + } +} + +// ///////////////////////////////// +// ================================ +// +// VectorSliceMut type +// +// ================================ +// ///////////////////////////////// + +/// ... +#[derive(PartialEq, Eq)] +pub struct VectorSliceMut<'a, T, O = Column> { + /// ... + pub elements: &'a mut [T], + _orientation: PhantomData, +} + +/// ... +pub type ColVecSliMut<'a, T> = VectorSliceMut<'a, T, Column>; +/// ... +pub type RowVecSliMut<'a, T> = VectorSliceMut<'a, T, Row>; + +// ================================ +// +// Constructors +// +// ================================ + +impl<'a, T, O> VectorSliceMut<'a, T, O> { + /// ... + #[inline] + pub fn new(slice: &'a mut [T]) -> Self { + VectorSliceMut { elements: 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.elements + } +} + +impl<'a, T, O> std::ops::DerefMut for VectorSliceMut<'a, T, O> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.elements + } +} + +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 + } +} + +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.elements.iter_mut() + } +} + +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.elements.iter_mut() + } +} + +impl<'a, T, O> fmt::Display for VectorSliceMut<'a, T, O> +where + T: fmt::Debug, + O: 'static, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + 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: 'static, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("VectorSliceMut") + .field("orientation", &self.orientation_name()) + .field("elements", &self.elements) + .finish() + } +} + +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 +// +// ================================ + +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 + } +} + +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, +{ + 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 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 + 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 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 + Copy, + { + self.check_same_length_and_raise(other)?; + 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 + Copy, + { + 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] + fn elementwise_max(&self, other: &Self) -> Result + where + T: PartialOrd + Copy, + { + self.check_same_length_and_raise(other)?; + 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 + Copy, + { + 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(()) + } +} + +impl<'a, T, O> VectorOpsMut for VectorSliceMut<'a, T, O> +where + T: Copy, +{ + 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(()) + } +} + +impl<'a, T, O> VectorOpsFloat for VectorSliceMut<'a, T, O> +where + T: num::Float + std::iter::Sum, +{ + type Output = FlexVector; + + #[inline] + fn normalize(&self) -> Result + where + T: num::Float + std::ops::Div, + Self::Output: std::iter::FromIterator, + { + normalize_impl(self.as_slice(), self.norm()) + } + + #[inline] + fn normalize_into(&self, out: &mut [T]) -> Result<(), VectorError> + where + T: num::Float + 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 + T: num::Float + std::ops::Div + std::ops::Mul, + Self::Output: std::iter::FromIterator, + { + normalize_to_impl(self.as_slice(), self.norm(), magnitude) + } + + #[inline] + fn normalize_to_into(&self, magnitude: T, out: &mut [T]) -> Result<(), VectorError> + where + 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) + } + + #[inline] + fn lerp(&self, end: &Self, weight: T) -> Result + where + T: num::Float, + { + 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 lerp_into(&self, end: &Self, weight: T, 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(), + )); + } + 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, end: &Self) -> Result + where + T: num::Float, + { + self.lerp(end, num::cast(0.5).unwrap()) + } + + #[inline] + fn midpoint_into(&self, end: &Self, out: &mut [T]) -> Result<(), VectorError> + where + T: num::Float, + { + self.lerp_into(end, num::cast(0.5).unwrap(), out) + } + + #[inline] + fn distance(&self, other: &Self) -> Result + where + T: num::Float + 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 + 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 + 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 + 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 + 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 + 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 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 + T: num::Float + 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)) + } +} + +impl<'a, T, O> VectorOpsFloatMut for VectorSliceMut<'a, T, O> +where + T: num::Float + std::iter::Sum, +{ + type Output = Self; + + #[inline] + fn mut_normalize(&mut self) -> Result<(), VectorError> + where + T: num::Float + std::ops::Div, + { + 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: num::Float + 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, + { + 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(()) + } +} + +impl<'a, N, O> VectorOpsComplex for VectorSliceMut<'a, Complex, O> +where + 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>, + { + 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 + N: num::Float, + Complex: Copy + + PartialEq + + std::ops::Div, Output = Complex> + + std::ops::Mul, Output = Complex>, + Self::Output: std::iter::FromIterator>, + { + normalize_to_impl( + self.as_slice(), + Complex::new(self.norm(), N::zero()), + Complex::new(magnitude, N::zero()), + ) + } + + #[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 + 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, + { + 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 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 + N: num::Float + 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 + 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, + { + 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 + 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 + std::iter::Sum, + 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 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 + N: num::Float + 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 +// +// ================================ + +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 + // + // ================================ + + /// 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::*; + 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}; + + // ///////////////////////////////// + // ================================ + // + // 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.elements, &[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.elements, + &[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.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.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.elements, &[]); + } + + #[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.elements, + &[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.elements, + &[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.elements, &[]); + } + + // -- Deref trait for VectorSlice -- + #[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 for VectorSlice -- + + #[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)] + ); + } + + // -- 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 }")); + } + + // -- 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); + } + + // -- 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] + 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 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] + 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]); + } + + // -- 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 -- + + #[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()); + } + + // -- 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]; + 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()); + } + + // -- dot_to_f64 -- + + #[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()); + } + + // -- cross -- + + #[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()); + } + + // -- 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]; + 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()); + } + + // -- 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]; + 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()); + } + + // -- 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, []); + } + + // -- 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()); + } + + // -- 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]; + 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); + } + } + + // -- 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]; + 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()); + } + + // -- 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]; + 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]); + } + + // -- 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]; + 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); + } + + // -- 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 -- + + #[test] + fn test_vector_slice_complex_normalize() { + 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() { + 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() { + 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() { + 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() { + 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() { + 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() { + 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() { + 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() { + 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() { + 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() { + 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()); + } + + // -- 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] + fn test_vector_slice_complex_midpoint() { + 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); + } + } + + // -- 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] + fn test_vector_slice_complex_distance() { + 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() { + 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() { + 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() { + 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() { + 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() { + 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() { + 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() { + 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() { + 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()); + } + + // -- 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] + fn test_vector_slice_complex_cosine_similarity_parallel() { + 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() { + 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() { + 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() { + 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() { + 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() { + 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()); + } + + // ================================ + // + // 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); + } + + // ///////////////////////////////// + // ================================ + // + // 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.elements, &mut [1, 2, 3, 4, 5]); + // Mutate through the slice + vslice.elements[0] = 10; + assert_eq!(vslice.elements[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.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.elements[0] = Complex::new(7.0, 8.0); + assert_eq!(vslice.elements[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.elements, &mut [20, 30, 40]); + vslice.elements[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.elements, &mut [7, 8, 9]); + vslice.elements[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.elements, &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.elements, + &mut [Complex::new(20.0, 2.0), Complex::new(30.0, 3.0), Complex::new(40.0, 4.0),] + ); + vslice.elements[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.elements, + &mut [Complex::new(7.0, 0.0), Complex::new(8.0, 1.0), Complex::new(9.0, 2.0),] + ); + vslice.elements[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.elements, &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]); + } + + // -- 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() { + 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)]); + } + + // -- 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 }")); + } + + // -- 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] + 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)]); + } + + // -- 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 -- + + #[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()); + } + + // -- 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]; + 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()); + } + + // -- cross -- + + #[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()); + } + + // -- 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]; + 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()); + } + + // -- 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]; + 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()); + } + + // -- 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] + 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]); + } + + // -- 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_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]; + 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); + } + } + + // -- 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()); + } + + // -- 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] + 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()); + } + + // -- 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]; + 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]); + } + + // -- 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]; + 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); + } + + // -- 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] + 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()); + } + + // -- VectorOpsComplex trait for VectorSliceMut -- + + // -- normalize -- + + #[test] + fn test_vector_slice_mut_complex_normalize() { + 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() { + 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_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() { + 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() { + 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() { + 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() { + 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() { + 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() { + 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() { + 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() { + 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()); + } + + // -- 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] + fn test_vector_slice_mut_complex_midpoint() { + 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); + } + } + + // -- 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] + fn test_vector_slice_mut_complex_distance() { + 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() { + 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() { + 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() { + 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() { + 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() { + 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() { + 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() { + 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() { + 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()); + } + + // -- 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] + fn test_vector_slice_mut_complex_cosine_similarity_parallel() { + 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() { + 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() { + 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() { + 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() { + 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() { + 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()); + } + + // ================================ + // + // 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); + } +}