matrix_oxide/
vector.rs

1use crate::numbers::Numeric;
2use std::ops::{Add, Mul};
3
4pub trait VectorOps<T>
5where
6    T: Numeric + Default + Mul<Output = T> + Add<Output = T> + Clone,
7{
8    /// Compute the dot product between 2 vectors. This outputs a single number
9    /// that provides information about the relationship between the 2 vectors.
10    ///
11    /// NOTE: The two vectors MUST have the same dimensionality in order to
12    /// compute the dot product for them.
13    fn dot_product(&self, b: &[T]) -> Option<T>;
14
15    /// Compute the dot product of a vector and itself.
16    fn squared_norm(&self) -> T;
17
18    /// Compute a shrunken version of the vector.
19    ///
20    /// NOTE: The scalar (λ) MUST meet the following condition
21    /// to shrink a vector: 0 < λ < 1. (The scalar must be
22    /// greater than 0 and also less than 1).
23    ///
24    /// NOTE: The shrunken vector will always be of type f64
25    /// no matter the original type of the vector pre shrink.
26    fn shrink(&self, scalar: f64) -> Option<Vec<f64>>;
27
28    /// Compute a stretched version of the vector.
29    ///
30    /// NOTE: The scalar (λ) MUST meet the following condition
31    /// to str a vector: 0 > λ > 1. (The scalar must be
32    /// less than 0 and also greater than 1).
33    ///
34    /// NOTE: A vector stretched with a negative scalar means
35    /// geometrically the vector will do a 180 and then be stretched.
36    ///
37    /// NOTE: The stretched vector will always be of type f64
38    /// no matter the original type of the vector pre stretch.
39    fn stretch<U>(&self, scalar: U) -> Option<Vec<f64>>
40    where
41        U: Into<f64> + PartialOrd + Copy;
42}
43impl<T> VectorOps<T> for Vec<T>
44where
45    T: Numeric + Default + Mul<Output = T> + Add<Output = T> + Clone,
46{
47    /// Compute the dot product between 2 vectors. This outputs a single number
48    /// that provides information about the relationship between the 2 vectors.
49    ///
50    /// NOTE: The two vectors MUST have the same dimensionality in order to
51    /// compute the dot product for them.
52    fn dot_product(&self, b: &[T]) -> Option<T> {
53        // In order to compute the dot product between two
54        // vectors they most have the same dimensionality
55        if self.len() != b.len() {
56            return None;
57        }
58
59        // Element wise multiplication on the vectors while accumulating a sum
60        // of the products, which after summing each product is the dot product.
61        Some(self.iter().zip(b).fold(T::default(), |acc, (ai, bi)| {
62            (ai.clone() * bi.clone()) + acc
63        }))
64    }
65
66    /// Compute the squared norm of a vector, the dot product of a vector and itself.
67    fn squared_norm(&self) -> T {
68        self.iter()
69            .fold(T::default(), |acc, x| (x.clone() * x.clone()) + acc)
70    }
71
72    /// Compute a shrunken version of the vector.
73    ///
74    /// NOTE: The scalar (λ) MUST meet the following condition
75    /// to shrink a vector: 0 < λ < 1. (The scalar must be
76    /// greater than 0 and also less than 1).
77    ///
78    /// NOTE: The shrunken vector will always be of type f64
79    /// no matter the original type of the vector pre shrink.
80    fn shrink(&self, scalar: f64) -> Option<Vec<f64>> {
81        // In order to shrink a vector the scalar must
82        // be greater than 0.0 and less than 1.0
83        if !(0.0..1.0).contains(&scalar) {
84            return None;
85        }
86
87        let shrunken_vector = self.iter().map(|x| x.as_f64() * scalar).collect();
88
89        Some(shrunken_vector)
90    }
91
92    /// Compute a stretched version of the vector.
93    ///
94    /// NOTE: The scalar (λ) MUST meet the following condition
95    /// to str a vector: 0 > λ > 1. (The scalar must be
96    /// less than 0 and also greater than 1).
97    ///
98    /// NOTE: A vector stretched with a negative scalar means
99    /// geometrically the vector will do a 180 and then be stretched.
100    ///
101    /// NOTE: The stretched vector will always be of type f64
102    /// no matter the original type of the vector pre stretch.
103    fn stretch<U>(&self, scalar: U) -> Option<Vec<f64>>
104    where
105        U: Into<f64> + PartialOrd + Copy,
106    {
107        let scalar: f64 = scalar.into();
108
109        // In order to stretch a vector the scalar must
110        // be less than 0.0 and greater than 1.0
111        if (0.0..1.0).contains(&scalar) {
112            return None;
113        }
114
115        let stretched_vector = self.iter().map(|x| x.as_f64() * scalar).collect();
116
117        Some(stretched_vector)
118    }
119}
120
121#[cfg(test)]
122mod tests {
123    use super::*;
124
125    #[test]
126    fn test_dot_product_integers() {
127        let vec1 = vec![1, 2, 3];
128        let vec2 = vec![4, 5, 6];
129        let result = vec1.dot_product(&vec2).unwrap();
130        assert_eq!(result, 32);
131    }
132
133    #[test]
134    fn test_dot_product_floats() {
135        let vec1 = vec![1.0, 2.0, 3.0];
136        let vec2 = vec![4.0, 5.0, 6.0];
137        let result = vec1.dot_product(&vec2).unwrap();
138        assert!(((result - 32.0) as f32).abs() < f32::EPSILON);
139    }
140
141    #[test]
142    fn test_dot_product_empty_vectors() {
143        let vec1: Vec<i32> = vec![];
144        let vec2: Vec<i32> = vec![];
145        let result = vec1.dot_product(&vec2).unwrap();
146        assert_eq!(result, 0);
147    }
148
149    #[test]
150    fn test_dot_product_mismatched_lengths() {
151        let vec1 = vec![1, 2];
152        let vec2 = vec![3, 4, 5];
153        let result = vec1.dot_product(&vec2);
154        assert_eq!(result, None);
155    }
156
157    #[test]
158    fn test_dot_product_single_element() {
159        let vec1 = vec![7];
160        let vec2 = vec![3];
161        let result = vec1.dot_product(&vec2).unwrap();
162        assert_eq!(result, 21);
163    }
164
165    #[test]
166    fn test_dot_product_with_zeros() {
167        let vec1 = vec![0, 0, 0];
168        let vec2 = vec![1, 2, 3];
169        let result = vec1.dot_product(&vec2).unwrap();
170        assert_eq!(result, 0);
171    }
172
173    #[test]
174    fn test_dot_product_negative_numbers() {
175        let vec1 = vec![-1, -2, -3];
176        let vec2 = vec![4, 5, 6];
177        let result = vec1.dot_product(&vec2).unwrap();
178        assert_eq!(result, -32);
179    }
180
181    #[test]
182    fn test_squared_norm_empty() {
183        let v: Vec<i32> = vec![];
184        assert_eq!(v.squared_norm(), 0);
185    }
186
187    #[test]
188    fn test_squared_norm_single_element() {
189        let v = vec![5];
190        assert_eq!(v.squared_norm(), 25);
191    }
192
193    #[test]
194    fn test_squared_norm_multiple_integers() {
195        let v = vec![2, -3, 4];
196        assert_eq!(v.squared_norm(), 29);
197    }
198
199    #[test]
200    fn test_squared_norm_with_negatives() {
201        let v = vec![-1, -2, -3];
202        assert_eq!(v.squared_norm(), 14);
203    }
204
205    #[test]
206    fn test_squared_norm_large_values() {
207        let v = vec![1000, 2000, 3000];
208        assert_eq!(v.squared_norm(), 14_000_000);
209    }
210
211    #[test]
212    fn test_squared_norm_floats() {
213        let v = vec![1.0_f64, 2.5_f64, 3.2_f64];
214        let result = v.squared_norm();
215        let expected = 17.49;
216        let epsilon = 1e-10;
217        assert!((result - expected).abs() < epsilon);
218    }
219
220    #[test]
221    fn test_shrink_invalid_scalar() {
222        let v = vec![1_i32, 2, 3];
223        // Scalar is 1.0 (not less than 1.0) → should return None.
224        assert_eq!(v.shrink(1.0), None);
225
226        // Negative scalar → should return None.
227        assert_eq!(v.shrink(-0.1), None);
228    }
229
230    #[test]
231    fn test_shrink_i8() {
232        let v: Vec<i8> = vec![10, 20, 30];
233        let scalar = 0.5;
234        let result = v.shrink(scalar).expect("Expected valid shrink result");
235        // Each element should be multiplied by 0.5: 10 * 0.5 = 5.0, etc.
236        assert_eq!(result, vec![5.0, 10.0, 15.0]);
237    }
238
239    #[test]
240    fn test_shrink_i32() {
241        let v: Vec<i32> = vec![1000, 2000, 3000];
242        let scalar = 0.2;
243        let result = v.shrink(scalar).expect("Expected valid shrink result");
244        assert_eq!(result, vec![200.0, 400.0, 600.0]);
245    }
246
247    #[test]
248    fn test_shrink_i64() {
249        let v: Vec<i64> = vec![1000, 2000, 3000];
250        let scalar = 0.1;
251        let result = v.shrink(scalar).expect("Expected valid shrink result");
252        assert_eq!(result, vec![100.0, 200.0, 300.0]);
253    }
254
255    #[test]
256    fn test_shrink_u64() {
257        let v: Vec<u64> = vec![1000, 2000, 3000];
258        let scalar = 0.1;
259        let result = v.shrink(scalar).expect("Expected valid shrink result");
260        assert_eq!(result, vec![100.0, 200.0, 300.0]);
261    }
262
263    #[test]
264    fn test_shrink_f32() {
265        let v: Vec<f32> = vec![10.0, 20.0, 30.0];
266        let scalar = 0.5;
267        let result = v.shrink(scalar).expect("Expected valid shrink result");
268        assert_eq!(result, vec![5.0, 10.0, 15.0]);
269    }
270
271    #[test]
272    fn test_shrink_f64() {
273        let v: Vec<f64> = vec![10.0, 20.0, 30.0];
274        let scalar = 0.5;
275        let result = v.shrink(scalar).expect("Expected valid shrink result");
276        assert_eq!(result, vec![5.0, 10.0, 15.0]);
277    }
278
279    #[test]
280    fn test_stretch_with_scalar_in_range_returns_none() {
281        let vec: Vec<f64> = vec![1.0, 2.0, 3.0];
282        assert_eq!(vec.stretch(0.5), None);
283    }
284
285    #[test]
286    fn test_stretch_with_scalar_equal_to_one_returns_same_vector() {
287        let vec: Vec<f64> = vec![1.0, 2.0, 3.0];
288        let expected: Vec<f64> = vec![1.0, 2.0, 3.0];
289        assert_eq!(vec.stretch(1.0), Some(expected));
290    }
291
292    #[test]
293    fn test_stretch_with_scalar_greater_than_one() {
294        let vec: Vec<f64> = vec![1.0, 2.0, 3.0];
295        let scalar = 2.0;
296        let expected: Vec<f64> = vec![2.0, 4.0, 6.0];
297        assert_eq!(vec.stretch(scalar), Some(expected));
298    }
299
300    #[test]
301    fn test_stretch_with_integer_scalar() {
302        let vec: Vec<f64> = vec![1.0, 2.0, 3.0];
303        let scalar = 3;
304        let expected: Vec<f64> = vec![3.0, 6.0, 9.0];
305        assert_eq!(vec.stretch(scalar), Some(expected));
306    }
307
308    #[test]
309    fn test_stretch_with_negative_scalar() {
310        let vec: Vec<f64> = vec![1.0, 2.0, 3.0];
311        let scalar = -2.0;
312        let expected: Vec<f64> = vec![-2.0, -4.0, -6.0];
313        assert_eq!(vec.stretch(scalar), Some(expected));
314    }
315}