algebra_basics_rs/lib.rs
1//! Basic math formulas for quadratic equations and 2x2 matrices.
2//!
3//! This crate currently supports:
4//! - quadratic equation solving (`ax^2 + bx + c = 0`)
5//! - 2x2 matrix determinant
6//! - other common 2x2 matrix operations: trace, transpose, inverse
7//! - solving a 2x2 linear system using Cramer's rule
8
9/// A 2x2 matrix represented as:
10/// `[[a, b], [c, d]]`
11pub type Matrix2x2 = [[f64; 2]; 2];
12
13/// Errors returned by math operations in this crate.
14#[derive(Debug, Clone, PartialEq)]
15pub enum MathError {
16 /// The `a` coefficient is zero, so the equation is not quadratic.
17 NotQuadratic,
18 /// The matrix cannot be inverted or used with Cramer's rule because
19 /// its determinant is zero.
20 SingularMatrix,
21}
22
23/// The root structure returned by [`solve_quadratic`].
24#[derive(Debug, Clone, PartialEq)]
25pub enum QuadraticRoots {
26 /// Two distinct real roots.
27 TwoReal(f64, f64),
28 /// One repeated real root.
29 OneReal(f64),
30 /// A pair of complex-conjugate roots `(real, imaginary)`.
31 Complex((f64, f64), (f64, f64)),
32}
33
34/// Computes the discriminant of a quadratic equation: `b^2 - 4ac`.
35///
36/// The sign of the discriminant tells the root type:
37/// - positive: two distinct real roots
38/// - zero: one repeated real root
39/// - negative: two complex-conjugate roots
40pub fn discriminant(a: f64, b: f64, c: f64) -> f64 {
41 b * b - 4.0 * a * c
42}
43
44/// Solves the quadratic equation `ax^2 + bx + c = 0`.
45///
46/// Returns:
47/// - [`QuadraticRoots::TwoReal`] when discriminant is positive
48/// - [`QuadraticRoots::OneReal`] when discriminant is zero
49/// - [`QuadraticRoots::Complex`] when discriminant is negative
50///
51/// # Errors
52/// Returns [`MathError::NotQuadratic`] when `a == 0.0`.
53pub fn solve_quadratic(a: f64, b: f64, c: f64) -> Result<QuadraticRoots, MathError> {
54 if a == 0.0 {
55 return Err(MathError::NotQuadratic);
56 }
57
58 let d = discriminant(a, b, c);
59 let two_a = 2.0 * a;
60
61 if d > 0.0 {
62 let sqrt_d = d.sqrt();
63 Ok(QuadraticRoots::TwoReal(
64 (-b + sqrt_d) / two_a,
65 (-b - sqrt_d) / two_a,
66 ))
67 } else if d == 0.0 {
68 Ok(QuadraticRoots::OneReal(-b / two_a))
69 } else {
70 let real = -b / two_a;
71 let imag = (-d).sqrt() / two_a;
72 Ok(QuadraticRoots::Complex((real, imag), (real, -imag)))
73 }
74}
75
76/// Computes the determinant of a 2x2 matrix.
77///
78/// For `[[a, b], [c, d]]`, the determinant is `ad - bc`.
79pub fn determinant_2x2(m: Matrix2x2) -> f64 {
80 m[0][0] * m[1][1] - m[0][1] * m[1][0]
81}
82
83/// Computes the trace of a 2x2 matrix (`a + d`).
84///
85/// This is one of the other common matrix quantities used in linear algebra.
86pub fn trace_2x2(m: Matrix2x2) -> f64 {
87 m[0][0] + m[1][1]
88}
89
90/// Computes the transpose of a 2x2 matrix.
91pub fn transpose_2x2(m: Matrix2x2) -> Matrix2x2 {
92 [[m[0][0], m[1][0]], [m[0][1], m[1][1]]]
93}
94
95/// Computes the inverse of a 2x2 matrix.
96///
97/// For `[[a, b], [c, d]]`, inverse is:
98/// `(1/det) * [[d, -b], [-c, a]]`.
99///
100/// # Errors
101/// Returns [`MathError::SingularMatrix`] when determinant is zero.
102pub fn inverse_2x2(m: Matrix2x2) -> Result<Matrix2x2, MathError> {
103 let det = determinant_2x2(m);
104 if det == 0.0 {
105 return Err(MathError::SingularMatrix);
106 }
107
108 Ok([
109 [m[1][1] / det, -m[0][1] / det],
110 [-m[1][0] / det, m[0][0] / det],
111 ])
112}
113
114/// Solves a 2x2 linear system `A * x = b` using Cramer's rule.
115///
116/// `a` is the coefficient matrix and `b` is `[b1, b2]`.
117/// Returns solution `[x, y]`.
118///
119/// # Errors
120/// Returns [`MathError::SingularMatrix`] when `det(A) == 0.0`.
121pub fn solve_linear_2x2(a: Matrix2x2, b: [f64; 2]) -> Result<[f64; 2], MathError> {
122 let det_a = determinant_2x2(a);
123 if det_a == 0.0 {
124 return Err(MathError::SingularMatrix);
125 }
126
127 let det_x = determinant_2x2([[b[0], a[0][1]], [b[1], a[1][1]]]);
128 let det_y = determinant_2x2([[a[0][0], b[0]], [a[1][0], b[1]]]);
129
130 Ok([det_x / det_a, det_y / det_a])
131}