LCOV - code coverage report
Current view: top level - math/linalg - matrix_io.h (source / functions) Coverage Total Hit
Test: lcov.info Lines: 100.0 % 37 37
Test Date: 2026-04-26 21:52:20 Functions: 81.0 % 21 17
Legend: Lines: hit not hit

            Line data    Source code
       1              : /// \file matrix_io.h
       2              : /// \brief Stream output operators for all matrix types in the trackingLib math library.
       3              : ///
       4              : /// This file provides template-based operator<< implementations for streaming matrix objects
       5              : /// to any std::ostream (cout, files, stringstream). The design uses SFINAE-based type detection
       6              : /// to work with all matrix-like types that provide the required interface.
       7              : ///
       8              : /// Key features:
       9              : /// - Works with any std::ostream (cout, files, stringstream)
      10              : /// - Template-based implementation for compile-time type safety
      11              : /// - SFINAE-based type detection for C++17 compatibility
      12              : /// - Specialized formatting for DiagonalMatrix, TriangularMatrix, and Vector
      13              : /// - Future-proof design using at_unsafe() interface
      14              : ///
      15              : /// \note This file replaces the previous print() methods across all matrix types,
      16              : ///       providing idiomatic C++ stream output without code duplication.
      17              : ///
      18              : /// \see matrix.h for the base Matrix class
      19              : /// \see diagonal_matrix.h for DiagonalMatrix specialization
      20              : /// \see vector.h for Vector specialization
      21              : 
      22              : #ifndef C63CC10A_92E8_4A79_8412_56D9CFCB8DB4
      23              : #define C63CC10A_92E8_4A79_8412_56D9CFCB8DB4
      24              : 
      25              : #include "base/first_include.h" // IWYU pragma: keep
      26              : #include "math/linalg/triangular_matrix.h"
      27              : #include <iomanip>
      28              : #include <ostream>
      29              : #include <type_traits>
      30              : 
      31              : namespace tracking
      32              : {
      33              : namespace math
      34              : {
      35              : 
      36              : // Forward declarations
      37              : template <typename ValueType_, sint32 Size_>
      38              : class DiagonalMatrix;
      39              : 
      40              : template <typename ValueType_, sint32 Size_>
      41              : class Vector;
      42              : 
      43              : /// \brief SFINAE helper to detect matrix-like types at compile time.
      44              : ///
      45              : /// This trait uses SFINAE (Substitution Failure is Not An Error) to detect whether
      46              : /// a type provides the interface expected of matrix-like objects. It checks for:
      47              : /// - An at_unsafe(sint32, sint32) method for element access
      48              : /// - A nested value_type typedef
      49              : /// - Static Rows and Cols constants
      50              : ///
      51              : /// This allows the operator<< template to work with any matrix type that provides
      52              : /// the required interface, enabling compile-time polymorphism without inheritance.
      53              : ///
      54              : /// \tparam T The type to check for matrix-like properties
      55              : /// \tparam Enable SFINAE parameter (automatically deduced)
      56              : ///
      57              : /// \see operator<<(std::ostream&, const M&) for usage
      58              : template <typename T, typename = void>
      59              : struct is_matrix_like: std::false_type
      60              : {
      61              : };
      62              : 
      63              : template <typename T>
      64              : struct is_matrix_like<T,
      65              :                       std::void_t<decltype(std::declval<const T&>().at_unsafe(sint32{}, sint32{})),
      66              :                                   typename T::value_type,
      67              :                                   decltype(T::Rows),
      68              :                                   decltype(T::Cols)>>: std::true_type
      69              : {
      70              : };
      71              : 
      72              : /// \brief Convenience variable template for is_matrix_like trait.
      73              : ///
      74              : /// Provides a more convenient syntax for checking if a type is matrix-like.
      75              : /// Equivalent to is_matrix_like<T>::value.
      76              : ///
      77              : /// \tparam T The type to check
      78              : ///
      79              : /// \see is_matrix_like
      80              : template <typename T>
      81              : inline constexpr bool is_matrix_like_v = is_matrix_like<T>::value;
      82              : 
      83              : /// \brief Template operator<< for streaming matrix-like objects to output streams.
      84              : ///
      85              : /// This function provides idiomatic C++ stream output for all matrix types in the trackingLib
      86              : /// math library. It uses SFINAE to enable the operator only for types that provide the
      87              : /// required matrix interface.
      88              : ///
      89              : /// The output format provides:
      90              : /// - Fixed-width columns for alignment
      91              : /// - 6 decimal places for floating-point types with sign display
      92              : /// - Comma-separated values within rows
      93              : /// - One row per line
      94              : ///
      95              : /// \tparam M The matrix type (automatically deduced)
      96              : /// \param[in,out] os The output stream to write to
      97              : /// \param[in] matrix The matrix object to stream
      98              : /// \return Reference to the output stream for chaining
      99              : ///
     100              : /// \note Uses at_unsafe() for element access, which is future-proof for packed storage
     101              : /// \note Formatting is optimized for readability in debug/console output
     102              : ///
     103              : /// \see operator<<(std::ostream&, const DiagonalMatrix&) for DiagonalMatrix specialization
     104              : ///
     105              : /// Usage example:
     106              : /// \code{.cpp}
     107              : /// Matrix<float32, 2, 3> mat = /* ... */;
     108              : /// std::cout << mat << std::endl;
     109              : /// // Output:
     110              : /// // +1.000000, +2.000000, +3.000000
     111              : /// // +4.000000, +5.000000, +6.000000
     112              : /// \endcode
     113              : template <typename M>
     114           19 : auto operator<<(std::ostream& os, const M& matrix) -> std::enable_if_t<is_matrix_like_v<M>, std::ostream&>
     115              : {
     116              :   using value_type = typename M::value_type;
     117              : 
     118              :   // Access elements via at_unsafe() - works regardless of internal memory layout
     119              :   // This is future-proof for packed triangular matrixes
     120           83 :   for (sint32 row = 0; row < M::Rows; ++row)
     121              :   {
     122          280 :     for (sint32 col = 0; col < M::Cols; ++col)
     123              :     {
     124              :       if constexpr (std::is_floating_point_v<value_type>)
     125              :       {
     126          208 :         os << std::fixed << std::setprecision(10) << std::showpos << std::setw(16) << matrix.at_unsafe(row, col);
     127              :       }
     128              :       else
     129              :       {
     130            8 :         os << std::setw(8) << matrix.at_unsafe(row, col);
     131              :       }
     132              : 
     133          216 :       if (col < M::Cols - 1)
     134              :       {
     135          152 :         os << ", ";
     136              :       }
     137              :     }
     138           64 :     os << "\n";
     139              :   }
     140           19 :   return os;
     141              : }
     142              : 
     143              : /// \brief Specialization of operator<< for TriangularMatrix.
     144              : ///
     145              : /// TriangularMatrix requires special handling because it doesn't allow to access
     146              : /// off-diagonal matrix elements
     147              : /// This specialization outputs the full matrix representation with zeros off-diagonal.
     148              : ///
     149              : /// The output format matches the general matrix operator<< for consistency:
     150              : /// - Fixed-width columns for alignment
     151              : /// - 6 decimal places for floating-point types with sign display
     152              : /// - Triangular elements from stored values, off-diagonal as zero
     153              : ///
     154              : /// \tparam ValueType_ The atomic data type of internal elements
     155              : /// \tparam Size_ The matrix dimension (compile-time constant)
     156              : /// \tparam IsLower_ Whether the matrix is lower triangular
     157              : /// \tparam IsRowMajor_ Whether the matrix is stored in row-major order
     158              : /// \param[in,out] os The output stream to write to
     159              : /// \param[in] matrix The TriangularMatrix object to stream
     160              : /// \return Reference to the output stream for chaining
     161              : ///
     162              : /// \note This specialization is necessary because TriangularMatrix::at_unsafe() has
     163              : ///       restricted access to only triangular elements
     164              : ///
     165              : /// \see TriangularMatrix for the matrix class
     166              : /// \see operator<<(std::ostream&, const M&) for general matrix streaming
     167              : template <typename ValueType_, sint32 Size_, bool IsLower_, bool IsRowMajor_>
     168            1 : std::ostream& operator<<(std::ostream&                                                                     os,
     169              :                          const tracking::math::TriangularMatrix<ValueType_, Size_, IsLower_, IsRowMajor_>& matrix)
     170              : {
     171            4 :   for (sint32 row = 0; row < Size_; ++row)
     172              :   {
     173           12 :     for (sint32 col = 0; col < Size_; ++col)
     174              :     {
     175              :       if constexpr (std::is_floating_point_v<ValueType_>)
     176              :       {
     177            9 :         os << std::fixed << std::setprecision(10) << std::showpos << std::setw(16);
     178              :       }
     179              :       else
     180              :       {
     181              :         os << std::setw(8);
     182              :       }
     183              : 
     184              :       // For triangular matrix, only triangular elements are stored
     185            9 :       if ((IsLower_ && row >= col) || (!IsLower_ && row <= col))
     186              :       {
     187            6 :         os << matrix.at_unsafe(row, col);
     188              :       }
     189              :       else
     190              :       {
     191            3 :         os << static_cast<ValueType_>(0);
     192              :       }
     193              : 
     194            9 :       if (col < Size_ - 1)
     195              :       {
     196            6 :         os << ", ";
     197              :       }
     198              :     }
     199            3 :     os << "\n";
     200              :   }
     201            1 :   return os;
     202              : }
     203              : 
     204              : /// \brief Specialization of operator<< for DiagonalMatrix.
     205              : ///
     206              : /// DiagonalMatrix requires special handling because it stores only diagonal elements
     207              : /// and has a different at_unsafe() signature (single index instead of row,col).
     208              : /// This specialization outputs the full matrix representation with zeros off-diagonal.
     209              : ///
     210              : /// The output format matches the general matrix operator<< for consistency:
     211              : /// - Fixed-width columns for alignment
     212              : /// - 6 decimal places for floating-point types with sign display
     213              : /// - Diagonal elements from stored values, off-diagonal as zero
     214              : ///
     215              : /// \tparam value_type The element type (float32, float64, etc.)
     216              : /// \tparam Size The matrix dimension (compile-time constant)
     217              : /// \param[in,out] os The output stream to write to
     218              : /// \param[in] matrix The DiagonalMatrix object to stream
     219              : /// \return Reference to the output stream for chaining
     220              : ///
     221              : /// \note This specialization is necessary because DiagonalMatrix::at_unsafe() takes
     222              : ///       only one parameter (diagonal index) unlike general matrixes
     223              : ///
     224              : /// \see DiagonalMatrix for the matrix class
     225              : /// \see operator<<(std::ostream&, const M&) for general matrix streaming
     226              : ///
     227              : /// Usage example:
     228              : /// \code{.cpp}
     229              : /// DiagonalMatrix<float32, 3> diag = /* diagonal [1, 2, 3] */;
     230              : /// std::cout << diag << std::endl;
     231              : /// // Output:
     232              : /// // +1.000000, +0.000000, +0.000000
     233              : /// // +0.000000, +2.000000, +0.000000
     234              : /// // +0.000000, +0.000000, +3.000000
     235              : /// \endcode
     236              : template <typename ValueType_, sint32 Size_>
     237            1 : std::ostream& operator<<(std::ostream& os, const tracking::math::DiagonalMatrix<ValueType_, Size_>& matrix)
     238              : {
     239            4 :   for (sint32 row = 0; row < Size_; ++row)
     240              :   {
     241           12 :     for (sint32 col = 0; col < Size_; ++col)
     242              :     {
     243              :       if constexpr (std::is_floating_point_v<ValueType_>)
     244              :       {
     245            9 :         os << std::fixed << std::setprecision(10) << std::showpos << std::setw(16);
     246              :       }
     247              :       else
     248              :       {
     249              :         os << std::setw(8);
     250              :       }
     251              : 
     252              :       // For diagonal matrix, only diagonal elements are stored
     253            9 :       if (row == col)
     254              :       {
     255            3 :         os << matrix.at_unsafe(row);
     256              :       }
     257              :       else
     258              :       {
     259            6 :         os << static_cast<ValueType_>(0);
     260              :       }
     261              : 
     262            9 :       if (col < Size_ - 1)
     263              :       {
     264            6 :         os << ", ";
     265              :       }
     266              :     }
     267            3 :     os << "\n";
     268              :   }
     269            1 :   return os;
     270              : }
     271              : 
     272              : /// \brief Specialization of operator<< for Vector.
     273              : ///
     274              : /// Vector requires special handling because it has a different at_unsafe() signature
     275              : /// (single index instead of row,col) and represents a column vector.
     276              : /// This specialization outputs the vector as a column matrix.
     277              : ///
     278              : /// The output format matches the general matrix operator<< for consistency:
     279              : /// - Fixed-width columns for alignment
     280              : /// - 6 decimal places for floating-point types with sign display
     281              : /// - Vector elements as a single column
     282              : ///
     283              : /// \tparam ValueType_ The atomic data type of internal elements
     284              : /// \tparam Size_ The vector dimension (compile-time constant)
     285              : /// \param[in,out] os The output stream to write to
     286              : /// \param[in] vector The Vector object to stream
     287              : /// \return Reference to the output stream for chaining
     288              : ///
     289              : /// \note This specialization is necessary because Vector::at_unsafe() takes
     290              : ///       only one parameter (vector index) unlike general matrixes
     291              : ///
     292              : /// \see Vector for the vector class
     293              : /// \see operator<<(std::ostream&, const M&) for general matrix streaming
     294              : ///
     295              : /// Usage example:
     296              : /// \code{.cpp}
     297              : /// Vector<float32, 3> vec = /* [1, 2, 3] */;
     298              : /// std::cout << vec << std::endl;
     299              : /// // Output:
     300              : /// // +1.000000
     301              : /// // +2.000000
     302              : /// // +3.000000
     303              : /// \endcode
     304              : template <typename ValueType_, sint32 Size_>
     305            2 : std::ostream& operator<<(std::ostream& os, const tracking::math::Vector<ValueType_, Size_>& vector)
     306              : {
     307            9 :   for (sint32 row = 0; row < Size_; ++row)
     308              :   {
     309              :     if constexpr (std::is_floating_point_v<ValueType_>)
     310              :     {
     311            3 :       os << std::fixed << std::setprecision(10) << std::showpos << std::setw(16) << vector.at_unsafe(row);
     312              :     }
     313              :     else
     314              :     {
     315            4 :       os << std::setw(8) << vector.at_unsafe(row);
     316              :     }
     317            7 :     os << "\n";
     318              :   }
     319            2 :   return os;
     320              : }
     321              : 
     322              : } // namespace math
     323              : } // namespace tracking
     324              : 
     325              : #endif // C63CC10A_92E8_4A79_8412_56D9CFCB8DB4
        

Generated by: LCOV version 2.0-1