lars/matrix/
mat2.rs

1//! 2×2 Matrix utilities.
2//!
3//! Provides a small, self-contained 2×2 matrix type [`Mat2`] with
4//! support for basic linear algebra operations, including addition,
5//! subtraction, scalar and matrix multiplication, and inversion.
6//!
7//! This type is designed to pair naturally with the [`Vec2`] struct
8//! for 2D linear transformations.
9
10use crate::Vec2;
11use derive_more::{Add, Constructor, Sub, Div};
12use std::ops::Mul;
13
14/// A 2×2 matrix of `f64` values.
15///
16/// The matrix is stored in **row-major order**:
17///
18/// ```text
19/// | a  b |
20/// | c  d |
21/// ```
22///
23/// # Examples
24/// ```
25/// use lars::{Mat2, Vec2};
26///
27/// let m = Mat2::IDENTITY;
28/// let v = Vec2::ONE;
29///
30/// assert_eq!(m * v, v);
31/// ```
32#[derive(Constructor, Copy, Clone, Debug, Add, Sub, PartialOrd, Div)]
33pub struct Mat2 {
34    /// Top-left element.
35    pub a: f64,
36    /// Top-right element.
37    pub b: f64,
38    /// Bottom-left element.
39    pub c: f64,
40    /// Bottom-right element.
41    pub d: f64,
42}
43
44const EPSILON: f64 = 1e-9;
45
46impl PartialEq for Mat2 {
47    fn eq(&self, other: &Self) -> bool {
48        (self.a - other.a).abs() < EPSILON &&
49        (self.b - other.b).abs() < EPSILON &&
50        (self.c - other.c).abs() < EPSILON &&
51        (self.d - other.d).abs() < EPSILON
52    }
53}
54
55impl Mat2 {
56    /// The **identity matrix**:
57    ///
58    /// ```text
59    /// | 1  0 |
60    /// | 0  1 |
61    /// ```
62    pub const IDENTITY: Mat2 = Mat2::new(1.0, 0.0, 0.0, 1.0);
63
64    /// The **zero matrix**:
65    ///
66    /// ```text
67    /// | 0  0 |
68    /// | 0  0 |
69    /// ```
70    pub const ZERO: Mat2 = Mat2::new(0.0, 0.0, 0.0, 0.0);
71
72    /// Returns the **determinant** of the matrix.
73    ///
74    /// Computed as:
75    /// \[
76    /// \det(M) = ad - bc
77    /// \]
78    ///
79    /// # Examples
80    /// ```
81    /// use lars::Mat2;
82    /// let m = Mat2::new(7.0, 2.0, 6.0, 2.0);
83    /// assert_eq!(m.determinant(), 2.0);
84    /// ```
85    pub fn determinant(&self) -> f64 {
86        self.a * self.d - self.b * self.c
87    }
88
89    /// Returns the **inverse** of the matrix, if it exists.
90    ///
91    /// Computed as:
92    /// \[
93    /// M^{-1} = \frac{1}{\det(M)} \begin{bmatrix} d & -b \\ -c & a \end{bmatrix}
94    /// \]
95    ///
96    /// # Panics
97    /// Panics if the matrix is singular (determinant = 0).
98    ///
99    /// # Examples
100    /// ```
101    /// use lars::Mat2;
102    /// let m = Mat2::new(7.0, 2.0, 6.0, 2.0);
103    /// assert_eq!(m.inverse(), Mat2::new(1.0, -1.0, -3.0, 3.5));
104    /// ```
105    pub fn inverse(&self) -> Mat2 {
106        if self.determinant() == 0.0 {
107            panic!("Matrix is singular and cannot be inverted.");
108        };
109        let rec_det = 1.0 / self.determinant();
110
111        rec_det * Mat2::new(self.d, -self.b, -self.c, self.a)
112    }
113}
114
115/// Implements **matrix–scalar multiplication** (`Mat2 * f64`).
116///
117/// Each element of the matrix is scaled by the scalar.
118///
119/// # Examples
120/// ```
121/// use lars::Mat2;
122/// let m = Mat2::new(1.0, 2.0, 3.0, 4.0);
123/// assert_eq!(m * 2.0, Mat2::new(2.0, 4.0, 6.0, 8.0));
124/// ```
125impl Mul<f64> for Mat2 {
126    type Output = Mat2;
127    fn mul(self, s: f64) -> Mat2 {
128        Mat2::new(self.a * s, self.b * s, self.c * s, self.d * s)
129    }
130}
131
132/// Implements **scalar–matrix multiplication** (`f64 * Mat2`).
133///
134/// # Examples
135/// ```
136/// use lars::Mat2;
137/// let m = Mat2::new(1.0, 2.0, 3.0, 4.0);
138/// assert_eq!(2.0 * m, Mat2::new(2.0, 4.0, 6.0, 8.0));
139/// ```
140impl Mul<Mat2> for f64 {
141    type Output = Mat2;
142    fn mul(self, s: Mat2) -> Mat2 {
143        Mat2::new(s.a * self, s.b * self, s.c * self, s.d * self)
144    }
145}
146
147/// Implements **matrix–vector multiplication** (`Mat2 * Vec2`).
148///
149/// Performs the linear transformation of the vector by the matrix.
150///
151/// \[
152/// \begin{bmatrix} a & b \\ c & d \end{bmatrix}
153/// \begin{bmatrix} x \\ y \end{bmatrix}
154/// =
155/// \begin{bmatrix} ax + by \\ cx + dy \end{bmatrix}
156/// \]
157///
158/// # Examples
159/// ```
160/// use lars::{Mat2, Vec2};
161/// let m = Mat2::new(1.0, 2.0, 3.0, 4.0);
162/// let v = Vec2::new(1.0, 1.0);
163/// assert_eq!(m * v, Vec2::new(3.0, 7.0));
164/// ```
165impl Mul<Vec2> for Mat2 {
166    type Output = Vec2;
167    fn mul(self, v: Vec2) -> Vec2 {
168        let x = self.a * v.x + self.b * v.y;
169        let y = self.c * v.x + self.d * v.y;
170        Vec2::new(x, y)
171    }
172}
173
174/// Implements **matrix–matrix multiplication** (`Mat2 * Mat2`).
175///
176/// Standard linear algebra multiplication:
177///
178/// \[
179/// M_1 \times M_2 =
180/// \begin{bmatrix}
181/// a_1a_2 + b_1c_2 & a_1b_2 + b_1d_2 \\
182/// c_1a_2 + d_1c_2 & c_1b_2 + d_1d_2
183/// \end{bmatrix}
184/// \]
185///
186/// # Examples
187/// ```
188/// use lars::Mat2;
189/// let a = Mat2::IDENTITY;
190/// let b = Mat2::new(1.0, 2.0, 3.0, 4.0);
191/// assert_eq!(a * b, b);
192/// ```
193impl Mul<Mat2> for Mat2 {
194    type Output = Mat2;
195    fn mul(self, m: Mat2) -> Mat2 {
196        let a = self.a * m.a + self.b * m.c;
197        let b = self.a * m.b + self.b * m.d;
198        let c = self.c * m.a + self.d * m.c;
199        let d = self.c * m.b + self.d * m.d;
200        Mat2::new(a, b, c, d)
201    }
202}
203
204#[cfg(test)]
205mod tests {
206    use super::*;
207
208    #[test]
209    fn test_add() {
210        let m = Mat2::new(1.0, 2.0, 3.0, 4.0);
211        assert_eq!(m + m, Mat2::new(2.0, 4.0, 6.0, 8.0));
212    }
213
214    #[test]
215    fn sub() {
216        let m = Mat2::new(1.0, 2.0, 3.0, 4.0);
217        assert_eq!(m - m, Mat2::ZERO);
218    }
219
220    #[test]
221    fn test_s_mul1() {
222        let m = Mat2::new(1.0, 2.0, 3.0, 4.0);
223        assert_eq!(m * 1.0, m)
224    }
225
226    #[test]
227    fn test_s_mul2() {
228        let m = Mat2::new(1.0, 2.0, 3.0, 4.0);
229        assert_eq!(1.0 * m, m)
230    }
231
232    #[test]
233    fn test_v_mul1() {
234        let i = Mat2::IDENTITY;
235        let v = Vec2::new(1.0, 1.0);
236        assert_eq!(i * v, v)
237    }
238
239    #[test]
240    fn test_mat_mul() {
241        let a = Mat2::IDENTITY;
242        let b = Mat2::new(1.0, 2.0, 3.0, 4.0);
243        assert_eq!(a * b, b);
244    }
245
246    #[test]
247    fn test_determinant() {
248        let m = Mat2::new(7.0, 2.0, 6.0, 2.0);
249        assert_eq!(m.determinant(), 2.0);
250    }
251
252    #[test]
253    fn test_inverse() {
254        let m = Mat2::new(7.0, 2.0, 6.0, 2.0);
255        assert_eq!(m.inverse(), Mat2::new(1.0, -1.0, -3.0, 3.5))
256    }
257
258    #[test]
259    fn test_inverse_singular() {
260        let m = Mat2::new(1.0, 2.0, 2.0, 4.0);
261        let result = std::panic::catch_unwind(|| {
262            m.inverse();
263        });
264        assert!(result.is_err());
265    }
266}