geo_nd/
matrixr_op.rs

1/*a Copyright
2
3Licensed under the Apache License, Version 2.0 (the "License");
4you may not use this file except in compliance with the License.
5You may obtain a copy of the License at
6
7  http://www.apache.org/licenses/LICENSE-2.0
8
9Unless required by applicable law or agreed to in writing, software
10distributed under the License is distributed on an "AS IS" BASIS,
11WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12See the License for the specific language governing permissions and
13limitations under the License.
14
15@file    matrixr_op.rs
16@brief   Rectangular matrix operations - part of geometry library
17 */
18
19//a Imports
20use crate::vector_op as vector;
21use crate::{Float, Num};
22
23//a Constructors
24//fp zero
25/// Crate a new matrix which is all zeros
26pub fn zero<V: Num, const RC: usize>() -> [V; RC] {
27    [V::zero(); RC]
28}
29
30//mp set_zero
31/// Set the matrix to have all elements of zero
32pub fn set_zero<V: Num>(m: &mut [V]) {
33    vector::set_zero(m)
34}
35
36//fp is_zero
37/// Return true if the matrix is all zeros
38pub fn is_zero<V: Num>(m: &[V]) -> bool {
39    vector::is_zero(m)
40}
41
42//cp scale
43/// Consume the vector and return a new vector that is the original
44/// scaled in each coordinate a single scaling factor
45pub fn scale<V: Num, const RC: usize>(m: [V; RC], s: V) -> [V; RC] {
46    vector::scale(m, s)
47}
48
49//cp reduce
50/// Consume the vector and return a new vector that is the original
51/// reduces in scale in each coordinate by a single scaling factor
52pub fn reduce<V: Num, const RC: usize>(m: [V; RC], s: V) -> [V; RC] {
53    vector::reduce(m, s)
54}
55
56//cp add
57/// Consume the vector, and return a new vector that is the sum of
58/// this and a borrowed other vector scaled
59pub fn add<V: Num, const RC: usize>(m: [V; RC], other: &[V; RC], scale: V) -> [V; RC] {
60    vector::add(m, other, scale)
61}
62
63//cp sub
64/// Consume the vector, and return a new vector that is the difference of
65/// this and a borrowed other vector scaled
66pub fn sub<V: Num, const RC: usize>(m: [V; RC], other: &[V; RC], scale: V) -> [V; RC] {
67    vector::sub(m, other, scale)
68}
69
70//cp absmax
71/// Consume the vector, and return a new vector that is the sum of
72/// this and a borrowed other vector scaled
73pub fn absmax<V: Float>(m: &[V]) -> V {
74    m.iter().fold(V::zero(), |acc, c| V::max(acc, V::abs(*c)))
75}
76
77//cp normalize
78/// Update the new matrix with every element e scaled so that max(abs(e)) is 1.
79pub fn normalize<V: Float, const RC: usize, const C: usize>(mut m: [V; RC]) -> [V; RC] {
80    let l = absmax::<V>(&m);
81    if l < V::epsilon() {
82        set_zero::<V>(&mut m);
83        m
84    } else {
85        reduce::<V, RC>(m, l)
86    }
87}
88
89//cp transpose
90/// Transpose the matrix
91///
92/// The matrices are row-major, so successive entries of m are adjacent columns
93///
94/// The output matrix has adjacent entries that are therefore adjacent rows in m
95pub fn transpose<V: Float, const RC: usize, const R: usize, const C: usize>(m: [V; RC]) -> [V; RC] {
96    assert_eq!(RC, R * C);
97    let mut v = zero::<V, RC>();
98    for r in 0..R {
99        for c in 0..C {
100            v[r + c * R] = m[c + r * C];
101        }
102    }
103    v
104}
105
106//mp multiply
107/// Multiply two matrices
108///
109/// The first matrix is R by X, the second ix X by C, so the result is
110/// R by C
111pub fn multiply<
112    V: Float,
113    const RX: usize, // Total elements in first matrix
114    const XC: usize, // Total elements in second matrix
115    const RC: usize, // Total elements in result
116    const R: usize,  // Number of rows in first matrix
117    const X: usize,  // Number of cols in first matrix, rows in second matrix
118    const C: usize,  // Number of cols in second matrix
119>(
120    a: &[V; RX],
121    b: &[V; XC],
122) -> [V; RC] {
123    assert_eq!(RX, R * X);
124    assert_eq!(RC, R * C);
125    assert_eq!(XC, X * C);
126    let mut m = [V::zero(); RC];
127    for r in 0..R {
128        for c in 0..C {
129            let mut v = V::zero();
130            for x in 0..X {
131                v += a[r * X + x] * b[x * C + c];
132            }
133            m[r * C + c] = v;
134        }
135    }
136    m
137}
138
139//mp multiply_dyn
140/// Multiply two matrices, dynamically sized
141///
142/// Until generic const expressions work cleanly this is a workaround
143pub fn multiply_dyn<V: Float>(
144    r: usize,
145    x: usize,
146    c: usize,
147    a: &[V],     // r by x
148    b: &[V],     // x by c
149    m: &mut [V], // r by c
150) {
151    assert!(a.len() >= r * x);
152    assert!(b.len() >= x * c);
153    assert!(m.len() >= r * c);
154    for ri in 0..r {
155        for ci in 0..c {
156            let mut v = V::zero();
157            for xi in 0..x {
158                v += a[ri * x + xi] * b[xi * c + ci];
159            }
160            m[ri * c + ci] = v;
161        }
162    }
163}
164
165//mp transform_vec
166/// Transform a vector
167pub fn transform_vec<V: Float, const RD: usize, const R: usize, const D: usize>(
168    m: &[V; RD],
169    v: &[V; D],
170) -> [V; R] {
171    multiply::<V, RD, D, R, R, D, 1>(m, v)
172}
173
174//a Formatting
175//mp fmt - format a `Matrix` for display
176/// Format the matrix for display
177///
178/// This can be used by the Display or Debug traits of a type which
179/// contains an array. It is not possible to use this method directly
180/// without a `Formatter` instance.
181///
182/// To display an array `m` as a matrix use `&MatrixType::new(&m)` (see [MatrixType])
183///
184/// # Examples
185///
186/// ```
187/// use geo_nd::matrix;
188/// struct Mat { c : [f32;6] };
189/// impl std::fmt::Display for Mat {
190///   fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { matrix::fmt::<f32,2>(f, &self.c) }
191/// }
192/// assert_eq!( format!("{}", &Mat{c:[0., 1., 2., 3., 4., 5.]} ), "[0,1 2,3 4,5]" );
193/// ```
194pub fn fmt<V: Num, const C: usize>(f: &mut std::fmt::Formatter, v: &[V]) -> std::fmt::Result {
195    let mut c = 0;
196    for (i, value) in v.iter().enumerate() {
197        if i == 0 {
198            write!(f, "[{value}")?;
199        } else if c == 0 {
200            write!(f, " {value}")?;
201        } else {
202            write!(f, ",{value}")?;
203        }
204        c += 1;
205        if c == C {
206            c = 0;
207        }
208    }
209    write!(f, "]")
210}
211
212//a MatrixType, used in formatting
213/// A structure that supports the Debug and Display traits, borrowing
214/// a matrix; this permits a relatively simple format of a matrix
215/// through using the [`new`] constructor of the type
216///
217/// # Example
218///
219/// ```
220/// use geo_nd::matrix::{identity2, MatrixType};
221/// assert_eq!( format!("{}", MatrixType::<f32,4,2>::new(&identity2())), "[1,0 0,1]" );
222/// ```
223///
224#[derive(Debug)]
225pub struct MatrixType<'a, V: Num, const RC: usize, const C: usize> {
226    /// Contents of the matrix
227    m: &'a [V; RC],
228}
229
230//ip MatrixType
231impl<'a, V: Num, const RC: usize, const C: usize> MatrixType<'a, V, RC, C> {
232    //fp new
233    /// Create a new MatrixType by borrowing an array of Num; this may then be formatted using its Display trait
234    pub fn new(m: &'a [V; RC]) -> Self {
235        Self { m }
236    }
237}
238
239//ip Display for MatrixType
240impl<'a, V: Num, const RC: usize, const C: usize> std::fmt::Display for MatrixType<'a, V, RC, C> {
241    //
242    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
243        fmt::<V, C>(f, self.m)
244    }
245}