GCC Code Coverage Report


Directory: ./
File: lib/geogram/basic/matrix.h
Date: 2026-09-07 02:25:23
Exec Total Coverage
Lines: 18 18 100.0%
Functions: 2 2 100.0%
Branches: 26 26 100.0%

Line Branch Exec Source
1 /*
2 * Copyright (c) 2000-2022 Inria
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 * * Neither the name of the ALICE Project-Team nor the names of its
14 * contributors may be used to endorse or promote products derived from this
15 * software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 *
29 * Contact: Bruno Levy
30 *
31 * https://www.inria.fr/fr/bruno-levy
32 *
33 * Inria,
34 * Domaine de Voluceau,
35 * 78150 Le Chesnay - Rocquencourt
36 * FRANCE
37 *
38 */
39
40 #ifndef GEOGRAM_BASIC_MATRIX
41 #define GEOGRAM_BASIC_MATRIX
42
43 #include <geogram/basic/common.h>
44 #include <geogram/basic/vecg.h>
45 #include <initializer_list>
46
47 /**
48 * \file geogram/basic/matrix.h
49 * \brief Generic matrix type
50 */
51
52 namespace GEO {
53
54 /************************************************************************/
55
56
57 /**
58 * \brief A matrix type
59 * \details Matrix implements a square matrix of dimension \p DIM.
60 * containing coefficients of type \p T. Type \p T is expected to be a
61 * numeric type. Matrix provides the classical matrix operations.
62 * \tparam FT type of the matrix elements
63 * \tparam DIM dimension of the matrix
64 */
65 template <index_t DIM, class FT>
66 class Matrix {
67 public:
68 /** This matrix type */
69 typedef Matrix<DIM, FT> matrix_type;
70
71 /** The type of the values */
72 typedef FT value_type;
73
74 /** The dimension of the matrix */
75 static constexpr index_t dim = DIM;
76
77 /**
78 * \brief Default constructor
79 * \details This initializes the matrix to the identity matrix
80 * \see load_identity()
81 */
82 508 inline Matrix() {
83 load_identity();
84 }
85
86 /**
87 * \brief Constructs a matrix from an array of values.
88 * \param[in] vals a const pointer to the DIM*DIM values,
89 * coefficients of the same rows are consecutive in memory,
90 * i is the slowly varying index and j the quickly varying one.
91 */
92 explicit Matrix(const FT* vals) {
93 for(index_t i = 0; i < DIM; i++) {
94 for(index_t j = 0; j < DIM; j++) {
95 coeff_[i][j] = *vals;
96 ++vals;
97 }
98 }
99 }
100
101 /**
102 * \brief Constructs a matrix from 2d array of initializers.
103 * \param[in] Mi a 2d array of values to be copied to the matrix.
104 */
105 4 Matrix(const std::initializer_list< std::initializer_list<FT> >& Mi) {
106 index_t i = 0;
107
2/2
✓ Branch 0 taken 16 times.
✓ Branch 1 taken 4 times.
20 for(auto& it: Mi) {
108 index_t j = 0;
109
2/2
✓ Branch 0 taken 64 times.
✓ Branch 1 taken 16 times.
80 for(auto& jt: it) {
110 geo_debug_assert(i < DIM);
111 geo_debug_assert(j < DIM);
112 64 coeff_[i][j] = jt;
113 64 ++j;
114 }
115 16 ++i;
116 }
117 4 }
118
119
120 /**
121 * \brief Gets the matrix dimension
122 * \return the value of \p DIM
123 */
124 inline index_t dimension() const {
125 return DIM;
126 }
127
128 /**
129 * \brief Clears the matrix
130 * \details This resets all values to 0 (zero)
131 */
132 inline void load_zero() {
133 for(index_t i = 0; i < DIM; i++) {
134 for(index_t j = 0; j < DIM; j++) {
135 coeff_[i][j] = FT(0);
136 }
137 }
138 }
139
140 /**
141 * \brief Sets the matrix to identity
142 * \details This sets all coefficients of this matrix to be equal to
143 * \p DIM x \p DIM identity matrix.
144 */
145 inline void load_identity() {
146
6/6
✓ Branch 0 taken 1016 times.
✓ Branch 1 taken 254 times.
✓ Branch 2 taken 1016 times.
✓ Branch 3 taken 254 times.
✓ Branch 4 taken 1016 times.
✓ Branch 5 taken 254 times.
3556 for(index_t i = 0; i < DIM; i++) {
147
6/6
✓ Branch 0 taken 4064 times.
✓ Branch 1 taken 1016 times.
✓ Branch 2 taken 4064 times.
✓ Branch 3 taken 1016 times.
✓ Branch 4 taken 4064 times.
✓ Branch 5 taken 1016 times.
15240 for(index_t j = 0; j < DIM; j++) {
148
6/6
✓ Branch 0 taken 3048 times.
✓ Branch 1 taken 1016 times.
✓ Branch 2 taken 3048 times.
✓ Branch 3 taken 1016 times.
✓ Branch 4 taken 3048 times.
✓ Branch 5 taken 1016 times.
21336 coeff_[i][j] = (i == j) ? FT(1) : FT(0);
149 }
150 }
151 }
152
153 /**
154 * \brief Tests whether a matrix is the identity matrix.
155 * \retval true if the matrix is the identity matrix
156 * \retval false otherwise
157 */
158 inline bool is_identity() const {
159 for(index_t i = 0; i < DIM; i++) {
160 for(index_t j = 0; j < DIM; j++) {
161 FT rhs = ((i == j) ? FT(1) : FT(0));
162 if(coeff_[i][j] != rhs) {
163 return false;
164 }
165 }
166 }
167 return true;
168 }
169
170 /**
171 * \brief Gets a modifiable element
172 * \details Gets element at row \p i and column \p j in the matrix. If
173 * indices are out of range, the function calls abort().
174 * \param[in] i row index of the element
175 * \param[in] j column index of the element
176 * \return a reference to the element at coordinates (\p i, \p j).
177 */
178 inline FT& operator() (index_t i, index_t j) {
179 geo_debug_assert(i < DIM);
180 geo_debug_assert(j < DIM);
181 return coeff_[i][j];
182 }
183
184 /**
185 * \brief Gets a non-modifiable element
186 * \details Gets element at row \p i and column \p j in the matrix. If
187 * indices are out of range, the function calls abort().
188 * \param[in] i row index of the element
189 * \param[in] j column index of the element
190 * \return a const reference to the element at coordinates (\p i, \p j).
191 */
192 inline const FT& operator() (index_t i, index_t j) const {
193 geo_debug_assert(i < DIM);
194 geo_debug_assert(j < DIM);
195 258 return coeff_[i][j];
196 }
197
198 /**
199 * \brief Adds a matrix in place
200 * \details This adds matrix \p m to this matrix in place.
201 * \param[in] m a matrix of the same dimension
202 * \return a reference to this matrix
203 */
204 inline matrix_type& operator+= (const matrix_type& m) {
205 for(index_t i = 0; i < DIM; i++) {
206 for(index_t j = 0; j < DIM; j++) {
207 coeff_[i][j] += m.coeff_[i][j];
208 }
209 }
210 return *this;
211 }
212
213 /**
214 * \brief Subtracts a matrix in place
215 * \details This subtracts matrix \p m from this matrix in place.
216 * \param[in] m a matrix of the same dimension
217 * \return a reference to this matrix
218 */
219 inline matrix_type& operator-= (const matrix_type& m) {
220 for(index_t i = 0; i < DIM; i++) {
221 for(index_t j = 0; j < DIM; j++) {
222 coeff_[i][j] -= m.coeff_[i][j];
223 }
224 }
225 return *this;
226 }
227
228 /**
229 * \brief Multiplies by a scalar in place
230 * \details This multiplies all the coefficients of this matrix by the
231 * value \p val.
232 * \param[in] val a scalar value of the same type than matrix elements
233 * \return a reference to this matrix
234 */
235 inline matrix_type& operator*= (FT val) {
236 for(index_t i = 0; i < DIM; i++) {
237 for(index_t j = 0; j < DIM; j++) {
238 coeff_[i][j] *= val;
239 }
240 }
241 return *this;
242 }
243
244 /**
245 * \brief Divides by a scalar in place
246 * \details This divides all the coefficients of this matrix by the
247 * value \p val.
248 * \param[in] val a scalar value of the same type than matrix elements
249 * \return a reference to this matrix
250 */
251 inline matrix_type& operator/= (FT val) {
252 for(index_t i = 0; i < DIM; i++) {
253 for(index_t j = 0; j < DIM; j++) {
254 coeff_[i][j] /= val;
255 }
256 }
257 return *this;
258 }
259
260 /**
261 * \brief Adds 2 matrices
262 * \details Builds a matrix by adding matrix \p m to this matrix.
263 * \param[in] m another matrix
264 * \return the matrix (\p this + \p m)
265 */
266 inline matrix_type operator+ (const matrix_type& m) const {
267 matrix_type result = *this;
268 result += m;
269 return result;
270 }
271
272 /**
273 * \brief Subtracts 2 matrices
274 * \details Builds a matrix by subtracting matrix \p m to this matrix.
275 * \param[in] m another matrix
276 * \return the matrix (\p this + \p m)
277 */
278 inline matrix_type operator- (const matrix_type& m) const {
279 matrix_type result = *this;
280 result -= m;
281 return result;
282 }
283
284 /**
285 * \brief Multiplies a matrix by a scalar
286 * \details Builds a matrix by multiplying all the coefficients of
287 * this matrix by scalar value \p val.
288 * \param[in] val a scalar value of the same type than matrix elements
289 * \return the resulting matrix
290 */
291 inline matrix_type operator* (FT val) const {
292 matrix_type result = *this;
293 result *= val;
294 return result;
295 }
296
297 /**
298 * \brief Divides a matrix by a scalar
299 * \details Builds a matrix by dividing all the coefficients of
300 * this matrix by scalar value \p val.
301 * \param[in] val a scalar value of the same type than matrix elements
302 * \return the resulting matrix
303 */
304 inline matrix_type operator/ (FT val) const {
305 matrix_type result = *this;
306 result /= val;
307 return result;
308 }
309
310 /**
311 * \brief Multiplies 2 matrices
312 * \details Builds a matrix by multiplying this matrix by matrix \p
313 * m.
314 * \param[in] m another matrix
315 * \return the matrix (\p this * \p m)
316 */
317 matrix_type operator* (const matrix_type& m) const {
318 matrix_type result;
319 for(index_t i = 0; i < DIM; i++) {
320 for(index_t j = 0; j < DIM; j++) {
321 result.coeff_[i][j] = FT(0);
322 for(index_t k = 0; k < DIM; k++) {
323 result.coeff_[i][j] += coeff_[i][k] * m.coeff_[k][j];
324 }
325 }
326 }
327 return result;
328 }
329
330 /**
331 * \brief Computes the inverse matrix
332 * \details Computes matrix \p M such that (\p this * \p M) = identity
333 * \return the inverse matrix
334 */
335 matrix_type inverse() const {
336 matrix_type result;
337 bool invertible = compute_inverse(result);
338 geo_assert(invertible);
339 return result;
340 }
341
342
343 /**
344 * \brief Computes the inverse matrix
345 * \details Computes matrix \p M such that (\p this * \p M) = identity
346 * \param[out] result the inverse matrix
347 * \param[in] min_val minimum absolute value of pivot. If lower than
348 * that, the matrix is considered to be non-invertible.
349 * \return true if the matrix is invertible
350 * \retval false otherwise
351 */
352 bool compute_inverse(
353 matrix_type& result, value_type min_val = value_type(0)
354 ) const {
355 FT val=FT(0.0), val2=FT(0.0);
356 matrix_type tmp = (*this);
357
358 result.load_identity();
359
360 for(index_t i = 0; i != DIM; i++) {
361 val = tmp(i, i); /* find pivot */
362 index_t ind = i;
363 for(index_t j = i + 1; j != DIM; j++) {
364 if(fabs(tmp(j, i)) > fabs(val)) {
365 ind = j;
366 val = tmp(j, i);
367 }
368 }
369
370 if(ind != i) {
371 for(index_t j = 0; j != DIM; j++) {
372 val2 = result(i, j);
373 result(i, j) = result(ind, j);
374 result(ind, j) = val2; /* swap columns */
375 val2 = tmp(i, j);
376 tmp(i, j) = tmp(ind, j);
377 tmp(ind, j) = val2;
378 }
379 }
380
381 if(abs(val) <= min_val) {
382 return false;
383 }
384
385 for(index_t j = 0; j != DIM; j++) {
386 tmp(i, j) /= val;
387 result(i, j) /= val;
388 }
389
390 for(index_t j = 0; j != DIM; j++) {
391 if(j == i) {
392 continue; /* eliminate column */
393 }
394 val = tmp(j, i);
395 for(index_t k = 0; k != DIM; k++) {
396 tmp(j, k) -= tmp(i, k) * val;
397 result(j, k) -= result(i, k) * val;
398 }
399 }
400 }
401
402 return true;
403 }
404
405 /**
406 * \brief Computes the transposed matrix
407 * \return the transposed matrix
408 */
409 matrix_type transpose() const {
410 matrix_type result;
411 for(index_t i = 0; i < DIM; i++) {
412 for(index_t j = 0; j < DIM; j++) {
413 result(i, j) = (* this)(j, i);
414 }
415 }
416 return result;
417 }
418
419 /** For interfacing with Fortran, OpenGL etc... */
420
421 /**
422 * \brief Gets non-modifiable matrix data
423 * \return a const pointer to the first element of the matrix
424 */
425 inline const FT* data() const {
426 return &(coeff_[0][0]);
427 }
428
429 /** For interfacing with Fortran, OpenGL etc... */
430
431 /**
432 * \brief Gets modifiable matrix data
433 * \return a pointer to the first element of the matrix
434 */
435 inline FT* data() {
436 return &(coeff_[0][0]);
437 }
438
439 /**
440 * \brief Gets the lower triangle of the matrix
441 * \details Gets all the coefficients of the matrix under the
442 * diagonal (included) to array \p store, Array \p store must be large
443 * enough to contain (DIM * (DIM+1))/2 values.
444 * \param[in] store an array of at least (DIM * (DIM+1))/2 values
445 */
446 void get_lower_triangle(FT* store) const {
447 for(index_t i = 0; i < DIM; i++) {
448 for(index_t j = 0; j <= i; j++) {
449 *store++ = coeff_[i][j];
450 }
451 }
452 }
453
454 private:
455 FT coeff_[DIM][DIM];
456 };
457
458 /************************************************************************/
459
460 /**
461 * \brief Writes a matrix to a stream
462 * \details This writes the coefficients of matrix \p m separated by a
463 * space character to the output stream \p output.
464 * \param[in] output the output stream
465 * \param[in] m the matrix to write
466 * \return a reference to the output stream \p output
467 * \relates Matrix
468 */
469 template <index_t DIM, class FT>
470 inline std::ostream& operator<< (
471 std::ostream& output, const Matrix<DIM, FT>& m
472 ) {
473 const char* sep = "";
474 for(index_t i = 0; i < DIM; i++) {
475 for(index_t j = 0; j < DIM; j++) {
476 output << sep << m(i, j);
477 sep = " ";
478 }
479 }
480 return output;
481 }
482
483 /**
484 * \brief Reads a matrix from a stream
485 * \details This reads \p DIM * \p DIM coefficients from the input stream
486 * \p input and stores them in matrix \p m
487 * \param[in] input the input stream
488 * \param[out] m the matrix to read
489 * \return a reference to the input stream \p input
490 * \relates Matrix
491 */
492 template <index_t DIM, class FT>
493 inline std::istream& operator>> (
494 std::istream& input, Matrix<DIM, FT>& m
495 ) {
496 for(index_t i = 0; i < DIM; i++) {
497 for(index_t j = 0; j < DIM; j++) {
498 input >> m(i, j);
499 }
500 }
501 return input;
502 }
503
504 /************************************************************************/
505
506 /**
507 * \brief Multiplies a matrix by a vector
508 * \details Multiplies matrix \p M by vector \p x and stores the result in
509 * vector y. Vectors \p x and \p y are given as arrays of elements and
510 * must at least contain \p DIM elements, otherwise the result is
511 * undefined.
512 * \param[in] M a \p DIM x \p DIM matrix
513 * \param[in] x the input vector
514 * \param[in] y the result of the multiplication
515 * \tparam FT the type of the matrix elements
516 * \tparam DIM the dimension of the matrix
517 * \relates Matrix
518 */
519 template <index_t DIM, class FT> inline
520 void mult(const Matrix<DIM, FT>& M, const FT* x, FT* y) {
521 for(index_t i = 0; i < DIM; i++) {
522 y[i] = 0;
523 for(index_t j = 0; j < DIM; j++) {
524 y[i] += M(i, j) * x[j];
525 }
526 }
527 }
528
529 /************************************************************************/
530
531 /**
532 * \brief Computes a matrix vector product.
533 * \param[in] M the matrix
534 * \param[in] x the vector
535 * \return \p M times \p x
536 * \note This function copies the resulting vector, thus it is not
537 * very efficient and should be only used when prototyping.
538 */
539 template <index_t DIM, class FT> inline
540 60625 vecng<DIM,FT> operator*(
541 const Matrix<DIM, FT>& M, const vecng<DIM,FT>& x
542 ) {
543 vecng<DIM,FT> y;
544
2/2
✓ Branch 0 taken 242500 times.
✓ Branch 1 taken 60625 times.
303125 for(index_t i = 0; i < DIM; i++) {
545 242500 y[i] = 0;
546
2/2
✓ Branch 0 taken 970000 times.
✓ Branch 1 taken 242500 times.
1212500 for(index_t j = 0; j < DIM; j++) {
547 970000 y[i] += M(i, j) * x[j];
548 }
549 }
550 60625 return y;
551 }
552
553 /************************************************************************/
554
555 /**
556 * \brief Computes a matrix vector product.
557 * \param[in] x the vector considered as a row vector
558 * \param[in] M the matrix
559 * \return \p x times \p M
560 * \note This function copies the resulting vector, thus it is not
561 * very efficient and should be only used when prototyping.
562 */
563 template <index_t DIM, class FT> inline
564 vecng<DIM,FT> operator*(
565 const vecng<DIM,FT>& x, const Matrix<DIM, FT>& M
566 ) {
567 vecng<DIM,FT> y;
568 for(index_t i = 0; i < DIM; i++) {
569 y[i] = 0;
570 for(index_t j = 0; j < DIM; j++) {
571 y[i] += M(j, i) * x[j];
572 }
573 }
574 return y;
575 }
576
577
578 /************************************************************************/
579
580 #ifndef GOMGEN
581
582 /**
583 * \brief Computes a matrix vector product.
584 * \param[in] M the matrix
585 * \param[in] x the vector
586 * \return \p M times \p x
587 * \note This function copies the resulting vector, thus it is not
588 * very efficient and should be only used when prototyping.
589 */
590 template <index_t DIM, class FT>
591 [[deprecated("use operator*(matrix, vector) instead")]]
592 inline vecng<DIM,FT> mult(
593 const Matrix<DIM, FT>& M, const vecng<DIM,FT>& x
594 ) {
595 vecng<DIM,FT> y;
596 for(index_t i = 0; i < DIM; i++) {
597 y[i] = 0;
598 for(index_t j = 0; j < DIM; j++) {
599 y[i] += M(i, j) * x[j];
600 }
601 }
602 return y;
603 }
604
605 #endif
606
607 /************************************************************************/
608
609 }
610
611 #endif
612