From a5dbd01aa1306805ca055e5bb27a67ad3606e168 Mon Sep 17 00:00:00 2001 From: Cynopolis Date: Wed, 21 May 2025 17:48:18 -0400 Subject: [PATCH] Added a merge checker script that has to run before you can merge to main Updated merge checker and seperated the matrix tests fro mthe timing tests --- .gitea/workflows/Merge-Checker.yaml | 34 ++++- README.md | 1 + unit-tests/CMakeLists.txt | 9 ++ unit-tests/matrix-test-timings.txt | 14 -- unit-tests/matrix-tests.cpp | 216 +++------------------------ unit-tests/matrix-timing-tests.cpp | 119 +++++++++++++++ unit-tests/run-matrix-timing-test.sh | 7 - 7 files changed, 181 insertions(+), 219 deletions(-) delete mode 100644 unit-tests/matrix-test-timings.txt create mode 100644 unit-tests/matrix-timing-tests.cpp delete mode 100755 unit-tests/run-matrix-timing-test.sh diff --git a/.gitea/workflows/Merge-Checker.yaml b/.gitea/workflows/Merge-Checker.yaml index 4a220dd..969eb70 100644 --- a/.gitea/workflows/Merge-Checker.yaml +++ b/.gitea/workflows/Merge-Checker.yaml @@ -5,22 +5,44 @@ on: branches: ["**"] jobs: - build: + build_and_test: runs-on: ubuntu-latest steps: - name: Checkout source code uses: actions/checkout@v3 - - name: Install dependencies (CMake + Ninja + Compiler) + - name: Install dependencies (CMake + Ninja + build tools) run: | sudo apt-get update - sudo apt-get install -y cmake ninja-build build-essential + sudo apt-get install -y cmake ninja-build build-essential time - name: Configure project with CMake - run: | - cmake -G Ninja -S . -B build/ + run: cmake -G Ninja -S . -B build/ - name: Build with Ninja + run: ninja -C build/ + + - name: Run all unit tests except matrix-timing-tests run: | - ninja -C build/ + for test_exec in build/unit-tests/matrix-tests build/unit-tests/quaternion-tests build/unit-tests/vector-3d-tests; do + if [ -x "$test_exec" ]; then + echo "Running $test_exec" + "$test_exec" + else + echo "Warning: $test_exec not found or not executable" + fi + done + + - name: Run matrix-timing-tests with per-test timing output + run: | + if [ -x build/unit-tests/matrix-timing-tests ]; then + echo "Running matrix-timing-tests with timing" + # Run the test executable and capture timing info + # Assuming the executable prints timings per test internally + # If not, you might want to wrap each subtest with 'time' or + # run the entire executable with time: + /usr/bin/time -v build/unit-tests/matrix-timing-tests + else + echo "matrix-timing-tests executable not found or not executable" + fi \ No newline at end of file diff --git a/README.md b/README.md index b62bce7..34751ed 100644 --- a/README.md +++ b/README.md @@ -1 +1,2 @@ +# Introduction This matrix math library is focused on embedded development and avoids any heap memory allocation unless you explicitly ask for it. diff --git a/unit-tests/CMakeLists.txt b/unit-tests/CMakeLists.txt index 9ee3fa1..a57babd 100644 --- a/unit-tests/CMakeLists.txt +++ b/unit-tests/CMakeLists.txt @@ -16,6 +16,15 @@ target_link_libraries(matrix-tests Catch2::Catch2WithMain ) +# matrix timing tests +add_executable(matrix-timing-tests matrix-timing-tests.cpp) + +target_link_libraries(matrix-timing-tests + PRIVATE + matrix + Catch2::Catch2WithMain +) + # Vector 3D Tests add_executable(vector-3d-tests vector-tests.cpp) diff --git a/unit-tests/matrix-test-timings.txt b/unit-tests/matrix-test-timings.txt deleted file mode 100644 index 1e2dff2..0000000 --- a/unit-tests/matrix-test-timings.txt +++ /dev/null @@ -1,14 +0,0 @@ -Addition: 0.419 s -Subtraction: 0.421 s -Multiplication: 3.297 s -Scalar Multiplication: 0.329 s -Element Multiply: 0.306 s -Element Divide: 0.302 s -Minor Matrix: 0.331 s -Determinant: 0.177 s -Matrix of Minors: 0.766 s -Invert: 0.183 s -Transpose: 0.215 s -Normalize: 0.315 s -GET ROW: 0.008 s -GET COLUMN: 0.43 s diff --git a/unit-tests/matrix-tests.cpp b/unit-tests/matrix-tests.cpp index a6818a7..e6d8862 100644 --- a/unit-tests/matrix-tests.cpp +++ b/unit-tests/matrix-tests.cpp @@ -10,15 +10,13 @@ #include #include -TEST_CASE("Elementary Matrix Operations", "Matrix") -{ +TEST_CASE("Elementary Matrix Operations", "Matrix") { std::array arr2{5, 6, 7, 8}; Matrix<2, 2> mat1{1, 2, 3, 4}; Matrix<2, 2> mat2{arr2}; Matrix<2, 2> mat3{}; - SECTION("Initialization") - { + SECTION("Initialization") { // array initialization REQUIRE(mat1.Get(0, 0) == 1); REQUIRE(mat1.Get(0, 1) == 2); @@ -40,17 +38,14 @@ TEST_CASE("Elementary Matrix Operations", "Matrix") // large matrix Matrix<255, 255> mat6{}; mat6.Fill(4); - for (uint8_t row{0}; row < 255; row++) - { - for (uint8_t column{0}; column < 255; column++) - { + for (uint8_t row{0}; row < 255; row++) { + for (uint8_t column{0}; column < 255; column++) { REQUIRE(mat6.Get(row, column) == 4); } } } - SECTION("Fill") - { + SECTION("Fill") { mat1.Fill(0); REQUIRE(mat1.Get(0, 0) == 0); REQUIRE(mat1.Get(0, 1) == 0); @@ -70,12 +65,10 @@ TEST_CASE("Elementary Matrix Operations", "Matrix") REQUIRE(mat3.Get(1, 1) == -20); } - SECTION("Addition") - { + SECTION("Addition") { std::string strBuf1 = ""; mat1.ToString(strBuf1); - std::cout << "Matrix 1:\n" - << strBuf1 << std::endl; + std::cout << "Matrix 1:\n" << strBuf1 << std::endl; mat1.Add(mat2, mat3); @@ -93,8 +86,7 @@ TEST_CASE("Elementary Matrix Operations", "Matrix") REQUIRE(mat3.Get(1, 1) == 12); } - SECTION("Subtraction") - { + SECTION("Subtraction") { mat1.Sub(mat2, mat3); REQUIRE(mat3.Get(0, 0) == -4); @@ -111,8 +103,7 @@ TEST_CASE("Elementary Matrix Operations", "Matrix") REQUIRE(mat3.Get(1, 1) == -4); } - SECTION("Multiplication") - { + SECTION("Multiplication") { mat1.Mult(mat2, mat3); REQUIRE(mat3.Get(0, 0) == 19); @@ -131,8 +122,7 @@ TEST_CASE("Elementary Matrix Operations", "Matrix") // TODO: You need to add non-square multiplications to this. } - SECTION("Scalar Multiplication") - { + SECTION("Scalar Multiplication") { mat1.Mult(2, mat3); REQUIRE(mat3.Get(0, 0) == 2); @@ -141,8 +131,7 @@ TEST_CASE("Elementary Matrix Operations", "Matrix") REQUIRE(mat3.Get(1, 1) == 8); } - SECTION("Element Multiply") - { + SECTION("Element Multiply") { mat1.ElementMultiply(mat2, mat3); REQUIRE(mat3.Get(0, 0) == 5); @@ -151,8 +140,7 @@ TEST_CASE("Elementary Matrix Operations", "Matrix") REQUIRE(mat3.Get(1, 1) == 32); } - SECTION("Element Divide") - { + SECTION("Element Divide") { mat1.ElementDivide(mat2, mat3); REQUIRE_THAT(mat3.Get(0, 0), Catch::Matchers::WithinRel(0.2f, 1e-6f)); @@ -161,8 +149,7 @@ TEST_CASE("Elementary Matrix Operations", "Matrix") REQUIRE_THAT(mat3.Get(1, 1), Catch::Matchers::WithinRel(0.5f, 1e-6f)); } - SECTION("Minor Matrix") - { + SECTION("Minor Matrix") { // what about matrices of 0,0 or 1,1? // minor matrix for 2x2 matrix Matrix<1, 1> minorMat1{}; @@ -198,8 +185,7 @@ TEST_CASE("Elementary Matrix Operations", "Matrix") REQUIRE(minorMat4.Get(1, 1) == 5); } - SECTION("Determinant") - { + SECTION("Determinant") { float det1 = mat1.Det(); REQUIRE_THAT(det1, Catch::Matchers::WithinRel(-2.0F, 1e-6f)); @@ -216,8 +202,7 @@ TEST_CASE("Elementary Matrix Operations", "Matrix") REQUIRE_THAT(det5, Catch::Matchers::WithinRel(6.0F, 1e-6f)); } - SECTION("Matrix of Minors") - { + SECTION("Matrix of Minors") { mat1.MatrixOfMinors(mat3); REQUIRE_THAT(mat3.Get(0, 0), Catch::Matchers::WithinRel(4.0F, 1e-6f)); REQUIRE_THAT(mat3.Get(0, 1), Catch::Matchers::WithinRel(3.0F, 1e-6f)); @@ -238,8 +223,7 @@ TEST_CASE("Elementary Matrix Operations", "Matrix") REQUIRE_THAT(mat5.Get(2, 2), Catch::Matchers::WithinRel(-3.0F, 1e-6f)); } - SECTION("Invert") - { + SECTION("Invert") { mat3 = mat1.Invert(); REQUIRE_THAT(mat3.Get(0, 0), Catch::Matchers::WithinRel(-2.0F, 1e-6f)); REQUIRE_THAT(mat3.Get(0, 1), Catch::Matchers::WithinRel(1.0F, 1e-6f)); @@ -247,8 +231,7 @@ TEST_CASE("Elementary Matrix Operations", "Matrix") REQUIRE_THAT(mat3.Get(1, 1), Catch::Matchers::WithinRel(-0.5F, 1e-6f)); }; - SECTION("Transpose") - { + SECTION("Transpose") { // transpose a square matrix mat3 = mat1.Transpose(); @@ -271,8 +254,7 @@ TEST_CASE("Elementary Matrix Operations", "Matrix") REQUIRE(mat5.Get(2, 1) == 6); } - SECTION("Normalize") - { + SECTION("Normalize") { mat1.Normalize(mat3); float sqrt_30{sqrt(30)}; @@ -292,8 +274,7 @@ TEST_CASE("Elementary Matrix Operations", "Matrix") Catch::Matchers::WithinRel(0.957591346325f, 1e-6f)); } - SECTION("GET ROW") - { + SECTION("GET ROW") { Matrix<1, 2> mat1Rows{}; mat1.GetRow(0, mat1Rows); REQUIRE(mat1Rows.Get(0, 0) == 1); @@ -304,8 +285,7 @@ TEST_CASE("Elementary Matrix Operations", "Matrix") REQUIRE(mat1Rows.Get(0, 1) == 4); } - SECTION("GET COLUMN") - { + SECTION("GET COLUMN") { Matrix<2, 1> mat1Columns{}; mat1.GetColumn(0, mat1Columns); REQUIRE(mat1Columns.Get(0, 0) == 1); @@ -316,12 +296,8 @@ TEST_CASE("Elementary Matrix Operations", "Matrix") REQUIRE(mat1Columns.Get(1, 0) == 4); } - SECTION("Get Sub-Matrices") - { - Matrix<3, 3> mat4{ - 1, 2, 3, - 4, 5, 6, - 7, 8, 9}; + SECTION("Get Sub-Matrices") { + Matrix<3, 3> mat4{1, 2, 3, 4, 5, 6, 7, 8, 9}; Matrix<2, 2> mat5 = mat4.SubMatrix<2, 2, 0, 0>(); @@ -347,12 +323,8 @@ TEST_CASE("Elementary Matrix Operations", "Matrix") REQUIRE(mat7.Get(0, 2) == 3); } - SECTION("Set Sub-Matrices") - { - Matrix<3, 3> startMatrix{ - 1, 2, 3, - 4, 5, 6, - 7, 8, 9}; + SECTION("Set Sub-Matrices") { + Matrix<3, 3> startMatrix{1, 2, 3, 4, 5, 6, 7, 8, 9}; Matrix<3, 3> mat4 = startMatrix; Matrix<2, 2> mat5{10, 11, 12, 13}; @@ -381,144 +353,4 @@ TEST_CASE("Elementary Matrix Operations", "Matrix") REQUIRE(mat4.Get(0, 1) == 11); REQUIRE(mat4.Get(0, 2) == 12); } -} - -// basically re-run all of the previous tests with huge matrices and time the -// results. -TEST_CASE("Timing Tests", "Matrix") -{ - std::array arr1{}; - for (uint16_t i{0}; i < 50 * 50; i++) - { - arr1[i] = i; - } - std::array arr2{5, 6, 7, 8}; - for (uint16_t i{50 * 50}; i < 2 * 50 * 50; i++) - { - arr2[i] = i; - } - Matrix<50, 50> mat1{arr1}; - Matrix<50, 50> mat2{arr2}; - Matrix<50, 50> mat3{}; - - // A smaller matrix to use for really badly optimized operations - Matrix<4, 4> mat4{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; - Matrix<4, 4> mat5{}; - - SECTION("Addition") - { - for (uint32_t i{0}; i < 10000; i++) - { - mat3 = mat1 + mat2; - } - } - - SECTION("Subtraction") - { - for (uint32_t i{0}; i < 10000; i++) - { - mat3 = mat1 - mat2; - } - } - - SECTION("Multiplication") - { - for (uint32_t i{0}; i < 1000; i++) - { - mat3 = mat1 * mat2; - } - } - - SECTION("Scalar Multiplication") - { - for (uint32_t i{0}; i < 10000; i++) - { - mat3 = mat1 * 3; - } - } - - SECTION("Element Multiply") - { - for (uint32_t i{0}; i < 10000; i++) - { - mat1.ElementMultiply(mat2, mat3); - } - } - - SECTION("Element Divide") - { - for (uint32_t i{0}; i < 10000; i++) - { - mat1.ElementDivide(mat2, mat3); - } - } - - SECTION("Minor Matrix") - { - // what about matrices of 0,0 or 1,1? - // minor matrix for 2x2 matrix - Matrix<49, 49> minorMat1{}; - for (uint32_t i{0}; i < 10000; i++) - { - mat1.MinorMatrix(minorMat1, 0, 0); - } - } - - SECTION("Determinant") - { - for (uint32_t i{0}; i < 100000; i++) - { - float det1 = mat4.Det(); - } - } - - SECTION("Matrix of Minors") - { - for (uint32_t i{0}; i < 100000; i++) - { - mat4.MatrixOfMinors(mat5); - } - } - - SECTION("Invert") - { - for (uint32_t i{0}; i < 100000; i++) - { - mat5 = mat4.Invert(); - } - }; - - SECTION("Transpose") - { - for (uint32_t i{0}; i < 10000; i++) - { - mat3 = mat1.Transpose(); - } - } - - SECTION("Normalize") - { - for (uint32_t i{0}; i < 10000; i++) - { - mat1.Normalize(mat3); - } - } - - SECTION("GET ROW") - { - Matrix<1, 50> mat1Rows{}; - for (uint32_t i{0}; i < 1000000; i++) - { - mat1.GetRow(0, mat1Rows); - } - } - - SECTION("GET COLUMN") - { - Matrix<50, 1> mat1Columns{}; - for (uint32_t i{0}; i < 1000000; i++) - { - mat1.GetColumn(0, mat1Columns); - } - } } \ No newline at end of file diff --git a/unit-tests/matrix-timing-tests.cpp b/unit-tests/matrix-timing-tests.cpp new file mode 100644 index 0000000..9c63afe --- /dev/null +++ b/unit-tests/matrix-timing-tests.cpp @@ -0,0 +1,119 @@ +// include the unit test framework first +#include +#include + +// include the module you're going to test next +#include "Matrix.hpp" + +// any other libraries +#include +#include + +// basically re-run all of the matrix tests with huge matrices and time the +// results. +TEST_CASE("Timing Tests", "Matrix") { + std::array arr1{}; + for (uint16_t i{0}; i < 50 * 50; i++) { + arr1[i] = i; + } + std::array arr2{5, 6, 7, 8}; + for (uint16_t i{50 * 50}; i < 2 * 50 * 50; i++) { + arr2[i] = i; + } + Matrix<50, 50> mat1{arr1}; + Matrix<50, 50> mat2{arr2}; + Matrix<50, 50> mat3{}; + + // A smaller matrix to use for really badly optimized operations + Matrix<4, 4> mat4{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; + Matrix<4, 4> mat5{}; + + SECTION("Addition") { + for (uint32_t i{0}; i < 10000; i++) { + mat3 = mat1 + mat2; + } + } + + SECTION("Subtraction") { + for (uint32_t i{0}; i < 10000; i++) { + mat3 = mat1 - mat2; + } + } + + SECTION("Multiplication") { + for (uint32_t i{0}; i < 1000; i++) { + mat3 = mat1 * mat2; + } + } + + SECTION("Scalar Multiplication") { + for (uint32_t i{0}; i < 10000; i++) { + mat3 = mat1 * 3; + } + } + + SECTION("Element Multiply") { + for (uint32_t i{0}; i < 10000; i++) { + mat1.ElementMultiply(mat2, mat3); + } + } + + SECTION("Element Divide") { + for (uint32_t i{0}; i < 10000; i++) { + mat1.ElementDivide(mat2, mat3); + } + } + + SECTION("Minor Matrix") { + // what about matrices of 0,0 or 1,1? + // minor matrix for 2x2 matrix + Matrix<49, 49> minorMat1{}; + for (uint32_t i{0}; i < 10000; i++) { + mat1.MinorMatrix(minorMat1, 0, 0); + } + } + + SECTION("Determinant") { + for (uint32_t i{0}; i < 100000; i++) { + float det1 = mat4.Det(); + } + } + + SECTION("Matrix of Minors") { + for (uint32_t i{0}; i < 100000; i++) { + mat4.MatrixOfMinors(mat5); + } + } + + SECTION("Invert") { + for (uint32_t i{0}; i < 100000; i++) { + mat5 = mat4.Invert(); + } + }; + + SECTION("Transpose") { + for (uint32_t i{0}; i < 10000; i++) { + mat3 = mat1.Transpose(); + } + } + + SECTION("Normalize") { + for (uint32_t i{0}; i < 10000; i++) { + mat1.Normalize(mat3); + } + } + + SECTION("GET ROW") { + Matrix<1, 50> mat1Rows{}; + for (uint32_t i{0}; i < 1000000; i++) { + mat1.GetRow(0, mat1Rows); + } + } + + SECTION("GET COLUMN") { + Matrix<50, 1> mat1Columns{}; + for (uint32_t i{0}; i < 1000000; i++) { + mat1.GetColumn(0, mat1Columns); + } + } +} \ No newline at end of file diff --git a/unit-tests/run-matrix-timing-test.sh b/unit-tests/run-matrix-timing-test.sh deleted file mode 100755 index f6d6638..0000000 --- a/unit-tests/run-matrix-timing-test.sh +++ /dev/null @@ -1,7 +0,0 @@ -# be in the root folder of this project when you run this -cd build/ -ninja matrix-tests -echo "Running tests. This will take a while." -./unit-tests/matrix-tests -n "Timing Tests" -d yes > ../unit-tests/matrix-test-timings-temp.txt -cd ../unit-tests/ -python3 test-timing-post-process.py \ No newline at end of file