Skip to main content

multicalc/linear_algebra/
vector.rs

1//! Fixed-size, stack-allocated column vector.
2
3use core::ops::{Add, AddAssign, Index, IndexMut, Mul, Neg, Sub, SubAssign};
4
5use crate::scalar::Numeric;
6
7/// A column vector of `N` components, stored inline on the stack.
8///
9/// ```
10/// use multicalc::linear_algebra::Vector;
11/// let a = Vector::new([1.0, 2.0, 3.0]);
12/// let b = Vector::from([4.0, 5.0, 6.0]);
13///
14/// assert_eq!(a[0], 1.0);
15/// assert_eq!(a + b, Vector::new([5.0, 7.0, 9.0]));
16/// assert_eq!(b - a, Vector::new([3.0, 3.0, 3.0]));
17/// assert_eq!(-a, Vector::new([-1.0, -2.0, -3.0]));
18/// assert_eq!(a * 2.0, Vector::new([2.0, 4.0, 6.0]));
19/// assert_eq!(a.dot(b), 32.0);
20/// ```
21#[repr(transparent)]
22#[derive(Debug, Clone, Copy, PartialEq)]
23#[must_use]
24pub struct Vector<const N: usize, T = f64> {
25    data: [T; N],
26}
27
28impl<const N: usize, T> Vector<N, T> {
29    /// Wraps `N` components into a vector.
30    #[inline]
31    pub const fn new(data: [T; N]) -> Self {
32        Vector { data }
33    }
34
35    /// Builds a vector by calling `f` with each index in `0..N`.
36    ///
37    /// ```
38    /// use multicalc::linear_algebra::Vector;
39    /// let v = Vector::<4>::from_fn(|i| i as f64);
40    /// assert_eq!(v.into_array(), [0.0, 1.0, 2.0, 3.0]);
41    /// ```
42    #[inline]
43    pub fn from_fn(f: impl FnMut(usize) -> T) -> Self {
44        Vector {
45            data: core::array::from_fn(f),
46        }
47    }
48
49    /// Borrows the components as an array.
50    ///
51    /// ```
52    /// use multicalc::linear_algebra::Vector;
53    /// assert_eq!(Vector::new([1.0, 2.0]).as_array(), &[1.0, 2.0]);
54    /// ```
55    #[inline]
56    #[must_use]
57    pub const fn as_array(&self) -> &[T; N] {
58        &self.data
59    }
60
61    /// Borrows the components as a slice.
62    ///
63    /// ```
64    /// use multicalc::linear_algebra::Vector;
65    /// assert_eq!(Vector::new([1.0, 2.0]).as_slice(), &[1.0, 2.0]);
66    /// ```
67    #[inline]
68    #[must_use]
69    pub const fn as_slice(&self) -> &[T] {
70        &self.data
71    }
72
73    /// Borrows the components as a mutable slice.
74    ///
75    /// ```
76    /// use multicalc::linear_algebra::Vector;
77    /// let mut v = Vector::new([1.0, 2.0]);
78    /// v.as_mut_slice()[0] = 9.0;
79    /// assert_eq!(v[0], 9.0);
80    /// ```
81    #[inline]
82    pub fn as_mut_slice(&mut self) -> &mut [T] {
83        &mut self.data
84    }
85
86    /// Consumes the vector, returning its components.
87    #[inline]
88    #[must_use]
89    pub fn into_array(self) -> [T; N] {
90        self.data
91    }
92}
93
94impl<const N: usize, T: Copy> Vector<N, T> {
95    /// Builds a vector from a slice, or `None` if `slice.len()` is not `N`.
96    ///
97    /// ```
98    /// use multicalc::linear_algebra::Vector;
99    /// assert!(Vector::<3>::try_from_slice(&[1.0, 2.0, 3.0]).is_some());
100    /// assert!(Vector::<3>::try_from_slice(&[1.0, 2.0]).is_none());
101    /// ```
102    #[inline]
103    #[must_use]
104    pub fn try_from_slice(slice: &[T]) -> Option<Self> {
105        (slice.len() == N).then(|| Self::from_fn(|i| slice[i]))
106    }
107}
108
109impl<const N: usize, T: Numeric> Vector<N, T> {
110    /// The zero vector.
111    ///
112    /// ```
113    /// use multicalc::linear_algebra::Vector;
114    /// let v: Vector<3> = Vector::zeros();
115    /// assert_eq!(v.into_array(), [0.0, 0.0, 0.0]);
116    /// ```
117    #[inline]
118    pub fn zeros() -> Self {
119        Vector::from_fn(|_| T::ZERO)
120    }
121
122    /// Multiplies every component by `scalar`.
123    ///
124    /// ```
125    /// use multicalc::linear_algebra::Vector;
126    /// assert_eq!(Vector::new([1.0, 2.0]).scale(3.0), Vector::new([3.0, 6.0]));
127    /// ```
128    #[inline]
129    pub fn scale(self, scalar: T) -> Self {
130        Vector::from_fn(|i| self.data[i] * scalar)
131    }
132
133    /// The dot product `Σ self[i] * rhs[i]`, summed left to right.
134    ///
135    /// ```
136    /// use multicalc::linear_algebra::Vector;
137    /// assert_eq!(Vector::new([1.0, 2.0, 3.0]).dot(Vector::new([4.0, 5.0, 6.0])), 32.0);
138    /// assert_eq!(Vector::new([1.0, 0.0]).dot(Vector::new([0.0, 1.0])), 0.0);
139    /// ```
140    #[inline]
141    #[must_use]
142    pub fn dot(self, rhs: Self) -> T {
143        let mut sum = T::ZERO;
144        for (&a, &b) in self.data.iter().zip(&rhs.data) {
145            sum += a * b;
146        }
147        sum
148    }
149
150    /// The squared Euclidean norm `self · self` (no square root).
151    ///
152    /// ```
153    /// use multicalc::linear_algebra::Vector;
154    /// assert_eq!(Vector::new([3.0, 4.0]).norm_squared(), 25.0);
155    /// ```
156    #[inline]
157    #[must_use]
158    pub fn norm_squared(self) -> T {
159        self.dot(self)
160    }
161
162    /// The Euclidean norm `√(self · self)`.
163    ///
164    /// ```
165    /// use multicalc::linear_algebra::Vector;
166    /// assert_eq!(Vector::new([3.0, 4.0]).norm(), 5.0);
167    /// ```
168    #[inline]
169    #[must_use]
170    pub fn norm(self) -> T {
171        self.norm_squared().sqrt()
172    }
173
174    /// Returns `true` when every component is neither infinite nor NaN.
175    ///
176    /// ```
177    /// use multicalc::linear_algebra::Vector;
178    /// assert!(Vector::new([1.0, -2.0]).is_finite());
179    /// assert!(!Vector::new([1.0, f64::NAN]).is_finite());
180    /// ```
181    #[inline]
182    #[must_use]
183    pub fn is_finite(self) -> bool {
184        self.data.iter().all(|x| x.is_finite())
185    }
186}
187
188impl<const N: usize, T> From<[T; N]> for Vector<N, T> {
189    #[inline]
190    fn from(data: [T; N]) -> Self {
191        Vector { data }
192    }
193}
194
195impl<const N: usize, T> Index<usize> for Vector<N, T> {
196    type Output = T;
197
198    #[inline]
199    fn index(&self, index: usize) -> &T {
200        &self.data[index]
201    }
202}
203
204impl<const N: usize, T> IndexMut<usize> for Vector<N, T> {
205    #[inline]
206    fn index_mut(&mut self, index: usize) -> &mut T {
207        &mut self.data[index]
208    }
209}
210
211impl<const N: usize, T: Numeric> Add for Vector<N, T> {
212    type Output = Self;
213
214    #[inline]
215    fn add(self, rhs: Self) -> Self {
216        Vector::from_fn(|i| self.data[i] + rhs.data[i])
217    }
218}
219
220impl<const N: usize, T: Numeric> AddAssign for Vector<N, T> {
221    #[inline]
222    fn add_assign(&mut self, rhs: Self) {
223        for (a, &b) in self.data.iter_mut().zip(&rhs.data) {
224            *a += b;
225        }
226    }
227}
228
229impl<const N: usize, T: Numeric> Sub for Vector<N, T> {
230    type Output = Self;
231
232    #[inline]
233    fn sub(self, rhs: Self) -> Self {
234        Vector::from_fn(|i| self.data[i] - rhs.data[i])
235    }
236}
237
238impl<const N: usize, T: Numeric> SubAssign for Vector<N, T> {
239    #[inline]
240    fn sub_assign(&mut self, rhs: Self) {
241        for (a, &b) in self.data.iter_mut().zip(&rhs.data) {
242            *a -= b;
243        }
244    }
245}
246
247impl<const N: usize, T: Numeric> Neg for Vector<N, T> {
248    type Output = Self;
249
250    #[inline]
251    fn neg(self) -> Self {
252        Vector::from_fn(|i| -self.data[i])
253    }
254}
255
256impl<const N: usize, T: Numeric> Mul<T> for Vector<N, T> {
257    type Output = Self;
258
259    #[inline]
260    fn mul(self, scalar: T) -> Self {
261        self.scale(scalar)
262    }
263}
264
265impl<T: Numeric> Vector<3, T> {
266    /// The cross product `self × rhs`, available only for 3-D vectors.
267    ///
268    /// ```
269    /// use multicalc::linear_algebra::Vector;
270    /// let x = Vector::new([1.0, 0.0, 0.0]);
271    /// let y = Vector::new([0.0, 1.0, 0.0]);
272    /// assert_eq!(x.cross(y), Vector::new([0.0, 0.0, 1.0]));
273    /// ```
274    #[inline]
275    pub fn cross(self, rhs: Self) -> Self {
276        let [ax, ay, az] = self.data;
277        let [bx, by, bz] = rhs.data;
278        Vector::new([ay * bz - az * by, az * bx - ax * bz, ax * by - ay * bx])
279    }
280
281    /// The scalar triple product `self · (b × c)`: the signed volume spanned by the three vectors.
282    ///
283    /// ```
284    /// use multicalc::linear_algebra::Vector;
285    /// let x = Vector::new([1.0, 0.0, 0.0]);
286    /// let y = Vector::new([0.0, 1.0, 0.0]);
287    /// let z = Vector::new([0.0, 0.0, 1.0]);
288    /// assert_eq!(x.scalar_triple(y, z), 1.0);
289    /// ```
290    #[inline]
291    #[must_use]
292    pub fn scalar_triple(self, b: Self, c: Self) -> T {
293        self.dot(b.cross(c))
294    }
295}
296
297impl<T: Numeric> Vector<2, T> {
298    /// The 2-D cross product `self[0] * rhs[1] - self[1] * rhs[0]` — the scalar z-component of the
299    /// 3-D cross, available only for 2-D vectors.
300    ///
301    /// ```
302    /// use multicalc::linear_algebra::Vector;
303    /// let x = Vector::new([1.0, 0.0]);
304    /// let y = Vector::new([0.0, 1.0]);
305    /// assert_eq!(x.cross(y), 1.0);
306    /// assert_eq!(y.cross(x), -1.0);
307    /// ```
308    #[inline]
309    #[must_use]
310    pub fn cross(self, rhs: Self) -> T {
311        self[0] * rhs[1] - self[1] * rhs[0]
312    }
313}