diff --git a/numerical/math/Matrix.hpp b/numerical/math/Matrix.hpp index 18bca0e..9c10356 100644 --- a/numerical/math/Matrix.hpp +++ b/numerical/math/Matrix.hpp @@ -63,7 +63,7 @@ namespace math [[nodiscard]] OPTIMIZE_FOR_SPEED friend constexpr Matrix operator+(const Matrix& lhs, const Matrix& rhs) { - Matrix result; + Matrix result{ Uninitialized{} }; for (size_type i = 0; i < size; ++i) result.data[i] = lhs.data[i] + rhs.data[i]; return result; @@ -71,7 +71,7 @@ namespace math [[nodiscard]] OPTIMIZE_FOR_SPEED friend constexpr Matrix operator-(const Matrix& lhs, const Matrix& rhs) { - Matrix result; + Matrix result{ Uninitialized{} }; for (size_type i = 0; i < size; ++i) result.data[i] = lhs.data[i] - rhs.data[i]; return result; @@ -80,23 +80,12 @@ namespace math template [[nodiscard]] OPTIMIZE_FOR_SPEED friend constexpr Matrix operator*(const Matrix& lhs, const Matrix& rhs) { - Matrix result; - for (size_type i = 0; i < Rows; ++i) - { - for (size_type j = 0; j < RhsCols; ++j) - { - T sum{}; - for (size_type k = 0; k < Cols; ++k) - sum += lhs.at(i, k) * rhs.at(k, j); - result.at(i, j) = sum; - } - } - return result; + return lhs.Multiply(rhs); } [[nodiscard]] OPTIMIZE_FOR_SPEED friend constexpr Matrix operator*(const Matrix& lhs, const T& scalar) { - Matrix result; + Matrix result{ Uninitialized{} }; for (size_type i = 0; i < size; ++i) result.data[i] = lhs.data[i] * scalar; return result; @@ -121,7 +110,19 @@ namespace math [[nodiscard]] constexpr Matrix GetColumn(size_type col) const; private: - std::array data = {}; + template + friend class Matrix; + + struct Uninitialized + {}; + + constexpr explicit Matrix(Uninitialized) noexcept + {} + + template + [[nodiscard]] constexpr Matrix Multiply(const Matrix& rhs) const; + + std::array data; }; template @@ -141,10 +142,13 @@ namespace math } template - constexpr Matrix::Matrix() noexcept = default; + constexpr Matrix::Matrix() noexcept + : data{} + {} template OPTIMIZE_FOR_SPEED constexpr Matrix::Matrix(std::initializer_list> init) + : data{} { size_t row = 0; for (const auto& row_list : init) @@ -263,11 +267,30 @@ namespace math return *this; } + template + template + OPTIMIZE_FOR_SPEED constexpr Matrix + Matrix::Multiply(const Matrix& rhs) const + { + Matrix result{ typename Matrix::Uninitialized{} }; + for (size_type i = 0; i < Rows; ++i) + { + for (size_type j = 0; j < RhsCols; ++j) + { + T sum{}; + for (size_type k = 0; k < Cols; ++k) + sum += at(i, k) * rhs.at(k, j); + result.at(i, j) = sum; + } + } + return result; + } + template OPTIMIZE_FOR_SPEED constexpr Matrix Matrix::Transpose() const { - Matrix result; + Matrix result{ typename Matrix::Uninitialized{} }; for (size_type i = 0; i < Rows; ++i) for (size_type j = 0; j < Cols; ++j) result.at(j, i) = at(i, j); @@ -328,7 +351,7 @@ namespace math { static_assert(BlockRows <= Rows && BlockCols <= Cols, "Requested block exceeds source matrix dimensions"); - Matrix result; + Matrix result{ typename Matrix::Uninitialized{} }; for (size_type r = 0; r < BlockRows; ++r) for (size_type c = 0; c < BlockCols; ++c) result.at(r, c) = at(rowOffset + r, colOffset + c); @@ -339,7 +362,7 @@ namespace math [[nodiscard]] OPTIMIZE_FOR_SPEED constexpr Matrix Matrix::GetColumn(size_type col) const { - Vector result; + Vector result{ typename Vector::Uninitialized{} }; for (size_type r = 0; r < Rows; ++r) result.at(r, 0) = at(r, col); return result; diff --git a/numerical/math/test/TestMatrix.cpp b/numerical/math/test/TestMatrix.cpp index 163a019..0b2ec28 100644 --- a/numerical/math/test/TestMatrix.cpp +++ b/numerical/math/test/TestMatrix.cpp @@ -427,6 +427,24 @@ TEST_F(MatrixFloatShapeTest, three_by_three_compound_assignment_and_trace) EXPECT_NEAR(square3.Transpose().at(0, 2), 7.0f, math::Tolerance()); } +TEST_F(MatrixFloatShapeTest, operators_write_every_element_of_their_result) +{ + constexpr math::Matrix column{ { 1.0f }, { 2.0f }, { 3.0f } }; + constexpr auto outer = column * column.Transpose(); + constexpr auto sum = outer + outer; + constexpr auto difference = sum - outer; + constexpr auto scaled = difference * 0.5f; + constexpr auto block = scaled.GetBlock<2, 2>(1, 1); + constexpr auto lastColumn = scaled.GetColumn(2); + + EXPECT_NEAR(outer.at(2, 1), 6.0f, math::Tolerance()); + EXPECT_NEAR(sum.at(0, 2), 6.0f, math::Tolerance()); + EXPECT_NEAR(difference.at(1, 1), 4.0f, math::Tolerance()); + EXPECT_NEAR(scaled.at(2, 2), 4.5f, math::Tolerance()); + EXPECT_NEAR(block.at(1, 0), 3.0f, math::Tolerance()); + EXPECT_NEAR(lastColumn.at(0, 0), 1.5f, math::Tolerance()); +} + TEST_F(MatrixFloatShapeTest, non_square_shapes_transpose_and_index) { math::Matrix row{ { 1.0f, 2.0f, 3.0f, 4.0f } };