Skip to main content

conspire/math/tensor/rank_1/
mod.rs

1#[cfg(test)]
2mod test;
3use super::{ContractWith, Differentiate, Erase};
4use crate::math::{Current, Projection, Reference};
5
6pub(crate) mod cross;
7pub(crate) mod list;
8pub(crate) mod list_2d;
9pub(crate) mod vec;
10pub(crate) mod vec_2d;
11
12use std::{
13    array::from_fn,
14    fmt::{self, Debug, Display, Formatter},
15    iter::Sum,
16    marker::PhantomData,
17    ops::{Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign},
18};
19
20use crate::units::{UnitDiv, UnitMul};
21use crate::{
22    ABS_TOL,
23    math::{
24        matrix::vector::Vector,
25        tensor::{
26            Jacobian, Quantity, Solution, Tensor, TensorArray, rank_0::TensorRank0,
27            rank_1::list::TensorRank1List, rank_2::TensorRank2,
28        },
29        write_tensor_rank_0,
30    },
31    units::Dimensionless,
32};
33
34use crate::math::assert::FiniteDifference;
35
36/// A *d*-dimensional tensor of rank 1.
37///
38/// `D` is the dimension, `I` is the configuration.
39#[repr(transparent)]
40pub struct TensorRank1<const D: usize, I, U = Dimensionless>(
41    pub(super) [Quantity<U>; D],
42    pub(super) PhantomData<I>,
43);
44
45impl<const D: usize, I, U> Clone for TensorRank1<D, I, U> {
46    fn clone(&self) -> Self {
47        Self(self.0, PhantomData)
48    }
49}
50
51impl<const D: usize, I, U> Debug for TensorRank1<D, I, U> {
52    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
53        self.0.fmt(f)
54    }
55}
56
57impl<const D: usize, I, U> PartialEq for TensorRank1<D, I, U> {
58    fn eq(&self, other: &Self) -> bool {
59        self.0 == other.0
60    }
61}
62
63impl<const D: usize, I, U> TensorRank1<D, I, U> {
64    pub(super) fn canonical(&self) -> &TensorRank1<D, Reference, Dimensionless> {
65        unsafe { &*(self as *const Self as *const TensorRank1<D, Reference, Dimensionless>) }
66    }
67    /// Asserts that the tensor carries the given unit.
68    pub fn with_unit<V>(self) -> TensorRank1<D, I, V> {
69        relabel(self.into_canonical())
70    }
71    /// Returns the direction the tensor points in.
72    pub fn normalized(self) -> TensorRank1<D, I, Dimensionless> {
73        let norm = self.norm().value();
74        (self / norm).with_unit()
75    }
76    fn into_canonical(self) -> TensorRank1<D, Reference, Dimensionless> {
77        unsafe {
78            (&self as *const Self)
79                .cast::<TensorRank1<D, Reference, Dimensionless>>()
80                .read()
81        }
82    }
83}
84
85pub(super) fn relabel<const D: usize, I, U>(
86    tensor: TensorRank1<D, Reference, Dimensionless>,
87) -> TensorRank1<D, I, U> {
88    unsafe {
89        (&tensor as *const TensorRank1<D, Reference, Dimensionless>)
90            .cast::<TensorRank1<D, I, U>>()
91            .read()
92    }
93}
94
95impl<const D: usize, I, U> TensorRank1<D, I, U> {
96    /// Associated function for const type conversion.
97    pub const fn const_from(array: [TensorRank0; D]) -> Self {
98        let mut entries = [Quantity::new(0.0); D];
99        let mut i = 0;
100        while i < D {
101            entries[i] = Quantity::new(array[i]);
102            i += 1;
103        }
104        Self(entries, PhantomData)
105    }
106}
107
108impl<const D: usize, I, U> Default for TensorRank1<D, I, U> {
109    fn default() -> Self {
110        Self::zero()
111    }
112}
113
114impl<const D: usize, U> From<TensorRank1<D, Reference, U>> for TensorRank1<D, Current, U> {
115    fn from(tensor_rank_1: TensorRank1<D, Reference, U>) -> Self {
116        Self(tensor_rank_1.0, PhantomData)
117    }
118}
119
120impl<const D: usize, U> From<&TensorRank1<D, Reference, U>> for TensorRank1<D, Current, U> {
121    fn from(tensor_rank_1: &TensorRank1<D, Reference, U>) -> Self {
122        Self(tensor_rank_1.0, PhantomData)
123    }
124}
125
126impl<const D: usize, U> From<TensorRank1<D, Current, U>> for TensorRank1<D, Reference, U> {
127    fn from(tensor_rank_1: TensorRank1<D, Current, U>) -> Self {
128        Self(tensor_rank_1.0, PhantomData)
129    }
130}
131
132impl<const D: usize, U> From<&TensorRank1<D, Current, U>> for TensorRank1<D, Reference, U> {
133    fn from(tensor_rank_1: &TensorRank1<D, Current, U>) -> Self {
134        Self(tensor_rank_1.0, PhantomData)
135    }
136}
137
138impl<const D: usize, U> From<TensorRank1<D, Projection, U>> for TensorRank1<D, Reference, U> {
139    fn from(tensor_rank_1: TensorRank1<D, Projection, U>) -> Self {
140        Self(tensor_rank_1.0, PhantomData)
141    }
142}
143
144impl<const D: usize, U> From<&TensorRank1<D, Projection, U>> for TensorRank1<D, Reference, U> {
145    fn from(tensor_rank_1: &TensorRank1<D, Projection, U>) -> Self {
146        Self(tensor_rank_1.0, PhantomData)
147    }
148}
149
150impl<const D: usize, I, U> Display for TensorRank1<D, I, U> {
151    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
152        write!(f, "\x1B[s")?;
153        write!(f, "[")?;
154        self.iter()
155            .try_for_each(|entry| write_tensor_rank_0(f, &entry.value()))?;
156        write!(f, "\x1B[2D]")
157    }
158}
159
160impl<const D: usize, I, U> TensorRank1<D, I, U> {
161    /// Returns a raw pointer to the slice’s buffer.
162    pub const fn as_ptr(&self) -> *const TensorRank0 {
163        self.0.as_ptr().cast()
164    }
165    /// Returns an orthonormal basis whose first vector is this one's direction.
166    pub fn orthonormal_basis(&self) -> TensorRank1List<D, I, D, Dimensionless> {
167        let norm = self.norm().value();
168        assert!(
169            norm > ABS_TOL,
170            "Cannot build an orthonormal basis from the zero vector"
171        );
172        let mut basis = TensorRank1List::zero();
173        basis[0] = (self / norm).with_unit();
174        let mut filled = 1;
175        for i in 0..D {
176            if filled == D {
177                break;
178            }
179            let mut v: TensorRank1<D, I, Dimensionless> = zero();
180            v[i] = Quantity::new(1.0);
181            basis.iter().take(filled).for_each(|q| v -= q * (&v * q));
182            let v_norm = v.norm().value();
183            if v_norm > ABS_TOL {
184                basis[filled] = v / v_norm;
185                filled += 1;
186            }
187        }
188        assert!(filled == D, "Failed to construct full orthonormal basis");
189        basis
190    }
191}
192
193impl<const D: usize, I, U> FiniteDifference for TensorRank1<D, I, U> {
194    fn error_fd(&self, comparator: &Self, epsilon: TensorRank0) -> Option<(bool, usize)> {
195        let error_count = self
196            .iter()
197            .zip(comparator.iter())
198            .filter(|&(&self_i, &comparator_i)| self_i.differs(comparator_i, epsilon))
199            .count();
200        if error_count > 0 {
201            Some((true, error_count))
202        } else {
203            None
204        }
205    }
206}
207
208impl<const D: usize, I, U> Solution for TensorRank1<D, I, U> {
209    fn decrement_from(&mut self, _other: &Vector) {
210        unimplemented!()
211    }
212    fn decrement_from_chained(&mut self, _other: &mut Vector, _vector: &Vector) {
213        unimplemented!()
214    }
215}
216
217impl<const D: usize, I, U> Jacobian for TensorRank1<D, I, U> {
218    fn fill_into(&self, _vector: &mut Vector) {
219        unimplemented!()
220    }
221    fn fill_into_chained(self, _other: Vector, _vector: &mut Vector) {
222        unimplemented!()
223    }
224}
225
226impl<const D: usize, I, U> Sub<Vector> for TensorRank1<D, I, U> {
227    type Output = Self;
228    fn sub(self, _vector: Vector) -> Self::Output {
229        unimplemented!()
230    }
231}
232
233impl<const D: usize, I, U> Sub<&Vector> for TensorRank1<D, I, U> {
234    type Output = Self;
235    fn sub(self, _vector: &Vector) -> Self::Output {
236        unimplemented!()
237    }
238}
239
240impl<const D: usize, I, U> Erase for TensorRank1<D, I, U> {
241    type Erased = TensorRank1<D, Reference, Dimensionless>;
242    fn erase(&self) -> &Self::Erased {
243        self.canonical()
244    }
245}
246
247impl<const D: usize, I, U, V> Mul<Quantity<V>> for TensorRank1<D, I, U>
248where
249    U: UnitMul<V>,
250{
251    type Output = TensorRank1<D, I, <U as UnitMul<V>>::Output>;
252    fn mul(self, quantity: Quantity<V>) -> Self::Output {
253        relabel(self.into_canonical() * quantity.value())
254    }
255}
256
257impl<const D: usize, I, U, V> Mul<Quantity<V>> for &TensorRank1<D, I, U>
258where
259    U: UnitMul<V>,
260{
261    type Output = TensorRank1<D, I, <U as UnitMul<V>>::Output>;
262    fn mul(self, quantity: Quantity<V>) -> Self::Output {
263        relabel(self.canonical() * quantity.value())
264    }
265}
266
267impl<const D: usize, I, U, V> Mul<&Quantity<V>> for TensorRank1<D, I, U>
268where
269    U: UnitMul<V>,
270{
271    type Output = TensorRank1<D, I, <U as UnitMul<V>>::Output>;
272    fn mul(self, quantity: &Quantity<V>) -> Self::Output {
273        self * *quantity
274    }
275}
276
277impl<const D: usize, I, U, V> Mul<&Quantity<V>> for &TensorRank1<D, I, U>
278where
279    U: UnitMul<V>,
280{
281    type Output = TensorRank1<D, I, <U as UnitMul<V>>::Output>;
282    fn mul(self, quantity: &Quantity<V>) -> Self::Output {
283        self * *quantity
284    }
285}
286
287impl<const D: usize, I, U, V> Div<Quantity<V>> for TensorRank1<D, I, U>
288where
289    U: UnitDiv<V>,
290{
291    type Output = TensorRank1<D, I, <U as UnitDiv<V>>::Output>;
292    fn div(self, quantity: Quantity<V>) -> Self::Output {
293        relabel(self.into_canonical() / quantity.value())
294    }
295}
296
297impl<const D: usize, I, U, V> Div<Quantity<V>> for &TensorRank1<D, I, U>
298where
299    U: UnitDiv<V>,
300{
301    type Output = TensorRank1<D, I, <U as UnitDiv<V>>::Output>;
302    fn div(self, quantity: Quantity<V>) -> Self::Output {
303        relabel(self.canonical() / quantity.value())
304    }
305}
306
307impl<const D: usize, I, U> Tensor for TensorRank1<D, I, U> {
308    type Item = Quantity<U>;
309    type Unit = U;
310    fn full_contraction(&self, tensor_rank_1: &Self) -> TensorRank0 {
311        self.iter()
312            .zip(tensor_rank_1.iter())
313            .map(|(self_i, other_i)| self_i.value() * other_i.value())
314            .sum()
315    }
316    fn iter(&self) -> impl Iterator<Item = &Self::Item> {
317        self.0.iter()
318    }
319    fn iter_mut(&mut self) -> impl Iterator<Item = &mut Self::Item> {
320        self.0.iter_mut()
321    }
322    fn len(&self) -> usize {
323        D
324    }
325    fn size(&self) -> usize {
326        D
327    }
328}
329
330impl<const D: usize, I, U> IntoIterator for TensorRank1<D, I, U> {
331    type Item = Quantity<U>;
332    type IntoIter = std::array::IntoIter<Self::Item, D>;
333    fn into_iter(self) -> Self::IntoIter {
334        self.0.into_iter()
335    }
336}
337
338impl<const D: usize, I, U> TensorArray for TensorRank1<D, I, U> {
339    type Array = [Quantity<U>; D];
340    type Item = Quantity<U>;
341    fn as_array(&self) -> Self::Array {
342        self.0
343    }
344    fn identity() -> Self {
345        ones()
346    }
347    fn zero() -> Self {
348        zero()
349    }
350}
351
352/// Returns the rank-1 tensor of ones as a constant.
353pub(crate) const fn ones<const D: usize, I, U>() -> TensorRank1<D, I, U> {
354    TensorRank1([Quantity::new(1.0); D], PhantomData)
355}
356
357/// Returns the rank-1 zero tensor as a constant.
358pub const fn zero<const D: usize, I, U>() -> TensorRank1<D, I, U> {
359    TensorRank1([Quantity::new(0.0); D], PhantomData)
360}
361
362impl<const D: usize, I, U> From<[Quantity<U>; D]> for TensorRank1<D, I, U> {
363    fn from(array: [Quantity<U>; D]) -> Self {
364        Self(array, PhantomData)
365    }
366}
367
368impl<const D: usize, I, U> From<[TensorRank0; D]> for TensorRank1<D, I, U> {
369    fn from(array: [TensorRank0; D]) -> Self {
370        Self(array.map(Quantity::new), PhantomData)
371    }
372}
373
374impl<const D: usize, I, U> From<TensorRank1<D, I, U>> for [TensorRank0; D] {
375    fn from(tensor_rank_1: TensorRank1<D, I, U>) -> Self {
376        tensor_rank_1.0.map(|entry| entry.value())
377    }
378}
379
380impl<const D: usize, I, U> From<Vec<TensorRank0>> for TensorRank1<D, I, U> {
381    fn from(vec: Vec<TensorRank0>) -> Self {
382        Self(
383            TryInto::<[TensorRank0; D]>::try_into(vec)
384                .unwrap()
385                .map(Quantity::new),
386            PhantomData,
387        )
388    }
389}
390
391impl<const D: usize, I, U> From<TensorRank1<D, I, U>> for Vec<TensorRank0> {
392    fn from(tensor_rank_1: TensorRank1<D, I, U>) -> Self {
393        tensor_rank_1.0.iter().map(|entry| entry.value()).collect()
394    }
395}
396
397impl<const D: usize, I, U> From<Vector> for TensorRank1<D, I, U> {
398    fn from(_vector: Vector) -> Self {
399        unimplemented!()
400    }
401}
402
403impl<const D: usize, I, U> FromIterator<TensorRank0> for TensorRank1<D, I, U> {
404    fn from_iter<Ii: IntoIterator<Item = TensorRank0>>(into_iterator: Ii) -> Self {
405        into_iterator.into_iter().map(Quantity::new).collect()
406    }
407}
408
409impl<const D: usize, I, U> FromIterator<Quantity<U>> for TensorRank1<D, I, U> {
410    fn from_iter<Ii: IntoIterator<Item = Quantity<U>>>(into_iterator: Ii) -> Self {
411        let mut tensor_rank_1 = zero();
412        tensor_rank_1
413            .iter_mut()
414            .zip(into_iterator)
415            .for_each(|(tensor_rank_1_i, value_i)| *tensor_rank_1_i = value_i);
416        tensor_rank_1
417    }
418}
419
420impl<const D: usize, I, U> Index<usize> for TensorRank1<D, I, U> {
421    type Output = Quantity<U>;
422    fn index(&self, index: usize) -> &Self::Output {
423        &self.0[index]
424    }
425}
426
427impl<const D: usize, I, U> IndexMut<usize> for TensorRank1<D, I, U> {
428    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
429        &mut self.0[index]
430    }
431}
432
433impl<const D: usize, I, U> Sum for TensorRank1<D, I, U> {
434    fn sum<Ii>(iter: Ii) -> Self
435    where
436        Ii: Iterator<Item = Self>,
437    {
438        iter.reduce(|mut acc, item| {
439            acc += item;
440            acc
441        })
442        .unwrap_or_else(Self::default)
443    }
444}
445
446impl<'a, const D: usize, I, U> Sum<&'a Self> for TensorRank1<D, I, U> {
447    fn sum<Ii>(iter: Ii) -> Self
448    where
449        Ii: Iterator<Item = &'a Self>,
450    {
451        iter.fold(Self::default(), |mut acc, item| {
452            acc += item;
453            acc
454        })
455    }
456}
457
458impl<const D: usize, I, U> Neg for TensorRank1<D, I, U> {
459    type Output = Self;
460    fn neg(self) -> Self::Output {
461        from_fn(|i| -self[i]).into()
462    }
463}
464
465impl<const D: usize, I, U> Neg for &TensorRank1<D, I, U> {
466    type Output = TensorRank1<D, I, U>;
467    fn neg(self) -> Self::Output {
468        from_fn(|i| -self[i]).into()
469    }
470}
471
472impl<const D: usize, I, U> Div<TensorRank0> for TensorRank1<D, I, U> {
473    type Output = Self;
474    fn div(mut self, tensor_rank_0: TensorRank0) -> Self::Output {
475        self /= tensor_rank_0;
476        self
477    }
478}
479
480impl<const D: usize, I, U> Div<TensorRank0> for &TensorRank1<D, I, U> {
481    type Output = TensorRank1<D, I, U>;
482    fn div(self, tensor_rank_0: TensorRank0) -> Self::Output {
483        self.iter().map(|self_i| self_i / tensor_rank_0).collect()
484    }
485}
486
487impl<const D: usize, I, U> Div<&TensorRank0> for TensorRank1<D, I, U> {
488    type Output = Self;
489    fn div(mut self, tensor_rank_0: &TensorRank0) -> Self::Output {
490        self /= tensor_rank_0;
491        self
492    }
493}
494
495impl<const D: usize, I, U> Div<&TensorRank0> for &TensorRank1<D, I, U> {
496    type Output = TensorRank1<D, I, U>;
497    fn div(self, tensor_rank_0: &TensorRank0) -> Self::Output {
498        self.iter().map(|self_i| self_i / tensor_rank_0).collect()
499    }
500}
501
502impl<const D: usize, I, U> DivAssign<TensorRank0> for TensorRank1<D, I, U> {
503    fn div_assign(&mut self, tensor_rank_0: TensorRank0) {
504        self.iter_mut().for_each(|self_i| *self_i /= &tensor_rank_0);
505    }
506}
507
508impl<const D: usize, I, U> DivAssign<&TensorRank0> for TensorRank1<D, I, U> {
509    fn div_assign(&mut self, tensor_rank_0: &TensorRank0) {
510        self.iter_mut().for_each(|self_i| *self_i /= tensor_rank_0);
511    }
512}
513
514impl<const D: usize, I, U> Mul<TensorRank0> for TensorRank1<D, I, U> {
515    type Output = Self;
516    fn mul(mut self, tensor_rank_0: TensorRank0) -> Self::Output {
517        self *= tensor_rank_0;
518        self
519    }
520}
521
522impl<const D: usize, I, U> Mul<TensorRank0> for &TensorRank1<D, I, U> {
523    type Output = TensorRank1<D, I, U>;
524    fn mul(self, tensor_rank_0: TensorRank0) -> Self::Output {
525        self.iter().map(|self_i| self_i * tensor_rank_0).collect()
526    }
527}
528
529impl<const D: usize, I, U> Mul<&TensorRank0> for TensorRank1<D, I, U> {
530    type Output = Self;
531    fn mul(mut self, tensor_rank_0: &TensorRank0) -> Self::Output {
532        self *= tensor_rank_0;
533        self
534    }
535}
536
537impl<const D: usize, I, U> Mul<&TensorRank0> for &TensorRank1<D, I, U> {
538    type Output = TensorRank1<D, I, U>;
539    fn mul(self, tensor_rank_0: &TensorRank0) -> Self::Output {
540        self.iter().map(|self_i| self_i * tensor_rank_0).collect()
541    }
542}
543
544impl<const D: usize, I, U> MulAssign<TensorRank0> for TensorRank1<D, I, U> {
545    fn mul_assign(&mut self, tensor_rank_0: TensorRank0) {
546        self.iter_mut().for_each(|self_i| *self_i *= &tensor_rank_0);
547    }
548}
549
550impl<const D: usize, I, U> MulAssign<&TensorRank0> for TensorRank1<D, I, U> {
551    fn mul_assign(&mut self, tensor_rank_0: &TensorRank0) {
552        self.iter_mut().for_each(|self_i| *self_i *= tensor_rank_0);
553    }
554}
555
556impl<const D: usize, I, U> Add for TensorRank1<D, I, U> {
557    type Output = Self;
558    fn add(mut self, tensor_rank_1: Self) -> Self::Output {
559        self += tensor_rank_1;
560        self
561    }
562}
563
564impl<const D: usize, I, U> Add<&Self> for TensorRank1<D, I, U> {
565    type Output = Self;
566    fn add(mut self, tensor_rank_1: &Self) -> Self::Output {
567        self += tensor_rank_1;
568        self
569    }
570}
571
572impl<const D: usize, I, U> Add<TensorRank1<D, I, U>> for &TensorRank1<D, I, U> {
573    type Output = TensorRank1<D, I, U>;
574    fn add(self, mut tensor_rank_1: TensorRank1<D, I, U>) -> Self::Output {
575        tensor_rank_1 += self;
576        tensor_rank_1
577    }
578}
579
580impl<const D: usize, I, U> Add<Self> for &TensorRank1<D, I, U> {
581    type Output = TensorRank1<D, I, U>;
582    fn add(self, tensor_rank_1: Self) -> Self::Output {
583        tensor_rank_1
584            .iter()
585            .zip(self.iter())
586            .map(|(tensor_rank_1_i, self_i)| self_i + *tensor_rank_1_i)
587            .collect()
588    }
589}
590
591impl<const D: usize, I, U> AddAssign for TensorRank1<D, I, U> {
592    fn add_assign(&mut self, tensor_rank_1: Self) {
593        self.iter_mut()
594            .zip(tensor_rank_1)
595            .for_each(|(self_i, tensor_rank_1_i)| *self_i += tensor_rank_1_i);
596    }
597}
598
599impl<const D: usize, I, U> AddAssign<&Self> for TensorRank1<D, I, U> {
600    fn add_assign(&mut self, tensor_rank_1: &Self) {
601        self.iter_mut()
602            .zip(tensor_rank_1.iter())
603            .for_each(|(self_i, tensor_rank_1_i)| *self_i += tensor_rank_1_i);
604    }
605}
606
607impl<const D: usize, I, U> Sub for TensorRank1<D, I, U> {
608    type Output = Self;
609    fn sub(mut self, tensor_rank_1: Self) -> Self::Output {
610        self -= tensor_rank_1;
611        self
612    }
613}
614
615impl<const D: usize, I, U> Sub<&Self> for TensorRank1<D, I, U> {
616    type Output = Self;
617    fn sub(mut self, tensor_rank_1: &Self) -> Self::Output {
618        self -= tensor_rank_1;
619        self
620    }
621}
622
623impl<const D: usize, I, U> Sub<TensorRank1<D, I, U>> for &TensorRank1<D, I, U> {
624    type Output = TensorRank1<D, I, U>;
625    fn sub(self, mut tensor_rank_1: TensorRank1<D, I, U>) -> Self::Output {
626        tensor_rank_1
627            .iter_mut()
628            .zip(self.iter())
629            .for_each(|(tensor_rank_1_i, self_i)| *tensor_rank_1_i = self_i - *tensor_rank_1_i);
630        tensor_rank_1
631    }
632}
633
634impl<const D: usize, I, U> Sub<Self> for &TensorRank1<D, I, U> {
635    type Output = TensorRank1<D, I, U>;
636    fn sub(self, tensor_rank_1: Self) -> Self::Output {
637        tensor_rank_1
638            .iter()
639            .zip(self.iter())
640            .map(|(tensor_rank_1_i, self_i)| self_i - *tensor_rank_1_i)
641            .collect()
642    }
643}
644
645impl<const D: usize, I, U> SubAssign for TensorRank1<D, I, U> {
646    fn sub_assign(&mut self, tensor_rank_1: Self) {
647        self.iter_mut()
648            .zip(tensor_rank_1)
649            .for_each(|(self_i, tensor_rank_1_i)| *self_i -= tensor_rank_1_i);
650    }
651}
652
653impl<const D: usize, I, U> SubAssign<&Self> for TensorRank1<D, I, U> {
654    fn sub_assign(&mut self, tensor_rank_1: &Self) {
655        self.iter_mut()
656            .zip(tensor_rank_1.iter())
657            .for_each(|(self_i, tensor_rank_1_i)| *self_i -= tensor_rank_1_i);
658    }
659}
660
661impl<const D: usize, I, U> Mul for TensorRank1<D, I, U> {
662    type Output = TensorRank0;
663    fn mul(self, tensor_rank_1: Self) -> Self::Output {
664        self.into_iter()
665            .zip(tensor_rank_1)
666            .map(|(self_i, tensor_rank_1_i)| self_i.value() * tensor_rank_1_i.value())
667            .sum()
668    }
669}
670
671impl<const D: usize, I, U, V> Mul<&TensorRank1<D, I, V>> for TensorRank1<D, I, U>
672where
673    U: UnitMul<V>,
674{
675    type Output = Quantity<<U as UnitMul<V>>::Output>;
676    fn mul(self, tensor_rank_1: &TensorRank1<D, I, V>) -> Self::Output {
677        Quantity::new(
678            self.into_iter()
679                .zip(tensor_rank_1.iter())
680                .map(|(self_i, tensor_rank_1_i)| self_i.value() * tensor_rank_1_i.value())
681                .sum(),
682        )
683    }
684}
685
686impl<const D: usize, I, U, V> Mul<TensorRank1<D, I, V>> for &TensorRank1<D, I, U>
687where
688    U: UnitMul<V>,
689{
690    type Output = Quantity<<U as UnitMul<V>>::Output>;
691    fn mul(self, tensor_rank_1: TensorRank1<D, I, V>) -> Self::Output {
692        Quantity::new(
693            self.iter()
694                .zip(tensor_rank_1)
695                .map(|(self_i, tensor_rank_1_i)| self_i.value() * tensor_rank_1_i.value())
696                .sum(),
697        )
698    }
699}
700
701impl<const D: usize, I, U, V> Mul<&TensorRank1<D, I, V>> for &TensorRank1<D, I, U>
702where
703    U: UnitMul<V>,
704{
705    type Output = Quantity<<U as UnitMul<V>>::Output>;
706    fn mul(self, tensor_rank_1: &TensorRank1<D, I, V>) -> Self::Output {
707        Quantity::new(
708            self.iter()
709                .zip(tensor_rank_1.iter())
710                .map(|(self_i, tensor_rank_1_i)| self_i.value() * tensor_rank_1_i.value())
711                .sum(),
712        )
713    }
714}
715
716#[allow(clippy::suspicious_arithmetic_impl)]
717impl<const D: usize, I, J, U, V> Div<TensorRank2<D, I, J, V>> for &TensorRank1<D, I, U>
718where
719    U: UnitDiv<V>,
720{
721    type Output = TensorRank1<D, J, <U as UnitDiv<V>>::Output>;
722    fn div(self, tensor_rank_2: TensorRank2<D, I, J, V>) -> Self::Output {
723        relabel(tensor_rank_2.canonical().clone().inverse() * self.canonical())
724    }
725}
726
727impl<const D: usize, I, U, V> ContractWith<TensorRank1<D, I, V>> for TensorRank1<D, I, U>
728where
729    U: UnitMul<V>,
730{
731    type Output = Quantity<<U as UnitMul<V>>::Output>;
732    fn contract_with(&self, tensor_rank_1: &TensorRank1<D, I, V>) -> Self::Output {
733        Quantity::new(self.canonical().full_contraction(tensor_rank_1.canonical()))
734    }
735}
736
737impl<const D: usize, I, U, T> Differentiate<T> for TensorRank1<D, I, U>
738where
739    U: UnitDiv<T>,
740{
741    type Derivative = TensorRank1<D, I, <U as UnitDiv<T>>::Output>;
742}