static_math/
matrix2x2.rs

1//-------------------------------------------------------------------------
2// @file matrix2x2.rs
3//
4// @date 06/01/20 22:14:25
5// @author Martin Noblia
6// @email mnoblia@disroot.org
7//
8// @brief
9//
10// @detail
11//
12// Licence MIT:
13// Copyright <2020> <Martin Noblia>
14//
15// Permission is hereby granted, free of charge, to any person obtaining a copy
16// of this software and associated documentation files (the "Software"), to deal
17// in the Software without restriction, including without limitation the rights
18// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
19// copies of the Software, and to permit persons to whom the Software is
20// furnished to do so, subject to the following conditions:
21//
22// The above copyright notice and this permission notice shall be included in
23// all copies or substantial portions of the Software.  THE SOFTWARE IS PROVIDED
24// "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
25// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
26// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
27// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
28// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30//-------------------------------------------------------------------------
31// imports
32#![macro_use]
33use core::fmt;
34use core::ops::{Add, AddAssign, Div, Mul, Neg, Sub, SubAssign};
35use core::ops::{Deref, DerefMut, Index, IndexMut};
36
37use crate::slices_methods::*;
38use crate::traits::LinearAlgebra;
39use crate::utils::nearly_zero;
40use crate::vector2::*;
41use num::{Float, Num, One, Signed, Zero};
42
43/// A static Matrix of 2x2 shape
44#[derive(Copy, Clone, Debug, PartialEq)]
45pub struct M22<T>([[T; 2]; 2]);
46
47impl<T> M22<T> {
48    #[inline(always)]
49    pub const fn new(data_input: [[T; 2]; 2]) -> Self {
50        Self(data_input)
51    }
52
53    #[inline(always)]
54    pub const fn new_from(a: T, b: T, c: T, d: T) -> Self {
55        Self::new([[a, b], [c, d]])
56    }
57
58    #[inline(always)]
59    pub const fn rows(&self) -> usize {
60        self.0.len()
61    }
62
63    #[inline(always)]
64    pub const fn cols(&self) -> usize {
65        self.rows()
66    }
67}
68
69impl<T: Float + core::iter::Sum> LinearAlgebra<T> for M22<T> {
70    fn rows(&self) -> usize {
71        self.0.len()
72    }
73
74    fn cols(&self) -> usize {
75        self.rows()
76    }
77
78    #[inline(always)]
79    fn det(&self) -> T {
80        (self[(0, 0)] * self[(1, 1)]) - (self[(1, 0)] * self[(0, 1)])
81    }
82
83    #[inline(always)]
84    fn transpose(&self) -> M22<T> {
85        M22::new([[self[(0, 0)], self[(1, 0)]], [self[(0, 1)], self[(1, 1)]]])
86    }
87
88    #[inline(always)]
89    fn trace(&self) -> T {
90        self[(0, 0)] + self[(1, 1)]
91    }
92
93    #[inline(always)]
94    fn norm2(&self) -> T {
95        T::sqrt(self[(0, 0)] * self[(0, 0)] + self[(0, 1)] * self[(0, 1)] +
96                self[(1, 0)] * self[(1, 0)] + self[(1, 1)] * self[(1, 1)])
97    }
98
99    #[inline(always)]
100    fn inverse(&self) -> Option<Self> {
101        let det = self.det();
102        if !nearly_zero(det) {
103            let det_recip = det.recip();
104            Some(M22::new([[self[(1, 1)] * det_recip, -self[(0, 1)] * det_recip],
105                          [-self[(1, 0)] * det_recip, self[(0, 0)] * det_recip]]))
106        } else {
107            None
108        }
109    }
110
111    /// Calculate de QR factorization of the M22 via gram-schmidt
112    /// orthogonalization process
113    fn qr(&self) -> Option<(Self, Self)> {
114        if !nearly_zero(self.det()) {
115            let cols = self.get_cols();
116            let mut q: [V2<T>; 2] = *M22::zeros().get_cols();
117            for i in 0..q.len() {
118                let mut q_tilde = cols[i];
119                for elem in q.iter().take(i) {
120                    q_tilde -= *elem * project_x_over_y(&*cols[i], &**elem);
121                }
122                normalize(&mut *q_tilde);
123                q[i] = q_tilde;
124            }
125            let basis = V2::new_from(q[0], q[1]);
126            let q = M22::new_from_vecs(basis);
127            let r = q.transpose() * (*self);
128            Some((q, r))
129        } else {
130            None
131        }
132    }
133}
134
135impl<T: Num + Copy> M22<T> {
136    /// contruct identity matrix
137    pub fn identity() -> M22<T> {
138        <M22<T> as One>::one()
139    }
140
141    /// construct the matrix with all zeros
142    pub fn zeros() -> M22<T> {
143        <M22<T> as Zero>::zero()
144    }
145
146    /// transform the matrix to a flatten vector
147    pub fn as_vec(&self) -> [T; 4] {
148        let mut result = [T::zero(); 4];
149        for (index, element) in self.iter().flatten().enumerate() {
150            result[index] = *element;
151        }
152        result
153    }
154
155    /// construct the matrix from columns-vectors
156    pub fn new_from_vecs(cols: V2<V2<T>>) -> Self {
157        let mut result = Self::zeros();
158
159        for i in 0..result.cols() {
160            result[(i, 0)] = cols[0][i];
161            result[(i, 1)] = cols[1][i];
162        }
163        result
164    }
165
166    /// get the diagonal of the matrix
167    pub fn get_diagonal(&self) -> V2<T> {
168        let mut result = V2::zeros();
169        let mut index: usize = 0;
170        for i in 0..self.rows() {
171            for j in 0..self.cols() {
172                if i == j {
173                    result[index] = self[(i, j)];
174                    index += 1;
175                }
176            }
177        }
178        result
179    }
180}
181
182// M22 * V2
183impl<T: Num + Copy> Mul<V2<T>> for M22<T> {
184    type Output = V2<T>;
185
186    #[inline(always)]
187    fn mul(self, rhs: V2<T>) -> V2<T> {
188        V2::new_from(self[(0, 0)] * rhs[0] + self[(0, 1)] * rhs[1],
189                     self[(1, 0)] * rhs[0] + self[(1, 1)] * rhs[1])
190    }
191}
192
193// M22 + M22
194impl<T: Num + Copy> Add for M22<T> {
195    type Output = Self;
196
197    #[inline(always)]
198    fn add(self, rhs: Self) -> Self {
199        Self::new([[self[(0, 0)] + rhs[(0, 0)], self[(0, 1)] + rhs[(0, 1)]],
200                   [self[(1, 0)] + rhs[(1, 0)], self[(1, 1)] + rhs[(1, 1)]]])
201    }
202}
203
204// M22 += M22
205impl<T: Num + Copy> AddAssign for M22<T> {
206    #[inline(always)]
207    fn add_assign(&mut self, other: Self) {
208        *self = *self + other
209    }
210}
211
212// M22 - M22
213impl<T: Num + Copy> Sub for M22<T> {
214    type Output = Self;
215
216    #[inline(always)]
217    fn sub(self, rhs: Self) -> Self {
218        Self::new([[self[(0, 0)] - rhs[(0, 0)], self[(0, 1)] - rhs[(0, 1)]],
219                   [self[(1, 0)] - rhs[(1, 0)], self[(1, 1)] - rhs[(1, 1)]]])
220    }
221}
222
223// M22 -= M22
224impl<T: Num + Copy> SubAssign for M22<T> {
225    #[inline]
226    fn sub_assign(&mut self, other: Self) {
227        *self = *self - other
228    }
229}
230
231impl<T: Num + Copy> M22<T> {
232    /// get the rows of the matrix as a vectors
233    pub fn get_rows(self) -> V2<V2<T>> {
234        let mut r0 = V2::zeros();
235        let mut r1 = V2::zeros();
236
237        for j in 0..self.rows() {
238            r0[j] = self[(0, j)];
239            r1[j] = self[(1, j)]
240        }
241
242        V2::new([r0, r1])
243    }
244
245    /// get the columns of the matrix as a vectors
246    pub fn get_cols(self) -> V2<V2<T>> {
247        let mut c0 = V2::zeros();
248        let mut c1 = V2::zeros();
249
250        for i in 0..self.cols() {
251            c0[i] = self[(i, 0)];
252            c1[i] = self[(i, 1)]
253        }
254
255        V2::new([c0, c1])
256    }
257
258    /// Applies `f` of each element in the M22
259    pub fn for_each(&self, f: impl Fn(T) -> T) -> Self {
260        let mut result = Self::zeros();
261        for i in 0..self.rows() {
262            for j in 0..self.cols() {
263                result[(i, j)] = f(self[(i, j)]);
264            }
265        }
266        result
267    }
268}
269
270// NOTE(elsuizo:2020-06-10): maybe an error here is better
271impl<T: Float + core::iter::Sum> M22<T> {
272    /// calculate the real eigen values for the matrix
273    pub fn real_eigenvals(&self) -> Option<V2<T>> {
274        let tau = self.trace();
275        let delta = self.det();
276        let tau_2 = tau * tau;
277        let four = T::from(4)?;
278        let discr = tau_2 - four * delta;
279        if discr < T::zero() {
280            None
281        } else {
282            let two = T::from(2)?;
283            let lambda2 = (tau - T::sqrt(discr)) / two;
284            let lambda1 = (tau + T::sqrt(discr)) / two;
285            Some(V2::new([lambda1, lambda2]))
286        }
287    }
288}
289
290// FIXME(elsuizo:2020-06-19): this is a hack
291// f32 * M22<f32>
292impl Mul<M22<f32>> for f32 {
293    type Output = M22<f32>;
294
295    #[inline]
296    fn mul(self, rhs: M22<f32>) -> M22<f32> {
297        M22::new([[rhs[(0, 0)] * self, rhs[(0, 1)] * self],
298                  [rhs[(1, 0)] * self, rhs[(1, 1)] * self]])
299    }
300}
301
302// M22 * constant
303impl<T: Num + Copy> Mul<T> for M22<T> {
304    type Output = M22<T>;
305
306    #[inline(always)]
307    fn mul(self, rhs: T) -> M22<T> {
308        Self::new([[self[(0, 0)] * rhs, self[(0, 1)] * rhs],
309                   [self[(1, 0)] * rhs, self[(1, 1)] * rhs]])
310    }
311}
312
313// M22 / constant
314impl<T: Num + Copy> Div<T> for M22<T> {
315    type Output = Self;
316
317    fn div(self, rhs: T) -> Self::Output {
318        Self::new([[self[(0, 0)] / rhs, self[(0, 1)] / rhs],
319                   [self[(1, 0)] / rhs, self[(1, 1)] / rhs]])
320    }
321}
322
323// M22 * M22
324impl<T: Num + Copy> Mul for M22<T> {
325    type Output = Self;
326
327    #[inline(always)]
328    fn mul(self, rhs: Self) -> Self {
329        let a1 = self[(0, 0)];
330        let b1 = self[(0, 1)];
331        let c1 = self[(1, 0)];
332        let d1 = self[(1, 1)];
333
334        let a2 = rhs[(0, 0)];
335        let b2 = rhs[(0, 1)];
336        let c2 = rhs[(1, 0)];
337        let d2 = rhs[(1, 1)];
338
339        Self::new([[a1 * a2 + b1 * c2, a1 * b2 + b1 * d2],
340                   [c1 * a2 + d1 * c2, c1 * b2 + d1 * d2]])
341    }
342}
343
344// -M22
345impl<T: Num + Copy + Signed> Neg for M22<T> {
346    type Output = Self;
347
348    #[inline(always)]
349    fn neg(self) -> Self {
350        Self::new([[-self[(0, 0)], -self[(0, 1)]],
351                   [-self[(1, 0)], -self[(1, 1)]]])
352    }
353}
354
355impl<T: Num + Copy> Zero for M22<T> {
356    #[inline(always)]
357    fn zero() -> M22<T> {
358        M22::new([[T::zero(); 2]; 2])
359    }
360
361    fn is_zero(&self) -> bool {
362        *self == M22::zero()
363    }
364}
365
366impl<T: Num + Copy> One for M22<T> {
367    /// Create an identity matrix
368    fn one() -> M22<T> {
369        let one = T::one();
370        let zero = T::zero();
371        M22::new([[one, zero], [zero, one]])
372    }
373}
374
375impl<T> Deref for M22<T> {
376    type Target = [[T; 2]; 2];
377    #[inline]
378    fn deref(&self) -> &Self::Target {
379        &self.0
380    }
381}
382
383impl<T> DerefMut for M22<T> {
384    #[inline]
385    fn deref_mut(&mut self) -> &mut Self::Target {
386        &mut self.0
387    }
388}
389
390impl<T> From<[[T; 2]; 2]> for M22<T> {
391    fn from(data: [[T; 2]; 2]) -> M22<T> {
392        M22(data)
393    }
394}
395
396impl<T> Index<(usize, usize)> for M22<T> {
397    type Output = T;
398    #[inline(always)]
399    fn index(&self, index: (usize, usize)) -> &T {
400        &self.0[index.0][index.1]
401    }
402}
403
404impl<T> IndexMut<(usize, usize)> for M22<T> {
405    #[inline(always)]
406    fn index_mut(&mut self, index: (usize, usize)) -> &mut T {
407        &mut self.0[index.0][index.1]
408    }
409}
410
411//-------------------------------------------------------------------------
412//                        macros
413//-------------------------------------------------------------------------
414#[macro_export]
415macro_rules! m22_new {
416    ($($first_row:expr),* ; $($second_row:expr),*) => {
417        M22::new([[$($first_row),*], [$($second_row),*]])
418    }
419}
420
421//-------------------------------------------------------------------------
422//                        Display for M22
423//-------------------------------------------------------------------------
424impl<T: Num + fmt::Display> fmt::Display for M22<T> {
425    fn fmt(&self, dest: &mut fmt::Formatter) -> fmt::Result {
426        println!();
427        writeln!(dest, "|{0:^3.2} {1:^3.2}|", self[(0, 0)], self[(0, 1)])?;
428        writeln!(dest, "|{0:^3.2} {1:^3.2}|", self[(1, 0)], self[(1, 1)])
429    }
430}
431
432//-------------------------------------------------------------------------
433//                        constants
434//-------------------------------------------------------------------------
435// TODO(elsuizo): no se si me sirve esto pero lo podriamos dejar
436pub const M22_ZEROS: M22<f32> = M22::new([[0.0, 0.0], [0.0, 0.0]]);
437pub const M22_IDENT: M22<f32> = M22::new([[1.0, 0.0], [0.0, 1.0]]);
438//-------------------------------------------------------------------------
439//                        testing
440//-------------------------------------------------------------------------
441
442#[cfg(test)]
443mod test_matrix2x2 {
444    use crate::matrix2x2::M22;
445    use crate::traits::LinearAlgebra;
446    use crate::utils::{compare_vecs, nearly_equal};
447    use crate::vector2::V2;
448
449    const EPS: f32 = 1e-7;
450
451    #[test]
452    fn create_m22_floats() {
453        let matrix = M22::new([[0.0, 1.0], [2.0, 3.0]]);
454        assert_eq!(matrix[(0, 0)], 0.0);
455        assert_eq!(matrix[(0, 1)], 1.0);
456        assert_eq!(matrix[(1, 0)], 2.0);
457        assert_eq!(matrix[(1, 1)], 3.0);
458    }
459
460    #[test]
461    fn create_m22_test() {
462        let m = m22_new!(0.0, 1.0;
463                         2.0, 3.0);
464
465        assert_eq!(m[(0, 0)], 0.0);
466        assert_eq!(m[(0, 1)], 1.0);
467        assert_eq!(m[(1, 0)], 2.0);
468        assert_eq!(m[(1, 1)], 3.0);
469    }
470
471    #[test]
472    fn create_m22_ints() {
473        let m = M22::new([[0, 1], [2, 3]]);
474        assert_eq!(m[(0, 0)], 0);
475        assert_eq!(m[(0, 1)], 1);
476        assert_eq!(m[(1, 0)], 2);
477        assert_eq!(m[(1, 1)], 3);
478    }
479
480    #[test]
481    fn create_identity_floats() {
482        let expected = M22::new([[1.0, 0.0], [0.0, 1.0]]);
483        let result: M22<f64> = M22::identity();
484        assert_eq!(result.as_vec(), expected.as_vec());
485    }
486
487    #[test]
488    fn create_identity_ints() {
489        let expected = M22::new([[1, 0], [0, 1]]);
490        let result: M22<i32> = M22::identity();
491        assert_eq!(result.as_vec(), expected.as_vec());
492    }
493
494    #[test]
495    fn add_m22_floats() {
496        let m1 = M22::new([[1.0, 2.0], [3.0, 4.0]]);
497        let m2 = M22::new([[5.0, 6.0], [7.0, 8.0]]);
498        let expected = M22::new([[6.0, 8.0], [10.0, 12.0]]);
499        let result = m1 + m2;
500        assert_eq!(result.as_vec(), expected.as_vec());
501    }
502
503    #[test]
504    fn sub_test() {
505        let m1 = m22_new!(1.0, 2.0;
506                          3.0, 4.0);
507        let m2 = m22_new!(5.0, 6.0;
508                          7.0, 8.0);
509        let expected = m22_new!( -4.0,  -4.0;
510                                 -4.0,  -4.0);
511        let result = m1 - m2;
512        assert_eq!(result.as_vec(), expected.as_vec());
513    }
514
515    #[test]
516    fn add_m22_ints() {
517        let m1 = M22::new([[1, 2], [3, 4]]);
518        let m2 = M22::new([[5, 6], [7, 8]]);
519        let expected = M22::new([[6, 8], [10, 12]]);
520        let result = m1 + m2;
521        assert_eq!(result.as_vec(), expected.as_vec());
522    }
523
524    #[test]
525    fn test_determinant() {
526        let m1 = M22::new([[1.0, 2.0], [1.0, 2.0]]);
527        let result = m1.det();
528        let expected = 0.0;
529        assert_eq!(result, expected);
530    }
531
532    #[test]
533    fn product_with_vector2_rhs_test() {
534        let m1 = M22::new([[1.0, 2.0], [3.0, 4.0]]);
535        let v = V2::new([1.0, 2.0]);
536
537        let result = m1 * v;
538        let expected = V2::new([5.0, 11.0]);
539        assert_eq!(
540            &result[..],
541            &expected[..],
542            "\nExpected\n{:?}\nfound\n{:?}",
543            &result[..],
544            &expected[..]
545        );
546    }
547
548    #[test]
549    fn product_with_matrix2x2_rhs_test() {
550        let v = V2::new([1.0, 2.0]);
551        let m1 = M22::new([[1.0, 2.0], [3.0, 4.0]]);
552        let result = v * m1;
553        let expected = V2::new([7.0, 10.0]);
554        assert_eq!(
555            &result[..],
556            &expected[..],
557            "\nExpected\n{:?}\nfound\n{:?}",
558            &result[..],
559            &expected[..]
560        );
561    }
562
563    #[test]
564    fn inverse_test() {
565        // NOTE(elsuizo:2020-06-02): no se si conviene asi o poner el numero
566        // directamente
567        use super::test_matrix2x2::EPS;
568        let m1 = M22::new([[1.0, 2.0], [3.0, 4.0]]);
569        let expected = M22::new([[-2.0, 1.0], [1.5, -0.5]]);
570        if let Some(result) = m1.inverse() {
571            assert!(compare_vecs(&result.as_vec(), &expected.as_vec(), EPS));
572        }
573    }
574
575    #[test]
576    fn inverse_fail() {
577        let m1 = M22::new([[1.0, 2.0], [1.0, 2.0]]);
578        let result = m1.inverse();
579        assert!(result.is_none())
580    }
581
582    #[test]
583    fn get_columns_test() {
584        let m1 = m22_new!(1.0, 2.0;
585                          3.0, 4.0);
586        let result = m1.get_cols();
587
588        let expected1 = V2::new([1.0, 3.0]);
589        let expected2 = V2::new([2.0, 4.0]);
590        let expected = V2::new([expected1, expected2]);
591        assert_eq!(
592            &result[..],
593            &expected[..],
594            "\nExpected\n{:?}\nfound\n{:?}",
595            &result[..],
596            &expected[..]
597        );
598    }
599
600    #[test]
601    fn get_rows_test() {
602        let m1 = m22_new!(1.0, 2.0;
603                          3.0, 4.0);
604        let result = m1.get_rows();
605
606        let expected1 = V2::new([1.0, 2.0]);
607        let expected2 = V2::new([3.0, 4.0]);
608        let expected = V2::new([expected1, expected2]);
609        assert_eq!(
610            &result[..],
611            &expected[..],
612            "\nExpected\n{:?}\nfound\n{:?}",
613            &result[..],
614            &expected[..]
615        );
616    }
617
618    #[test]
619    fn new_from_vecs_test() {
620        let expected = m22_new!(1.0, 2.0;
621                                3.0, 4.0);
622
623        let cols = expected.get_cols();
624
625        let result = M22::new_from_vecs(cols);
626
627        assert!(compare_vecs(&result.as_vec(), &expected.as_vec(), EPS));
628    }
629
630    #[test]
631    fn qr_test() {
632        let expected = m22_new!(10.0, 2.0;
633                                3.0, -4.0);
634        if let Some((q, r)) = expected.qr() {
635            let result = q * r;
636            assert!(compare_vecs(&result.as_vec(), &expected.as_vec(), EPS));
637            assert!(nearly_equal(q.det().abs(), 1.0, EPS));
638        }
639    }
640
641    #[test]
642    fn get_diagonal() {
643        let m = m22_new!(10.0, 2.0;
644                         3.0, -4.0);
645        let result = m.get_diagonal();
646        let expected = V2::new([10.0, -4.0]);
647        assert_eq!(
648            &result[..],
649            &expected[..],
650            "\nExpected\n{:?}\nfound\n{:?}",
651            &result[..],
652            &expected[..]
653        );
654    }
655
656    #[test]
657    fn for_each_test() {
658        let m = m22_new!(10.0, 2.0;
659                         3.0, -4.0);
660        let result = m.for_each(|element| element + 37.0);
661        let expected = m22_new!(47.0, 39.0;
662                                40.0, 33.0);
663
664        assert!(compare_vecs(&result.as_vec(), &expected.as_vec(), EPS));
665    }
666}