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}