├── cmake └── math3dConfig.cmake.in ├── include └── math3d │ ├── core.h │ ├── opflags.h │ ├── math3d.hpp │ ├── utils.h │ ├── lu.hpp │ ├── vecn.hpp │ ├── quaternion.hpp │ ├── matn.hpp │ └── core.cuh ├── examples ├── CMakeLists.txt ├── vector.cpp └── matrix.cpp ├── tests ├── CMakeLists.txt ├── test_math3d.cpp ├── test_lu.cpp ├── test_vecn.cpp ├── test_matn.cpp └── test_quaternion.cpp ├── .gitignore ├── LICENSE ├── src └── utils.cpp ├── CMakeLists.txt └── README.md /cmake/math3dConfig.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") 4 | check_required_components("@PROJECT_NAME@") 5 | -------------------------------------------------------------------------------- /include/math3d/core.h: -------------------------------------------------------------------------------- 1 | #ifndef CORE_H 2 | #define CORE_H 3 | 4 | #include "lu.hpp" 5 | #include "matn.hpp" 6 | #include "opflags.h" 7 | #include "quaternion.hpp" 8 | #include "vecn.hpp" 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /examples/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | add_executable(vectorExample "vector.cpp") 3 | target_link_libraries(vectorExample PRIVATE ${PROJECT_NAME}) 4 | 5 | 6 | add_executable(matrixExample "matrix.cpp") 7 | target_link_libraries(matrixExample PRIVATE ${PROJECT_NAME}) 8 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # declare test executable 2 | 3 | 4 | include(FetchContent) 5 | 6 | FetchContent_Declare( 7 | googletest 8 | GIT_REPOSITORY https://github.com/google/googletest.git 9 | GIT_TAG v1.14.0 10 | ) 11 | FetchContent_MakeAvailable(googletest) 12 | add_library(GTest::GTest INTERFACE IMPORTED) 13 | target_link_libraries(GTest::GTest INTERFACE gtest_main) 14 | 15 | add_executable( 16 | ${PROJECT_NAME}_Test 17 | "test_matn.cpp" 18 | "test_vecn.cpp" 19 | "test_quaternion.cpp" 20 | "test_lu.cpp" 21 | "test_math3d.cpp" 22 | ) 23 | 24 | target_link_libraries(${PROJECT_NAME}_Test PRIVATE GTest::GTest ${PROJECT_NAME}) 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | 34 | # cmake 35 | CMakeLists.txt.user 36 | CMakeCache.txt 37 | CMakeFiles 38 | CMakeScripts 39 | Testing 40 | Makefile 41 | cmake_install.cmake 42 | install_manifest.txt 43 | compile_commands.json 44 | CTestTestfile.cmake 45 | _deps 46 | 47 | # build 48 | build/ 49 | test/ 50 | -------------------------------------------------------------------------------- /include/math3d/opflags.h: -------------------------------------------------------------------------------- 1 | #ifndef OPFLAGS_H 2 | #define OPFLAGS_H 3 | 4 | #include 5 | 6 | namespace math3d { 7 | 8 | enum opstatus_t : uint8_t { 9 | SUCCESS = 1, 10 | INDEX_ERROR = 2, 11 | SIZE_ERROR = 3, 12 | ARG_ERROR = 4, 13 | LU_ERROR = 5, 14 | NOT_IMPLEMENTED = 6 15 | }; 16 | 17 | struct OpResult { 18 | // 19 | size_t line_info = 0; 20 | const char *file_name{nullptr}; 21 | const char *fn_name{nullptr}; 22 | const char *call_name{nullptr}; 23 | const char *duration_info{nullptr}; 24 | 25 | opstatus_t status = NOT_IMPLEMENTED; 26 | bool success = false; 27 | 28 | OpResult() = default; 29 | ~OpResult() = default; 30 | 31 | OpResult(size_t line, const char *fname, 32 | const char *funcname, const char *cname, 33 | opstatus_t op) 34 | : line_info(line), file_name(fname), 35 | fn_name(funcname), call_name(cname), status(op), 36 | success(op == SUCCESS) {} 37 | }; 38 | 39 | } // namespace math3d 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 DKE 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/utils.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | namespace math3d { 6 | 7 | bool CHECK(OpResult r) { return r.status == SUCCESS; } 8 | 9 | std::stringstream to_xml(const OpResult &res) { 10 | std::stringstream s; 11 | s << ""; 17 | return s; 18 | } 19 | OpResult INFO(const OpResult &res) { 20 | std::stringstream s = to_xml(res); 21 | std::cerr << s.str() << std::endl; 22 | return res; 23 | } 24 | OpResult INFO_VERBOSE(const OpResult &res) { 25 | return INFO(res); 26 | } 27 | 28 | } // namespace math3d 29 | 30 | // 31 | std::ostream &operator<<(std::ostream &out, 32 | math3d::opstatus_t flag) { 33 | switch (flag) { 34 | case math3d::SUCCESS: { 35 | out << "SUCCESS"; 36 | break; 37 | } 38 | case math3d::SIZE_ERROR: { 39 | out << "SIZE_ERROR"; 40 | break; 41 | } 42 | case math3d::INDEX_ERROR: { 43 | out << "INDEX_ERROR"; 44 | break; 45 | } 46 | case math3d::ARG_ERROR: { 47 | out << "ARG_ERROR"; 48 | break; 49 | } 50 | case math3d::NOT_IMPLEMENTED: { 51 | out << "NOT_IMPLEMENTED"; 52 | break; 53 | } 54 | case math3d::LU_ERROR: { 55 | out << "LU_DECOMPOSITION_ERROR"; 56 | break; 57 | } 58 | } 59 | return out; 60 | } 61 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | project("math3d" 4 | VERSION 0.1.0 5 | DESCRIPTION "A C-ish C++ library with mostly n dimensional objects tailored for 3d math" 6 | HOMEPAGE_URL "https://github.com/D-K-E/math3d") 7 | 8 | 9 | include(GNUInstallDirs) 10 | 11 | add_library(${PROJECT_NAME} "src/utils.cpp") 12 | add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) 13 | 14 | 15 | target_include_directories( 16 | ${PROJECT_NAME} PUBLIC 17 | $ 18 | $) 19 | 20 | # provide install targets 21 | install(TARGETS ${PROJECT_NAME} 22 | EXPORT ${PROJECT_NAME}_Targets 23 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 24 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 25 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) 26 | 27 | 28 | include(CMakePackageConfigHelpers) 29 | write_basic_package_version_file("${PROJECT_NAME}ConfigVersion.cmake" 30 | VERSION ${PROJECT_VERSION} 31 | COMPATIBILITY SameMajorVersion) 32 | 33 | configure_package_config_file( 34 | "${PROJECT_SOURCE_DIR}/cmake/${PROJECT_NAME}Config.cmake.in" 35 | "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" 36 | INSTALL_DESTINATION 37 | ${CMAKE_INSTALL_DATAROOTDIR}/${PROJECT_NAME}/cmake 38 | ) 39 | 40 | # create name space for selecting 41 | install(EXPORT ${PROJECT_NAME}_Targets 42 | FILE ${PROJECT_NAME}Targets.cmake 43 | NAMESPACE ${PROJECT_NAME}:: 44 | DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${PROJECT_NAME}/cmake) 45 | 46 | 47 | install(FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" 48 | "${PROJECT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" 49 | DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${PROJECT_NAME}/cmake) 50 | 51 | install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/math3d DESTINATION include) 52 | 53 | option(MATH3D_BUILD_TESTS "Build test programs" OFF) 54 | if(MATH3D_BUILD_TESTS) 55 | add_subdirectory("tests") 56 | endif(MATH3D_BUILD_TESTS) 57 | 58 | option(MATH3D_BUILD_EXAMPLES "Build example programs" OFF) 59 | if (MATH3D_BUILD_EXAMPLES) 60 | add_subdirectory("examples") 61 | endif(MATH3D_BUILD_EXAMPLES) 62 | -------------------------------------------------------------------------------- /include/math3d/math3d.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MATH3D_HPP 2 | #define MATH3D_HPP 3 | 4 | #include "core.h" 5 | #include "utils.h" 6 | 7 | namespace math3d { 8 | 9 | template 10 | OpResult to_skew_mat(const vecn::VecN &vec, 11 | matn::MatN &out) { 12 | // 13 | T a3; 14 | vec(2, a3); 15 | T a2; 16 | vec(1, a2); 17 | T a1; 18 | vec(0, a1); 19 | 20 | matn::MatN m; 21 | 22 | T c1[] = {0, a3, -a2}; 23 | T c2[] = {-a3, 0, a1}; 24 | T c3[] = {a2, -a1, 0}; 25 | m.set_column(0, c1); 26 | m.set_column(1, c2); 27 | m.set_column(2, c3); 28 | out = m; 29 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 30 | "to_skew_mat", SUCCESS); 31 | } 32 | 33 | /**convert quaternion to rotation matrix 34 | from Vince, 2011, Quaternion ..., p. 123 35 | */ 36 | template 37 | OpResult toRotMat3x3(const quaternion::Quaternion &q_in, 38 | matn::MatN &out) { 39 | quaternion::Quaternion q; 40 | q_in.normalized(q); 41 | // 42 | T s; 43 | q.scalar(s); 44 | 45 | // 46 | vecn::VecN xyz; 47 | q.vector(xyz); 48 | T x; 49 | xyz(0, x); 50 | T y; 51 | xyz(1, y); 52 | T z; 53 | xyz(2, z); 54 | 55 | // to rotation matrix 56 | T c1_1 = 1 - (2 * ((y * y) + (z * z))); 57 | T c1_2 = 2 * (x * y + s * z); 58 | T c1_3 = 2 * (x * z - s * y); 59 | // 60 | T c2_1 = 2 * (x * y - s * z); 61 | T c2_2 = 1 - 2 * ((x * x) + (z * z)); 62 | T c2_3 = 2 * (y * z + s * x); 63 | // 64 | T c3_1 = 2 * (x * z + s * y); 65 | T c3_2 = 2 * (y * z - s * x); 66 | T c3_3 = 1 - 2 * ((x * x) + (y * y)); 67 | 68 | T c1[] = {c1_1, c1_2, c1_3}; 69 | T c2[] = {c2_1, c2_2, c2_3}; 70 | T c3[] = {c3_1, c3_2, c3_3}; 71 | matn::MatN m; 72 | m.set_column(0, c1); 73 | m.set_column(1, c2); 74 | m.set_column(2, c3); 75 | out = m; 76 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 77 | "toRotMat3x3", SUCCESS); 78 | } 79 | 80 | template 81 | OpResult invert_mat(const matn::MatN &m, 82 | matn::MatN &out) { 83 | // 84 | lu::LUdecomp lu_d(m); 85 | matn::MatN B; 86 | matn::identity(B); 87 | matn::MatN inv_m; 88 | lu_d.solve_mat(B, inv_m); 89 | out = inv_m; 90 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 91 | "invert_mat", SUCCESS); 92 | } 93 | 94 | } // namespace math3d 95 | 96 | #endif 97 | -------------------------------------------------------------------------------- /examples/vector.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | typedef float real; 4 | 5 | int main(void) { 6 | // 7 | math3d::vecn::VecN v1({-21, 1, 2, 1.53, 3}); 8 | math3d::vecn::VecN v2(3); 9 | 10 | // access to different properties of vector 11 | 12 | // size 13 | std::size_t size; 14 | v1(size); 15 | std::cout << (size == 5) << std::endl; 16 | // prints out: 1 17 | 18 | // all content 19 | real a[5]; 20 | v1(a); 21 | for (std::size_t i = 0; i < size; ++i) { 22 | std::cout << a[i] << " "; 23 | } 24 | std::cout << std::endl; 25 | // prints out: -21 1 2 1.53 3 26 | 27 | // single member 28 | real b; 29 | v1(3, b); 30 | std::cout << (b == 1.53f) << std::endl; 31 | // prints out: 1 32 | 33 | // set a new value 34 | v1(math3d::vecn::make_vecn_cell(0.1f, 1)); 35 | // or 36 | v1({.content = 0.1f, .index = 1}); 37 | 38 | // some basic math 39 | math3d::vecn::VecN out; 40 | v1.add(v2, out); 41 | std::cout << out << std::endl; 42 | // prints out: { -18, 3.1, 5, 4.53, 6 } 43 | 44 | // 45 | v1.subtract(v2, out); 46 | std::cout << out << std::endl; 47 | // prints out: { -24, -2.9, -1, -1.47, 0 } 48 | 49 | // elementwise multiplication 50 | v1.multiply(v2, out); 51 | std::cout << out << std::endl; 52 | // prints out: { -63, 0.3, 6, 4.59, 9 } 53 | 54 | v1.divide(v2, out); 55 | std::cout << out << std::endl; 56 | // prints out: { -7, 0.0333333, 0.666667, 0.51, 1 } 57 | 58 | // what if there is a 0 division? 59 | math3d::OpResult result = v1.divide(0, out); 60 | std::cout << (result.status == math3d::ARG_ERROR) 61 | << std::endl; 62 | // prints out: 1 63 | 64 | // notice the `out` doesn't get modified 65 | std::cout << out << std::endl; 66 | // prints out: { -7, 0.0333333, 0.666667, 0.51, 1 } 67 | 68 | // dot product 69 | real out2; 70 | v1.dot(v2, out2); 71 | std::cout << out2 << std::endl; 72 | // prints out: -43.11 73 | 74 | // some information about the call 75 | math3d::OpResult res; 76 | INFO_VERBOSE_MATH3D(v1.dot(v2, out2), res); 77 | // prints to std::cerr some useful information in xml 78 | // format 79 | /* 83 | */ 84 | 85 | // create a base vector 86 | math3d::vecn::base(out); 87 | std::cout << out << std::endl; 88 | // prints out: { 0, 1, 0, 0, 0 } 89 | 90 | math3d::vecn::base(out); 91 | std::cout << out << std::endl; 92 | // prints out: { 0, 0, 0, 1, 0 } 93 | 94 | return 0; 95 | } 96 | -------------------------------------------------------------------------------- /tests/test_math3d.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | typedef float real; 5 | using namespace math3d::lu; 6 | using namespace math3d::matn; 7 | using namespace math3d::vecn; 8 | using namespace math3d::quaternion; 9 | using namespace math3d; 10 | 11 | TEST(Math3DTest, test_invert_mat) { 12 | 13 | MatN m; 14 | m.set_column(0, {25, 64, 144}); 15 | m.set_column(1, {5, 8, 12}); 16 | m.set_column(2, {1, 1, 1}); 17 | MatN X; 18 | invert_mat(m, X); 19 | real c1[3], c2[3], c3[3]; 20 | X.get_column(0, c1); 21 | X.get_column(1, c2); 22 | X.get_column(2, c3); 23 | // 24 | EXPECT_EQ(std::round(c1[0] * 100.0f) / 100.0f, 0.05f); 25 | EXPECT_EQ(std::round(c1[1] * 100.0f) / 100.0f, -0.95f); 26 | EXPECT_EQ(std::round(c1[2] * 100.0f) / 100.0f, 4.57f); 27 | // 28 | EXPECT_EQ(std::round(c2[0] * 100.0f) / 100.0f, -0.08f); 29 | EXPECT_EQ(std::round(c2[1] * 100.0f) / 100.0f, 1.42f); 30 | EXPECT_EQ(std::round(c2[2] * 100.0f) / 100.0f, -5.0f); 31 | // 32 | EXPECT_EQ(std::round(c3[0] * 100.0f) / 100.0f, 0.04f); 33 | EXPECT_EQ(std::round(c3[1] * 100.0f) / 100.0f, -0.46f); 34 | EXPECT_EQ(std::round(c3[2] * 100.0f) / 100.0f, 1.43f); 35 | } 36 | 37 | TEST(Math3DTest, test_toRotMat3x3) { 38 | /* 39 | 0.35 + 0.2i + 0.3j + 0.1k 40 | 41 | 0.238095 0.190476 0.952381 42 | 0.72381 0.619048 -0.304762 43 | -0.647619 0.761905 0.0095236 44 | */ 45 | 46 | MatN m; 47 | Quaternion q(0.35f, 48 | VecN({0.2f, 0.3f, 0.1f})); 49 | toRotMat3x3(q, m); 50 | real c1[3], c2[3], c3[3]; 51 | m.get_column(0, c1); 52 | m.get_column(1, c2); 53 | m.get_column(2, c3); 54 | // 55 | EXPECT_EQ(std::round(c1[0] * 100.0f) / 100.0f, 0.24f); 56 | EXPECT_EQ(std::round(c1[1] * 100.0f) / 100.0f, 0.72f); 57 | EXPECT_EQ(std::round(c1[2] * 100.0f) / 100.0f, -0.65f); 58 | // 59 | EXPECT_EQ(std::round(c2[0] * 100.0f) / 100.0f, 0.19f); 60 | EXPECT_EQ(std::round(c2[1] * 100.0f) / 100.0f, 0.62f); 61 | EXPECT_EQ(std::round(c2[2] * 100.0f) / 100.0f, 0.76f); 62 | // 63 | EXPECT_EQ(std::round(c3[0] * 100.0f) / 100.0f, 0.95f); 64 | EXPECT_EQ(std::round(c3[1] * 100.0f) / 100.0f, -0.3f); 65 | EXPECT_EQ(std::round(c3[2] * 100.0f) / 100.0f, 0.01f); 66 | } 67 | 68 | TEST(Math3DTest, test_debug_check_m_true) { 69 | VecN v; 70 | std::size_t vsize = 5; 71 | OpResult res; 72 | CHECK_MATH3D(v(vsize), res); 73 | EXPECT_EQ(res.success, true); 74 | std::string cname = "v(vsize)"; 75 | std::string rcname = res.call_name; 76 | EXPECT_EQ(rcname, cname); 77 | } 78 | 79 | TEST(MatnTest, test_divide_scalar_value_false_check_macro) { 80 | 81 | real mv[] = {0, 1, 2, 0, 2, 4}; 82 | 83 | MatN m(mv); 84 | MatN out; 85 | auto r = CHECK(m.divide(0, out)); 86 | EXPECT_EQ(r, false); 87 | // 88 | } 89 | 90 | TEST(Math3DTest, test_debug_check_true) { 91 | VecN v; 92 | std::size_t vsize = 5; 93 | bool vflag = CHECK(v(vsize)); 94 | EXPECT_EQ(vflag, true); 95 | } 96 | -------------------------------------------------------------------------------- /examples/matrix.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | typedef float real; 4 | 5 | int main(void) { 6 | // empty constructor 7 | math3d::matn::MatN m1; 8 | 9 | // single array constructor 10 | math3d::matn::MatN m2({0, 1, 2, 0, 2, 4}); 11 | std::cout << m2 << std::endl; 12 | /** prints: 13 | 0 1 2 14 | 0 2 4 15 | */ 16 | 17 | // single value constructor 18 | math3d::matn::MatN m3(2.0f); 19 | std::cout << m3 << std::endl; 20 | /** 21 | 2 2 2 22 | 2 2 2 23 | */ 24 | 25 | // access to certain properties 26 | std::size_t snb; 27 | m2(snb); // get size 28 | std::cout << snb << std::endl; 29 | // 6 30 | 31 | std::size_t rnb, cols; 32 | auto r = m2(rnb, cols); // get row and col number 33 | std::cout << rnb << "x" << cols << std::endl; 34 | // 2x3 35 | 36 | // get column 37 | real c1[2]; 38 | m2.get_column(1, c1); 39 | for (std::size_t i = 0; i < 2; ++i) { 40 | std::cout << c1[i] << " "; 41 | } 42 | std::cout << std::endl; 43 | // prints: 1 2 44 | 45 | // get row 46 | real r1[3]; 47 | m2.get_row(1, r1); 48 | for (std::size_t i = 0; i < 3; ++i) { 49 | std::cout << r1[i] << " "; 50 | } 51 | std::cout << std::endl; 52 | // prints: 0 2 4 53 | 54 | // set value 55 | m2({.content = 1.1, .row = 0, .column = 1}); 56 | std::cout << m2 << std::endl; 57 | /* 58 | 0 1.1 2 59 | 0 2 4 60 | */ 61 | 62 | // set column 63 | c1[0] = 4.1f; 64 | c1[1] = 0.2f; 65 | m2.set_column(1, c1); 66 | std::cout << m2 << std::endl; 67 | /* 68 | 0 4.1 2 69 | 0 0.2 4 70 | */ 71 | 72 | // set row 73 | r1[0] = -1.1; 74 | r1[1] = 2.01; 75 | r1[2] = 3.1f; 76 | m2.set_row(1, r1); 77 | std::cout << m2 << std::endl; 78 | /* 79 | 0 4.1 2 80 | -1.1 2.01 3.1 81 | */ 82 | 83 | // set 2 rows 84 | real r2[3 * 2] = {1, 9, 6, 3, 0, 4}; 85 | math3d::matn::MatN padded; 86 | m1.add_rows<6>(r2, padded); 87 | std::cout << padded << std::endl; 88 | /* 89 | 0 0 90 | 0 0 91 | 0 0 92 | 1 9 93 | 6 3 94 | 0 4 95 | */ 96 | 97 | // transpose 98 | math3d::matn::MatN m2_T; 99 | m2.transpose(m2_T); 100 | std::cout << m2_T << std::endl; 101 | /** 102 | 0 4.1 103 | -1.1 2.01 104 | 3.1 0 105 | */ 106 | 107 | // some basic arithmetic 108 | math3d::matn::MatN mout; 109 | m2.add(m3, mout); 110 | std::cout << mout << std::endl; 111 | /* 112 | 2 6.1 4 113 | 0.9 4.01 5.1 114 | */ 115 | 116 | m2.subtract(m3, mout); 117 | std::cout << mout << std::endl; 118 | /* 119 | -2 2.1 0 120 | -3.1 0.009 1.1 121 | */ 122 | 123 | m2.hadamard_product(m3, mout); 124 | std::cout << mout << std::endl; 125 | /* 126 | 0 8.2 4 127 | -2.2 4.02 6.2 128 | */ 129 | 130 | m2.divide(m3, mout); 131 | std::cout << mout << std::endl; 132 | /* 133 | 0 2.05 1 134 | -0.55 1.005 1.55 135 | */ 136 | 137 | math3d::matn::MatN mout2; 138 | m2.multiply(m2_T, mout2); 139 | std::cout << mout2 << std::endl; 140 | /* 141 | 1.69 8.241 142 | 7.399 -0.4699 143 | */ 144 | 145 | // make the identity matrix 146 | math3d::matn::identity(mout2); 147 | std::cout << mout2 << std::endl; 148 | /* 149 | 1 0 150 | 0 1 151 | */ 152 | return 0; 153 | } 154 | -------------------------------------------------------------------------------- /tests/test_lu.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | typedef float real; 5 | using namespace math3d::lu; 6 | using namespace math3d::matn; 7 | using namespace math3d::vecn; 8 | using namespace math3d; 9 | 10 | /** 11 | all test values are 12 | from:https://math.libretexts.org/Bookshelves/Linear_Algebra/Introduction_to_Matrix_Algebra_(Kaw)/01%3A_Chapters/1.07%3A_LU_Decomposition_Method_for_Solving_Simultaneous_Linear_Equations 13 | */ 14 | 15 | TEST(LUTest, test_L) { 16 | // 17 | 18 | MatN m; 19 | m.set_column(0, {25, 64, 144}); 20 | m.set_column(1, {5, 8, 12}); 21 | m.set_column(2, {1, 1, 1}); 22 | LUdecomp lu_d(m); 23 | MatN L; 24 | lu_d.lower(L); 25 | real row1[3], row2[3], row3[3]; 26 | L.get_row(0, row1); 27 | L.get_row(1, row2); 28 | L.get_row(2, row3); 29 | // 30 | EXPECT_EQ(row1[0], 1); 31 | EXPECT_EQ(row1[1], 0); 32 | EXPECT_EQ(row1[2], 0); 33 | // 34 | EXPECT_EQ(std::round(row2[0] * 100.0f) / 100.0f, 2.56f); 35 | EXPECT_EQ(std::round(row2[1] * 100.0f) / 100.0f, 1.0f); 36 | EXPECT_EQ(std::round(row2[2] * 100.0f) / 100.0f, 0); 37 | // 38 | EXPECT_EQ(std::round(row3[0] * 100.0f) / 100.0f, 5.76f); 39 | EXPECT_EQ(std::round(row3[1] * 100.0f) / 100.0f, 3.5f); 40 | EXPECT_EQ(std::round(row3[2] * 100.0f) / 100.0f, 1.0); 41 | } 42 | TEST(LUTest, test_U) { 43 | 44 | MatN m; 45 | m.set_column(0, {25, 64, 144}); 46 | m.set_column(1, {5, 8, 12}); 47 | m.set_column(2, {1, 1, 1}); 48 | LUdecomp lu_d(m); 49 | MatN U; 50 | lu_d.upper(U); 51 | real row1[3], row2[3], row3[3]; 52 | U.get_row(0, row1); 53 | U.get_row(1, row2); 54 | U.get_row(2, row3); 55 | // 56 | EXPECT_EQ(row1[0], 25); 57 | EXPECT_EQ(row1[1], 5); 58 | EXPECT_EQ(row1[2], 1); 59 | // 60 | EXPECT_EQ(std::round(row2[0] * 100.0f) / 100.0f, 0); 61 | EXPECT_EQ(std::round(row2[1] * 100.0f) / 100.0f, -4.8f); 62 | EXPECT_EQ(std::round(row2[2] * 100.0f) / 100.0f, -1.56f); 63 | // 64 | EXPECT_EQ(std::round(row3[0] * 100.0f) / 100.0f, 0); 65 | EXPECT_EQ(std::round(row3[1] * 100.0f) / 100.0f, 0); 66 | EXPECT_EQ(std::round(row3[2] * 100.0f) / 100.0f, 0.7f); 67 | } 68 | TEST(LUTest, test_solve_forward) { 69 | 70 | MatN m; 71 | m.set_column(0, {25, 64, 144}); 72 | m.set_column(1, {5, 8, 12}); 73 | m.set_column(2, {1, 1, 1}); 74 | VecN b({106.8f, 177.2f, 279.2f}); 75 | LUdecomp lu_d(m); 76 | VecN x; 77 | lu_d.solve_forward(b, x); 78 | real row[3]; 79 | x(row); 80 | // 81 | EXPECT_EQ(std::round(row[0] * 100.0f) / 100.0f, 106.8f); 82 | EXPECT_EQ(std::round(row[1] * 100.0f) / 100.0f, -96.21f); 83 | EXPECT_EQ(std::round(row[2] * 100.0f) / 100.0f, 0.76f); 84 | // 85 | } 86 | TEST(LUTest, test_solve_backward) { 87 | 88 | MatN m; 89 | m.set_column(0, {25, 64, 144}); 90 | m.set_column(1, {5, 8, 12}); 91 | m.set_column(2, {1, 1, 1}); 92 | 93 | VecN b({106.8f, -96.21f, 0.76f}); 94 | LUdecomp lu_d(m); 95 | VecN x; 96 | lu_d.solve_backward(b, x); 97 | real row[3]; 98 | x(row); 99 | // 100 | EXPECT_EQ(std::round(row[0] * 100.0f) / 100.0f, 0.29f); 101 | EXPECT_EQ(std::round(row[1] * 100.0f) / 100.0f, 19.69f); 102 | EXPECT_EQ(std::round(row[2] * 100.0f) / 100.0f, 1.09f); 103 | // 104 | } 105 | 106 | TEST(LUTest, test_solve) { 107 | 108 | MatN m; 109 | m.set_column(0, {25, 64, 144}); 110 | m.set_column(1, {5, 8, 12}); 111 | m.set_column(2, {1, 1, 1}); 112 | VecN b({106.8f, 177.2f, 279.2f}); 113 | LUdecomp lu_d(m); 114 | VecN x; 115 | lu_d.solve(b, x); 116 | real row[3]; 117 | x(row); 118 | // 119 | EXPECT_EQ(std::round(row[0] * 100.0f) / 100.0f, 0.29f); 120 | EXPECT_EQ(std::round(row[1] * 100.0f) / 100.0f, 19.69f); 121 | EXPECT_EQ(std::round(row[2] * 100.0f) / 100.0f, 1.09f); 122 | // 123 | } 124 | 125 | TEST(LUTest, test_solve_mat) { 126 | 127 | MatN m; 128 | m.set_column(0, {25, 64, 144}); 129 | m.set_column(1, {5, 8, 12}); 130 | m.set_column(2, {1, 1, 1}); 131 | MatN B; 132 | identity(B); 133 | LUdecomp lu_d(m); 134 | MatN X; 135 | lu_d.solve_mat<3>(B, X); 136 | real c1[3], c2[3], c3[3]; 137 | X.get_column(0, c1); 138 | X.get_column(1, c2); 139 | X.get_column(2, c3); 140 | // 141 | EXPECT_EQ(std::round(c1[0] * 100.0f) / 100.0f, 0.05f); 142 | EXPECT_EQ(std::round(c1[1] * 100.0f) / 100.0f, -0.95f); 143 | EXPECT_EQ(std::round(c1[2] * 100.0f) / 100.0f, 4.57f); 144 | // 145 | EXPECT_EQ(std::round(c2[0] * 100.0f) / 100.0f, -0.08f); 146 | EXPECT_EQ(std::round(c2[1] * 100.0f) / 100.0f, 1.42f); 147 | EXPECT_EQ(std::round(c2[2] * 100.0f) / 100.0f, -5.0f); 148 | // 149 | EXPECT_EQ(std::round(c3[0] * 100.0f) / 100.0f, 0.04f); 150 | EXPECT_EQ(std::round(c3[1] * 100.0f) / 100.0f, -0.46f); 151 | EXPECT_EQ(std::round(c3[2] * 100.0f) / 100.0f, 1.43f); 152 | } 153 | -------------------------------------------------------------------------------- /include/math3d/utils.h: -------------------------------------------------------------------------------- 1 | #include "core.h" 2 | #include 3 | #include 4 | #include 5 | 6 | std::ostream &operator<<(std::ostream &out, 7 | math3d::opstatus_t flag); 8 | 9 | template 10 | std::ostream & 11 | operator<<(std::ostream &out, 12 | const math3d::matn::MatN &m) { 13 | T arr[R * C]; 14 | m(arr); 15 | for (std::size_t i = 0; i < (R * C); i++) { 16 | if (i % C == 0) { 17 | out << std::endl; 18 | } 19 | out << " " << arr[i] << " "; 20 | } 21 | out << std::endl; 22 | return out; 23 | } 24 | 25 | template 26 | std::ostream & 27 | operator<<(std::ostream &out, 28 | const math3d::vecn::VecN &m) { 29 | T arr[R]; 30 | m(arr); 31 | out << "{ " << arr[0]; 32 | for (std::size_t i = 1; i < R; i++) { 33 | out << ", " << arr[i]; 34 | } 35 | out << " }"; 36 | return out; 37 | } 38 | 39 | template 40 | std::ostream & 41 | operator<<(std::ostream &out, 42 | const math3d::quaternion::QuaternionBase &b) { 43 | switch (b) { 44 | case math3d::quaternion::kSCALAR: { 45 | out << "scalar_base" << std::endl; 46 | break; 47 | } 48 | case math3d::quaternion::kI: { 49 | out << "i_base" << std::endl; 50 | break; 51 | } 52 | case math3d::quaternion::kJ: { 53 | out << "j_base::" << std::endl; 54 | break; 55 | } 56 | case math3d::quaternion::kK: { 57 | out << "k_base::" << std::endl; 58 | break; 59 | } 60 | } 61 | return out; 62 | } 63 | template 64 | std::ostream &operator<<( 65 | std::ostream &out, 66 | const math3d::quaternion::QuaternionComponent &c) { 67 | switch (c.base) { 68 | case math3d::quaternion::kSCALAR: { 69 | out << c.r << std::endl; 70 | break; 71 | } 72 | case math3d::quaternion::kI: { 73 | out << c.r << "i"; 74 | break; 75 | } 76 | case math3d::quaternion::kJ: { 77 | out << c.r << "j"; 78 | break; 79 | } 80 | case math3d::quaternion::kK: { 81 | out << c.r << "k"; 82 | break; 83 | } 84 | } 85 | return out; 86 | } 87 | 88 | template 89 | std::ostream & 90 | operator<<(std::ostream &out, 91 | const math3d::quaternion::Quaternion &q) { 92 | math3d::quaternion::QuaternionComponent c; 93 | q(math3d::quaternion::kSCALAR, c); 94 | out << c << " + "; 95 | q(math3d::quaternion::kI, c); 96 | out << c << " + "; 97 | q(math3d::quaternion::kJ, c); 98 | out << c << " + "; 99 | q(math3d::quaternion::kK, c); 100 | out << c << std::endl; 101 | return out; 102 | } 103 | 104 | namespace math3d { 105 | bool CHECK(OpResult r); 106 | OpResult INFO(const OpResult &res); 107 | OpResult INFO_VERBOSE(const OpResult &res); 108 | } // namespace math3d 109 | 110 | /**Checks if operation was successful*/ 111 | #define CHECK_MATH3D(call, res) \ 112 | do { \ 113 | res = call; \ 114 | res.call_name = #call; \ 115 | res.line_info = __LINE__; \ 116 | res.file_name = __FILE__; \ 117 | } while (0) 118 | 119 | /**Prints information about the operation*/ 120 | #define INFO_MATH3D(call, res) \ 121 | do { \ 122 | res = call; \ 123 | res.call_name = #call; \ 124 | res.line_info = __LINE__; \ 125 | res.file_name = __FILE__; \ 126 | if (res.status != SUCCESS) { \ 127 | res = math3d::INFO(res); \ 128 | } \ 129 | } while (0) 130 | 131 | /**Prints everything about operation result*/ 132 | #define INFO_VERBOSE_MATH3D(call, res) \ 133 | do { \ 134 | auto start = std::chrono::steady_clock::now(); \ 135 | res = call; \ 136 | auto stop = std::chrono::steady_clock::now(); \ 137 | auto duration = std::chrono::duration_cast< \ 138 | std::chrono::microseconds>(stop - start); \ 139 | auto str_inf = std::to_string( \ 140 | static_cast(duration.count())); \ 141 | res.duration_info = str_inf.c_str(); \ 142 | res.call_name = #call; \ 143 | res.line_info = __LINE__; \ 144 | res.file_name = __FILE__; \ 145 | INFO_VERBOSE(res); \ 146 | } while (0) 147 | -------------------------------------------------------------------------------- /include/math3d/lu.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LU_HPP 2 | #define LU_HPP 3 | #include "matn.hpp" 4 | #include "vecn.hpp" 5 | namespace math3d { 6 | namespace lu { 7 | 8 | template struct LUdecomp { 9 | matn::MatN LU; 10 | /** 11 | decompose the given matrix into two matrices, L, U, so 12 | that A=LU 13 | implemented from Golub and Van Loan 2013, Matrix 14 | computations, p. 116, algorithm 3.2.1 15 | */ 16 | LUdecomp(const matn::MatN &in_m) { 17 | T mdata[N * N]; 18 | in_m(mdata); 19 | 20 | T ludata[N * N]; 21 | memcpy(ludata, mdata, (N * N) * sizeof(T)); 22 | 23 | // 24 | LU = matn::MatN(ludata); 25 | for (std::size_t k = 0; k < (N - 1); ++k) { 26 | // 27 | 28 | for (std::size_t r = k + 1; r < N; ++r) { 29 | // 30 | T r_k; 31 | LU(r, k, r_k); 32 | T k_k; 33 | LU(k, k, k_k); 34 | 35 | // set L 36 | LU(matn::make_matn_cell(r_k / k_k, r, k)); 37 | } 38 | for (std::size_t i = k + 1; i < N; ++i) { 39 | for (std::size_t j = k + 1; j < N; ++j) { 40 | // 41 | T i_k; 42 | LU(i, k, i_k); 43 | T k_j; 44 | LU(k, j, k_j); 45 | 46 | // 47 | T i_j; 48 | LU(i, j, i_j); 49 | LU(matn::make_matn_cell(i_j - i_k * k_j, i, j)); 50 | } 51 | } 52 | } 53 | } 54 | 55 | // tested 56 | OpResult upper(matn::MatN &U) const { 57 | for (std::size_t i = 0; i < N; ++i) { 58 | for (std::size_t j = i; j < N; ++j) { 59 | T udata; 60 | LU(i, j, udata); 61 | U({.content = udata, .row = i, .column = j}); 62 | } 63 | } 64 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 65 | "upper", SUCCESS); 66 | } 67 | // tested 68 | OpResult lower(matn::MatN &L) const { 69 | matn::identity(L); 70 | for (std::size_t i = 0; i < (N - 1); ++i) { 71 | for (std::size_t j = i + 1; j < N; ++j) { 72 | T udata; 73 | LU(j, i, udata); 74 | L({.content = udata, .row = j, .column = i}); 75 | } 76 | } 77 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 78 | "lower", SUCCESS); 79 | } 80 | 81 | // from Golub and Van Loan 2013, Matrix computations, p. 82 | // 108 83 | // tested 84 | OpResult solve_forward(const vecn::VecN &b, 85 | vecn::VecN &x) const { 86 | matn::MatN L; 87 | OpResult res = lower(L); 88 | T b_0; 89 | b(0, b_0); 90 | T L_0; 91 | L(0, 0, L_0); 92 | if (L_0 == 0) { 93 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 94 | "solve_forward", ARG_ERROR); 95 | } 96 | x({.content = b_0 / L_0, .index = 0}); 97 | for (std::size_t i = 1; i < N; ++i) { 98 | T x_i; 99 | b(i, x_i); 100 | x({.content = x_i, .index = i}); 101 | 102 | // compute vdot 103 | T out = 0; 104 | for (std::size_t j = 0; j <= (i - 1); ++j) { 105 | T ij; 106 | L(i, j, ij); 107 | T x_j; 108 | x(j, x_j); 109 | out += ij * x_j; 110 | } 111 | 112 | x(i, x_i); 113 | T diff = x_i - out; 114 | 115 | T i_i; 116 | L(i, i, i_i); 117 | 118 | if (i_i == 0) { 119 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 120 | "solve_forward", ARG_ERROR); 121 | } 122 | x({.content = diff / i_i, .index = i}); 123 | } 124 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 125 | "solve_forward", SUCCESS); 126 | } 127 | 128 | // from Golub and Van Loan 2013, Matrix computations, p. 129 | // 108 130 | // tested 131 | OpResult solve_backward(const vecn::VecN &b, 132 | vecn::VecN &x) const { 133 | matn::MatN U; 134 | OpResult res = upper(U); 135 | T u_n; 136 | U(N - 1, N - 1, u_n); 137 | if (u_n == 0) { 138 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 139 | "solve_backward", ARG_ERROR); 140 | } 141 | T b_n; 142 | b(N - 1, b_n); 143 | x({.content = b_n / u_n, .index = N - 1}); 144 | 145 | std::size_t i = N - 2; 146 | 147 | while (i >= 0) { 148 | T b_i; 149 | b(i, b_i); 150 | x({.content = b_i, .index = i}); 151 | 152 | // compute vdot 153 | T out = 0; 154 | for (std::size_t j = i + 1; j < N; ++j) { 155 | T ij; 156 | U(i, j, ij); 157 | T x_j; 158 | x(j, x_j); 159 | out += ij * x_j; 160 | } 161 | // 162 | T x_i; 163 | x(i, x_i); 164 | T i_i; 165 | U(i, i, i_i); 166 | 167 | if (i_i == 0) { 168 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 169 | "solve_backward", ARG_ERROR); 170 | } 171 | 172 | x({.content = (x_i - out) / i_i, .index = i}); 173 | 174 | // size_t acts weird if we don't decrement it this way 175 | if (i == 0) { 176 | break; 177 | } else { 178 | --i; 179 | } 180 | } 181 | 182 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 183 | "solve_backward", SUCCESS); 184 | } 185 | OpResult solve(const vecn::VecN &b, 186 | vecn::VecN &x) { 187 | vecn::VecN in_x; 188 | OpResult res = solve_forward(b, in_x); 189 | if (res.status != SUCCESS) { 190 | return res; 191 | } 192 | res = solve_backward(in_x, x); 193 | if (res.status != SUCCESS) { 194 | return res; 195 | } 196 | 197 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 198 | "solve", SUCCESS); 199 | } 200 | 201 | // from Golub and Van Loan 2013, Matrix computations, p. 202 | // 108 203 | template 204 | OpResult solve_mat(const matn::MatN &B, 205 | matn::MatN &X) { 206 | for (std::size_t j = 0; j < Q; ++j) { 207 | // 208 | T b_j[N]; 209 | B.get_column(j, b_j); 210 | vecn::VecN b(b_j); 211 | 212 | T x_j[N]; 213 | X.get_column(j, x_j); 214 | // 215 | vecn::VecN x(x_j); 216 | OpResult res = solve(b, x); 217 | if (res.status != SUCCESS) { 218 | return res; 219 | } 220 | // pass data to x_j 221 | x(x_j); 222 | X.set_column(j, x_j); 223 | } 224 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 225 | "solve_mat", SUCCESS); 226 | } 227 | }; 228 | 229 | } // namespace lu 230 | } // namespace math3d 231 | #endif 232 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # math3d 2 | yet another 3d math library 3 | 4 | This one doesn't throw any exceptions. It doesn't allocate memory on the heap. 5 | It doesn't contain any virtual function, nor a recursive function. It also 6 | doesn't use any function pointers. No concurrency and threading libraries are 7 | used either. All methods are `const` qualified except for setters. 8 | 9 | Hence it is highly compatible with OpenCL-C++ and CUDA at the same time. 10 | For CUDA, one would need to add `__host__`, `__device__` attributes in front 11 | of the methods. 12 | For OpenCL-C++ you probably don't need anything, just copy-paste the stuff 13 | into your kernel and it should be good to go. 14 | 15 | We also provide 2 optional headers for facilitating integration of this 16 | library to cuda/opencl projects: 17 | 18 | - `include/math3d/core.cuh`: contains all 4 objects (`VecN`, `MatN`, `Quaternion`, 19 | `LUdecomp`) in a single header. It is destined to be used in CUDA projects. 20 | 21 | - `include/math3d/core_cl.h`: contains all 4 objects (`VecN`, `MatN`, `Quaternion`, 22 | `LUdecomp`) in a single header. It is destined to be used in OpenCL-C++ 23 | projects. 24 | 25 | # Usage 26 | 27 | Here are some usage examples. 28 | 29 | The following snippet outlines several operations that can be done with 30 | vectors. 31 | 32 | ```c++ 33 | // vector example 34 | #include 35 | 36 | typedef float real; 37 | 38 | int main(void) { 39 | // 40 | math3d::vecn::VecN v1({-21, 1, 2, 1.53, 3}); 41 | math3d::vecn::VecN v2(3); 42 | 43 | // access to different properties of vector 44 | 45 | // size 46 | std::size_t size; 47 | v1(size); 48 | std::cout << (size == 5) << std::endl; 49 | // prints out: 1 50 | 51 | // all content 52 | real a[5]; 53 | v1(a); 54 | for (std::size_t i = 0; i < size; ++i) { 55 | std::cout << a[i] << " "; 56 | } 57 | std::cout << std::endl; 58 | // prints out: -21 1 2 1.53 3 59 | 60 | // single member 61 | real b; 62 | v1(3, b); 63 | std::cout << (b == 1.53f) << std::endl; 64 | // prints out: 1 65 | 66 | // set a new value 67 | v1(math3d::vecn::make_vecn_cell(0.1f, 1)); 68 | // or 69 | v1({.content = 0.1f, .index = 1}); 70 | 71 | // some basic math 72 | math3d::vecn::VecN out; 73 | v1.add(v2, out); 74 | std::cout << out << std::endl; 75 | // prints out: { -18, 3.1, 5, 4.53, 6 } 76 | 77 | // 78 | v1.subtract(v2, out); 79 | std::cout << out << std::endl; 80 | // prints out: { -24, -2.9, -1, -1.47, 0 } 81 | 82 | // elementwise multiplication 83 | v1.multiply(v2, out); 84 | std::cout << out << std::endl; 85 | // prints out: { -63, 0.3, 6, 4.59, 9 } 86 | 87 | v1.divide(v2, out); 88 | std::cout << out << std::endl; 89 | // prints out: { -7, 0.0333333, 0.666667, 0.51, 1 } 90 | 91 | // what if there is a 0 division? 92 | math3d::OpResult result = v1.divide(0, out); 93 | std::cout << (result.status == math3d::ARG_ERROR) 94 | << std::endl; 95 | // prints out: 1 96 | 97 | // notice the `out` doesn't get modified 98 | std::cout << out << std::endl; 99 | // prints out: { -7, 0.0333333, 0.666667, 0.51, 1 } 100 | 101 | // dot product 102 | real out2; 103 | v1.dot(v2, out2); 104 | std::cout << out2 << std::endl; 105 | // prints out: -43.11 106 | 107 | // some information about the call 108 | math3d::OpResult res; 109 | INFO_VERBOSE_MATH3D(v1.dot(v2, out2), res); 110 | // prints to std::cerr some useful information in xml 111 | // format 112 | /* 116 | */ 117 | 118 | // create a base vector 119 | math3d::vecn::base(out); 120 | std::cout << out << std::endl; 121 | // prints out: { 0, 1, 0, 0, 0 } 122 | 123 | math3d::vecn::base(out); 124 | std::cout << out << std::endl; 125 | // prints out: { 0, 0, 0, 1, 0 } 126 | 127 | return 0; 128 | } 129 | ``` 130 | 131 | The following snippet outlines several operations that can be done with 132 | matrices: 133 | ```c++ 134 | // matrix example 135 | #include 136 | 137 | typedef float real; 138 | 139 | int main(void) { 140 | // empty constructor 141 | math3d::matn::MatN m1; 142 | 143 | // single array constructor 144 | math3d::matn::MatN m2({0, 1, 2, 0, 2, 4}); 145 | std::cout << m2 << std::endl; 146 | /** prints: 147 | 0 1 2 148 | 0 2 4 149 | */ 150 | 151 | // single value constructor 152 | math3d::matn::MatN m3(2.0f); 153 | std::cout << m3 << std::endl; 154 | /** 155 | 2 2 2 156 | 2 2 2 157 | */ 158 | 159 | // access to certain properties 160 | std::size_t snb; 161 | m2(snb); // get size 162 | std::cout << snb << std::endl; 163 | // 6 164 | 165 | std::size_t rnb, cols; 166 | auto r = m2(rnb, cols); // get row and col number 167 | std::cout << rnb << "x" << cols << std::endl; 168 | // 2x3 169 | 170 | // get column 171 | real c1[2]; 172 | m2.get_column(1, c1); 173 | for (std::size_t i = 0; i < 2; ++i) { 174 | std::cout << c1[i] << " "; 175 | } 176 | std::cout << std::endl; 177 | // prints: 1 2 178 | 179 | // get row 180 | real r1[3]; 181 | m2.get_row(1, r1); 182 | for (std::size_t i = 0; i < 3; ++i) { 183 | std::cout << r1[i] << " "; 184 | } 185 | std::cout << std::endl; 186 | // prints: 0 2 4 187 | 188 | // set value 189 | m2({.content = 1.1, .row = 0, .column = 1}); 190 | std::cout << m2 << std::endl; 191 | /* 192 | 0 1.1 2 193 | 0 2 4 194 | */ 195 | 196 | // set column 197 | c1[0] = 4.1f; 198 | c1[1] = 0.2f; 199 | m2.set_column(1, c1); 200 | std::cout << m2 << std::endl; 201 | /* 202 | 0 4.1 2 203 | 0 0.2 4 204 | */ 205 | 206 | // set row 207 | r1[0] = -1.1; 208 | r1[1] = 2.01; 209 | r1[2] = 3.1f; 210 | m2.set_row(1, r1); 211 | std::cout << m2 << std::endl; 212 | /* 213 | 0 4.1 2 214 | -1.1 2.01 3.1 215 | */ 216 | 217 | // set 2 rows 218 | real r2[3 * 2] = {1, 9, 6, 3, 0, 4}; 219 | math3d::matn::MatN padded; 220 | m1.add_rows<6>(r2, padded); 221 | std::cout << padded << std::endl; 222 | /* 223 | 0 0 224 | 0 0 225 | 0 0 226 | 1 9 227 | 6 3 228 | 0 4 229 | */ 230 | 231 | // transpose 232 | math3d::matn::MatN m2_T; 233 | m2.transpose(m2_T); 234 | std::cout << m2_T << std::endl; 235 | /** 236 | 0 4.1 237 | -1.1 2.01 238 | 3.1 0 239 | */ 240 | 241 | // some basic arithmetic 242 | math3d::matn::MatN mout; 243 | m2.add(m3, mout); 244 | std::cout << mout << std::endl; 245 | /* 246 | 2 6.1 4 247 | 0.9 4.01 5.1 248 | */ 249 | 250 | m2.subtract(m3, mout); 251 | std::cout << mout << std::endl; 252 | /* 253 | -2 2.1 0 254 | -3.1 0.009 1.1 255 | */ 256 | 257 | m2.hadamard_product(m3, mout); 258 | std::cout << mout << std::endl; 259 | /* 260 | 0 8.2 4 261 | -2.2 4.02 6.2 262 | */ 263 | 264 | m2.divide(m3, mout); 265 | std::cout << mout << std::endl; 266 | /* 267 | 0 2.05 1 268 | -0.55 1.005 1.55 269 | */ 270 | 271 | math3d::matn::MatN mout2; 272 | m2.multiply(m2_T, mout2); 273 | std::cout << mout2 << std::endl; 274 | /* 275 | 1.69 8.241 276 | 7.399 -0.4699 277 | */ 278 | 279 | // make the identity matrix 280 | math3d::matn::identity(mout2); 281 | std::cout << mout2 << std::endl; 282 | /* 283 | 1 0 284 | 0 1 285 | */ 286 | return 0; 287 | } 288 | ``` 289 | -------------------------------------------------------------------------------- /include/math3d/vecn.hpp: -------------------------------------------------------------------------------- 1 | #ifndef VECN_HPP 2 | #define VECN_HPP 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "opflags.h" 10 | 11 | namespace math3d { 12 | namespace vecn { 13 | 14 | template struct VecNCell { 15 | T content; 16 | std::size_t index = 0; 17 | }; 18 | 19 | template 20 | VecNCell make_vecn_cell(const T &c, std::size_t i) { 21 | VecNCell cell{.content = c, .index = i}; 22 | return cell; 23 | } 24 | 25 | template class VecN { 26 | 27 | public: 28 | /*! Tested */ 29 | VecN() { 30 | for (std::size_t i = 0; i < N; ++i) { 31 | data[i] = 0; 32 | } 33 | } 34 | /*! Tested */ 35 | VecN(const T (&arr)[N]) { 36 | memcpy(data, arr, N * sizeof(T)); 37 | } 38 | VecN(T s) { 39 | for (std::size_t i = 0; i < N; ++i) { 40 | data[i] = s; 41 | } 42 | } 43 | /*! Tested */ 44 | OpResult operator()(std::size_t index, T &out) const { 45 | if (index >= N) { 46 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, 47 | "(size_t, T&)", INDEX_ERROR); 48 | return vflag; 49 | } 50 | out = data[index]; 51 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, 52 | "(size_t, T&)", SUCCESS); 53 | return vflag; 54 | } 55 | OpResult operator()(T (&out)[N]) const { 56 | memcpy(out, data, N * sizeof(T)); 57 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, 58 | "(T (&)[N])", SUCCESS); 59 | return vflag; 60 | } 61 | constexpr OpResult operator()(std::size_t &out) const { 62 | out = N; 63 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, 64 | "(size_t)", SUCCESS); 65 | return vflag; 66 | } 67 | 68 | /*! Tested */ 69 | OpResult operator()(const VecNCell &cell) { 70 | if (cell.index >= N) { 71 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 72 | "(const VecNCell&)", INDEX_ERROR); 73 | } 74 | data[cell.index] = cell.content; 75 | 76 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 77 | "(const VecNCell&)", SUCCESS); 78 | } 79 | 80 | /*! Tested */ 81 | OpResult add(T v, T (&out)[N]) const { 82 | VecN v_1(v); 83 | T v_in[N]; 84 | v_1(v_in); 85 | // 86 | auto res = add(v_in, out); 87 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, 88 | "add(T, T (&)[N])", res.status); 89 | return vflag; 90 | } 91 | 92 | /*! Tested */ 93 | OpResult add(T v, VecN &vout) const { 94 | T out[N]; 95 | auto res = add(v, out); 96 | vout = VecN(out); 97 | 98 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, 99 | "add(T, VecN&", res.status); 100 | return vflag; 101 | } 102 | /*! Tested */ 103 | OpResult add(const T (&v)[N], T (&out)[N]) const { 104 | auto fn = [](T thisel, T argel) { 105 | return thisel + argel; 106 | }; 107 | auto res = apply_el(v, fn, out); 108 | 109 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, 110 | "add(const T(&)[N], T (&)[N])", 111 | res.status); 112 | return vflag; 113 | } 114 | /*! Tested */ 115 | OpResult add(const VecN &v, VecN &out) const { 116 | T v_in[N]; 117 | v(v_in); 118 | T vout[N]; 119 | auto res = add(v_in, vout); 120 | out = VecN(vout); 121 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, 122 | "add(const VecN&, VecN&)", 123 | res.status); 124 | return vflag; 125 | } 126 | // 127 | OpResult subtract(T v, T (&out)[N]) const { 128 | VecN v_1(v); 129 | T v_in[N]; 130 | v_1(v_in); 131 | auto res = subtract(v_in, out); 132 | 133 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, 134 | "subtract(T, T (&)[N])", res.status); 135 | return vflag; 136 | } 137 | /*! Tested */ 138 | OpResult subtract(T v, VecN &vout) const { 139 | VecN v_1(v); 140 | T v_in[N]; 141 | v_1(v_in); 142 | T out[N]; 143 | auto res = subtract(v_in, out); 144 | vout = VecN(out); 145 | 146 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, 147 | "subtract(T, VecN&", res.status); 148 | return vflag; 149 | } 150 | /*! Tested */ 151 | OpResult subtract(const T (&v)[N], T (&out)[N]) const { 152 | auto fn = [](T thisel, T argel) { 153 | return thisel - argel; 154 | }; 155 | auto res = apply_el(v, fn, out); 156 | 157 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, 158 | "subtract(const T(&)[N], T (&)[N])", 159 | res.status); 160 | return vflag; 161 | } 162 | /*! Tested */ 163 | OpResult subtract(const VecN &v, 164 | VecN &out) const { 165 | T v_in[N]; 166 | v(v_in); 167 | T vout[N]; 168 | auto res = subtract(v_in, vout); 169 | out = VecN(vout); 170 | 171 | OpResult vflag( 172 | __LINE__, __FILE__, __FUNCTION__, 173 | "subtract(const VecN&, VecN&)", 174 | res.status); 175 | return vflag; 176 | } 177 | // 178 | OpResult multiply(T v, T (&out)[N]) const { 179 | T v_in[N]; 180 | VecN vv(v); 181 | vv(v_in); 182 | 183 | auto res = multiply(v_in, out); 184 | 185 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, 186 | "multiply(T, T (&)[N])", res.status); 187 | return vflag; 188 | } 189 | /*! Tested */ 190 | OpResult multiply(T v, VecN &vout) const { 191 | T v_in[N]; 192 | VecN vv(v); 193 | vv(v_in); 194 | 195 | T out[N]; 196 | auto res = multiply(v_in, out); 197 | vout = VecN(out); 198 | 199 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, 200 | "multiply(T, VecN&", res.status); 201 | return vflag; 202 | } 203 | /*! Tested */ 204 | OpResult multiply(const T (&v)[N], T (&out)[N]) const { 205 | auto fn = [](T thisel, T argel) { 206 | return thisel * argel; 207 | }; 208 | auto res = apply_el(v, fn, out); 209 | 210 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, 211 | "multiply(const T(&)[N], T (&)[N])", 212 | res.status); 213 | return vflag; 214 | } 215 | /*! Tested */ 216 | OpResult multiply(const VecN &v, 217 | VecN &out) const { 218 | T v_in[N]; 219 | v(v_in); 220 | T vout[N]; 221 | auto res = multiply(v_in, vout); 222 | out = VecN(vout); 223 | 224 | OpResult vflag( 225 | __LINE__, __FILE__, __FUNCTION__, 226 | "multiply(const VecN&, VecN&)", 227 | res.status); 228 | return vflag; 229 | } 230 | 231 | // 232 | OpResult divide(T v, T (&out)[N]) const { 233 | if (v == static_cast(0)) { 234 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, 235 | "divide(T, T (&)[N])", ARG_ERROR); 236 | return vflag; 237 | } 238 | 239 | T v_in[N]; 240 | VecN v_(v); 241 | v_(v_in); 242 | 243 | auto res = divide(v_in, out); 244 | 245 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, 246 | "divide(T, T (&)[N])", res.status); 247 | return vflag; 248 | } 249 | /*! Tested */ 250 | OpResult divide(T v, VecN &vout) const { 251 | // check for zero division 252 | if (v == static_cast(0)) { 253 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, 254 | "divide(T, VecN&", ARG_ERROR); 255 | return vflag; 256 | } 257 | T v_in[N]; 258 | VecN v_(v); 259 | v_(v_in); 260 | T out[N]; 261 | 262 | auto res = divide(v_in, out); 263 | vout = VecN(out); 264 | 265 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, 266 | "divide(T, VecN&", res.status); 267 | return vflag; 268 | } 269 | /*! Tested */ 270 | OpResult divide(const T (&v)[N], T (&out)[N]) const { 271 | for (std::size_t j = 0; j < N; j++) { 272 | if (v[j] == static_cast(0)) { 273 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, 274 | "divide(const T(&)[N], T (&)[N])", 275 | ARG_ERROR); 276 | return vflag; 277 | } 278 | } 279 | auto fn = [](T thisel, T argel) { 280 | return thisel / argel; 281 | }; 282 | auto res = apply_el(v, fn, out); 283 | 284 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, 285 | "divide(const T(&)[N], T (&)[N])", 286 | res.status); 287 | return vflag; 288 | } 289 | /*! Tested */ 290 | OpResult divide(const VecN &v, 291 | VecN &out) const { 292 | // check zero division 293 | T v_in[N]; 294 | v(v_in); 295 | T vout[N]; 296 | auto res = divide(v_in, vout); 297 | out = VecN(vout); 298 | 299 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, 300 | "divide(const VecN&, VecN&)", 301 | res.status); 302 | return vflag; 303 | } 304 | OpResult dot(const T &v, T &out) const { 305 | T v_in[N]; 306 | VecN v_(v); 307 | v_(v_in); 308 | dot(v_in, out); 309 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, 310 | "dot(const T&, T &)", SUCCESS); 311 | return vflag; 312 | } 313 | OpResult dot(const T (&v)[N], T &out) const { 314 | out = static_cast(0); 315 | for (std::size_t i = 0; i < N; i++) { 316 | out += data[i] * v[i]; 317 | } 318 | 319 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, 320 | "dot(const T(&)[N], T &)", SUCCESS); 321 | return vflag; 322 | } 323 | OpResult dot(const VecN &v, T &out) const { 324 | T v_in[N]; 325 | v(v_in); 326 | dot(v_in, out); 327 | 328 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, 329 | "dot(const VecN&, T &)", SUCCESS); 330 | return vflag; 331 | } 332 | 333 | OpResult cross(const VecN &v, 334 | VecN &out) const { 335 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, 336 | "cross(const VecN&, VecN &)", 337 | NOT_IMPLEMENTED); 338 | return vflag; 339 | } 340 | 341 | private: 342 | /** holds the vector data*/ 343 | T data[N]; 344 | 345 | template 346 | OpResult apply_el(const T (&v)[N], const Func &fn, 347 | T (&out)[N]) const { 348 | 349 | for (std::size_t i = 0; i < N; i++) { 350 | out[i] = fn(data[i], v[i]); 351 | } 352 | OpResult vflag( 353 | __LINE__, __FILE__, __FUNCTION__, 354 | "apply_el(const T(&)[N], const Func&, T(&)[N])", 355 | SUCCESS); 356 | return vflag; 357 | } 358 | }; 359 | 360 | template 361 | OpResult base(VecN &vout) { 362 | if (BaseOrder >= N) { 363 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, 364 | "base(VecN&)", ARG_ERROR); 365 | return vflag; 366 | } 367 | VecN out; 368 | out(make_vecn_cell(1, BaseOrder)); 369 | vout = out; 370 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, 371 | "base(VecN&)", SUCCESS); 372 | return vflag; 373 | } 374 | 375 | template 376 | OpResult cross(const VecN &v1, const VecN &v2, 377 | T &out) { 378 | T t1; 379 | v1(0, t1); 380 | T t2; 381 | v1(1, t2); 382 | T v_1; 383 | v2(0, v_1); 384 | T v_2; 385 | v2(1, v_2); 386 | T t1v2 = t1 * v_2; 387 | T t2v1 = t2 * v_1; 388 | out = t1v2 - t2v1; 389 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, 390 | "cross(const VecN&, T&)", SUCCESS); 391 | return vflag; 392 | } 393 | 394 | template 395 | OpResult cross(const VecN &a, const VecN &b, 396 | VecN &out) { 397 | T tx; 398 | a(0, tx); 399 | T ty; 400 | a(1, ty); 401 | T tz; 402 | a(2, tz); 403 | // 404 | T vx; 405 | b(0, vx); 406 | T vy; 407 | b(1, vy); 408 | T vz; 409 | b(2, vz); 410 | // 411 | T x = ty * vz - tz * vy; 412 | T y = tz * vx - tx * vz; 413 | T z = tx * vy - ty * vx; 414 | out(make_vecn_cell(x, 0)); 415 | out(make_vecn_cell(y, 1)); 416 | out(make_vecn_cell(z, 2)); 417 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, 418 | "cross(const VecN&, VecN&)", 419 | SUCCESS); 420 | return vflag; 421 | } 422 | 423 | } // namespace vecn 424 | } // namespace math3d 425 | 426 | #endif 427 | -------------------------------------------------------------------------------- /include/math3d/quaternion.hpp: -------------------------------------------------------------------------------- 1 | #ifndef QUATERNION_HPP 2 | #define QUATERNION_HPP 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "opflags.h" 10 | #include "vecn.hpp" 11 | 12 | namespace math3d { 13 | namespace quaternion { 14 | // holds quaternion related operations 15 | 16 | /**Quaternion bases*/ 17 | enum QuaternionBase : std::uint8_t { 18 | kSCALAR = 1, // null value for quaternion base it has 19 | // no effect on computation 20 | kI = 2, // i base for the second quaternion component 21 | kJ = 3, // j base for the third quaternion component 22 | kK = 4 // k base for the fourth quaternion component 23 | }; 24 | 25 | /** 26 | \brief Quaternion component 27 | */ 28 | template struct QuaternionComponent { 29 | QuaternionBase base; 30 | T r; 31 | 32 | QuaternionComponent() : r(0), base(kSCALAR) {} 33 | QuaternionComponent(QuaternionBase b, T a) 34 | : base(b), r(a) {} 35 | }; 36 | 37 | template class Quaternion { 38 | public: 39 | Quaternion() 40 | : coeffs{static_cast(0), static_cast(1), 41 | static_cast(1), static_cast(1)} {} 42 | // 43 | Quaternion(T c1, const QuaternionComponent qs[3]) { 44 | coeffs[0] = c1; 45 | coeffs[static_cast(qs[0].base) - 1] = 46 | qs[0].r; 47 | coeffs[static_cast(qs[1].base) - 1] = 48 | qs[1].r; 49 | coeffs[static_cast(qs[2].base) - 1] = 50 | qs[2].r; 51 | } 52 | Quaternion(T c1, const vecn::VecN &vs) { 53 | coeffs[0] = c1; 54 | T v_0; 55 | vs(0, v_0); 56 | T v_1; 57 | vs(1, v_1); 58 | T v_2; 59 | vs(2, v_2); 60 | coeffs[1] = v_0; 61 | coeffs[2] = v_1; 62 | coeffs[3] = v_2; 63 | } 64 | 65 | Quaternion(const QuaternionComponent qs[4]) { 66 | coeffs[static_cast(qs[0].base) - 1] = 67 | qs[0].r; 68 | coeffs[static_cast(qs[1].base) - 1] = 69 | qs[1].r; 70 | coeffs[static_cast(qs[2].base) - 1] = 71 | qs[2].r; 72 | coeffs[static_cast(qs[3].base) - 1] = 73 | qs[3].r; 74 | } 75 | 76 | OpResult operator()(T &out) const { return scalar(out); } 77 | OpResult scalar(T &out) const { 78 | 79 | out = coeffs[0]; 80 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 81 | "scalar", SUCCESS); 82 | } 83 | OpResult vector(vecn::VecN &out) const { 84 | T v[3]; 85 | v[0] = coeffs[1]; 86 | v[1] = coeffs[2]; 87 | v[2] = coeffs[3]; 88 | out = vecn::VecN(v); 89 | 90 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 91 | "vector", SUCCESS); 92 | } 93 | OpResult operator()(vecn::VecN &out) const { 94 | return vector(out); 95 | } 96 | OpResult operator()(const QuaternionBase &b, 97 | QuaternionComponent &c) const { 98 | switch (b) { 99 | case kSCALAR: { 100 | c = QuaternionComponent(kSCALAR, coeffs[0]); 101 | break; 102 | } 103 | case kI: { 104 | c = QuaternionComponent(kI, coeffs[1]); 105 | break; 106 | } 107 | case kJ: { 108 | c = QuaternionComponent(kJ, coeffs[2]); 109 | break; 110 | } 111 | case kK: { 112 | c = QuaternionComponent(kK, coeffs[3]); 113 | break; 114 | } 115 | } 116 | 117 | return OpResult( 118 | __LINE__, __FILE__, __FUNCTION__, 119 | "(const QuaternionBase&, QuaternionComponent&)", 120 | SUCCESS); 121 | } 122 | 123 | OpResult 124 | operator()(const QuaternionComponent &c) const { 125 | QuaternionBase b = c.base; 126 | coeffs[static_cast(c.base) - 1] = c.r; 127 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 128 | "(const QuaternionComponent&)", 129 | SUCCESS); 130 | } 131 | OpResult multiply(T t, vecn::VecN &out) const { 132 | vecn::VecN vec; 133 | vector(vec); 134 | return vec.multiply(t, out); 135 | } 136 | OpResult add(T t, vecn::VecN &out) const { 137 | vecn::VecN vec; 138 | vector(vec); 139 | return vec.add(t, out); 140 | } 141 | OpResult subtract(T t, vecn::VecN &out) const { 142 | vecn::VecN vec; 143 | vector(vec); 144 | return vec.subtract(t, out); 145 | } 146 | OpResult divide(T t, vecn::VecN &out) const { 147 | if (t == 0) { 148 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 149 | "divide", ARG_ERROR); 150 | } 151 | vecn::VecN vec; 152 | vector(vec); 153 | return vec.divide(t, out); 154 | } 155 | /** arithmetic operations with a vector on vector part*/ 156 | OpResult multiply(vecn::VecN t, 157 | vecn::VecN &out) const { 158 | vecn::VecN vec; 159 | vector(vec); 160 | return vec.multiply(t, out); 161 | } 162 | OpResult add(vecn::VecN t, 163 | vecn::VecN &out) const { 164 | 165 | vecn::VecN vec; 166 | vector(vec); 167 | return vec.add(t, out); 168 | } 169 | OpResult subtract(vecn::VecN t, 170 | vecn::VecN &out) const { 171 | 172 | vecn::VecN vec; 173 | vector(vec); 174 | return vec.subtract(t, out); 175 | } 176 | OpResult divide(vecn::VecN t, 177 | vecn::VecN &out) const { 178 | for (std::size_t i = 0; i < 3; i++) { 179 | T v; 180 | t(i, v); 181 | if (v == 0) { 182 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 183 | "divide", ARG_ERROR); 184 | } 185 | } 186 | vecn::VecN vec; 187 | vector(vec); 188 | return vec.divide(t, out); 189 | } 190 | /** dot product and cross product for two vec3*/ 191 | OpResult dot(const vecn::VecN &t, T &out) const { 192 | 193 | vecn::VecN vec; 194 | vector(vec); 195 | return vec.dot(t, out); 196 | } 197 | OpResult cross(const vecn::VecN &t, 198 | vecn::VecN &out) const { 199 | vecn::VecN vec; 200 | vector(vec); 201 | return vecn::cross(vec, t, out); 202 | } 203 | 204 | /** Quaternion product as it is shown by Vince 2011 p. 205 | * 63 206 | Given a quaternion \f[q_a = [s_a, a]\f] and 207 | another quaternion \f[q_b = [s_b, b]\f] 208 | Their product is equal to: 209 | \f[s_a s_b - a \cdot b, s_a b + s_b a + a \times b \f] 210 | */ 211 | OpResult hamilton_product(const Quaternion &q_b, 212 | Quaternion &out) const { 213 | // s_a, s_b, a, b 214 | T s_a = static_cast(0); 215 | auto res = scalar(s_a); 216 | if (res.status != SUCCESS) { 217 | return res; 218 | } 219 | 220 | T s_b = static_cast(0); 221 | res = q_b.scalar(s_b); 222 | if (res.status != SUCCESS) { 223 | return res; 224 | } 225 | 226 | vecn::VecN a; 227 | res = vector(a); 228 | if (res.status != SUCCESS) { 229 | return res; 230 | } 231 | 232 | vecn::VecN b; 233 | res = q_b.vector(b); 234 | if (res.status != SUCCESS) 235 | return res; 236 | 237 | // s_a * s_b 238 | T s_ab = s_a * s_b; 239 | 240 | // a \cdot b 241 | T a_dot_b = static_cast(0); 242 | res = a.dot(b, a_dot_b); 243 | if (res.status != SUCCESS) 244 | return res; 245 | 246 | // a \times b 247 | vecn::VecN cross_ab; 248 | res = vecn::cross(a, b, cross_ab); 249 | if (res.status != SUCCESS) { 250 | return res; 251 | } 252 | 253 | // s_a * b + s_b * a + a \times b 254 | T out_v[3]; 255 | T b_s[3]; 256 | T a_s[3]; 257 | T ab_s[3]; 258 | b(b_s); 259 | a(a_s); 260 | cross_ab(ab_s); 261 | for (std::size_t i = 0; i < 3; i++) { 262 | out_v[i] = s_a * b_s[i] + s_b * a_s[i] + ab_s[i]; 263 | } 264 | vecn::VecN tout(out_v); 265 | out = Quaternion(s_ab - a_dot_b, tout); 266 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 267 | "hamilton_product", SUCCESS); 268 | } 269 | OpResult conjugate(Quaternion &out) const { 270 | T s = static_cast(0); 271 | auto res = scalar(s); 272 | if (res.status != SUCCESS) 273 | return res; 274 | 275 | vecn::VecN vec; 276 | res = vector(vec); 277 | 278 | if (res.status != SUCCESS) 279 | return res; 280 | 281 | res = multiply(static_cast(-1), vec); 282 | if (res.status != SUCCESS) 283 | return res; 284 | 285 | out = Quaternion(s, vec); 286 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 287 | "conjugate", SUCCESS); 288 | } 289 | /** 290 | \brief from Vince 2011 - Quaternions for Computer 291 | Graphics p. 69 292 | */ 293 | OpResult normalized(Quaternion &out) const { 294 | T nval = static_cast(0); 295 | auto res = norm(nval); 296 | if (res.status != SUCCESS) 297 | return res; 298 | T inv_mag = static_cast(1.0) / nval; 299 | 300 | res = scalar(nval); 301 | 302 | if (res.status != SUCCESS) 303 | return res; 304 | 305 | T scalar_part = nval * inv_mag; 306 | vecn::VecN vs; 307 | res = vector(vs); 308 | if (res.status != SUCCESS) 309 | return res; 310 | res = multiply(inv_mag, vs); 311 | 312 | if (res.status != SUCCESS) 313 | return res; 314 | out = Quaternion(scalar_part, vs); 315 | 316 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 317 | "normalized", SUCCESS); 318 | } 319 | /** 320 | \brief from Vince 2011 - Quaternions for Computer 321 | Graphics p. 69 322 | */ 323 | OpResult inversed(Quaternion &out) const { 324 | // 325 | T q_norm = static_cast(0); 326 | auto res = norm_squared(q_norm); 327 | if (res.status != SUCCESS) { 328 | return res; 329 | } 330 | 331 | Quaternion conj; 332 | res = conjugate(conj); 333 | if (res.status != SUCCESS) { 334 | return res; 335 | } 336 | T c_s; vecn::VecN c_v; conj(c_s); conj(c_v); vecn::VecN v; 337 | c_v.divide(q_norm, v); 338 | out = Quaternion(c_s/ q_norm, v); 339 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 340 | "inversed", SUCCESS); 341 | } 342 | /** 343 | \brief from Vince 2011 - Quaternions for Computer 344 | Graphics p. 69 345 | */ 346 | OpResult add(const Quaternion &q, 347 | Quaternion &out) const { 348 | auto fn = [](T thisval, T tval) { 349 | return thisval + tval; 350 | }; 351 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "add", 352 | apply_el(q, fn, out)); 353 | } 354 | /** 355 | \brief from Vince 2011 - Quaternions for Computer 356 | Graphics p. 69 357 | */ 358 | OpResult subtract(const Quaternion &q, 359 | Quaternion &out) const { 360 | auto fn = [](T thisval, T tval) { 361 | return thisval - tval; 362 | }; 363 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 364 | "subtract", apply_el(q, fn, out)); 365 | } 366 | /** 367 | \brief from Vince 2011 - Quaternions for Computer 368 | Graphics p. 69 369 | */ 370 | OpResult product(const Quaternion &q, 371 | Quaternion &out) const { 372 | return hamilton_product(q, out); 373 | } 374 | /** 375 | \brief from Vince 2011 - Quaternions for Computer 376 | Graphics p. 69 377 | */ 378 | OpResult product(T r, Quaternion &out) const { 379 | auto fn = [](T thisval, T tval) { 380 | return thisval * tval; 381 | }; 382 | Quaternion q(r, vecn::VecN(r)); 383 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 384 | "product", apply_el(q, fn, out)); 385 | } 386 | OpResult power(std::size_t i, Quaternion &out) const { 387 | Quaternion accumulant = *this; 388 | Quaternion result2 = *this; 389 | for (std::size_t j = 1; j < i; j++) { 390 | accumulant.hamilton_product(result2, accumulant); 391 | } 392 | out = accumulant; 393 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 394 | "power", SUCCESS); 395 | } 396 | OpResult squared(Quaternion &out) const { 397 | Quaternion r1 = *this; 398 | Quaternion r2 = *this; 399 | auto res = product(r1, out); 400 | if (res.status != SUCCESS) 401 | return res; 402 | 403 | res = product(r2, out); 404 | if (res.status != SUCCESS) 405 | return res; 406 | 407 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 408 | "squared", SUCCESS); 409 | } 410 | /** 411 | \brief from Vince 2011 - Quaternions for Computer 412 | Graphics p. 69 413 | */ 414 | OpResult norm(T &out) const { 415 | auto res = norm_squared(out); 416 | 417 | if (res.status != SUCCESS) 418 | return res; 419 | // 420 | out = sqrt(out); 421 | 422 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 423 | "norm", SUCCESS); 424 | } 425 | 426 | /** 427 | \brief from Vince 2011 - Quaternions for Computer 428 | Graphics p. 25 429 | */ 430 | OpResult norm_squared(T &out) const { 431 | T s = static_cast(0); 432 | auto res = scalar(s); 433 | if (res.status != SUCCESS) 434 | return res; 435 | vecn::VecN vec; 436 | res = vector(vec); 437 | if (res.status != SUCCESS) 438 | return res; 439 | 440 | T a = s * s; 441 | T b; 442 | T v[3]; 443 | vec(v); 444 | res = vec.dot(v, b); 445 | if (res.status != SUCCESS) 446 | return res; 447 | out = a + b; 448 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 449 | "norm_squared", SUCCESS); 450 | } 451 | OpResult magnitude(T &out) const { return norm(out); } 452 | 453 | private: 454 | template 455 | opstatus_t apply_el(const Quaternion &q, const Func &fn, 456 | Quaternion &out) const { 457 | T s; 458 | scalar(s); 459 | vecn::VecN v; 460 | vector(v); 461 | T vs[3]; 462 | v(vs); 463 | 464 | // 465 | T q_s; 466 | q.scalar(q_s); 467 | vecn::VecN q_v; 468 | q.vector(q_v); 469 | T qs[3]; 470 | q_v(qs); 471 | // 472 | T rs[3]; 473 | for (std::size_t i = 0; i < 3; ++i) { 474 | rs[i] = fn(vs[i], qs[i]); 475 | } 476 | T r = fn(s, q_s); 477 | out = Quaternion(r, vecn::VecN(rs)); 478 | return SUCCESS; 479 | } 480 | 481 | // 482 | T coeffs[4]; 483 | }; 484 | 485 | } // namespace quaternion 486 | 487 | }; // namespace math3d 488 | 489 | #endif 490 | -------------------------------------------------------------------------------- /tests/test_vecn.cpp: -------------------------------------------------------------------------------- 1 | // test file for quaternion 2 | #include 3 | #include 4 | 5 | /*! @{ 6 | */ 7 | 8 | typedef float real; 9 | using namespace math3d::vecn; 10 | using namespace math3d; 11 | 12 | /*! @{ testing constructors for the vector 13 | */ 14 | 15 | TEST(VecNTest, test_empty_constructor) { 16 | VecN vec; 17 | std::size_t vsize; 18 | vec(vsize); 19 | EXPECT_EQ(vsize, static_cast(2)); 20 | } 21 | 22 | TEST(VecNTest, test_vector_constructor) { 23 | real v1 = static_cast(-21); 24 | real v2 = static_cast(1); 25 | real v3 = static_cast(2); 26 | real v4 = static_cast(1.53); 27 | real v5 = static_cast(3); 28 | VecN v({v1, v2, v3, v4, v5}); 29 | real t1 = 0, t2 = 0, t3 = 0, t4 = 0, t5 = 0, t6 = 0; 30 | // test size 31 | std::size_t vsize = 6156; 32 | v(vsize); 33 | EXPECT_EQ(vsize, 5); 34 | 35 | // test values 36 | // first value 37 | EXPECT_EQ(v(0, t1).status, SUCCESS); 38 | EXPECT_EQ(t1, v1); 39 | 40 | // second value 41 | EXPECT_EQ(v(1, t2).status, SUCCESS); 42 | EXPECT_EQ(t2, v2); 43 | // third value 44 | 45 | EXPECT_EQ(v(2, t3).status, SUCCESS); 46 | EXPECT_EQ(t3, v3); 47 | 48 | // fourth value 49 | EXPECT_EQ(v(3, t4).status, SUCCESS); 50 | EXPECT_EQ(t4, v4); 51 | 52 | // fifth value 53 | EXPECT_EQ(v(4, t5).status, SUCCESS); 54 | EXPECT_EQ(t5, v5); 55 | 56 | // sixth value 57 | EXPECT_EQ(v(5, t6).status, INDEX_ERROR); 58 | } 59 | 60 | TEST(VecNTest, test_dimension_constructor) { 61 | VecN v(5); 62 | real r1 = static_cast(5); 63 | // test values 64 | real t1 = 0; 65 | // first value 66 | EXPECT_EQ(v(0, t1).status, SUCCESS); 67 | EXPECT_EQ(t1, r1); 68 | 69 | // second value 70 | EXPECT_EQ(v(1, t1).status, SUCCESS); 71 | EXPECT_EQ(t1, r1); 72 | // third value 73 | 74 | EXPECT_EQ(v(2, t1).status, SUCCESS); 75 | EXPECT_EQ(t1, r1); 76 | 77 | // fourth value 78 | EXPECT_EQ(v(3, t1).status, SUCCESS); 79 | EXPECT_EQ(t1, r1); 80 | 81 | // fifth value 82 | 83 | EXPECT_EQ(v(make_vecn_cell(t1, 4)).status, SUCCESS); 84 | EXPECT_EQ(t1, r1); 85 | 86 | // sixth value 87 | EXPECT_EQ(v(5, t1).status, INDEX_ERROR); 88 | EXPECT_EQ(v(make_vecn_cell(t1, 5)).status, INDEX_ERROR); 89 | } 90 | 91 | TEST(VecNTest, test_vecn_flag_fn_name) { 92 | VecN v; 93 | std::size_t vsize = 5; 94 | auto vflag = v(vsize); 95 | const std::string fname = vflag.fn_name; 96 | const std::string compval = "operator()"; 97 | EXPECT_EQ(fname, compval); 98 | } 99 | 100 | /*! @} */ 101 | 102 | /*! @{ testing base dimensions for the vector 103 | */ 104 | TEST(VecNTest, test_to_base_dimensions_vector) { 105 | // 106 | VecN vout{}; 107 | base(vout); 108 | real out[6]; 109 | vout(out); 110 | EXPECT_EQ(out[1], static_cast(1)); 111 | } 112 | TEST(VecNTest, test_to_base_dimensions_vecn) { 113 | // 114 | VecN vout; 115 | base(vout); 116 | real tout = 0; 117 | EXPECT_EQ(vout(1, tout).status, SUCCESS); 118 | EXPECT_EQ(tout, static_cast(1)); 119 | } 120 | 121 | /*! @} */ 122 | 123 | /*! @{ testing summation for the vector 124 | */ 125 | TEST(VecNTest, test_add_scalar_vector) { 126 | real inv[5]; 127 | inv[0] = 1; 128 | inv[1] = 2; 129 | inv[2] = 3; 130 | inv[3] = 2; 131 | inv[4] = 1; 132 | VecN v(inv); 133 | real out[5]; 134 | auto result = v.add(1, out).status; 135 | EXPECT_EQ(result, SUCCESS); 136 | EXPECT_EQ(out[0], 2); 137 | EXPECT_EQ(out[1], 3); 138 | EXPECT_EQ(out[2], 4); 139 | EXPECT_EQ(out[3], 3); 140 | EXPECT_EQ(out[4], 2); 141 | } 142 | TEST(VecNTest, test_add_scalar_vecn) { 143 | real inv[5]; 144 | inv[0] = 1; 145 | inv[1] = 2; 146 | inv[2] = 3; 147 | inv[3] = 2; 148 | inv[4] = 1; 149 | VecN v(inv); 150 | VecN out; 151 | auto result = v.add(1, out).status; 152 | EXPECT_EQ(result, SUCCESS); 153 | real t = static_cast(0); 154 | out(0, t); 155 | EXPECT_EQ(t, 2); 156 | out(1, t); 157 | EXPECT_EQ(t, 3); 158 | out(2, t); 159 | EXPECT_EQ(t, 4); 160 | out(3, t); 161 | EXPECT_EQ(t, 3); 162 | out(4, t); 163 | EXPECT_EQ(t, 2); 164 | } 165 | 166 | TEST(VecNTest, test_add_vector_to_vector_t) { 167 | real inv[5]; 168 | inv[0] = 1; 169 | inv[1] = 2; 170 | inv[2] = 3; 171 | inv[3] = 2; 172 | inv[4] = 1; 173 | VecN v(inv); 174 | real out[5]; 175 | real avec[5]; 176 | avec[0] = 1; 177 | avec[1] = 2; 178 | avec[2] = 2; 179 | avec[3] = 2; 180 | avec[4] = 1; 181 | auto result = v.add(avec, out).status; 182 | EXPECT_EQ(result, SUCCESS); 183 | EXPECT_EQ(out[0], static_cast(2)); 184 | EXPECT_EQ(out[1], static_cast(4)); 185 | EXPECT_EQ(out[2], static_cast(5)); 186 | EXPECT_EQ(out[3], static_cast(4)); 187 | EXPECT_EQ(out[4], static_cast(2)); 188 | } 189 | TEST(VecNTest, test_add_vecn_to_vecn_t) { 190 | real inv[5]; 191 | inv[0] = 1; 192 | inv[1] = 2; 193 | inv[2] = 3; 194 | inv[3] = 2; 195 | inv[4] = 1; 196 | VecN v(inv); 197 | 198 | // output vector 199 | VecN out; 200 | 201 | real avec[5]; 202 | // prepare vector to add 203 | avec[0] = 1; 204 | avec[1] = 2; 205 | avec[2] = 2; 206 | avec[3] = 2; 207 | avec[4] = 1; 208 | 209 | VecN avecn(avec); 210 | auto result = v.add(avecn, out).status; 211 | 212 | real t = static_cast(0); 213 | out(0, t); 214 | // 215 | EXPECT_EQ(result, SUCCESS); 216 | EXPECT_EQ(t, static_cast(2)); 217 | 218 | out(1, t); 219 | EXPECT_EQ(t, static_cast(4)); 220 | 221 | out(2, t); 222 | EXPECT_EQ(t, static_cast(5)); 223 | 224 | out(3, t); 225 | EXPECT_EQ(t, static_cast(4)); 226 | 227 | out(4, t); 228 | EXPECT_EQ(t, static_cast(2)); 229 | } 230 | /*! @} */ 231 | 232 | /*! @{ testing subtraction for the vector 233 | */ 234 | TEST(VecNTest, test_subtract_scalar_vector) { 235 | real inv[5]; 236 | inv[0] = 1; 237 | inv[1] = 2; 238 | inv[2] = 3; 239 | inv[3] = 2; 240 | inv[4] = 1; 241 | VecN v(inv); 242 | real out[5]; 243 | auto result = v.subtract(1, out).status; 244 | EXPECT_EQ(result, SUCCESS); 245 | EXPECT_EQ(out[0], 0); 246 | EXPECT_EQ(out[1], 1); 247 | EXPECT_EQ(out[2], 2); 248 | EXPECT_EQ(out[3], 1); 249 | EXPECT_EQ(out[4], 0); 250 | } 251 | TEST(VecNTest, test_subtract_scalar_vecn) { 252 | real inv[5]; 253 | inv[0] = 1; 254 | inv[1] = 2; 255 | inv[2] = 3; 256 | inv[3] = 2; 257 | inv[4] = 1; 258 | VecN v(inv); 259 | VecN out; 260 | auto result = v.subtract(1, out).status; 261 | EXPECT_EQ(result, SUCCESS); 262 | real t = static_cast(0); 263 | out(0, t); 264 | EXPECT_EQ(t, 0); 265 | out(1, t); 266 | EXPECT_EQ(t, 1); 267 | out(2, t); 268 | EXPECT_EQ(t, 2); 269 | out(3, t); 270 | EXPECT_EQ(t, 1); 271 | out(4, t); 272 | EXPECT_EQ(t, 0); 273 | } 274 | TEST(VecNTest, test_subtract_vector_to_vector_t) { 275 | real inv[5]; 276 | inv[0] = 1; 277 | inv[1] = 2; 278 | inv[2] = 3; 279 | inv[3] = 2; 280 | inv[4] = 1; 281 | VecN v(inv); 282 | real out[5]; 283 | real avec[5]; 284 | avec[0] = 1; 285 | avec[1] = 2; 286 | avec[2] = 2; 287 | avec[3] = 2; 288 | avec[4] = 1; 289 | auto result = v.subtract(avec, out).status; 290 | EXPECT_EQ(result, SUCCESS); 291 | EXPECT_EQ(out[0], static_cast(0)); 292 | EXPECT_EQ(out[1], static_cast(0)); 293 | EXPECT_EQ(out[2], static_cast(1)); 294 | EXPECT_EQ(out[3], static_cast(0)); 295 | EXPECT_EQ(out[4], static_cast(0)); 296 | } 297 | TEST(VecNTest, test_subtract_vecn_to_vecn_t) { 298 | real inv[5]; 299 | inv[0] = 1; 300 | inv[1] = 2; 301 | inv[2] = 3; 302 | inv[3] = 2; 303 | inv[4] = 1; 304 | VecN v(inv); 305 | 306 | // output vector 307 | VecN out; 308 | 309 | real avec[5]; 310 | // prepare vector to subtract 311 | avec[0] = 1; 312 | avec[1] = 2; 313 | avec[2] = 2; 314 | avec[3] = 2; 315 | avec[4] = 1; 316 | 317 | VecN avecn(avec); 318 | auto result = v.subtract(avecn, out).status; 319 | 320 | real t = static_cast(0); 321 | out(0, t); 322 | // 323 | EXPECT_EQ(result, SUCCESS); 324 | EXPECT_EQ(t, static_cast(0)); 325 | 326 | out(1, t); 327 | EXPECT_EQ(t, static_cast(0)); 328 | 329 | out(2, t); 330 | EXPECT_EQ(t, static_cast(1)); 331 | 332 | out(3, t); 333 | EXPECT_EQ(t, static_cast(0)); 334 | 335 | out(4, t); 336 | EXPECT_EQ(t, static_cast(0)); 337 | } 338 | 339 | /*! @} */ 340 | 341 | /*! @{ testing element-wise multiplication for the vector 342 | */ 343 | TEST(VecNTest, test_multiply_scalar_vector) { 344 | real inv[5]; 345 | inv[0] = 1; 346 | inv[1] = 2; 347 | inv[2] = 3; 348 | inv[3] = 2; 349 | inv[4] = 1; 350 | VecN v(inv); 351 | real out[5]; 352 | auto result = v.multiply(2, out).status; 353 | EXPECT_EQ(result, SUCCESS); 354 | EXPECT_EQ(out[0], 2); 355 | EXPECT_EQ(out[1], 4); 356 | EXPECT_EQ(out[2], 6); 357 | EXPECT_EQ(out[3], 4); 358 | EXPECT_EQ(out[4], 2); 359 | } 360 | TEST(VecNTest, test_multiply_scalar_vecn) { 361 | real inv[5]; 362 | inv[0] = 1; 363 | inv[1] = 2; 364 | inv[2] = 3; 365 | inv[3] = 2; 366 | inv[4] = 1; 367 | VecN v(inv); 368 | VecN out; 369 | auto result = v.multiply(2, out).status; 370 | EXPECT_EQ(result, SUCCESS); 371 | real t = static_cast(0); 372 | out(0, t); 373 | EXPECT_EQ(t, 2); 374 | out(1, t); 375 | EXPECT_EQ(t, 4); 376 | out(2, t); 377 | EXPECT_EQ(t, 6); 378 | out(3, t); 379 | EXPECT_EQ(t, 4); 380 | out(4, t); 381 | EXPECT_EQ(t, 2); 382 | } 383 | TEST(VecNTest, test_multiply_vector_to_vector_t) { 384 | real inv[5]; 385 | inv[0] = 1; 386 | inv[1] = 2; 387 | inv[2] = 3; 388 | inv[3] = 2; 389 | inv[4] = 1; 390 | VecN v(inv); 391 | real out[5]; 392 | real avec[5]; 393 | avec[0] = 1; 394 | avec[1] = 2; 395 | avec[2] = 2; 396 | avec[3] = 2; 397 | avec[4] = 1; 398 | auto result = v.multiply(avec, out).status; 399 | EXPECT_EQ(result, SUCCESS); 400 | EXPECT_EQ(out[0], static_cast(1)); 401 | EXPECT_EQ(out[1], static_cast(4)); 402 | EXPECT_EQ(out[2], static_cast(6)); 403 | EXPECT_EQ(out[3], static_cast(4)); 404 | EXPECT_EQ(out[4], static_cast(1)); 405 | } 406 | TEST(VecNTest, test_multiply_vecn_to_vecn_t) { 407 | real inv[5]; 408 | inv[0] = 1; 409 | inv[1] = 2; 410 | inv[2] = 3; 411 | inv[3] = 2; 412 | inv[4] = 1; 413 | VecN v(inv); 414 | 415 | // output vector 416 | VecN out; 417 | 418 | real avec[5]; 419 | // prepare vector to multiply 420 | avec[0] = 1; 421 | avec[1] = 2; 422 | avec[2] = 2; 423 | avec[3] = 2; 424 | avec[4] = 1; 425 | 426 | VecN avecn(avec); 427 | auto result = v.multiply(avecn, out).status; 428 | 429 | real t = static_cast(0); 430 | out(0, t); 431 | // 432 | EXPECT_EQ(result, SUCCESS); 433 | EXPECT_EQ(t, static_cast(1)); 434 | 435 | out(1, t); 436 | EXPECT_EQ(t, static_cast(4)); 437 | 438 | out(2, t); 439 | EXPECT_EQ(t, static_cast(6)); 440 | 441 | out(3, t); 442 | EXPECT_EQ(t, static_cast(4)); 443 | 444 | out(4, t); 445 | EXPECT_EQ(t, static_cast(1)); 446 | } 447 | 448 | /*! @} */ 449 | /*! @{ testing element-wise division for the vector 450 | */ 451 | TEST(VecNTest, test_divide_scalar_vector) { 452 | real inv[5]; 453 | inv[0] = 2; 454 | inv[1] = 4; 455 | inv[2] = 6; 456 | inv[3] = 2; 457 | inv[4] = 0; 458 | VecN v(inv); 459 | real out[5]; 460 | auto result = v.divide(2, out).status; 461 | EXPECT_EQ(result, SUCCESS); 462 | EXPECT_EQ(out[0], 1); 463 | EXPECT_EQ(out[1], 2); 464 | EXPECT_EQ(out[2], 3); 465 | EXPECT_EQ(out[3], 1); 466 | EXPECT_EQ(out[4], 0); 467 | } 468 | TEST(VecNTest, test_divide_scalar_vector_zero) { 469 | real inv[5]; 470 | inv[0] = 2; 471 | inv[1] = 4; 472 | inv[2] = 6; 473 | inv[3] = 2; 474 | inv[4] = 0; 475 | VecN v(inv); 476 | real out[5]; 477 | auto result = v.divide(0, out).status; 478 | EXPECT_EQ(result, ARG_ERROR); 479 | } 480 | TEST(VecNTest, test_divide_scalar_vecn) { 481 | real inv[5]; 482 | inv[0] = 2; 483 | inv[1] = 4; 484 | inv[2] = 6; 485 | inv[3] = 2; 486 | inv[4] = 0; 487 | VecN v(inv); 488 | VecN out; 489 | auto result = v.divide(2, out).status; 490 | EXPECT_EQ(result, SUCCESS); 491 | real t = static_cast(0); 492 | out(0, t); 493 | EXPECT_EQ(t, 1); 494 | out(1, t); 495 | EXPECT_EQ(t, 2); 496 | out(2, t); 497 | EXPECT_EQ(t, 3); 498 | out(3, t); 499 | EXPECT_EQ(t, 1); 500 | out(4, t); 501 | EXPECT_EQ(t, 0); 502 | } 503 | TEST(VecNTest, test_divide_scalar_vecn_zero) { 504 | real inv[5]; 505 | inv[0] = 2; 506 | inv[1] = 4; 507 | inv[2] = 6; 508 | inv[3] = 2; 509 | inv[4] = 0; 510 | VecN v(inv); 511 | VecN out; 512 | auto result = v.divide(0, out).status; 513 | EXPECT_EQ(result, ARG_ERROR); 514 | } 515 | TEST(VecNTest, test_divide_vector_to_vector_t) { 516 | real inv[5]; 517 | inv[0] = 1; 518 | inv[1] = 2; 519 | inv[2] = 4; 520 | inv[3] = 2; 521 | inv[4] = 1; 522 | VecN v(inv); 523 | real out[5]; 524 | real avec[5]; 525 | avec[0] = 1; 526 | avec[1] = 2; 527 | avec[2] = 2; 528 | avec[3] = 2; 529 | avec[4] = 1; 530 | auto result = v.divide(avec, out).status; 531 | EXPECT_EQ(result, SUCCESS); 532 | EXPECT_EQ(out[0], static_cast(1)); 533 | EXPECT_EQ(out[1], static_cast(1)); 534 | EXPECT_EQ(out[2], static_cast(2)); 535 | EXPECT_EQ(out[3], static_cast(1)); 536 | EXPECT_EQ(out[4], static_cast(1)); 537 | } 538 | TEST(VecNTest, test_divide_vector_to_vector_zero) { 539 | real inv[5]; 540 | inv[0] = 1; 541 | inv[1] = 2; 542 | inv[2] = 4; 543 | inv[3] = 2; 544 | inv[4] = 1; 545 | VecN v(inv); 546 | real out[5]; 547 | real avec[5]; 548 | avec[0] = 1; 549 | avec[1] = 2; 550 | avec[2] = 0; 551 | avec[3] = 2; 552 | avec[4] = 1; 553 | auto result = v.divide(avec, out).status; 554 | EXPECT_EQ(result, ARG_ERROR); 555 | } 556 | TEST(VecNTest, test_divide_vecn_to_vecn_t) { 557 | real inv[5]; 558 | inv[0] = 1; 559 | inv[1] = 2; 560 | inv[2] = 4; 561 | inv[3] = 2; 562 | inv[4] = 1; 563 | VecN v(inv); 564 | 565 | // output vector 566 | VecN out; 567 | 568 | real avec[5]; 569 | // prepare vector to divide 570 | avec[0] = 1; 571 | avec[1] = 2; 572 | avec[2] = 2; 573 | avec[3] = 2; 574 | avec[4] = 1; 575 | 576 | VecN avecn(avec); 577 | auto result = v.divide(avecn, out).status; 578 | 579 | real t = static_cast(0); 580 | out(0, t); 581 | // 582 | EXPECT_EQ(result, SUCCESS); 583 | EXPECT_EQ(t, static_cast(1)); 584 | 585 | out(1, t); 586 | EXPECT_EQ(t, static_cast(1)); 587 | 588 | out(2, t); 589 | EXPECT_EQ(t, static_cast(2)); 590 | 591 | out(3, t); 592 | EXPECT_EQ(t, static_cast(1)); 593 | 594 | out(4, t); 595 | EXPECT_EQ(t, static_cast(1)); 596 | } 597 | TEST(VecNTest, test_divide_vecn_to_vecn_zero) { 598 | real inv[5]; 599 | inv[0] = 1; 600 | inv[1] = 2; 601 | inv[2] = 4; 602 | inv[3] = 2; 603 | inv[4] = 1; 604 | VecN v(inv); 605 | 606 | // output vector 607 | VecN out; 608 | 609 | real avec[5]; 610 | // prepare vector to divide 611 | avec[0] = 1; 612 | avec[1] = 2; 613 | avec[2] = -10; 614 | avec[3] = 2; 615 | avec[4] = 0; 616 | 617 | VecN avecn(avec); 618 | auto result = v.divide(avecn, out).status; 619 | 620 | real t = static_cast(0); 621 | out(0, t); 622 | // 623 | EXPECT_EQ(result, ARG_ERROR); 624 | } 625 | 626 | /*! @} */ 627 | 628 | /*! @{ testing element-wise division for the vector 629 | */ 630 | 631 | TEST(VecNTest, test_dot_scalar_t) { 632 | real inv[5]; 633 | inv[0] = 1; // 2 634 | inv[1] = 2; // 4 635 | inv[2] = 3; // 6 636 | inv[3] = 2; // 4 637 | inv[4] = 1; // 2 638 | // sum == 18 639 | VecN v(inv); 640 | real out; 641 | auto result = v.dot(static_cast(2), out).status; 642 | EXPECT_EQ(result, SUCCESS); 643 | EXPECT_EQ(out, static_cast(18)); 644 | } 645 | TEST(VecNTest, test_dot_scalar_zero) { 646 | real inv[5]; 647 | inv[0] = 1; // 2 648 | inv[1] = 2; // 4 649 | inv[2] = 3; // 6 650 | inv[3] = 2; // 4 651 | inv[4] = 1; // 2 652 | // sum == 18 653 | VecN v(inv); 654 | real out; 655 | auto result = v.dot(static_cast(0), out).status; 656 | EXPECT_EQ(result, SUCCESS); 657 | EXPECT_EQ(out, static_cast(0)); 658 | } 659 | TEST(VecNTest, test_dot_vector_t) { 660 | real inv[5]; 661 | inv[0] = 1; // 1 662 | inv[1] = 2; // 4 663 | inv[2] = 3; // 3 664 | inv[3] = 2; // 0 665 | inv[4] = 1; // 0 666 | // sum == 8 667 | real avec[5]; 668 | VecN v(inv); 669 | avec[0] = 1; 670 | avec[1] = 2; 671 | avec[2] = 1; 672 | avec[3] = 0; 673 | avec[4] = 0; 674 | real out = static_cast(0); 675 | auto result = v.dot(avec, out).status; 676 | EXPECT_EQ(result, SUCCESS); 677 | EXPECT_EQ(out, static_cast(8)); 678 | } 679 | TEST(VecNTest, test_dot_vecn_t) { 680 | real inv[5]; 681 | inv[0] = 1; // 1 682 | inv[1] = 2; // 4 683 | inv[2] = 3; // 3 684 | inv[3] = 2; // 0 685 | inv[4] = 1; // 0 686 | // sum == 8 687 | real avec[5]; 688 | VecN v(inv); 689 | avec[0] = 1; 690 | avec[1] = 2; 691 | avec[2] = 1; 692 | avec[3] = 0; 693 | avec[4] = 0; 694 | VecN av(avec); 695 | real out = static_cast(0); 696 | auto result = v.dot(av, out).status; 697 | EXPECT_EQ(result, SUCCESS); 698 | EXPECT_EQ(out, static_cast(8)); 699 | } 700 | 701 | /*! @} */ 702 | -------------------------------------------------------------------------------- /tests/test_matn.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // 4 | #include 5 | #include 6 | #include 7 | 8 | /*! @{ 9 | */ 10 | 11 | typedef float real; 12 | using namespace math3d::matn; 13 | using namespace math3d; 14 | 15 | /*! @{ Test constructors of the matrix 16 | */ 17 | TEST(MatnTest, test_empty_constructor) { 18 | MatN m; 19 | std::size_t vsize = 0; 20 | auto r = m(vsize); 21 | EXPECT_EQ(vsize, static_cast(0)); 22 | EXPECT_EQ(r.status, SUCCESS); 23 | } 24 | TEST(MatnTest, test_single_arr_constructor) { 25 | 26 | real mv[] = {0, 1, 2, 0, 2, 4}; 27 | 28 | MatN m(mv); 29 | std::size_t vsize = 0; 30 | 31 | auto r = m(vsize); 32 | EXPECT_EQ(vsize, static_cast(6)); 33 | EXPECT_EQ(r.status, SUCCESS); 34 | 35 | std::size_t rows, cols; 36 | r = m(rows, cols); 37 | EXPECT_EQ(cols, static_cast(3)); 38 | EXPECT_EQ(r.status, SUCCESS); 39 | 40 | EXPECT_EQ(rows, static_cast(2)); 41 | EXPECT_EQ(r.status, SUCCESS); 42 | 43 | real arr[6]; 44 | r = m(arr); 45 | EXPECT_EQ(r.status, SUCCESS); 46 | EXPECT_DOUBLE_EQ(arr[0], static_cast(0)); 47 | EXPECT_DOUBLE_EQ(arr[1], static_cast(1)); 48 | EXPECT_DOUBLE_EQ(arr[2], static_cast(2)); 49 | EXPECT_DOUBLE_EQ(arr[3], static_cast(0)); 50 | EXPECT_DOUBLE_EQ(arr[4], static_cast(2)); 51 | EXPECT_DOUBLE_EQ(arr[5], static_cast(4)); 52 | } 53 | /*! @} 54 | */ 55 | 56 | TEST(MatnTest, test_static_zeros) { 57 | 58 | MatN m; 59 | auto r = MatN::zeros(m); 60 | EXPECT_EQ(r.status, SUCCESS); 61 | 62 | std::size_t vsize = 0; 63 | 64 | r = m(vsize); 65 | EXPECT_EQ(vsize, static_cast(6)); 66 | EXPECT_EQ(r.status, SUCCESS); 67 | 68 | std::size_t rows, cols; 69 | r = m(rows, cols); 70 | EXPECT_EQ(cols, static_cast(3)); 71 | EXPECT_EQ(r.status, SUCCESS); 72 | 73 | EXPECT_EQ(rows, static_cast(2)); 74 | 75 | real arr[6]; 76 | r = m(arr); 77 | EXPECT_EQ(r.status, SUCCESS); 78 | EXPECT_EQ(arr[0], static_cast(0)); 79 | EXPECT_EQ(arr[1], static_cast(0)); 80 | EXPECT_EQ(arr[2], static_cast(0)); 81 | EXPECT_EQ(arr[3], static_cast(0)); 82 | EXPECT_EQ(arr[4], static_cast(0)); 83 | EXPECT_EQ(arr[5], static_cast(0)); 84 | } 85 | TEST(MatnTest, test_static_from_row_cols_fill) { 86 | 87 | MatN m; 88 | MatN::from_row_cols(20, m); 89 | std::size_t vsize = 0; 90 | 91 | m(vsize); 92 | EXPECT_EQ(vsize, static_cast(6)); 93 | 94 | std::size_t rows, cols; 95 | m(rows, cols); 96 | EXPECT_EQ(cols, static_cast(3)); 97 | 98 | EXPECT_EQ(rows, static_cast(2)); 99 | 100 | real out = 0; 101 | auto r = m(0, out); 102 | EXPECT_EQ(out, static_cast(20)); 103 | EXPECT_EQ(r.status, SUCCESS); 104 | real arr[6]; 105 | r = m(arr); 106 | EXPECT_EQ(r.status, SUCCESS); 107 | EXPECT_EQ(arr[0], static_cast(20)); 108 | EXPECT_EQ(arr[1], static_cast(20)); 109 | EXPECT_EQ(arr[2], static_cast(20)); 110 | EXPECT_EQ(arr[3], static_cast(20)); 111 | EXPECT_EQ(arr[4], static_cast(20)); 112 | EXPECT_EQ(arr[5], static_cast(20)); 113 | } 114 | TEST(MatnTest, test_fill) { 115 | 116 | real vsize = static_cast(2); 117 | MatN m(vsize); 118 | 119 | real out = 0; 120 | auto res = m(0, out); 121 | EXPECT_EQ(res.status, SUCCESS); 122 | EXPECT_EQ(out, static_cast(2)); 123 | res = m(3, out); 124 | EXPECT_EQ(out, static_cast(2)); 125 | EXPECT_EQ(res.status, SUCCESS); 126 | } 127 | TEST(MatnTest, test_transpose) { 128 | 129 | MatN m; 130 | MatN::zeros(m); 131 | MatN out; 132 | MatN::zeros(out); 133 | 134 | auto res = m.transpose(out); 135 | EXPECT_EQ(res.status, SUCCESS); 136 | std::size_t rows, cols; 137 | // 138 | res = out(rows, cols); 139 | EXPECT_EQ(res.status, SUCCESS); 140 | EXPECT_EQ(cols, 2); 141 | // 142 | EXPECT_EQ(res.status, SUCCESS); 143 | EXPECT_EQ(rows, 3); 144 | // 145 | } 146 | TEST(MatnTest, test_col_nb) { 147 | 148 | MatN m; 149 | MatN::zeros(m); 150 | std::size_t rows, cnb; 151 | auto r = m(rows, cnb); 152 | EXPECT_EQ(r.status, SUCCESS); 153 | EXPECT_EQ(cnb, static_cast(3)); 154 | // 155 | } 156 | TEST(MatnTest, test_row_nb) { 157 | MatN m; 158 | MatN::zeros(m); 159 | std::size_t rnb, cols; 160 | auto r = m(rnb, cols); 161 | EXPECT_EQ(r.status, SUCCESS); 162 | EXPECT_EQ(rnb, static_cast(2)); 163 | // 164 | } 165 | TEST(MatnTest, test_get_size) { 166 | MatN m; 167 | MatN::zeros(m); 168 | std::size_t snb = 0; 169 | auto r = m(snb); 170 | EXPECT_EQ(r.status, SUCCESS); 171 | EXPECT_EQ(snb, static_cast(6)); 172 | // 173 | } 174 | TEST(MatnTest, test_add_scalar_value) { 175 | 176 | real mv[] = {0, 1, 2, 0, 2, 4}; 177 | 178 | MatN m(mv); 179 | MatN out; 180 | auto r = m.add(2, out); 181 | EXPECT_EQ(r.status, SUCCESS); 182 | real tval = static_cast(0); 183 | EXPECT_EQ(SUCCESS, (out(0, tval)).status); 184 | EXPECT_DOUBLE_EQ(tval, 2); 185 | EXPECT_EQ(SUCCESS, (out(1, tval)).status); 186 | EXPECT_DOUBLE_EQ(tval, 3); 187 | EXPECT_EQ(SUCCESS, out(2, tval).status); 188 | EXPECT_DOUBLE_EQ(tval, 4); 189 | EXPECT_EQ(SUCCESS, out(3, tval).status); 190 | EXPECT_DOUBLE_EQ(tval, 2); 191 | EXPECT_EQ(SUCCESS, out(4, tval).status); 192 | EXPECT_DOUBLE_EQ(tval, 4); 193 | EXPECT_EQ(SUCCESS, out(5, tval).status); 194 | EXPECT_DOUBLE_EQ(tval, 6); 195 | // 196 | } 197 | TEST(MatnTest, test_add_mat_value) { 198 | 199 | real mv[] = {0, 1, 2, 0, 2, 4}; 200 | 201 | MatN m(mv); 202 | MatN out; 203 | MatN tout(static_cast(2)); 204 | // filled matrix 205 | auto r = m.add(tout, out); 206 | EXPECT_EQ(r.status, SUCCESS); 207 | real tval = static_cast(0); 208 | EXPECT_EQ(SUCCESS, out(0, tval).status); 209 | EXPECT_DOUBLE_EQ(tval, 2); 210 | EXPECT_EQ(SUCCESS, out(1, tval).status); 211 | EXPECT_DOUBLE_EQ(tval, 3); 212 | EXPECT_EQ(SUCCESS, out(2, tval).status); 213 | EXPECT_DOUBLE_EQ(tval, 4); 214 | EXPECT_EQ(SUCCESS, out(3, tval).status); 215 | EXPECT_DOUBLE_EQ(tval, 2); 216 | EXPECT_EQ(SUCCESS, out(4, tval).status); 217 | EXPECT_DOUBLE_EQ(tval, 4); 218 | EXPECT_EQ(SUCCESS, out(5, tval).status); 219 | EXPECT_DOUBLE_EQ(tval, 6); 220 | // 221 | } 222 | TEST(MatnTest, test_subtract_scalar_value) { 223 | 224 | real mv[] = {0, 1, 2, 0, 2, 4}; 225 | 226 | MatN m(mv); 227 | MatN out; 228 | auto r = m.subtract(2, out); 229 | EXPECT_EQ(r.status, SUCCESS); 230 | real tval = static_cast(0); 231 | EXPECT_EQ(SUCCESS, out(0, tval).status); 232 | EXPECT_DOUBLE_EQ(tval, -2); 233 | EXPECT_EQ(SUCCESS, out(1, tval).status); 234 | EXPECT_DOUBLE_EQ(tval, -1); 235 | EXPECT_EQ(SUCCESS, out(2, tval).status); 236 | EXPECT_DOUBLE_EQ(tval, 0); 237 | EXPECT_EQ(SUCCESS, out(3, tval).status); 238 | EXPECT_DOUBLE_EQ(tval, -2); 239 | EXPECT_EQ(SUCCESS, out(4, tval).status); 240 | EXPECT_DOUBLE_EQ(tval, 0); 241 | EXPECT_EQ(SUCCESS, out(5, tval).status); 242 | EXPECT_DOUBLE_EQ(tval, 2); 243 | // 244 | } 245 | TEST(MatnTest, test_subtract_mat_value) { 246 | 247 | real mv[] = {0, 1, 2, 0, 2, 4}; 248 | 249 | MatN m(mv); 250 | MatN out; 251 | 252 | real mv2[] = {0, 1, 2, 0, 2, 4}; 253 | MatN tout(mv2); 254 | // filled matrix 255 | auto r = m.subtract(tout, out); 256 | EXPECT_EQ(r.status, SUCCESS); 257 | real tval = static_cast(0); 258 | EXPECT_EQ(SUCCESS, out(0, tval).status); 259 | EXPECT_DOUBLE_EQ(tval, 0); 260 | EXPECT_EQ(SUCCESS, out(1, tval).status); 261 | EXPECT_DOUBLE_EQ(tval, 0); 262 | EXPECT_EQ(SUCCESS, out(2, tval).status); 263 | EXPECT_DOUBLE_EQ(tval, 0); 264 | EXPECT_EQ(SUCCESS, out(3, tval).status); 265 | EXPECT_DOUBLE_EQ(tval, 0); 266 | EXPECT_EQ(SUCCESS, out(4, tval).status); 267 | EXPECT_DOUBLE_EQ(tval, 0); 268 | EXPECT_EQ(SUCCESS, out(5, tval).status); 269 | EXPECT_DOUBLE_EQ(tval, 0); 270 | // 271 | } 272 | TEST(MatnTest, test_hadamard_product_scalar_value) { 273 | 274 | real mv[] = {0, 1, 2, 0, 2, 4}; 275 | 276 | MatN m(mv); 277 | MatN out; 278 | auto r = m.hadamard_product(2, out); 279 | EXPECT_EQ(r.status, SUCCESS); 280 | real tval = static_cast(0); 281 | EXPECT_EQ(SUCCESS, out(0, tval).status); 282 | EXPECT_EQ(tval, 0); 283 | EXPECT_EQ(SUCCESS, out(1, tval).status); 284 | EXPECT_EQ(tval, 2); 285 | EXPECT_EQ(SUCCESS, out(2, tval).status); 286 | EXPECT_EQ(tval, 4); 287 | EXPECT_EQ(SUCCESS, out(3, tval).status); 288 | EXPECT_EQ(tval, 0); 289 | EXPECT_EQ(SUCCESS, out(4, tval).status); 290 | EXPECT_EQ(tval, 4); 291 | EXPECT_EQ(SUCCESS, out(5, tval).status); 292 | EXPECT_EQ(tval, 8); 293 | // 294 | } 295 | TEST(MatnTest, test_hadamard_product_mat_value) { 296 | 297 | real mv[] = {0, 1, 2, 0, 2, 4}; 298 | 299 | MatN m(mv); 300 | MatN out; 301 | 302 | real mv2[] = {0, 1, 2, 0, 2, 4}; 303 | MatN tout(mv2); 304 | // filled matrix 305 | auto r = m.hadamard_product(tout, out); 306 | EXPECT_EQ(r.status, SUCCESS); 307 | real tval = static_cast(0); 308 | EXPECT_EQ(SUCCESS, out(0, tval).status); 309 | EXPECT_EQ(tval, 0); 310 | EXPECT_EQ(SUCCESS, out(1, tval).status); 311 | EXPECT_EQ(tval, 1); 312 | EXPECT_EQ(SUCCESS, out(2, tval).status); 313 | EXPECT_EQ(tval, 4); 314 | EXPECT_EQ(SUCCESS, out(3, tval).status); 315 | EXPECT_EQ(tval, 0); 316 | EXPECT_EQ(SUCCESS, out(4, tval).status); 317 | EXPECT_EQ(tval, 4); 318 | EXPECT_EQ(SUCCESS, out(5, tval).status); 319 | EXPECT_EQ(tval, 16); 320 | // 321 | } 322 | TEST(MatnTest, test_divide_scalar_value_false) { 323 | 324 | real mv[] = {0, 1, 2, 0, 2, 4}; 325 | 326 | MatN m(mv); 327 | MatN out; 328 | auto r = m.divide(0, out); 329 | EXPECT_EQ(r.status, ARG_ERROR); 330 | // 331 | } 332 | TEST(MatnTest, test_divide_mat_false) { 333 | 334 | real mv[] = {0, 1, 2, 0, 2, 4}; 335 | 336 | MatN m(mv); 337 | MatN out; 338 | 339 | real mv2[] = {0, 1, 2, 0, 2, 4}; 340 | MatN tout(mv2); 341 | // filled matrix 342 | auto r = m.divide(tout, out); 343 | EXPECT_EQ(r.status, ARG_ERROR); 344 | // 345 | } 346 | TEST(MatnTest, test_divide_scalar_value_true) { 347 | 348 | real mv[] = {0, 1, 2, 0, 2, 4}; 349 | 350 | MatN m(mv); 351 | MatN out; 352 | auto r = m.divide(2, out); 353 | EXPECT_EQ(r.status, SUCCESS); 354 | // 355 | real tval = static_cast(0); 356 | EXPECT_EQ(SUCCESS, out(0, tval).status); 357 | EXPECT_EQ(tval, 0); 358 | EXPECT_EQ(SUCCESS, out(1, tval).status); 359 | EXPECT_EQ(tval, static_cast(0.5)); 360 | EXPECT_EQ(SUCCESS, out(2, tval).status); 361 | EXPECT_EQ(tval, 1); 362 | EXPECT_EQ(SUCCESS, out(3, tval).status); 363 | EXPECT_EQ(tval, 0); 364 | EXPECT_EQ(SUCCESS, out(4, tval).status); 365 | EXPECT_EQ(tval, 1); 366 | EXPECT_EQ(SUCCESS, out(5, tval).status); 367 | EXPECT_EQ(tval, 2); 368 | } 369 | TEST(MatnTest, test_divide_mat_true) { 370 | 371 | real mv[] = {0, 1, 2, 0, 2, 4}; 372 | 373 | MatN m(mv); 374 | MatN out; 375 | 376 | real mv2[] = {1, 1, 2, 1, 2, 4}; 377 | MatN tout(mv2); 378 | // filled matrix 379 | auto r = m.divide(tout, out); 380 | EXPECT_EQ(r.status, SUCCESS); 381 | // 382 | real tval = static_cast(0); 383 | EXPECT_EQ(SUCCESS, out(0, tval).status); 384 | EXPECT_EQ(tval, 0); 385 | EXPECT_EQ(SUCCESS, out(1, tval).status); 386 | EXPECT_EQ(tval, 1); 387 | EXPECT_EQ(SUCCESS, out(2, tval).status); 388 | EXPECT_EQ(tval, 1); 389 | EXPECT_EQ(SUCCESS, out(3, tval).status); 390 | EXPECT_EQ(tval, 0); 391 | EXPECT_EQ(SUCCESS, out(4, tval).status); 392 | EXPECT_EQ(tval, 1); 393 | EXPECT_EQ(SUCCESS, out(5, tval).status); 394 | EXPECT_EQ(tval, 1); 395 | } 396 | TEST(MatnTest, test_multiply_mat) { 397 | 398 | // values from S. Lang, Introduction to Linear Algebra, 399 | // 1986, p. 48 400 | 401 | real Amat_values[] = {2, 1, 5, 1, 3, 2}; 402 | real Bmat_values[] = {3, 4, -1, 2, 2, 1}; 403 | 404 | MatN A(Amat_values); 405 | MatN B(Bmat_values); 406 | MatN out; 407 | // 408 | 409 | auto start = std::chrono::steady_clock::now(); 410 | auto res = A.multiply(B, out); 411 | auto stop = std::chrono::steady_clock::now(); 412 | auto duration = 413 | std::chrono::duration_cast( 414 | stop - start); 415 | 416 | RecordProperty("duration in microseconds %i", 417 | (int)duration.count()); 418 | 419 | EXPECT_EQ(res.status, SUCCESS); 420 | // 421 | real tval = static_cast(0); 422 | EXPECT_EQ(SUCCESS, out(0, tval).status); 423 | EXPECT_EQ(tval, 15); 424 | EXPECT_EQ(SUCCESS, out(1, tval).status); 425 | EXPECT_EQ(tval, 15); 426 | EXPECT_EQ(SUCCESS, out(2, tval).status); 427 | EXPECT_EQ(tval, 4); 428 | EXPECT_EQ(SUCCESS, out(3, tval).status); 429 | EXPECT_EQ(tval, 12); 430 | } 431 | TEST(MatnTest, test_gaxpy) { 432 | 433 | // values from Golub, Van Loan 2013, p.5 434 | 435 | real Amat_values[] = {1, 2, 3, 4, 5, 6}; 436 | real x[] = {7, 8}; 437 | real y[] = {0.0, 0.0, 0.0}; 438 | 439 | MatN A(Amat_values); 440 | auto start = std::chrono::high_resolution_clock::now(); 441 | // 442 | auto res = A.gaxpy(x, y); 443 | auto stop = std::chrono::high_resolution_clock::now(); 444 | auto duration = 445 | std::chrono::duration_cast( 446 | stop - start); 447 | 448 | RecordProperty("duration in microseconds %i", 449 | (int)duration.count()); 450 | 451 | EXPECT_EQ(res.status, SUCCESS); 452 | 453 | // 454 | EXPECT_EQ(y[0], 23); 455 | EXPECT_EQ(y[1], 53); 456 | EXPECT_EQ(y[2], 83); 457 | } 458 | TEST(MatnTest, test_outer_product) { 459 | 460 | // values from Golub, Van Loan 2013, p.7 461 | 462 | real x[] = {1, 2, 3}; 463 | real y[] = {4, 5}; 464 | MatN out(0); 465 | MatN m(0); 466 | // 467 | auto start = std::chrono::steady_clock::now(); 468 | auto res = m.outer_product<3, 2>(x, y, out); 469 | auto stop = std::chrono::steady_clock::now(); 470 | auto duration = 471 | std::chrono::duration_cast( 472 | stop - start); 473 | 474 | RecordProperty("duration in microseconds %i", 475 | (int)duration.count()); 476 | 477 | EXPECT_EQ(res.status, SUCCESS); 478 | 479 | real d[6]; 480 | out(d); 481 | 482 | // 483 | EXPECT_EQ(d[0], 4); 484 | EXPECT_EQ(d[1], 5); 485 | EXPECT_EQ(d[2], 8); 486 | EXPECT_EQ(d[3], 10); 487 | EXPECT_EQ(d[4], 12); 488 | EXPECT_EQ(d[5], 15); 489 | } 490 | TEST(MatnTest, test_multiply_scalar) { 491 | 492 | // values from S. Lang, Introduction to Linear Algebra, 493 | // 1986, p. 48 494 | 495 | real Amat_values[] = {2, 1, 5, 1, 3, 2}; 496 | real B = 2; 497 | 498 | MatN A(Amat_values); 499 | MatN out; 500 | // 501 | auto res = A.multiply(B, out); 502 | // 503 | EXPECT_EQ(res.status, SUCCESS); 504 | // 505 | real tval = static_cast(0); 506 | EXPECT_EQ(SUCCESS, out(0, tval).status); 507 | EXPECT_EQ(tval, 16); 508 | EXPECT_EQ(SUCCESS, out(1, tval).status); 509 | EXPECT_EQ(tval, 16); 510 | EXPECT_EQ(SUCCESS, out(2, tval).status); 511 | EXPECT_EQ(tval, 12); 512 | EXPECT_EQ(SUCCESS, out(3, tval).status); 513 | EXPECT_EQ(tval, 12); 514 | } 515 | TEST(MatnTest, test_dot_mat) { 516 | 517 | // values from S. Lang, Introduction to Linear Algebra, 518 | // 1986, p. 48 519 | 520 | real Amat_values[] = {2, 1, 5, 1, 3, 2}; 521 | real Bmat_values[] = {3, 4, -1, 2, 2, 1}; 522 | 523 | MatN A(Amat_values); 524 | MatN B(Bmat_values); 525 | MatN out; 526 | // 527 | auto res = A.dot(B, out); 528 | EXPECT_EQ(res.status, SUCCESS); 529 | // 530 | real tval = static_cast(0); 531 | EXPECT_EQ(SUCCESS, out(0, tval).status); 532 | EXPECT_EQ(tval, 15); 533 | EXPECT_EQ(SUCCESS, out(1, tval).status); 534 | EXPECT_EQ(tval, 15); 535 | EXPECT_EQ(SUCCESS, out(2, tval).status); 536 | EXPECT_EQ(tval, 4); 537 | EXPECT_EQ(SUCCESS, out(3, tval).status); 538 | EXPECT_EQ(tval, 12); 539 | } 540 | TEST(MatnTest, test_dot_scalar) { 541 | 542 | // values from S. Lang, Introduction to Linear Algebra, 543 | // 1986, p. 48 544 | 545 | real Amat_values[] = {2, 1, 5, 1, 3, 2}; 546 | real B = 2; 547 | 548 | MatN A(Amat_values); 549 | MatN out; 550 | // 551 | auto res = A.dot(B, out); 552 | // 553 | EXPECT_EQ(res.status, SUCCESS); 554 | // 555 | real tval = static_cast(0); 556 | EXPECT_EQ(SUCCESS, out(0, tval).status); 557 | EXPECT_EQ(tval, 16); 558 | EXPECT_EQ(SUCCESS, out(1, tval).status); 559 | EXPECT_EQ(tval, 16); 560 | EXPECT_EQ(SUCCESS, out(2, tval).status); 561 | EXPECT_EQ(tval, 12); 562 | EXPECT_EQ(SUCCESS, out(3, tval).status); 563 | EXPECT_EQ(tval, 12); 564 | } 565 | TEST(MatnTest, test_dot_vector) { 566 | // values from 567 | // https://people.math.umass.edu/~havens/m235Lectures/Lecture05.pdf 568 | 569 | real Amat_values[] = {1, -4, 7, -2, 5, -8, 3, -6, 9}; 570 | real Bv[] = {2, 1, -1}; 571 | 572 | MatN A(Amat_values); 573 | MatN B(Bv); 574 | MatN out; 575 | // 576 | auto res = A.dot(B, out); 577 | // 578 | EXPECT_EQ(res.status, SUCCESS); 579 | // 580 | real tval = static_cast(0); 581 | EXPECT_EQ(SUCCESS, out(0, tval).status); 582 | EXPECT_EQ(tval, -9); 583 | EXPECT_EQ(SUCCESS, out(1, tval).status); 584 | EXPECT_EQ(tval, 9); 585 | EXPECT_EQ(SUCCESS, out(2, tval).status); 586 | EXPECT_EQ(tval, -9); 587 | } 588 | TEST(MatnTest, test_add_rows) { 589 | 590 | real new_rows[] = {1, -4, 7, -2, 5, -8, 3, -6, 9}; 591 | real Avals[] = {2, 1, -1}; 592 | 593 | MatN A(Avals); 594 | MatN out; 595 | // 596 | auto res = A.add_rows<9>(new_rows, out); 597 | // 598 | EXPECT_EQ(res.status, SUCCESS); 599 | real check[12]; 600 | out(check); 601 | // 602 | EXPECT_EQ(check[0], 2); 603 | EXPECT_EQ(check[1], 1); 604 | EXPECT_EQ(check[2], -1); 605 | EXPECT_EQ(check[3], 1); 606 | EXPECT_EQ(check[4], -4); 607 | EXPECT_EQ(check[5], 7); 608 | EXPECT_EQ(check[6], -2); 609 | EXPECT_EQ(check[7], 5); 610 | EXPECT_EQ(check[8], -8); 611 | EXPECT_EQ(check[9], 3); 612 | EXPECT_EQ(check[10], -6); 613 | EXPECT_EQ(check[11], 9); 614 | } 615 | TEST(MatnTest, test_add_columns) { 616 | 617 | real new_columns[] = {1, -4, 7, -2, 5, -8, 3, -6, 9}; 618 | real Avals[] = {2, 1, -1}; 619 | 620 | MatN A(Avals); 621 | MatN out; 622 | // 623 | auto res = A.add_columns<9>(new_columns, out); 624 | // 625 | EXPECT_EQ(res.status, SUCCESS); 626 | real check[12]; 627 | out(check); 628 | // 629 | 630 | EXPECT_EQ(check[0], 2); 631 | EXPECT_EQ(check[1], 1); 632 | EXPECT_EQ(check[2], -2); 633 | EXPECT_EQ(check[3], 3); 634 | EXPECT_EQ(check[4], 1); 635 | EXPECT_EQ(check[5], -4); 636 | EXPECT_EQ(check[6], 5); 637 | EXPECT_EQ(check[7], -6); 638 | EXPECT_EQ(check[8], -1); 639 | EXPECT_EQ(check[9], 7); 640 | EXPECT_EQ(check[10], -8); 641 | EXPECT_EQ(check[11], 9); 642 | } 643 | TEST(MatnTest, test_identity) { 644 | 645 | MatN out; 646 | auto res = identity(out); 647 | // 648 | // 649 | EXPECT_EQ(res.status, SUCCESS); 650 | real check[9]; 651 | out(check); 652 | // 653 | 654 | EXPECT_EQ(check[0], 1); 655 | EXPECT_EQ(check[1], 0); 656 | EXPECT_EQ(check[2], 0); 657 | EXPECT_EQ(check[3], 0); 658 | EXPECT_EQ(check[4], 1); 659 | EXPECT_EQ(check[5], 0); 660 | EXPECT_EQ(check[6], 0); 661 | EXPECT_EQ(check[7], 0); 662 | EXPECT_EQ(check[8], 1); 663 | } 664 | -------------------------------------------------------------------------------- /include/math3d/matn.hpp: -------------------------------------------------------------------------------- 1 | #ifndef MATN_HPP 2 | #define MATN_HPP 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "opflags.h" 11 | 12 | namespace math3d { 13 | 14 | namespace matn { 15 | 16 | template struct MatNCell { 17 | T content; 18 | std::size_t row = 0; 19 | std::size_t column = 0; 20 | int index = -1; 21 | }; 22 | 23 | template 24 | MatNCell make_matn_cell(const T &c, 25 | std::size_t row_param, 26 | std::size_t col_param) { 27 | MatNCell cell{ 28 | .content = c, .row = row_param, .column = col_param}; 29 | return cell; 30 | } 31 | 32 | template 33 | MatNCell make_matn_cell(const T &c, std::size_t i) { 34 | MatNCell cell{.content = c, 35 | .row = 0, 36 | .column = 0, 37 | .index = static_cast(i)}; 38 | return cell; 39 | } 40 | 41 | template 43 | class MatN { 44 | /** holds the vector data*/ 45 | T data[ColNb * RowNb]; 46 | static const std::size_t Size = ColNb * RowNb; 47 | 48 | public: 49 | MatN() { 50 | for (std::size_t i = 0; i < Size; ++i) { 51 | data[i] = 0; 52 | } 53 | } 54 | template MatN(const T (&vd)[N]) { 55 | if (N == Size) { 56 | memcpy(data, vd, Size * sizeof(T)); 57 | } else if (N < Size) { 58 | for (size_t i = 0; i < N; ++i) { 59 | data[i] = vd[i]; 60 | } 61 | for (size_t i = N; i < Size; ++i) { 62 | data[i] = 0; 63 | } 64 | } else { 65 | for (size_t i = 0; i < Size; ++i) { 66 | data[i] = vd[i]; 67 | } 68 | } 69 | } 70 | 71 | MatN(T fill_value) { 72 | for (std::size_t i = 0; i < Size; ++i) { 73 | data[i] = fill_value; 74 | } 75 | } 76 | 77 | static OpResult 78 | from_row_cols(T v, MatN &out) { 79 | MatN mat(v); 80 | out = mat; 81 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 82 | "from_row_cols", SUCCESS); 83 | } 84 | 85 | /**\brief Create matrix based on argument matrix*/ 86 | static OpResult zeros(MatN &out) { 87 | MatN::from_row_cols(0, out); 88 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 89 | "zeros", SUCCESS); 90 | } 91 | // tested 92 | template 94 | OpResult fill(T v, 95 | MatN &out) const { 96 | std::size_t s = 0; 97 | out(s); 98 | for (std::size_t i = 0; i < s; i++) { 99 | out(i, v); 100 | } 101 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 102 | "fill", SUCCESS); 103 | } 104 | // tested 105 | OpResult transpose(MatN &out) const { 106 | 107 | for (std::size_t i = 0; i < RowNb; i++) { 108 | for (std::size_t j = 0; j < ColNb; j++) { 109 | T tout = static_cast(0); 110 | (*this)(i, j, tout); // getter 111 | out(make_matn_cell(tout, i, j)); // setter 112 | } 113 | } 114 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 115 | "transpose", SUCCESS); 116 | } 117 | 118 | OpResult operator()(std::size_t &rows, 119 | std::size_t &cols) const { 120 | rows = RowNb; 121 | cols = ColNb; 122 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 123 | "get_rows_cols", SUCCESS); 124 | } 125 | 126 | // tested 127 | OpResult operator()(std::size_t &out) const { 128 | out = Size; 129 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 130 | "get_size", SUCCESS); 131 | } 132 | OpResult operator()(std::size_t row, std::size_t col, 133 | T &out) const { 134 | std::size_t index = row * ColNb + col; 135 | OpResult r = (*this)(index, out); 136 | if (r.status != SUCCESS) 137 | return r; 138 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "get", 139 | SUCCESS); 140 | } 141 | OpResult operator()(std::size_t index, T &out) const { 142 | if (index >= Size) 143 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 144 | "get", INDEX_ERROR); 145 | out = data[index]; 146 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "get", 147 | SUCCESS); 148 | } 149 | OpResult operator()(T (&out)[RowNb * ColNb]) const { 150 | memcpy(out, data, Size * sizeof(T)); 151 | // for (std::size_t i = 0; i < size; ++i){ 152 | // out[i] = data[i]; 153 | // } 154 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "get", 155 | SUCCESS); 156 | } 157 | OpResult operator()(const MatNCell &cell) { 158 | std::size_t row = cell.row; 159 | std::size_t col = cell.column; 160 | T el = cell.content; 161 | int cell_index = cell.index; 162 | std::size_t index; 163 | if (cell_index < 0) { 164 | index = row * ColNb + col; 165 | } else { 166 | index = static_cast(cell_index); 167 | } 168 | // 169 | if (index >= Size) { 170 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 171 | "set", INDEX_ERROR); 172 | } 173 | data[index] = el; 174 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "set", 175 | SUCCESS); 176 | } 177 | OpResult get_column(std::size_t index, 178 | T (&out)[RowNb]) const { 179 | if (index >= ColNb) { 180 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 181 | "get_column", INDEX_ERROR); 182 | } 183 | for (std::size_t i = 0; i < RowNb; i++) { 184 | out[i] = data[i * ColNb + index]; 185 | } 186 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 187 | "get_column", SUCCESS); 188 | } 189 | OpResult set_column(std::size_t index, 190 | const T (&idata)[RowNb]) { 191 | if (index >= ColNb) { 192 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 193 | "set_column", INDEX_ERROR); 194 | } 195 | for (std::size_t i = 0; i < RowNb; i++) { 196 | data[i * ColNb + index] = idata[i]; 197 | } 198 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 199 | "set_column", SUCCESS); 200 | } 201 | OpResult get_row(std::size_t index, 202 | T (&out)[ColNb]) const { 203 | if (index >= RowNb) { 204 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 205 | "get_row", INDEX_ERROR); 206 | } 207 | for (std::size_t i = 0; i < ColNb; i++) { 208 | out[i] = data[index * ColNb + i]; 209 | } 210 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 211 | "get_row", SUCCESS); 212 | } 213 | OpResult set_row(std::size_t index, 214 | const T (&idata)[ColNb]) { 215 | if (index >= RowNb) { 216 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 217 | "set_row", INDEX_ERROR); 218 | } 219 | for (std::size_t i = 0; i < ColNb; i++) { 220 | data[index * ColNb + i] = idata[i]; 221 | } 222 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 223 | "set_row", SUCCESS); 224 | } 225 | 226 | /**Obtain submatrix TODO*/ 227 | OpResult submat(std::size_t row_start, 228 | std::size_t col_start, 229 | MatN &out) const { 230 | std::size_t row_size = RowNb - row_start; 231 | std::size_t col_size = ColNb - col_start; 232 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 233 | "submat", NOT_IMPLEMENTED); 234 | } 235 | OpResult add(const MatN &v, 236 | MatN &out) const { 237 | auto fn = [](T matv, T val) { return matv + val; }; 238 | return apply(v, fn, out); 239 | } 240 | OpResult add(T v, MatN &out) const { 241 | auto fn = [](T matv, T val) { return matv + val; }; 242 | return apply(v, fn, out); 243 | } 244 | OpResult subtract(const MatN &v, 245 | MatN &out) const { 246 | auto fn = [](T matv, T val) { return matv - val; }; 247 | return apply(v, fn, out); 248 | } 249 | OpResult subtract(T v, MatN &out) const { 250 | auto fn = [](T matv, T val) { return matv - val; }; 251 | return apply(v, fn, out); 252 | } 253 | OpResult 254 | hadamard_product(const MatN &v, 255 | MatN &out) const { 256 | auto fn = [](T matv, T val) { return matv * val; }; 257 | return apply(v, fn, out); 258 | } 259 | OpResult 260 | hadamard_product(T v, MatN &out) const { 261 | auto fn = [](T matv, T val) { return matv * val; }; 262 | return apply(v, fn, out); 263 | } 264 | OpResult divide(const MatN &v, 265 | MatN &out) const { 266 | std::size_t osize = 0; 267 | v(osize); 268 | for (std::size_t i = 0; i < osize; i++) { 269 | T tout = static_cast(0); 270 | v(i, tout); // getter 271 | if (tout == static_cast(0)) { 272 | // zero division risk 273 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 274 | "divide", ARG_ERROR); 275 | } 276 | } 277 | auto fn = [](T matv, T val) { return matv / val; }; 278 | return apply(v, fn, out); 279 | } 280 | OpResult divide(T v, MatN &out) const { 281 | if (v == static_cast(0)) { 282 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 283 | "divide", ARG_ERROR); 284 | } 285 | auto fn = [](T matv, T val) { return matv / val; }; 286 | return apply(v, fn, out); 287 | } 288 | /**Declares inner vector product*/ 289 | template 290 | OpResult vdot(const T (&x)[N], const T (&y)[N], T &out) { 291 | if (N == 0) { 292 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 293 | "vdot", SIZE_ERROR); 294 | } 295 | 296 | out = static_cast(0); 297 | for (std::size_t i = 0; i < N; i++) { 298 | out += x[i] * y[i]; 299 | } 300 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 301 | "vdot", SUCCESS); 302 | } 303 | 304 | /**Declares inner vector product with scalars*/ 305 | template 306 | OpResult vdot_s(const T (&x)[N], const T &a, 307 | T (&out)[N]) { 308 | 309 | if (N == 0) { 310 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 311 | "vdot_s", SIZE_ERROR); 312 | } 313 | for (std::size_t i = 0; i < N; i++) { 314 | out[i] = x[i] * a; 315 | } 316 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 317 | "vdot_s", SUCCESS); 318 | } 319 | /**Implements saxpy algorithm from Golub, Van Loan 2013, 320 | * p. 4 alg.1.1.2*/ 321 | template 322 | OpResult saxpy(const T &a, const T (&x)[N], 323 | T (&y)[N]) const { 324 | if (N == 0) { 325 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 326 | "saxpy", SIZE_ERROR); 327 | } 328 | for (std::size_t i = 0; i < N; i++) { 329 | y[i] += x[i] * a; // 330 | } 331 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 332 | "saxpy", SUCCESS); 333 | } 334 | /** 335 | Implements gaxpy algorithm from Golub, Van Loan 2013, p. 336 | 4 alg.1.1.3 337 | 338 | as specified in p. 6-7 339 | */ 340 | OpResult gaxpy(const T (&x)[ColNb], T (&y)[RowNb]) const { 341 | for (std::size_t j = 0; j < ColNb; j++) { 342 | T c_j[RowNb]; 343 | get_column(j, c_j); 344 | saxpy(x[j], c_j, y); 345 | } 346 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 347 | "gaxpy", SUCCESS); 348 | } 349 | /** 350 | Implements outer product update from Golub, Van Loan 351 | 2013, p. 7 as a series of saxpy operations 352 | */ 353 | template 354 | OpResult outer_product(const T (&x)[Rn], const T (&y)[Cn], 355 | MatN &out) const { 356 | for (std::size_t i = 0; i < Rn; i++) { 357 | T A_i[Cn]; 358 | out.get_row(i, A_i); 359 | saxpy(x[i], y, A_i); 360 | out.set_row(i, A_i); 361 | } 362 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 363 | "outer_product", SUCCESS); 364 | } 365 | template 366 | OpResult multiply(T v, 367 | MatN &out) const { 368 | // m x n \cdot vmat (n x l) = out (m x l) 369 | // RowNb x ColNb \codt (n x l) = out (OutRowNb x 370 | // OutColNb) 371 | MatN vmat(v); 372 | 373 | auto r = multiply(vmat, out); 374 | if (r.status != SUCCESS) 375 | return r; 376 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 377 | "multiply", SUCCESS); 378 | } 379 | /*matrix to matrix multiplication*/ 380 | template 381 | OpResult dot(const MatN &v, 382 | MatN &out) const { 383 | return multiply(v, out); 384 | } 385 | /*matrix to scalar multiplication*/ 386 | template 387 | OpResult dot(T v, MatN &out) const { 388 | return multiply(v, out); 389 | } 390 | /*matrix to vector multiplication*/ 391 | OpResult dot(const T (&v)[ColNb], 392 | MatN &out) const { 393 | MatN vmat(v); 394 | auto r = multiply<1>(vmat, out); 395 | if (r.status != SUCCESS) 396 | return r; 397 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "dot", 398 | SUCCESS); 399 | } 400 | 401 | /** 402 | m x n \cdot vmat (n x l) = out (m x l) 403 | RowNb x ColNb \codt (OutRowNb x OutColNb) = out (RowNb x 404 | OutColNb) 405 | 406 | We are using the kij (row outer product) variant from 407 | Golub, van Loan 2013, p. 11 alg. 1.1.8 due to 408 | implementing this algorithm in C++. For fortran etc one 409 | should use jki since it access matrices by column. For 410 | a comparison of algorithms see table 1.1.1 in p. 9 411 | 412 | tested 413 | */ 414 | template 415 | OpResult multiply(const MatN &B, 416 | MatN &out) const { 417 | 418 | // fill out matrix with zero 419 | out = MatN(static_cast(0)); 420 | for (std::size_t k = 0; k < ColNb; k++) { 421 | // x vector 422 | T A_k[RowNb]; 423 | get_column(k, A_k); 424 | 425 | // y vector 426 | T B_k[OutColNb]; 427 | B.get_row(k, B_k); 428 | 429 | // compute their outer product 430 | outer_product(A_k, B_k, out); 431 | } 432 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 433 | "multiply", SUCCESS); 434 | } 435 | /** 436 | add row 437 | */ 438 | OpResult add_row(const T (&r_data)[ColNb], 439 | MatN &out) const { 440 | return add_rows(r_data, out); 441 | } 442 | /** 443 | add rows if the incoming data has a size of multiple of 444 | number of columns 445 | of this array 446 | */ 447 | template 448 | OpResult add_rows( 449 | const T (&r_data)[InRow], 450 | MatN &out) const { 451 | if ((InRow % ColNb) != 0) { 452 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 453 | "add_rows", SIZE_ERROR); 454 | } 455 | // fill output matrix with zeros 456 | out = MatN(0); 457 | 458 | // fill with the output matrix with current matrix 459 | // elements 460 | std::size_t i = 0; 461 | std::size_t j = 0; 462 | for (i = 0; i < RowNb; i++) { 463 | for (j = 0; j < ColNb; j++) { 464 | T value = static_cast(0); 465 | (*this)(i, j, value); // getter 466 | out(make_matn_cell(value, i, j)); // setter 467 | } 468 | } 469 | 470 | // fill from r_data the remaining values 471 | std::size_t nb_of_rows_to_add = 472 | static_cast(InRow / ColNb); 473 | for (i = 0; i <= nb_of_rows_to_add; i++) { 474 | std::size_t row = RowNb + i; 475 | for (std::size_t j = 0; j < ColNb; j++) { 476 | T row_val = r_data[i * ColNb + j]; 477 | out(make_matn_cell(row_val, row, j)); // setter 478 | } 479 | } 480 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 481 | "add_rows", SUCCESS); 482 | } 483 | /** 484 | add column 485 | */ 486 | OpResult 487 | add_column(const T (&r_data)[RowNb], 488 | MatN &out) const { 489 | return add_columns(r_data, out); 490 | } 491 | template 492 | OpResult add_columns( 493 | const T (&c_data)[InCol], 494 | MatN &out) const { 495 | if ((InCol % RowNb) != 0) { 496 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 497 | "add_columns", SIZE_ERROR); 498 | } 499 | // fill output matrix with zeros 500 | MatN::zeros(out); 501 | 502 | // fill with the output matrix with current matrix 503 | // elements 504 | std::size_t i = 0; 505 | std::size_t j = 0; 506 | for (i = 0; i < RowNb; i++) { 507 | for (j = 0; j < ColNb; j++) { 508 | T value = static_cast(0); 509 | (*this)(i, j, value); // getter 510 | out(make_matn_cell(value, i, j)); // setter 511 | } 512 | } 513 | // fill from c_data the remaining values 514 | std::size_t nb_of_cols_to_add = 515 | static_cast(InCol / RowNb); 516 | 517 | // even if there are zero columns to add the output 518 | // should be one 519 | for (i = 0; i < nb_of_cols_to_add; i++) { 520 | std::size_t col = ColNb + i; 521 | for (j = 0; j < RowNb; j++) { 522 | T col_val = c_data[i * RowNb + j]; 523 | out(make_matn_cell(col_val, j, col)); 524 | } 525 | } 526 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 527 | "add_columns", SUCCESS); 528 | } 529 | 530 | private: 531 | template 532 | OpResult apply(const MatN &vmat, 533 | const Func &fn, 534 | MatN &out) const { 535 | for (std::size_t i = 0; i < Size; i++) { 536 | T tout = static_cast(0); 537 | vmat(i, tout); // getter 538 | T val = fn(data[i], tout); 539 | auto r = out(make_matn_cell(val, i)); 540 | if (r.status != SUCCESS) 541 | return r; 542 | } 543 | 544 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 545 | "apply", SUCCESS); 546 | } 547 | template 548 | OpResult apply(const Func &fn, 549 | MatN &out) const { 550 | for (std::size_t i = 0; i < Size; i++) { 551 | T val = fn(data[i]); 552 | auto r = out(make_matn_cell(val, i)); 553 | if (r.status != SUCCESS) 554 | return r; 555 | } 556 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 557 | "apply", SUCCESS); 558 | } 559 | template 560 | OpResult apply(const T &v, const Func &fn, 561 | MatN &out) const { 562 | for (std::size_t i = 0; i < Size; i++) { 563 | T val = fn(data[i], v); 564 | auto r = out(make_matn_cell(val, i)); 565 | if (r.status != SUCCESS) 566 | return r; 567 | } 568 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 569 | "apply", SUCCESS); 570 | } 571 | }; 572 | 573 | template 574 | OpResult identity(MatN &out) { 575 | MatN mat; 576 | for (std::size_t i = 0; i < N; i++) { 577 | mat(make_matn_cell(static_cast(1), i, i)); 578 | } 579 | out = mat; 580 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 581 | "identity", SUCCESS); 582 | } 583 | 584 | } // namespace matn 585 | 586 | } // namespace math3d 587 | 588 | #endif 589 | -------------------------------------------------------------------------------- /tests/test_quaternion.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | typedef float real; 5 | using namespace math3d::quaternion; 6 | using namespace math3d; 7 | 8 | /*! @{ 9 | Test accessors of Quaternion object. These functions help 10 | us to access 11 | real parts, that is the scalar and the vector part of the 12 | Quaternion object. 13 | */ 14 | 15 | TEST(QuaternionTest, test_scalar1) { 16 | Quaternion q; 17 | real t = static_cast(4561); 18 | auto res = q.scalar(t); 19 | 20 | EXPECT_EQ(t, 0); 21 | EXPECT_EQ(res.status, SUCCESS); 22 | } 23 | TEST(QuaternionTest, test_vector1) { 24 | Quaternion q; 25 | vecn::VecN v_s; 26 | auto res = q.vector(v_s); 27 | real vs[3]; 28 | v_s(vs); 29 | real comp[3] = {1, 1, 1}; 30 | EXPECT_EQ(res.status, SUCCESS); 31 | EXPECT_EQ(vs[0], comp[0]); 32 | EXPECT_EQ(vs[1], comp[1]); 33 | EXPECT_EQ(vs[2], comp[2]); 34 | }; 35 | 36 | /*! @{ test component accessors */ 37 | TEST(QuaternionTest, test_get_component_0) { 38 | Quaternion q(2, vecn::VecN(3)); 39 | QuaternionComponent comp; 40 | auto result = q(kSCALAR, comp); 41 | EXPECT_EQ(result.status, SUCCESS); 42 | EXPECT_EQ(comp.r, 2); 43 | EXPECT_EQ(comp.base, kSCALAR); 44 | } 45 | 46 | TEST(QuaternionTest, test_get_component_1) { 47 | Quaternion q(2, vecn::VecN(3)); 48 | QuaternionComponent comp; 49 | auto result = q(kI, comp); 50 | EXPECT_EQ(result.status, SUCCESS); 51 | EXPECT_EQ(comp.r, 3); 52 | EXPECT_EQ(comp.base, kI); 53 | } 54 | 55 | TEST(QuaternionTest, test_get_component_2) { 56 | Quaternion q(2, vecn::VecN(3)); 57 | QuaternionComponent comp; 58 | auto result = q(kJ, comp); 59 | EXPECT_EQ(result.status, SUCCESS); 60 | EXPECT_EQ(comp.r, 3); 61 | EXPECT_EQ(comp.base, kJ); 62 | } 63 | 64 | TEST(QuaternionTest, test_get_component_3) { 65 | Quaternion q(2, vecn::VecN(3)); 66 | QuaternionComponent comp; 67 | auto result = q(kK, comp); 68 | EXPECT_EQ(result.status, SUCCESS); 69 | EXPECT_EQ(comp.r, 3); 70 | EXPECT_EQ(comp.base, kK); 71 | } 72 | /*! @} */ 73 | /*! @} */ 74 | 75 | /*! @{ Test constructors of the Quaternion object*/ 76 | 77 | TEST(QuaternionTest, test_constructor_0) { 78 | Quaternion q; 79 | real s = static_cast(1); 80 | auto res = q.scalar(s); 81 | EXPECT_EQ(res.status, SUCCESS); 82 | 83 | vecn::VecN vec2; 84 | res = q.vector(vec2); 85 | EXPECT_EQ(res.status, SUCCESS); 86 | real vec[3]; 87 | vec2(vec); 88 | // 89 | EXPECT_EQ(s, 0); 90 | EXPECT_EQ(vec[0], 1); 91 | EXPECT_EQ(vec[1], 1); 92 | EXPECT_EQ(vec[2], 1); 93 | } 94 | 95 | TEST(QuaternionTest, test_constructor_1) { 96 | real v[3] = {3, 4, 5}; 97 | Quaternion q(2, vecn::VecN(v)); 98 | 99 | real s = static_cast(1); 100 | auto res = q.scalar(s); 101 | EXPECT_EQ(res.status, SUCCESS); 102 | 103 | vecn::VecN vec2; 104 | res = q.vector(vec2); 105 | EXPECT_EQ(res.status, SUCCESS); 106 | // 107 | real vec[3]; 108 | vec2(vec); 109 | EXPECT_EQ(s, 2); 110 | EXPECT_EQ(vec[0], 3); 111 | EXPECT_EQ(vec[1], 4); 112 | EXPECT_EQ(vec[2], 5); 113 | } 114 | 115 | TEST(QuaternionTest, test_constructor_2) { 116 | real c_1 = 2; 117 | real cs[3]; 118 | cs[0] = 3; 119 | cs[1] = 4; 120 | cs[2] = 5; 121 | Quaternion q(c_1, vecn::VecN(cs)); 122 | 123 | real s = static_cast(1); 124 | auto res = q.scalar(s); 125 | EXPECT_EQ(res.status, SUCCESS); 126 | 127 | vecn::VecN vec2; 128 | res = q.vector(vec2); 129 | EXPECT_EQ(res.status, SUCCESS); 130 | real vec[3]; 131 | vec2(vec); 132 | // 133 | EXPECT_EQ(s, 2); 134 | EXPECT_EQ(vec[0], 3); 135 | EXPECT_EQ(vec[1], 4); 136 | EXPECT_EQ(vec[2], 5); 137 | } 138 | 139 | TEST(QuaternionTest, test_constructor_3) { 140 | QuaternionComponent c1(kSCALAR, 2); 141 | QuaternionComponent c2(kI, 3); 142 | QuaternionComponent c3(kJ, 4); 143 | QuaternionComponent c4(kK, 5); 144 | QuaternionComponent cs[] = {c1, c2, c3, c4}; 145 | Quaternion q(cs); 146 | 147 | real s = static_cast(1); 148 | auto res = q.scalar(s); 149 | EXPECT_EQ(res.status, SUCCESS); 150 | 151 | vecn::VecN vec1; 152 | res = q.vector(vec1); 153 | EXPECT_EQ(res.status, SUCCESS); 154 | real vec[3]; 155 | vec1(vec); 156 | // 157 | EXPECT_EQ(s, 2); 158 | EXPECT_EQ(vec[0], 3); 159 | EXPECT_EQ(vec[1], 4); 160 | EXPECT_EQ(vec[2], 5); 161 | } 162 | 163 | TEST(QuaternionTest, test_constructor_4) { 164 | real c1 = 2; 165 | QuaternionComponent c2(kI, 3); 166 | QuaternionComponent c3(kJ, 4); 167 | QuaternionComponent c4(kK, 5); 168 | QuaternionComponent cs[] = {c2, c3, c4}; 169 | Quaternion q(c1, cs); 170 | 171 | real s = static_cast(1); 172 | auto res = q.scalar(s); 173 | EXPECT_EQ(res.status, SUCCESS); 174 | 175 | vecn::VecN vec1; 176 | res = q.vector(vec1); 177 | EXPECT_EQ(res.status, SUCCESS); 178 | real vec[3]; 179 | vec1(vec); 180 | // 181 | EXPECT_EQ(s, 2); 182 | EXPECT_EQ(vec[0], 3); 183 | EXPECT_EQ(vec[1], 4); 184 | EXPECT_EQ(vec[2], 5); 185 | } 186 | 187 | TEST(QuaternionTest, test_constructor_5) { 188 | real c1 = 2; 189 | vecn::VecN cs({3, 4, 5}); 190 | Quaternion q(c1, cs); 191 | real s = static_cast(1); 192 | auto res = q.scalar(s); 193 | EXPECT_EQ(res.status, SUCCESS); 194 | 195 | vecn::VecN vec1; 196 | res = q.vector(vec1); 197 | EXPECT_EQ(res.status, SUCCESS); 198 | real vec[3]; 199 | vec1(vec); 200 | // 201 | EXPECT_EQ(s, 2); 202 | EXPECT_EQ(vec[0], 3); 203 | EXPECT_EQ(vec[1], 4); 204 | EXPECT_EQ(vec[2], 5); 205 | } 206 | 207 | /*! @} */ 208 | 209 | /*! @{ Test vector operations */ 210 | 211 | TEST(QuaternionTest, test_vector_multiplication_0) { 212 | Quaternion q(2, vecn::VecN({3, 4, 5})); 213 | vecn::VecN v1; 214 | 215 | auto res = q.multiply(2, v1); 216 | EXPECT_EQ(res.status, SUCCESS); 217 | 218 | real s = static_cast(1); 219 | res = q.scalar(s); 220 | EXPECT_EQ(res.status, SUCCESS); 221 | real v[3]; 222 | v1(v); 223 | 224 | // 225 | EXPECT_EQ(s, 2); 226 | EXPECT_EQ(v[0], 6); 227 | EXPECT_EQ(v[1], 8); 228 | EXPECT_EQ(v[2], 10); 229 | } 230 | TEST(QuaternionTest, test_vector_multiplication_1) { 231 | Quaternion q(2, vecn::VecN({3, 4, 5})); 232 | vecn::VecN v1; 233 | auto res = q.multiply(2, v1); 234 | EXPECT_EQ(res.status, SUCCESS); 235 | real v[3]; 236 | v1(v); 237 | // 238 | EXPECT_EQ(v[0], 6); 239 | EXPECT_EQ(v[1], 8); 240 | EXPECT_EQ(v[2], 10); 241 | } 242 | TEST(QuaternionTest, test_vector_multiplication_2) { 243 | Quaternion q(2, vecn::VecN({3, 4, 5})); 244 | vecn::VecN v1; 245 | real t[3]; 246 | t[0] = 2; 247 | t[1] = 3; 248 | t[2] = 1; 249 | auto result = q.multiply(t, v1); 250 | real v[3]; 251 | v1(v); 252 | EXPECT_EQ(v[0], 6); 253 | EXPECT_EQ(v[1], 12); 254 | EXPECT_EQ(v[2], 5); 255 | EXPECT_EQ(result.status, SUCCESS); 256 | } 257 | TEST(QuaternionTest, test_vector_addition_0) { 258 | Quaternion q(2, vecn::VecN({3, 4, 5})); 259 | vecn::VecN v_out; 260 | auto result = q.add(2, v_out); 261 | real v[3]; 262 | v_out(v); 263 | real s = static_cast(10); 264 | q.scalar(s); 265 | EXPECT_EQ(s, 2); 266 | EXPECT_EQ(v[0], 5); 267 | EXPECT_EQ(v[1], 6); 268 | EXPECT_EQ(v[2], 7); 269 | EXPECT_EQ(result.status, SUCCESS); 270 | } 271 | TEST(QuaternionTest, test_vector_addition_1) { 272 | Quaternion q(2, vecn::VecN({3, 4, 5})); 273 | vecn::VecN v_out; 274 | auto result = q.add(2, v_out); 275 | real v[3]; 276 | v_out(v); 277 | EXPECT_EQ(v[0], 5); 278 | EXPECT_EQ(v[1], 6); 279 | EXPECT_EQ(v[2], 7); 280 | EXPECT_EQ(result.status, SUCCESS); 281 | } 282 | TEST(QuaternionTest, test_vector_addition_2) { 283 | Quaternion q(2, vecn::VecN({3, 4, 5})); 284 | vecn::VecN v_in; 285 | vecn::VecN t({2, 3, 1}); 286 | auto result = q.add(t, v_in); 287 | real v[3]; 288 | v_in(v); 289 | EXPECT_EQ(v[0], 5); 290 | EXPECT_EQ(v[1], 7); 291 | EXPECT_EQ(v[2], 6); 292 | EXPECT_EQ(result.status, SUCCESS); 293 | } 294 | TEST(QuaternionTest, test_vector_subtraction_0) { 295 | Quaternion q(2, vecn::VecN({3, 4, 5})); 296 | vecn::VecN v_in; 297 | q.vector(v_in); 298 | auto result = q.subtract(2, v_in); 299 | real s = static_cast(22); 300 | q.scalar(s); 301 | real v[3]; 302 | v_in(v); 303 | EXPECT_EQ(s, 2); 304 | EXPECT_EQ(v[0], 1); 305 | EXPECT_EQ(v[1], 2); 306 | EXPECT_EQ(v[2], 3); 307 | EXPECT_EQ(result.status, SUCCESS); 308 | } 309 | TEST(QuaternionTest, test_vector_subtraction_1) { 310 | Quaternion q(2, vecn::VecN({3, 4, 5})); 311 | vecn::VecN v_in; 312 | auto result = q.subtract(2, v_in); 313 | real v[3]; 314 | v_in(v); 315 | EXPECT_EQ(v[0], 1); 316 | EXPECT_EQ(v[1], 2); 317 | EXPECT_EQ(v[2], 3); 318 | EXPECT_EQ(result.status, SUCCESS); 319 | } 320 | TEST(QuaternionTest, test_vector_subtraction_2) { 321 | Quaternion q(2, vecn::VecN({3, 4, 5})); 322 | vecn::VecN v_in; 323 | vecn::VecN t({2, 3, 1}); 324 | auto result = q.subtract(t, v_in); 325 | real v[3]; 326 | v_in(v); 327 | EXPECT_EQ(v[0], 1); 328 | EXPECT_EQ(v[1], 1); 329 | EXPECT_EQ(v[2], 4); 330 | EXPECT_EQ(result.status, SUCCESS); 331 | } 332 | TEST(QuaternionTest, test_vector_division_0) { 333 | Quaternion q(2, vecn::VecN({4, 4, 6})); 334 | vecn::VecN v_in; 335 | q.vector(v_in); 336 | auto result = q.divide(2, v_in); 337 | 338 | real s = static_cast(1); 339 | result = q.scalar(s); 340 | EXPECT_EQ(result.status, SUCCESS); 341 | real v[3]; 342 | v_in(v); 343 | 344 | EXPECT_EQ(s, 2); 345 | EXPECT_EQ(v[0], 2); 346 | EXPECT_EQ(v[1], 2); 347 | EXPECT_EQ(v[2], 3); 348 | EXPECT_EQ(result.status, SUCCESS); 349 | } 350 | TEST(QuaternionTest, test_vector_division_1) { 351 | Quaternion q(2, vecn::VecN({2, 4, 6})); 352 | vecn::VecN v_in; 353 | auto result = q.divide(2, v_in); 354 | real v[3]; 355 | v_in(v); 356 | EXPECT_EQ(v[0], 1); 357 | EXPECT_EQ(v[1], 2); 358 | EXPECT_EQ(v[2], 3); 359 | EXPECT_EQ(result.status, SUCCESS); 360 | } 361 | TEST(QuaternionTest, test_vector_division_2) { 362 | Quaternion q(2, vecn::VecN({4, 4, 5})); 363 | vecn::VecN v_in; 364 | vecn::VecN t({2, 4, 1}); 365 | auto result = q.divide(t, v_in); 366 | real v[3]; 367 | v_in(v); 368 | EXPECT_EQ(v[0], 2); 369 | EXPECT_EQ(v[1], 1); 370 | EXPECT_EQ(v[2], 5); 371 | EXPECT_EQ(result.status, SUCCESS); 372 | } 373 | TEST(QuaternionTest, test_vector_division_3) { 374 | Quaternion q(2, vecn::VecN({3, 4, 5})); 375 | vecn::VecN v; 376 | q.vector(v); 377 | auto result = q.divide(static_cast(0), v); 378 | EXPECT_EQ(result.status, ARG_ERROR); 379 | } 380 | TEST(QuaternionTest, test_vector_division_5) { 381 | Quaternion q(2, vecn::VecN({3, 4, 5})); 382 | vecn::VecN v; 383 | vecn::VecN t({2, 0, 1}); 384 | auto result = q.divide(t, v); 385 | EXPECT_EQ(result.status, ARG_ERROR); 386 | } 387 | TEST(QuaternionTest, test_vector_dot_0) { 388 | Quaternion q(1, vecn::VecN({9, 2, 7})); 389 | vecn::VecN t({4, 8, 10}); 390 | real out; 391 | auto result = q.dot(t, out); 392 | EXPECT_EQ(out, 122); 393 | EXPECT_EQ(result.status, SUCCESS); 394 | } 395 | TEST(QuaternionTest, test_vector_dot_1) { 396 | Quaternion q(0, vecn::VecN({9, 2, 7})); 397 | vecn::VecN t({4, 8, 10}); 398 | real s = static_cast(55156); 399 | auto result = q.dot(t, s); 400 | EXPECT_EQ(s, 122); 401 | EXPECT_EQ(result.status, SUCCESS); 402 | } 403 | TEST(QuaternionTest, test_vector_cross_0) { 404 | Quaternion q(1, vecn::VecN({2, 3, 4})); 405 | vecn::VecN vout; 406 | vecn::VecN t({5, 6, 7}); 407 | auto result = q.cross(t, vout); 408 | real out[3]; 409 | vout(out); 410 | EXPECT_EQ(result.status, SUCCESS); 411 | EXPECT_EQ(out[0], static_cast(-3)); 412 | EXPECT_EQ(out[1], static_cast(6)); 413 | EXPECT_EQ(out[2], static_cast(-3)); 414 | } 415 | /*! @} */ 416 | 417 | /*! @{ Test hamilton product operation from Vince 2011 - 418 | * Quaternions for 419 | * Computer Graphics, p. 70 */ 420 | TEST(QuaternionTest, test_hamilton_product) { 421 | Quaternion q_a(2, vecn::VecN({-2, 3, -4})); 422 | Quaternion q_b(1, vecn::VecN({-2, 5, -6})); 423 | Quaternion q_out; 424 | q_a.hamilton_product(q_b, q_out); 425 | // Quaternion q_ab = q_a * q_b; 426 | 427 | real s = static_cast(0); 428 | auto res = q_out.scalar(s); 429 | EXPECT_EQ(res.status, SUCCESS); 430 | 431 | vecn::VecN v_in; 432 | res = q_out.vector(v_in); 433 | EXPECT_EQ(res.status, SUCCESS); 434 | real v[3]; 435 | v_in(v); 436 | 437 | EXPECT_EQ(s, static_cast(-41)); 438 | EXPECT_EQ(v[0], static_cast(-4)); 439 | EXPECT_EQ(v[1], static_cast(9)); 440 | EXPECT_EQ(v[2], static_cast(-20)); 441 | } 442 | /*! @} */ 443 | /*! @{ Test conjugate operation from Vince 2011, p. 70 */ 444 | TEST(QuaternionTest, test_conjugate_0) { 445 | Quaternion q_b(2, vecn::VecN({-2, 3, -4})); 446 | Quaternion q_out; 447 | q_b.conjugate(q_out); 448 | 449 | real s = static_cast(0); 450 | auto res = q_out.scalar(s); 451 | EXPECT_EQ(res.status, SUCCESS); 452 | 453 | vecn::VecN v_in; 454 | res = q_out.vector(v_in); 455 | EXPECT_EQ(res.status, SUCCESS); 456 | real v[3]; 457 | v_in(v); 458 | 459 | EXPECT_EQ(s, static_cast(2)); 460 | EXPECT_EQ(v[0], static_cast(2)); 461 | EXPECT_EQ(v[1], static_cast(-3)); 462 | EXPECT_EQ(v[2], static_cast(4)); 463 | } 464 | TEST(QuaternionTest, test_conjugate_1) { 465 | Quaternion q_a(2, vecn::VecN({-2, 3, -4})); 466 | Quaternion q_b(1, vecn::VecN({-2, 5, -6})); 467 | Quaternion q_ab_prod; 468 | q_a.hamilton_product(q_b, q_ab_prod); 469 | 470 | Quaternion q_out1; 471 | q_ab_prod.conjugate(q_out1); 472 | 473 | // 474 | Quaternion q_b_conj; 475 | q_b.conjugate(q_b_conj); 476 | 477 | Quaternion q_a_conj; 478 | q_a.conjugate(q_a_conj); 479 | 480 | Quaternion q_out2; 481 | q_b_conj.hamilton_product(q_a_conj, q_out2); 482 | 483 | // 484 | real s = static_cast(0); 485 | real sq = static_cast(0); 486 | 487 | q_out1.scalar(s); 488 | q_out2.scalar(sq); 489 | 490 | // 491 | vecn::VecN v_in; 492 | vecn::VecN vq_in; 493 | q_out1.vector(v_in); 494 | q_out2.vector(vq_in); 495 | real v[3]; 496 | v_in(v); 497 | real vq[3]; 498 | vq_in(vq); 499 | 500 | EXPECT_EQ(s, sq); 501 | EXPECT_EQ(v[0], vq[0]); 502 | EXPECT_EQ(v[1], vq[1]); 503 | EXPECT_EQ(v[2], vq[2]); 504 | } 505 | 506 | /*! @{ arithmetic ops for Quaternions */ 507 | TEST(QuaternionTest, test_add_operator) { 508 | Quaternion q_a(2, vecn::VecN({-2, 3, -4})); 509 | Quaternion q_b(1, vecn::VecN({-2, 5, -6})); 510 | Quaternion q_ab; 511 | q_a.add(q_b, q_ab); 512 | 513 | real s = static_cast(1); 514 | auto res = q_ab.scalar(s); 515 | EXPECT_EQ(res.status, SUCCESS); 516 | 517 | vecn::VecN vec_in; 518 | res = q_ab.vector(vec_in); 519 | EXPECT_EQ(res.status, SUCCESS); 520 | real vec[3]; 521 | vec_in(vec); 522 | 523 | EXPECT_EQ(s, 3); 524 | EXPECT_EQ(vec[0], -4); 525 | EXPECT_EQ(vec[1], 8); 526 | EXPECT_EQ(vec[2], -10); 527 | } 528 | TEST(QuaternionTest, test_subtract_operator) { 529 | Quaternion q_a(2, vecn::VecN({-2, 3, -4})); 530 | Quaternion q_b(1, vecn::VecN({-2, 5, -6})); 531 | Quaternion q_ab; 532 | q_a.subtract(q_b, q_ab); 533 | 534 | real s = static_cast(1); 535 | auto res = q_ab.scalar(s); 536 | EXPECT_EQ(res.status, SUCCESS); 537 | 538 | vecn::VecN vec_in; 539 | res = q_ab.vector(vec_in); 540 | EXPECT_EQ(res.status, SUCCESS); 541 | real vec[3]; 542 | vec_in(vec); 543 | 544 | EXPECT_EQ(s, 1); 545 | EXPECT_EQ(vec[0], 0); 546 | EXPECT_EQ(vec[1], -2); 547 | EXPECT_EQ(vec[2], 2); 548 | } 549 | TEST(QuaternionTest, test_product_operator_0) { 550 | Quaternion q_a(2, vecn::VecN({-2, 3, -4})); 551 | Quaternion q_b(1, vecn::VecN({-2, 5, -6})); 552 | Quaternion q_ab; 553 | q_a.product(q_b, q_ab); 554 | 555 | real s = static_cast(1); 556 | auto res = q_ab.scalar(s); 557 | EXPECT_EQ(res.status, SUCCESS); 558 | 559 | vecn::VecN vec_i; 560 | res = q_ab.vector(vec_i); 561 | EXPECT_EQ(res.status, SUCCESS); 562 | real vec[3]; 563 | vec_i(vec); 564 | 565 | EXPECT_EQ(s, static_cast(-41)); 566 | EXPECT_EQ(vec[0], static_cast(-4)); 567 | EXPECT_EQ(vec[1], static_cast(9)); 568 | EXPECT_EQ(vec[2], static_cast(-20)); 569 | } 570 | TEST(QuaternionTest, test_product_operator_1) { 571 | Quaternion q_a(2, vecn::VecN({-2, 3, -4})); 572 | Quaternion q_ab; 573 | q_a.product(2, q_ab); 574 | 575 | real s = static_cast(1); 576 | auto res = q_ab.scalar(s); 577 | EXPECT_EQ(res.status, SUCCESS); 578 | 579 | vecn::VecN vec_in; 580 | res = q_ab.vector(vec_in); 581 | EXPECT_EQ(res.status, SUCCESS); 582 | real vec[3]; 583 | vec_in(vec); 584 | 585 | EXPECT_EQ(s, 4); 586 | EXPECT_EQ(vec[0], -4); 587 | EXPECT_EQ(vec[1], 6); 588 | EXPECT_EQ(vec[2], -8); 589 | } 590 | /*! @} */ 591 | 592 | /*! @{ Test determinant of Quaternion */ 593 | TEST(QuaternionTest, test_norm_squared_v1) { 594 | Quaternion q_a(2, vecn::VecN({-2, 3, -4})); 595 | real dv = static_cast(156); 596 | q_a.norm_squared(dv); 597 | // 4 + 4 + 9 + 16 598 | EXPECT_EQ(dv, static_cast(33)); 599 | } 600 | 601 | /*! Test determinant of Quaternion */ 602 | TEST(QuaternionTest, test_norm_squared_v2) { 603 | Quaternion q_a(2, vecn::VecN({-2, 3, -4})); 604 | real dv = static_cast(5); 605 | q_a.norm_squared(dv); 606 | // 4 + 4 + 9 + 16 607 | EXPECT_EQ(dv, static_cast(33)); 608 | } 609 | /*! @} */ 610 | 611 | /*! @{ Test magnitude of Quaternion */ 612 | TEST(QuaternionTest, test_magnitude) { 613 | Quaternion q_a(2, vecn::VecN({-2, 3, -4})); 614 | real dv = static_cast(5); 615 | q_a.magnitude(dv); 616 | // 4 + 4 + 9 + 16 617 | EXPECT_EQ(dv, static_cast(sqrt(33))); 618 | } 619 | 620 | /*! Test norm of Quaternion */ 621 | TEST(QuaternionTest, test_norm) { 622 | Quaternion q_a(2, vecn::VecN({-2, 3, -4})); 623 | real n = static_cast(0); 624 | auto res = q_a.norm(n); 625 | EXPECT_EQ(res.status, SUCCESS); 626 | // 4 + 4 + 9 + 16 627 | EXPECT_EQ(n, static_cast(sqrt(33))); 628 | } 629 | 630 | /*! @} */ 631 | 632 | /*! @{ Test exponent of Quaternion */ 633 | TEST(QuaternionTest, test_squared) { 634 | Quaternion q_a(2, vecn::VecN({-2, 3, -4})); 635 | Quaternion q_a2; 636 | q_a.squared(q_a2); 637 | 638 | // 639 | real s = static_cast(1); 640 | auto res = q_a2.scalar(s); 641 | EXPECT_EQ(res.status, SUCCESS); 642 | 643 | vecn::VecN vec_in; 644 | res = q_a2.vector(vec_in); 645 | EXPECT_EQ(res.status, SUCCESS); 646 | 647 | real vec[3]; 648 | vec_in(vec); 649 | EXPECT_EQ(s, -25); 650 | EXPECT_EQ(vec[0], -8); 651 | EXPECT_EQ(vec[1], 12); 652 | EXPECT_EQ(vec[2], -16); 653 | } 654 | 655 | TEST(QuaternionTest, test_power) { 656 | Quaternion q_a(2, vecn::VecN({-2, 3, -4})); 657 | Quaternion q_a2; 658 | q_a.power(2, q_a2); 659 | 660 | real s = static_cast(1); 661 | auto res = q_a2.scalar(s); 662 | EXPECT_EQ(res.status, SUCCESS); 663 | 664 | vecn::VecN vec_in; 665 | res = q_a2.vector(vec_in); 666 | EXPECT_EQ(res.status, SUCCESS); 667 | real vec[3]; 668 | vec_in(vec); 669 | 670 | EXPECT_EQ(s, -25); // might be -25 671 | EXPECT_EQ(vec[0], -8); 672 | EXPECT_EQ(vec[1], 12); 673 | EXPECT_EQ(vec[2], -16); 674 | } 675 | 676 | /*! @} */ 677 | 678 | /*! @{ Test inverse of Quaternion Vince 2011 - 679 | * Quaternions for Computer Graphics, p. 71 680 | */ 681 | TEST(QuaternionTest, test_inversed) { 682 | Quaternion q_a(2, vecn::VecN({-2, 3, -4})); 683 | Quaternion inv; 684 | q_a.inversed(inv); 685 | 686 | real s = static_cast(1); 687 | auto res = inv.scalar(s); 688 | EXPECT_EQ(res.status, SUCCESS); 689 | 690 | vecn::VecN vec_in; 691 | res = inv.vector(vec_in); 692 | EXPECT_EQ(res.status, SUCCESS); 693 | real vec[3]; 694 | vec_in(vec); 695 | 696 | EXPECT_EQ(s, static_cast( 697 | static_cast(1.0 / 33) * 2)); 698 | EXPECT_EQ(vec[0], static_cast( 699 | static_cast(1.0 / 33) * 2)); 700 | EXPECT_EQ(vec[1], static_cast( 701 | static_cast(1.0 / 33) * -3)); 702 | EXPECT_EQ(vec[2], static_cast( 703 | static_cast(1.0 / 33) * 4)); 704 | } 705 | /*! @} */ 706 | 707 | /*! @{ Test normalization method of Quaternion */ 708 | TEST(QuaternionTest, test_normalized) { 709 | Quaternion q_a(2, vecn::VecN({-2, 3, -4})); 710 | Quaternion n_q; 711 | q_a.normalized(n_q); 712 | 713 | real s = static_cast(1); 714 | auto res = n_q.scalar(s); 715 | EXPECT_EQ(res.status, SUCCESS); 716 | 717 | vecn::VecN vec_in; 718 | res = n_q.vector(vec_in); 719 | EXPECT_EQ(res.status, SUCCESS); 720 | 721 | EXPECT_EQ(s, static_cast( 722 | static_cast(1.0 / sqrt(33)) * 2)); 723 | real vec[3]; 724 | vec_in(vec); 725 | EXPECT_EQ(vec[0], 726 | static_cast( 727 | static_cast(1.0 / sqrt(33)) * -2)); 728 | EXPECT_EQ(vec[1], 729 | static_cast( 730 | static_cast(1.0 / sqrt(33)) * 3)); 731 | EXPECT_EQ(vec[2], 732 | static_cast( 733 | static_cast(1.0 / sqrt(33)) * -4)); 734 | } 735 | /*! @} */ 736 | -------------------------------------------------------------------------------- /include/math3d/core.cuh: -------------------------------------------------------------------------------- 1 | #ifndef CORE_CUH 2 | #define CORE_CUH 3 | 4 | #include 5 | 6 | namespace math3d { 7 | 8 | enum opstatus_t : uint8_t { 9 | SUCCESS = 1, 10 | INDEX_ERROR = 2, 11 | SIZE_ERROR = 3, 12 | ARG_ERROR = 4, 13 | LU_ERROR = 5, 14 | NOT_IMPLEMENTED = 6 15 | }; 16 | 17 | struct OpResult { 18 | // 19 | size_t line_info = 0; 20 | const char *file_name{nullptr}; 21 | const char *fn_name{nullptr}; 22 | const char *call_name{nullptr}; 23 | const char *duration_info{nullptr}; 24 | 25 | opstatus_t status = NOT_IMPLEMENTED; 26 | bool success = false; 27 | 28 | __host__ __device__ OpResult() = default; 29 | __host__ __device__ ~OpResult() = default; 30 | 31 | __host__ __device__ OpResult(size_t line, const char *fname, 32 | const char *funcname, const char *cname, 33 | opstatus_t op) 34 | : line_info(line), file_name(fname), fn_name(funcname), call_name(cname), 35 | status(op), success(op == SUCCESS) {} 36 | }; 37 | 38 | namespace vecn { 39 | 40 | template struct VecNCell { 41 | T content; 42 | std::size_t index = 0; 43 | }; 44 | 45 | template 46 | __host__ __device__ VecNCell make_vecn_cell(const T &c, std::size_t i) { 47 | VecNCell cell{.content = c, .index = i}; 48 | return cell; 49 | } 50 | 51 | template class VecN { 52 | 53 | public: 54 | /*! Tested */ 55 | __host__ __device__ VecN() { 56 | for (std::size_t i = 0; i < N; ++i) { 57 | data[i] = 0; 58 | } 59 | } 60 | /*! Tested */ 61 | __host__ __device__ VecN(const T (&arr)[N]) { 62 | memcpy(data, arr, N * sizeof(T)); 63 | } 64 | __host__ __device__ VecN(T s) { 65 | for (std::size_t i = 0; i < N; ++i) { 66 | data[i] = s; 67 | } 68 | } 69 | /*! Tested */ 70 | __host__ __device__ OpResult operator()(std::size_t index, T &out) const { 71 | if (index >= N) { 72 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, "(size_t, T&)", 73 | INDEX_ERROR); 74 | return vflag; 75 | } 76 | out = data[index]; 77 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, "(size_t, T&)", SUCCESS); 78 | return vflag; 79 | } 80 | __host__ __device__ OpResult operator()(T (&out)[N]) const { 81 | memcpy(out, data, N * sizeof(T)); 82 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, "(T (&)[N])", SUCCESS); 83 | return vflag; 84 | } 85 | __host__ __device__ constexpr OpResult operator()(std::size_t &out) const { 86 | out = N; 87 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, "(size_t)", SUCCESS); 88 | return vflag; 89 | } 90 | 91 | /*! Tested */ 92 | __host__ __device__ OpResult operator()(const VecNCell &cell) { 93 | if (cell.index >= N) { 94 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "(const VecNCell&)", 95 | INDEX_ERROR); 96 | } 97 | data[cell.index] = cell.content; 98 | 99 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "(const VecNCell&)", 100 | SUCCESS); 101 | } 102 | 103 | /*! Tested */ 104 | __host__ __device__ OpResult add(T v, T (&out)[N]) const { 105 | VecN v_1(v); 106 | T v_in[N]; 107 | v_1(v_in); 108 | // 109 | auto res = add(v_in, out); 110 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, "add(T, T (&)[N])", 111 | res.status); 112 | return vflag; 113 | } 114 | 115 | /*! Tested */ 116 | __host__ __device__ OpResult add(T v, VecN &vout) const { 117 | T out[N]; 118 | auto res = add(v, out); 119 | vout = VecN(out); 120 | 121 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, "add(T, VecN&", 122 | res.status); 123 | return vflag; 124 | } 125 | /*! Tested */ 126 | __host__ __device__ OpResult add(const T (&v)[N], T (&out)[N]) const { 127 | auto fn = [](T thisel, T argel) { return thisel + argel; }; 128 | auto res = apply_el(v, fn, out); 129 | 130 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, 131 | "add(const T(&)[N], T (&)[N])", res.status); 132 | return vflag; 133 | } 134 | /*! Tested */ 135 | __host__ __device__ OpResult add(const VecN &v, VecN &out) const { 136 | T v_in[N]; 137 | v(v_in); 138 | T vout[N]; 139 | auto res = add(v_in, vout); 140 | out = VecN(vout); 141 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, 142 | "add(const VecN&, VecN&)", res.status); 143 | return vflag; 144 | } 145 | // 146 | __host__ __device__ OpResult subtract(T v, T (&out)[N]) const { 147 | VecN v_1(v); 148 | T v_in[N]; 149 | v_1(v_in); 150 | auto res = subtract(v_in, out); 151 | 152 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, "subtract(T, T (&)[N])", 153 | res.status); 154 | return vflag; 155 | } 156 | /*! Tested */ 157 | __host__ __device__ OpResult subtract(T v, VecN &vout) const { 158 | VecN v_1(v); 159 | T v_in[N]; 160 | v_1(v_in); 161 | T out[N]; 162 | auto res = subtract(v_in, out); 163 | vout = VecN(out); 164 | 165 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, "subtract(T, VecN&", 166 | res.status); 167 | return vflag; 168 | } 169 | /*! Tested */ 170 | __host__ __device__ OpResult subtract(const T (&v)[N], T (&out)[N]) const { 171 | auto fn = [](T thisel, T argel) { return thisel - argel; }; 172 | auto res = apply_el(v, fn, out); 173 | 174 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, 175 | "subtract(const T(&)[N], T (&)[N])", res.status); 176 | return vflag; 177 | } 178 | /*! Tested */ 179 | __host__ __device__ OpResult subtract(const VecN &v, 180 | VecN &out) const { 181 | T v_in[N]; 182 | v(v_in); 183 | T vout[N]; 184 | auto res = subtract(v_in, vout); 185 | out = VecN(vout); 186 | 187 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, 188 | "subtract(const VecN&, VecN&)", res.status); 189 | return vflag; 190 | } 191 | // 192 | __host__ __device__ OpResult multiply(T v, T (&out)[N]) const { 193 | T v_in[N]; 194 | VecN vv(v); 195 | vv(v_in); 196 | 197 | auto res = multiply(v_in, out); 198 | 199 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, "multiply(T, T (&)[N])", 200 | res.status); 201 | return vflag; 202 | } 203 | /*! Tested */ 204 | __host__ __device__ OpResult multiply(T v, VecN &vout) const { 205 | T v_in[N]; 206 | VecN vv(v); 207 | vv(v_in); 208 | 209 | T out[N]; 210 | auto res = multiply(v_in, out); 211 | vout = VecN(out); 212 | 213 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, "multiply(T, VecN&", 214 | res.status); 215 | return vflag; 216 | } 217 | /*! Tested */ 218 | __host__ __device__ OpResult multiply(const T (&v)[N], T (&out)[N]) const { 219 | auto fn = [](T thisel, T argel) { return thisel * argel; }; 220 | auto res = apply_el(v, fn, out); 221 | 222 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, 223 | "multiply(const T(&)[N], T (&)[N])", res.status); 224 | return vflag; 225 | } 226 | /*! Tested */ 227 | __host__ __device__ OpResult multiply(const VecN &v, 228 | VecN &out) const { 229 | T v_in[N]; 230 | v(v_in); 231 | T vout[N]; 232 | auto res = multiply(v_in, vout); 233 | out = VecN(vout); 234 | 235 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, 236 | "multiply(const VecN&, VecN&)", res.status); 237 | return vflag; 238 | } 239 | 240 | // 241 | __host__ __device__ OpResult divide(T v, T (&out)[N]) const { 242 | if (v == static_cast(0)) { 243 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, "divide(T, T (&)[N])", 244 | ARG_ERROR); 245 | return vflag; 246 | } 247 | 248 | T v_in[N]; 249 | VecN v_(v); 250 | v_(v_in); 251 | 252 | auto res = divide(v_in, out); 253 | 254 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, "divide(T, T (&)[N])", 255 | res.status); 256 | return vflag; 257 | } 258 | /*! Tested */ 259 | __host__ __device__ OpResult divide(T v, VecN &vout) const { 260 | // check for zero division 261 | if (v == static_cast(0)) { 262 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, "divide(T, VecN&", 263 | ARG_ERROR); 264 | return vflag; 265 | } 266 | T v_in[N]; 267 | VecN v_(v); 268 | v_(v_in); 269 | T out[N]; 270 | 271 | auto res = divide(v_in, out); 272 | vout = VecN(out); 273 | 274 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, "divide(T, VecN&", 275 | res.status); 276 | return vflag; 277 | } 278 | /*! Tested */ 279 | __host__ __device__ OpResult divide(const T (&v)[N], T (&out)[N]) const { 280 | for (std::size_t j = 0; j < N; j++) { 281 | if (v[j] == static_cast(0)) { 282 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, 283 | "divide(const T(&)[N], T (&)[N])", ARG_ERROR); 284 | return vflag; 285 | } 286 | } 287 | auto fn = [](T thisel, T argel) { return thisel / argel; }; 288 | auto res = apply_el(v, fn, out); 289 | 290 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, 291 | "divide(const T(&)[N], T (&)[N])", res.status); 292 | return vflag; 293 | } 294 | /*! Tested */ 295 | __host__ __device__ OpResult divide(const VecN &v, 296 | VecN &out) const { 297 | // check zero division 298 | T v_in[N]; 299 | v(v_in); 300 | T vout[N]; 301 | auto res = divide(v_in, vout); 302 | out = VecN(vout); 303 | 304 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, 305 | "divide(const VecN&, VecN&)", res.status); 306 | return vflag; 307 | } 308 | __host__ __device__ OpResult dot(const T &v, T &out) const { 309 | T v_in[N]; 310 | VecN v_(v); 311 | v_(v_in); 312 | dot(v_in, out); 313 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, "dot(const T&, T &)", 314 | SUCCESS); 315 | return vflag; 316 | } 317 | __host__ __device__ OpResult dot(const T (&v)[N], T &out) const { 318 | out = static_cast(0); 319 | for (std::size_t i = 0; i < N; i++) { 320 | out += data[i] * v[i]; 321 | } 322 | 323 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, "dot(const T(&)[N], T &)", 324 | SUCCESS); 325 | return vflag; 326 | } 327 | __host__ __device__ OpResult dot(const VecN &v, T &out) const { 328 | T v_in[N]; 329 | v(v_in); 330 | dot(v_in, out); 331 | 332 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, 333 | "dot(const VecN&, T &)", SUCCESS); 334 | return vflag; 335 | } 336 | 337 | __host__ __device__ OpResult cross(const VecN &v, 338 | VecN &out) const { 339 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, 340 | "cross(const VecN&, VecN &)", NOT_IMPLEMENTED); 341 | return vflag; 342 | } 343 | 344 | private: 345 | /** holds the vector data*/ 346 | T data[N]; 347 | 348 | template 349 | __host__ __device__ OpResult apply_el(const T (&v)[N], const Func &fn, 350 | T (&out)[N]) const { 351 | 352 | for (std::size_t i = 0; i < N; i++) { 353 | out[i] = fn(data[i], v[i]); 354 | } 355 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, 356 | "apply_el(const T(&)[N], const Func&, T(&)[N])", SUCCESS); 357 | return vflag; 358 | } 359 | }; 360 | 361 | template 362 | __host__ __device__ OpResult base(VecN &vout) { 363 | if (BaseOrder >= N) { 364 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, "base(VecN&)", 365 | ARG_ERROR); 366 | return vflag; 367 | } 368 | VecN out; 369 | out(make_vecn_cell(1, BaseOrder)); 370 | vout = out; 371 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, "base(VecN&)", 372 | SUCCESS); 373 | return vflag; 374 | } 375 | 376 | template 377 | __host__ __device__ OpResult cross(const VecN &v1, const VecN &v2, 378 | T &out) { 379 | T t1; 380 | v1(0, t1); 381 | T t2; 382 | v1(1, t2); 383 | T v_1; 384 | v2(0, v_1); 385 | T v_2; 386 | v2(1, v_2); 387 | T t1v2 = t1 * v_2; 388 | T t2v1 = t2 * v_1; 389 | out = t1v2 - t2v1; 390 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, 391 | "cross(const VecN&, T&)", SUCCESS); 392 | return vflag; 393 | } 394 | 395 | template 396 | __host__ __device__ OpResult cross(const VecN &a, const VecN &b, 397 | VecN &out) { 398 | T tx; 399 | a(0, tx); 400 | T ty; 401 | a(1, ty); 402 | T tz; 403 | a(2, tz); 404 | // 405 | T vx; 406 | b(0, vx); 407 | T vy; 408 | b(1, vy); 409 | T vz; 410 | b(2, vz); 411 | // 412 | T x = ty * vz - tz * vy; 413 | T y = tz * vx - tx * vz; 414 | T z = tx * vy - ty * vx; 415 | out(make_vecn_cell(x, 0)); 416 | out(make_vecn_cell(y, 1)); 417 | out(make_vecn_cell(z, 2)); 418 | OpResult vflag(__LINE__, __FILE__, __FUNCTION__, 419 | "cross(const VecN&, VecN&)", SUCCESS); 420 | return vflag; 421 | } 422 | 423 | } // namespace vecn 424 | 425 | namespace quaternion { 426 | // holds quaternion related operations 427 | 428 | /**Quaternion bases*/ 429 | enum QuaternionBase : std::uint8_t { 430 | kSCALAR = 1, // null value for quaternion base it has 431 | // no effect on computation 432 | kI = 2, // i base for the second quaternion component 433 | kJ = 3, // j base for the third quaternion component 434 | kK = 4 // k base for the fourth quaternion component 435 | }; 436 | 437 | /** 438 | \brief Quaternion component 439 | */ 440 | template struct QuaternionComponent { 441 | QuaternionBase base; 442 | T r; 443 | 444 | __host__ __device__ QuaternionComponent() : r(0), base(kSCALAR) {} 445 | __host__ __device__ QuaternionComponent(QuaternionBase b, T a) 446 | : base(b), r(a) {} 447 | }; 448 | 449 | template class Quaternion { 450 | public: 451 | __host__ __device__ Quaternion() 452 | : coeffs{static_cast(0), static_cast(1), static_cast(1), 453 | static_cast(1)} {} 454 | // 455 | __host__ __device__ Quaternion(T c1, const QuaternionComponent qs[3]) { 456 | coeffs[0] = c1; 457 | coeffs[static_cast(qs[0].base) - 1] = qs[0].r; 458 | coeffs[static_cast(qs[1].base) - 1] = qs[1].r; 459 | coeffs[static_cast(qs[2].base) - 1] = qs[2].r; 460 | } 461 | __host__ __device__ Quaternion(T c1, const vecn::VecN &vs) { 462 | coeffs[0] = c1; 463 | T v_0; 464 | vs(0, v_0); 465 | T v_1; 466 | vs(1, v_1); 467 | T v_2; 468 | vs(2, v_2); 469 | coeffs[1] = v_0; 470 | coeffs[2] = v_1; 471 | coeffs[3] = v_2; 472 | } 473 | 474 | __host__ __device__ Quaternion(const QuaternionComponent qs[4]) { 475 | coeffs[static_cast(qs[0].base) - 1] = qs[0].r; 476 | coeffs[static_cast(qs[1].base) - 1] = qs[1].r; 477 | coeffs[static_cast(qs[2].base) - 1] = qs[2].r; 478 | coeffs[static_cast(qs[3].base) - 1] = qs[3].r; 479 | } 480 | 481 | __host__ __device__ OpResult operator()(T &out) const { return scalar(out); } 482 | __host__ __device__ OpResult scalar(T &out) const { 483 | 484 | out = coeffs[0]; 485 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "scalar", SUCCESS); 486 | } 487 | __host__ __device__ OpResult vector(vecn::VecN &out) const { 488 | T v[3]; 489 | v[0] = coeffs[1]; 490 | v[1] = coeffs[2]; 491 | v[2] = coeffs[3]; 492 | out = vecn::VecN(v); 493 | 494 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "vector", SUCCESS); 495 | } 496 | __host__ __device__ OpResult operator()(vecn::VecN &out) const { 497 | return vector(out); 498 | } 499 | __host__ __device__ OpResult operator()(const QuaternionBase &b, 500 | QuaternionComponent &c) const { 501 | switch (b) { 502 | case kSCALAR: { 503 | c = QuaternionComponent(kSCALAR, coeffs[0]); 504 | break; 505 | } 506 | case kI: { 507 | c = QuaternionComponent(kI, coeffs[1]); 508 | break; 509 | } 510 | case kJ: { 511 | c = QuaternionComponent(kJ, coeffs[2]); 512 | break; 513 | } 514 | case kK: { 515 | c = QuaternionComponent(kK, coeffs[3]); 516 | break; 517 | } 518 | } 519 | 520 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 521 | "(const QuaternionBase&, QuaternionComponent&)", 522 | SUCCESS); 523 | } 524 | 525 | __host__ __device__ OpResult 526 | operator()(const QuaternionComponent &c) const { 527 | QuaternionBase b = c.base; 528 | coeffs[static_cast(c.base) - 1] = c.r; 529 | return OpResult(__LINE__, __FILE__, __FUNCTION__, 530 | "(const QuaternionComponent&)", SUCCESS); 531 | } 532 | __host__ __device__ OpResult multiply(T t, vecn::VecN &out) const { 533 | vecn::VecN vec; 534 | vector(vec); 535 | return vec.multiply(t, out); 536 | } 537 | __host__ __device__ OpResult add(T t, vecn::VecN &out) const { 538 | vecn::VecN vec; 539 | vector(vec); 540 | return vec.add(t, out); 541 | } 542 | __host__ __device__ OpResult subtract(T t, vecn::VecN &out) const { 543 | vecn::VecN vec; 544 | vector(vec); 545 | return vec.subtract(t, out); 546 | } 547 | __host__ __device__ OpResult divide(T t, vecn::VecN &out) const { 548 | if (t == 0) { 549 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "divide", ARG_ERROR); 550 | } 551 | vecn::VecN vec; 552 | vector(vec); 553 | return vec.divide(t, out); 554 | } 555 | /** arithmetic operations with a vector on vector part*/ 556 | __host__ __device__ OpResult multiply(vecn::VecN t, 557 | vecn::VecN &out) const { 558 | vecn::VecN vec; 559 | vector(vec); 560 | return vec.multiply(t, out); 561 | } 562 | __host__ __device__ OpResult add(vecn::VecN t, 563 | vecn::VecN &out) const { 564 | 565 | vecn::VecN vec; 566 | vector(vec); 567 | return vec.add(t, out); 568 | } 569 | __host__ __device__ OpResult subtract(vecn::VecN t, 570 | vecn::VecN &out) const { 571 | 572 | vecn::VecN vec; 573 | vector(vec); 574 | return vec.subtract(t, out); 575 | } 576 | __host__ __device__ OpResult divide(vecn::VecN t, 577 | vecn::VecN &out) const { 578 | for (std::size_t i = 0; i < 3; i++) { 579 | T v; 580 | t(i, v); 581 | if (v == 0) { 582 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "divide", ARG_ERROR); 583 | } 584 | } 585 | vecn::VecN vec; 586 | vector(vec); 587 | return vec.divide(t, out); 588 | } 589 | /** dot product and cross product for two vec3*/ 590 | __host__ __device__ OpResult dot(const vecn::VecN &t, T &out) const { 591 | 592 | vecn::VecN vec; 593 | vector(vec); 594 | return vec.dot(t, out); 595 | } 596 | __host__ __device__ OpResult cross(const vecn::VecN &t, 597 | vecn::VecN &out) const { 598 | vecn::VecN vec; 599 | vector(vec); 600 | return vecn::cross(vec, t, out); 601 | } 602 | 603 | /** Quaternion product as it is shown by Vince 2011 p. 604 | * 63 605 | Given a quaternion \f[q_a = [s_a, a]\f] and 606 | another quaternion \f[q_b = [s_b, b]\f] 607 | Their product is equal to: 608 | \f[s_a s_b - a \cdot b, s_a b + s_b a + a \times b \f] 609 | */ 610 | __host__ __device__ OpResult hamilton_product(const Quaternion &q_b, 611 | Quaternion &out) const { 612 | // s_a, s_b, a, b 613 | T s_a = static_cast(0); 614 | auto res = scalar(s_a); 615 | if (res.status != SUCCESS) { 616 | return res; 617 | } 618 | 619 | T s_b = static_cast(0); 620 | res = q_b.scalar(s_b); 621 | if (res.status != SUCCESS) { 622 | return res; 623 | } 624 | 625 | vecn::VecN a; 626 | res = vector(a); 627 | if (res.status != SUCCESS) { 628 | return res; 629 | } 630 | 631 | vecn::VecN b; 632 | res = q_b.vector(b); 633 | if (res.status != SUCCESS) 634 | return res; 635 | 636 | // s_a * s_b 637 | T s_ab = s_a * s_b; 638 | 639 | // a \cdot b 640 | T a_dot_b = static_cast(0); 641 | res = a.dot(b, a_dot_b); 642 | if (res.status != SUCCESS) 643 | return res; 644 | 645 | // a \times b 646 | vecn::VecN cross_ab; 647 | res = vecn::cross(a, b, cross_ab); 648 | if (res.status != SUCCESS) { 649 | return res; 650 | } 651 | 652 | // s_a * b + s_b * a + a \times b 653 | T out_v[3]; 654 | T b_s[3]; 655 | T a_s[3]; 656 | T ab_s[3]; 657 | b(b_s); 658 | a(a_s); 659 | cross_ab(ab_s); 660 | for (std::size_t i = 0; i < 3; i++) { 661 | out_v[i] = s_a * b_s[i] + s_b * a_s[i] + ab_s[i]; 662 | } 663 | vecn::VecN tout(out_v); 664 | out = Quaternion(s_ab - a_dot_b, tout); 665 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "hamilton_product", 666 | SUCCESS); 667 | } 668 | __host__ __device__ OpResult conjugate(Quaternion &out) const { 669 | T s = static_cast(0); 670 | auto res = scalar(s); 671 | if (res.status != SUCCESS) 672 | return res; 673 | 674 | vecn::VecN vec; 675 | res = vector(vec); 676 | 677 | if (res.status != SUCCESS) 678 | return res; 679 | 680 | res = multiply(static_cast(-1), vec); 681 | if (res.status != SUCCESS) 682 | return res; 683 | 684 | out = Quaternion(s, vec); 685 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "conjugate", SUCCESS); 686 | } 687 | /** 688 | \brief from Vince 2011 - Quaternions for Computer 689 | Graphics p. 69 690 | */ 691 | __host__ __device__ OpResult normalized(Quaternion &out) const { 692 | T nval = static_cast(0); 693 | auto res = norm(nval); 694 | if (res.status != SUCCESS) 695 | return res; 696 | T inv_mag = static_cast(1.0) / nval; 697 | 698 | res = scalar(nval); 699 | 700 | if (res.status != SUCCESS) 701 | return res; 702 | 703 | T scalar_part = nval * inv_mag; 704 | vecn::VecN vs; 705 | res = vector(vs); 706 | if (res.status != SUCCESS) 707 | return res; 708 | res = multiply(inv_mag, vs); 709 | 710 | if (res.status != SUCCESS) 711 | return res; 712 | out = Quaternion(scalar_part, vs); 713 | 714 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "normalized", SUCCESS); 715 | } 716 | /** 717 | \brief from Vince 2011 - Quaternions for Computer 718 | Graphics p. 69 719 | */ 720 | __host__ __device__ OpResult inversed(Quaternion &out) const { 721 | // 722 | T q_norm = static_cast(0); 723 | auto res = norm_squared(q_norm); 724 | if (res.status != SUCCESS) { 725 | return res; 726 | } 727 | 728 | Quaternion conj; 729 | res = conjugate(conj); 730 | if (res.status != SUCCESS) { 731 | return res; 732 | } 733 | T c_s; 734 | vecn::VecN c_v; 735 | conj(c_s); 736 | conj(c_v); 737 | vecn::VecN v; 738 | c_v.divide(q_norm, v); 739 | out = Quaternion(c_s / q_norm, v); 740 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "inversed", SUCCESS); 741 | } 742 | /** 743 | \brief from Vince 2011 - Quaternions for Computer 744 | Graphics p. 69 745 | */ 746 | __host__ __device__ OpResult add(const Quaternion &q, 747 | Quaternion &out) const { 748 | auto fn = [](T thisval, T tval) { return thisval + tval; }; 749 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "add", 750 | apply_el(q, fn, out)); 751 | } 752 | /** 753 | \brief from Vince 2011 - Quaternions for Computer 754 | Graphics p. 69 755 | */ 756 | __host__ __device__ OpResult subtract(const Quaternion &q, 757 | Quaternion &out) const { 758 | auto fn = [](T thisval, T tval) { return thisval - tval; }; 759 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "subtract", 760 | apply_el(q, fn, out)); 761 | } 762 | /** 763 | \brief from Vince 2011 - Quaternions for Computer 764 | Graphics p. 69 765 | */ 766 | __host__ __device__ OpResult product(const Quaternion &q, 767 | Quaternion &out) const { 768 | return hamilton_product(q, out); 769 | } 770 | /** 771 | \brief from Vince 2011 - Quaternions for Computer 772 | Graphics p. 69 773 | */ 774 | __host__ __device__ OpResult product(T r, Quaternion &out) const { 775 | auto fn = [](T thisval, T tval) { return thisval * tval; }; 776 | Quaternion q(r, vecn::VecN(r)); 777 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "product", 778 | apply_el(q, fn, out)); 779 | } 780 | __host__ __device__ OpResult power(std::size_t i, Quaternion &out) const { 781 | Quaternion accumulant = *this; 782 | Quaternion result2 = *this; 783 | for (std::size_t j = 1; j < i; j++) { 784 | accumulant.hamilton_product(result2, accumulant); 785 | } 786 | out = accumulant; 787 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "power", SUCCESS); 788 | } 789 | __host__ __device__ OpResult squared(Quaternion &out) const { 790 | Quaternion r1 = *this; 791 | Quaternion r2 = *this; 792 | auto res = product(r1, out); 793 | if (res.status != SUCCESS) 794 | return res; 795 | 796 | res = product(r2, out); 797 | if (res.status != SUCCESS) 798 | return res; 799 | 800 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "squared", SUCCESS); 801 | } 802 | /** 803 | \brief from Vince 2011 - Quaternions for Computer 804 | Graphics p. 69 805 | */ 806 | __host__ __device__ OpResult norm(T &out) const { 807 | auto res = norm_squared(out); 808 | 809 | if (res.status != SUCCESS) 810 | return res; 811 | // 812 | out = sqrt(out); 813 | 814 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "norm", SUCCESS); 815 | } 816 | 817 | /** 818 | \brief from Vince 2011 - Quaternions for Computer 819 | Graphics p. 25 820 | */ 821 | __host__ __device__ OpResult norm_squared(T &out) const { 822 | T s = static_cast(0); 823 | auto res = scalar(s); 824 | if (res.status != SUCCESS) 825 | return res; 826 | vecn::VecN vec; 827 | res = vector(vec); 828 | if (res.status != SUCCESS) 829 | return res; 830 | 831 | T a = s * s; 832 | T b; 833 | T v[3]; 834 | vec(v); 835 | res = vec.dot(v, b); 836 | if (res.status != SUCCESS) 837 | return res; 838 | out = a + b; 839 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "norm_squared", SUCCESS); 840 | } 841 | __host__ __device__ OpResult magnitude(T &out) const { return norm(out); } 842 | 843 | private: 844 | template 845 | __host__ __device__ opstatus_t apply_el(const Quaternion &q, const Func &fn, 846 | Quaternion &out) const { 847 | T s; 848 | scalar(s); 849 | vecn::VecN v; 850 | vector(v); 851 | T vs[3]; 852 | v(vs); 853 | 854 | // 855 | T q_s; 856 | q.scalar(q_s); 857 | vecn::VecN q_v; 858 | q.vector(q_v); 859 | T qs[3]; 860 | q_v(qs); 861 | // 862 | T rs[3]; 863 | for (std::size_t i = 0; i < 3; ++i) { 864 | rs[i] = fn(vs[i], qs[i]); 865 | } 866 | T r = fn(s, q_s); 867 | out = Quaternion(r, vecn::VecN(rs)); 868 | return SUCCESS; 869 | } 870 | 871 | // 872 | T coeffs[4]; 873 | }; 874 | 875 | } // namespace quaternion 876 | 877 | namespace matn { 878 | 879 | template struct MatNCell { 880 | T content; 881 | std::size_t row = 0; 882 | std::size_t column = 0; 883 | int index = -1; 884 | }; 885 | 886 | template 887 | __host__ __device__ MatNCell 888 | make_matn_cell(const T &c, std::size_t row_param, std::size_t col_param) { 889 | MatNCell cell{.content = c, .row = row_param, .column = col_param}; 890 | return cell; 891 | } 892 | 893 | template 894 | __host__ __device__ MatNCell make_matn_cell(const T &c, std::size_t i) { 895 | MatNCell cell{ 896 | .content = c, .row = 0, .column = 0, .index = static_cast(i)}; 897 | return cell; 898 | } 899 | 900 | template 901 | class MatN { 902 | /** holds the vector data*/ 903 | T data[ColNb * RowNb]; 904 | static const std::size_t Size = ColNb * RowNb; 905 | 906 | public: 907 | __host__ __device__ MatN() { 908 | for (std::size_t i = 0; i < Size; ++i) { 909 | data[i] = 0; 910 | } 911 | } 912 | template __host__ __device__ MatN(const T (&vd)[N]) { 913 | if (N == Size) { 914 | memcpy(data, vd, Size * sizeof(T)); 915 | } else if (N < Size) { 916 | for (size_t i = 0; i < N; ++i) { 917 | data[i] = vd[i]; 918 | } 919 | for (size_t i = N; i < Size; ++i) { 920 | data[i] = 0; 921 | } 922 | } else { 923 | for (size_t i = 0; i < Size; ++i) { 924 | data[i] = vd[i]; 925 | } 926 | } 927 | } 928 | 929 | __host__ __device__ MatN(T fill_value) { 930 | for (std::size_t i = 0; i < Size; ++i) { 931 | data[i] = fill_value; 932 | } 933 | } 934 | 935 | static __host__ __device__ OpResult 936 | from_row_cols(T v, MatN &out) { 937 | MatN mat(v); 938 | out = mat; 939 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "from_row_cols", SUCCESS); 940 | } 941 | 942 | /**\brief Create matrix based on argument matrix*/ 943 | static __host__ __device__ OpResult zeros(MatN &out) { 944 | MatN::from_row_cols(0, out); 945 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "zeros", SUCCESS); 946 | } 947 | // tested 948 | template 949 | __host__ __device__ OpResult fill(T v, 950 | MatN &out) const { 951 | std::size_t s = 0; 952 | out(s); 953 | for (std::size_t i = 0; i < s; i++) { 954 | out(i, v); 955 | } 956 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "fill", SUCCESS); 957 | } 958 | // tested 959 | __host__ __device__ OpResult transpose(MatN &out) const { 960 | 961 | for (std::size_t i = 0; i < RowNb; i++) { 962 | for (std::size_t j = 0; j < ColNb; j++) { 963 | T tout = static_cast(0); 964 | (*this)(i, j, tout); // getter 965 | out(make_matn_cell(tout, i, j)); // setter 966 | } 967 | } 968 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "transpose", SUCCESS); 969 | } 970 | 971 | __host__ __device__ OpResult operator()(std::size_t &rows, 972 | std::size_t &cols) const { 973 | rows = RowNb; 974 | cols = ColNb; 975 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "get_rows_cols", SUCCESS); 976 | } 977 | 978 | // tested 979 | __host__ __device__ OpResult operator()(std::size_t &out) const { 980 | out = Size; 981 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "get_size", SUCCESS); 982 | } 983 | __host__ __device__ OpResult operator()(std::size_t row, std::size_t col, 984 | T &out) const { 985 | std::size_t index = row * ColNb + col; 986 | OpResult r = (*this)(index, out); 987 | if (r.status != SUCCESS) 988 | return r; 989 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "get", SUCCESS); 990 | } 991 | __host__ __device__ OpResult operator()(std::size_t index, T &out) const { 992 | if (index >= Size) 993 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "get", INDEX_ERROR); 994 | out = data[index]; 995 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "get", SUCCESS); 996 | } 997 | __host__ __device__ OpResult operator()(T (&out)[RowNb * ColNb]) const { 998 | memcpy(out, data, Size * sizeof(T)); 999 | // for (std::size_t i = 0; i < size; ++i){ 1000 | // out[i] = data[i]; 1001 | // } 1002 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "get", SUCCESS); 1003 | } 1004 | __host__ __device__ OpResult operator()(const MatNCell &cell) { 1005 | std::size_t row = cell.row; 1006 | std::size_t col = cell.column; 1007 | T el = cell.content; 1008 | int cell_index = cell.index; 1009 | std::size_t index; 1010 | if (cell_index < 0) { 1011 | index = row * ColNb + col; 1012 | } else { 1013 | index = static_cast(cell_index); 1014 | } 1015 | // 1016 | if (index >= Size) { 1017 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "set", INDEX_ERROR); 1018 | } 1019 | data[index] = el; 1020 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "set", SUCCESS); 1021 | } 1022 | __host__ __device__ OpResult get_column(std::size_t index, 1023 | T (&out)[RowNb]) const { 1024 | if (index >= ColNb) { 1025 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "get_column", 1026 | INDEX_ERROR); 1027 | } 1028 | for (std::size_t i = 0; i < RowNb; i++) { 1029 | out[i] = data[i * ColNb + index]; 1030 | } 1031 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "get_column", SUCCESS); 1032 | } 1033 | __host__ __device__ OpResult set_column(std::size_t index, 1034 | const T (&idata)[RowNb]) { 1035 | if (index >= ColNb) { 1036 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "set_column", 1037 | INDEX_ERROR); 1038 | } 1039 | for (std::size_t i = 0; i < RowNb; i++) { 1040 | data[i * ColNb + index] = idata[i]; 1041 | } 1042 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "set_column", SUCCESS); 1043 | } 1044 | __host__ __device__ OpResult get_row(std::size_t index, 1045 | T (&out)[ColNb]) const { 1046 | if (index >= RowNb) { 1047 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "get_row", INDEX_ERROR); 1048 | } 1049 | for (std::size_t i = 0; i < ColNb; i++) { 1050 | out[i] = data[index * ColNb + i]; 1051 | } 1052 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "get_row", SUCCESS); 1053 | } 1054 | __host__ __device__ OpResult set_row(std::size_t index, 1055 | const T (&idata)[ColNb]) { 1056 | if (index >= RowNb) { 1057 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "set_row", INDEX_ERROR); 1058 | } 1059 | for (std::size_t i = 0; i < ColNb; i++) { 1060 | data[index * ColNb + i] = idata[i]; 1061 | } 1062 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "set_row", SUCCESS); 1063 | } 1064 | 1065 | /**Obtain submatrix TODO*/ 1066 | __host__ __device__ OpResult submat(std::size_t row_start, 1067 | std::size_t col_start, 1068 | MatN &out) const { 1069 | std::size_t row_size = RowNb - row_start; 1070 | std::size_t col_size = ColNb - col_start; 1071 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "submat", 1072 | NOT_IMPLEMENTED); 1073 | } 1074 | __host__ __device__ OpResult add(const MatN &v, 1075 | MatN &out) const { 1076 | auto fn = [](T matv, T val) { return matv + val; }; 1077 | return apply(v, fn, out); 1078 | } 1079 | __host__ __device__ OpResult add(T v, MatN &out) const { 1080 | auto fn = [](T matv, T val) { return matv + val; }; 1081 | return apply(v, fn, out); 1082 | } 1083 | __host__ __device__ OpResult subtract(const MatN &v, 1084 | MatN &out) const { 1085 | auto fn = [](T matv, T val) { return matv - val; }; 1086 | return apply(v, fn, out); 1087 | } 1088 | __host__ __device__ OpResult subtract(T v, MatN &out) const { 1089 | auto fn = [](T matv, T val) { return matv - val; }; 1090 | return apply(v, fn, out); 1091 | } 1092 | __host__ __device__ OpResult hadamard_product( 1093 | const MatN &v, MatN &out) const { 1094 | auto fn = [](T matv, T val) { return matv * val; }; 1095 | return apply(v, fn, out); 1096 | } 1097 | __host__ __device__ OpResult 1098 | hadamard_product(T v, MatN &out) const { 1099 | auto fn = [](T matv, T val) { return matv * val; }; 1100 | return apply(v, fn, out); 1101 | } 1102 | __host__ __device__ OpResult divide(const MatN &v, 1103 | MatN &out) const { 1104 | std::size_t osize = 0; 1105 | v(osize); 1106 | for (std::size_t i = 0; i < osize; i++) { 1107 | T tout = static_cast(0); 1108 | v(i, tout); // getter 1109 | if (tout == static_cast(0)) { 1110 | // zero division risk 1111 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "divide", ARG_ERROR); 1112 | } 1113 | } 1114 | auto fn = [](T matv, T val) { return matv / val; }; 1115 | return apply(v, fn, out); 1116 | } 1117 | __host__ __device__ OpResult divide(T v, MatN &out) const { 1118 | if (v == static_cast(0)) { 1119 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "divide", ARG_ERROR); 1120 | } 1121 | auto fn = [](T matv, T val) { return matv / val; }; 1122 | return apply(v, fn, out); 1123 | } 1124 | /**Declares inner vector product*/ 1125 | template 1126 | __host__ __device__ OpResult vdot(const T (&x)[N], const T (&y)[N], T &out) { 1127 | if (N == 0) { 1128 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "vdot", SIZE_ERROR); 1129 | } 1130 | 1131 | out = static_cast(0); 1132 | for (std::size_t i = 0; i < N; i++) { 1133 | out += x[i] * y[i]; 1134 | } 1135 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "vdot", SUCCESS); 1136 | } 1137 | 1138 | /**Declares inner vector product with scalars*/ 1139 | template 1140 | __host__ __device__ OpResult vdot_s(const T (&x)[N], const T &a, 1141 | T (&out)[N]) { 1142 | 1143 | if (N == 0) { 1144 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "vdot_s", SIZE_ERROR); 1145 | } 1146 | for (std::size_t i = 0; i < N; i++) { 1147 | out[i] = x[i] * a; 1148 | } 1149 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "vdot_s", SUCCESS); 1150 | } 1151 | /**Implements saxpy algorithm from Golub, Van Loan 2013, 1152 | * p. 4 alg.1.1.2*/ 1153 | template 1154 | __host__ __device__ OpResult saxpy(const T &a, const T (&x)[N], 1155 | T (&y)[N]) const { 1156 | if (N == 0) { 1157 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "saxpy", SIZE_ERROR); 1158 | } 1159 | for (std::size_t i = 0; i < N; i++) { 1160 | y[i] += x[i] * a; // 1161 | } 1162 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "saxpy", SUCCESS); 1163 | } 1164 | /** 1165 | Implements gaxpy algorithm from Golub, Van Loan 2013, p. 1166 | 4 alg.1.1.3 1167 | 1168 | as specified in p. 6-7 1169 | */ 1170 | __host__ __device__ OpResult gaxpy(const T (&x)[ColNb], T (&y)[RowNb]) const { 1171 | for (std::size_t j = 0; j < ColNb; j++) { 1172 | T c_j[RowNb]; 1173 | get_column(j, c_j); 1174 | saxpy(x[j], c_j, y); 1175 | } 1176 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "gaxpy", SUCCESS); 1177 | } 1178 | /** 1179 | Implements outer product update from Golub, Van Loan 1180 | 2013, p. 7 as a series of saxpy operations 1181 | */ 1182 | template 1183 | __host__ __device__ OpResult outer_product(const T (&x)[Rn], const T (&y)[Cn], 1184 | MatN &out) const { 1185 | for (std::size_t i = 0; i < Rn; i++) { 1186 | T A_i[Cn]; 1187 | out.get_row(i, A_i); 1188 | saxpy(x[i], y, A_i); 1189 | out.set_row(i, A_i); 1190 | } 1191 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "outer_product", SUCCESS); 1192 | } 1193 | template 1194 | __host__ __device__ OpResult multiply(T v, 1195 | MatN &out) const { 1196 | // m x n \cdot vmat (n x l) = out (m x l) 1197 | // RowNb x ColNb \codt (n x l) = out (OutRowNb x 1198 | // OutColNb) 1199 | MatN vmat(v); 1200 | 1201 | auto r = multiply(vmat, out); 1202 | if (r.status != SUCCESS) 1203 | return r; 1204 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "multiply", SUCCESS); 1205 | } 1206 | /*matrix to matrix multiplication*/ 1207 | template 1208 | __host__ __device__ OpResult dot(const MatN &v, 1209 | MatN &out) const { 1210 | return multiply(v, out); 1211 | } 1212 | /*matrix to scalar multiplication*/ 1213 | template 1214 | __host__ __device__ OpResult dot(T v, MatN &out) const { 1215 | return multiply(v, out); 1216 | } 1217 | /*matrix to vector multiplication*/ 1218 | __host__ __device__ OpResult dot(const T (&v)[ColNb], 1219 | MatN &out) const { 1220 | MatN vmat(v); 1221 | auto r = multiply<1>(vmat, out); 1222 | if (r.status != SUCCESS) 1223 | return r; 1224 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "dot", SUCCESS); 1225 | } 1226 | 1227 | /** 1228 | m x n \cdot vmat (n x l) = out (m x l) 1229 | RowNb x ColNb \codt (OutRowNb x OutColNb) = out (RowNb x 1230 | OutColNb) 1231 | 1232 | We are using the kij (row outer product) variant from 1233 | Golub, van Loan 2013, p. 11 alg. 1.1.8 due to 1234 | implementing this algorithm in C++. For fortran etc one 1235 | should use jki since it access matrices by column. For 1236 | a comparison of algorithms see table 1.1.1 in p. 9 1237 | 1238 | tested 1239 | */ 1240 | template 1241 | __host__ __device__ OpResult multiply(const MatN &B, 1242 | MatN &out) const { 1243 | 1244 | // fill out matrix with zero 1245 | out = MatN(static_cast(0)); 1246 | for (std::size_t k = 0; k < ColNb; k++) { 1247 | // x vector 1248 | T A_k[RowNb]; 1249 | get_column(k, A_k); 1250 | 1251 | // y vector 1252 | T B_k[OutColNb]; 1253 | B.get_row(k, B_k); 1254 | 1255 | // compute their outer product 1256 | outer_product(A_k, B_k, out); 1257 | } 1258 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "multiply", SUCCESS); 1259 | } 1260 | /** 1261 | add row 1262 | */ 1263 | __host__ __device__ OpResult add_row(const T (&r_data)[ColNb], 1264 | MatN &out) const { 1265 | return add_rows(r_data, out); 1266 | } 1267 | /** 1268 | add rows if the incoming data has a size of multiple of 1269 | number of columns 1270 | of this array 1271 | */ 1272 | template 1273 | __host__ __device__ OpResult 1274 | add_rows(const T (&r_data)[InRow], 1275 | MatN &out) const { 1276 | if ((InRow % ColNb) != 0) { 1277 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "add_rows", SIZE_ERROR); 1278 | } 1279 | // fill output matrix with zeros 1280 | out = MatN(0); 1281 | 1282 | // fill with the output matrix with current matrix 1283 | // elements 1284 | std::size_t i = 0; 1285 | std::size_t j = 0; 1286 | for (i = 0; i < RowNb; i++) { 1287 | for (j = 0; j < ColNb; j++) { 1288 | T value = static_cast(0); 1289 | (*this)(i, j, value); // getter 1290 | out(make_matn_cell(value, i, j)); // setter 1291 | } 1292 | } 1293 | 1294 | // fill from r_data the remaining values 1295 | std::size_t nb_of_rows_to_add = static_cast(InRow / ColNb); 1296 | for (i = 0; i <= nb_of_rows_to_add; i++) { 1297 | std::size_t row = RowNb + i; 1298 | for (std::size_t j = 0; j < ColNb; j++) { 1299 | T row_val = r_data[i * ColNb + j]; 1300 | out(make_matn_cell(row_val, row, j)); // setter 1301 | } 1302 | } 1303 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "add_rows", SUCCESS); 1304 | } 1305 | /** 1306 | add column 1307 | */ 1308 | __host__ __device__ OpResult 1309 | add_column(const T (&r_data)[RowNb], MatN &out) const { 1310 | return add_columns(r_data, out); 1311 | } 1312 | template 1313 | __host__ __device__ OpResult 1314 | add_columns(const T (&c_data)[InCol], 1315 | MatN &out) const { 1316 | if ((InCol % RowNb) != 0) { 1317 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "add_columns", 1318 | SIZE_ERROR); 1319 | } 1320 | // fill output matrix with zeros 1321 | MatN::zeros(out); 1322 | 1323 | // fill with the output matrix with current matrix 1324 | // elements 1325 | std::size_t i = 0; 1326 | std::size_t j = 0; 1327 | for (i = 0; i < RowNb; i++) { 1328 | for (j = 0; j < ColNb; j++) { 1329 | T value = static_cast(0); 1330 | (*this)(i, j, value); // getter 1331 | out(make_matn_cell(value, i, j)); // setter 1332 | } 1333 | } 1334 | // fill from c_data the remaining values 1335 | std::size_t nb_of_cols_to_add = static_cast(InCol / RowNb); 1336 | 1337 | // even if there are zero columns to add the output 1338 | // should be one 1339 | for (i = 0; i < nb_of_cols_to_add; i++) { 1340 | std::size_t col = ColNb + i; 1341 | for (j = 0; j < RowNb; j++) { 1342 | T col_val = c_data[i * RowNb + j]; 1343 | out(make_matn_cell(col_val, j, col)); 1344 | } 1345 | } 1346 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "add_columns", SUCCESS); 1347 | } 1348 | 1349 | private: 1350 | template 1351 | __host__ __device__ OpResult apply(const MatN &vmat, 1352 | const Func &fn, 1353 | MatN &out) const { 1354 | for (std::size_t i = 0; i < Size; i++) { 1355 | T tout = static_cast(0); 1356 | vmat(i, tout); // getter 1357 | T val = fn(data[i], tout); 1358 | auto r = out(make_matn_cell(val, i)); 1359 | if (r.status != SUCCESS) 1360 | return r; 1361 | } 1362 | 1363 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "apply", SUCCESS); 1364 | } 1365 | template 1366 | __host__ __device__ OpResult apply(const Func &fn, 1367 | MatN &out) const { 1368 | for (std::size_t i = 0; i < Size; i++) { 1369 | T val = fn(data[i]); 1370 | auto r = out(make_matn_cell(val, i)); 1371 | if (r.status != SUCCESS) 1372 | return r; 1373 | } 1374 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "apply", SUCCESS); 1375 | } 1376 | template 1377 | __host__ __device__ OpResult apply(const T &v, const Func &fn, 1378 | MatN &out) const { 1379 | for (std::size_t i = 0; i < Size; i++) { 1380 | T val = fn(data[i], v); 1381 | auto r = out(make_matn_cell(val, i)); 1382 | if (r.status != SUCCESS) 1383 | return r; 1384 | } 1385 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "apply", SUCCESS); 1386 | } 1387 | }; 1388 | 1389 | template 1390 | __host__ __device__ OpResult identity(MatN &out) { 1391 | MatN mat; 1392 | for (std::size_t i = 0; i < N; i++) { 1393 | mat(make_matn_cell(static_cast(1), i, i)); 1394 | } 1395 | out = mat; 1396 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "identity", SUCCESS); 1397 | } 1398 | 1399 | } // namespace matn 1400 | 1401 | namespace lu { 1402 | 1403 | template struct LUdecomp { 1404 | matn::MatN LU; 1405 | /** 1406 | decompose the given matrix into two matrices, L, U, so 1407 | that A=LU 1408 | implemented from Golub and Van Loan 2013, Matrix 1409 | computations, p. 116, algorithm 3.2.1 1410 | */ 1411 | __host__ __device__ LUdecomp(const matn::MatN &in_m) { 1412 | T mdata[N * N]; 1413 | in_m(mdata); 1414 | 1415 | T ludata[N * N]; 1416 | memcpy(ludata, mdata, (N * N) * sizeof(T)); 1417 | 1418 | // 1419 | LU = matn::MatN(ludata); 1420 | for (std::size_t k = 0; k < (N - 1); ++k) { 1421 | // 1422 | 1423 | for (std::size_t r = k + 1; r < N; ++r) { 1424 | // 1425 | T r_k; 1426 | LU(r, k, r_k); 1427 | T k_k; 1428 | LU(k, k, k_k); 1429 | 1430 | // set L 1431 | LU(matn::make_matn_cell(r_k / k_k, r, k)); 1432 | } 1433 | for (std::size_t i = k + 1; i < N; ++i) { 1434 | for (std::size_t j = k + 1; j < N; ++j) { 1435 | // 1436 | T i_k; 1437 | LU(i, k, i_k); 1438 | T k_j; 1439 | LU(k, j, k_j); 1440 | 1441 | // 1442 | T i_j; 1443 | LU(i, j, i_j); 1444 | LU(matn::make_matn_cell(i_j - i_k * k_j, i, j)); 1445 | } 1446 | } 1447 | } 1448 | } 1449 | 1450 | // tested 1451 | __host__ __device__ OpResult upper(matn::MatN &U) const { 1452 | for (std::size_t i = 0; i < N; ++i) { 1453 | for (std::size_t j = i; j < N; ++j) { 1454 | T udata; 1455 | LU(i, j, udata); 1456 | U({.content = udata, .row = i, .column = j}); 1457 | } 1458 | } 1459 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "upper", SUCCESS); 1460 | } 1461 | // tested 1462 | __host__ __device__ OpResult lower(matn::MatN &L) const { 1463 | matn::identity(L); 1464 | for (std::size_t i = 0; i < (N - 1); ++i) { 1465 | for (std::size_t j = i + 1; j < N; ++j) { 1466 | T udata; 1467 | LU(j, i, udata); 1468 | L({.content = udata, .row = j, .column = i}); 1469 | } 1470 | } 1471 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "lower", SUCCESS); 1472 | } 1473 | 1474 | // from Golub and Van Loan 2013, Matrix computations, p. 1475 | // 108 1476 | // tested 1477 | __host__ __device__ OpResult solve_forward(const vecn::VecN &b, 1478 | vecn::VecN &x) const { 1479 | matn::MatN L; 1480 | OpResult res = lower(L); 1481 | T b_0; 1482 | b(0, b_0); 1483 | T L_0; 1484 | L(0, 0, L_0); 1485 | if (L_0 == 0) { 1486 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "solve_forward", 1487 | ARG_ERROR); 1488 | } 1489 | x({.content = b_0 / L_0, .index = 0}); 1490 | for (std::size_t i = 1; i < N; ++i) { 1491 | T x_i; 1492 | b(i, x_i); 1493 | x({.content = x_i, .index = i}); 1494 | 1495 | // compute vdot 1496 | T out = 0; 1497 | for (std::size_t j = 0; j <= (i - 1); ++j) { 1498 | T ij; 1499 | L(i, j, ij); 1500 | T x_j; 1501 | x(j, x_j); 1502 | out += ij * x_j; 1503 | } 1504 | 1505 | x(i, x_i); 1506 | T diff = x_i - out; 1507 | 1508 | T i_i; 1509 | L(i, i, i_i); 1510 | 1511 | if (i_i == 0) { 1512 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "solve_forward", 1513 | ARG_ERROR); 1514 | } 1515 | x({.content = diff / i_i, .index = i}); 1516 | } 1517 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "solve_forward", SUCCESS); 1518 | } 1519 | 1520 | // from Golub and Van Loan 2013, Matrix computations, p. 1521 | // 108 1522 | // tested 1523 | __host__ __device__ OpResult solve_backward(const vecn::VecN &b, 1524 | vecn::VecN &x) const { 1525 | matn::MatN U; 1526 | OpResult res = upper(U); 1527 | T u_n; 1528 | U(N - 1, N - 1, u_n); 1529 | if (u_n == 0) { 1530 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "solve_backward", 1531 | ARG_ERROR); 1532 | } 1533 | T b_n; 1534 | b(N - 1, b_n); 1535 | x({.content = b_n / u_n, .index = N - 1}); 1536 | 1537 | std::size_t i = N - 2; 1538 | 1539 | while (i >= 0) { 1540 | T b_i; 1541 | b(i, b_i); 1542 | x({.content = b_i, .index = i}); 1543 | 1544 | // compute vdot 1545 | T out = 0; 1546 | for (std::size_t j = i + 1; j < N; ++j) { 1547 | T ij; 1548 | U(i, j, ij); 1549 | T x_j; 1550 | x(j, x_j); 1551 | out += ij * x_j; 1552 | } 1553 | // 1554 | T x_i; 1555 | x(i, x_i); 1556 | T i_i; 1557 | U(i, i, i_i); 1558 | 1559 | if (i_i == 0) { 1560 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "solve_backward", 1561 | ARG_ERROR); 1562 | } 1563 | 1564 | x({.content = (x_i - out) / i_i, .index = i}); 1565 | 1566 | // size_t acts weird if we don't decrement it this way 1567 | if (i == 0) { 1568 | break; 1569 | } else { 1570 | --i; 1571 | } 1572 | } 1573 | 1574 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "solve_backward", 1575 | SUCCESS); 1576 | } 1577 | __host__ __device__ OpResult solve(const vecn::VecN &b, 1578 | vecn::VecN &x) { 1579 | vecn::VecN in_x; 1580 | OpResult res = solve_forward(b, in_x); 1581 | if (res.status != SUCCESS) { 1582 | return res; 1583 | } 1584 | res = solve_backward(in_x, x); 1585 | if (res.status != SUCCESS) { 1586 | return res; 1587 | } 1588 | 1589 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "solve", SUCCESS); 1590 | } 1591 | 1592 | // from Golub and Van Loan 2013, Matrix computations, p. 1593 | // 108 1594 | template 1595 | __host__ __device__ OpResult solve_mat(const matn::MatN &B, 1596 | matn::MatN &X) { 1597 | for (std::size_t j = 0; j < Q; ++j) { 1598 | // 1599 | T b_j[N]; 1600 | B.get_column(j, b_j); 1601 | vecn::VecN b(b_j); 1602 | 1603 | T x_j[N]; 1604 | X.get_column(j, x_j); 1605 | // 1606 | vecn::VecN x(x_j); 1607 | OpResult res = solve(b, x); 1608 | if (res.status != SUCCESS) { 1609 | return res; 1610 | } 1611 | // pass data to x_j 1612 | x(x_j); 1613 | X.set_column(j, x_j); 1614 | } 1615 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "solve_mat", SUCCESS); 1616 | } 1617 | }; 1618 | 1619 | } // namespace lu 1620 | template 1621 | __host__ __device__ OpResult to_skew_mat(const vecn::VecN &vec, 1622 | matn::MatN &out) { 1623 | // 1624 | T a3; 1625 | vec(2, a3); 1626 | T a2; 1627 | vec(1, a2); 1628 | T a1; 1629 | vec(0, a1); 1630 | 1631 | matn::MatN m; 1632 | 1633 | T c1[] = {0, a3, -a2}; 1634 | T c2[] = {-a3, 0, a1}; 1635 | T c3[] = {a2, -a1, 0}; 1636 | m.set_column(0, c1); 1637 | m.set_column(1, c2); 1638 | m.set_column(2, c3); 1639 | out = m; 1640 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "to_skew_mat", SUCCESS); 1641 | } 1642 | 1643 | /**convert quaternion to rotation matrix 1644 | from Vince, 2011, Quaternion ..., p. 123 1645 | */ 1646 | template 1647 | __host__ __device__ OpResult toRotMat3x3(const quaternion::Quaternion &q_in, 1648 | matn::MatN &out) { 1649 | quaternion::Quaternion q; 1650 | q_in.normalized(q); 1651 | // 1652 | T s; 1653 | q.scalar(s); 1654 | 1655 | // 1656 | vecn::VecN xyz; 1657 | q.vector(xyz); 1658 | T x; 1659 | xyz(0, x); 1660 | T y; 1661 | xyz(1, y); 1662 | T z; 1663 | xyz(2, z); 1664 | 1665 | // to rotation matrix 1666 | T c1_1 = 1 - (2 * ((y * y) + (z * z))); 1667 | T c1_2 = 2 * (x * y + s * z); 1668 | T c1_3 = 2 * (x * z - s * y); 1669 | // 1670 | T c2_1 = 2 * (x * y - s * z); 1671 | T c2_2 = 1 - 2 * ((x * x) + (z * z)); 1672 | T c2_3 = 2 * (y * z + s * x); 1673 | // 1674 | T c3_1 = 2 * (x * z + s * y); 1675 | T c3_2 = 2 * (y * z - s * x); 1676 | T c3_3 = 1 - 2 * ((x * x) + (y * y)); 1677 | 1678 | T c1[] = {c1_1, c1_2, c1_3}; 1679 | T c2[] = {c2_1, c2_2, c2_3}; 1680 | T c3[] = {c3_1, c3_2, c3_3}; 1681 | matn::MatN m; 1682 | m.set_column(0, c1); 1683 | m.set_column(1, c2); 1684 | m.set_column(2, c3); 1685 | out = m; 1686 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "toRotMat3x3", SUCCESS); 1687 | } 1688 | 1689 | template 1690 | __host__ __device__ OpResult invert_mat(const matn::MatN &m, 1691 | matn::MatN &out) { 1692 | // 1693 | lu::LUdecomp lu_d(m); 1694 | matn::MatN B; 1695 | matn::identity(B); 1696 | matn::MatN inv_m; 1697 | lu_d.solve_mat(B, inv_m); 1698 | out = inv_m; 1699 | return OpResult(__LINE__, __FILE__, __FUNCTION__, "invert_mat", SUCCESS); 1700 | } 1701 | 1702 | } // namespace math3d 1703 | 1704 | #endif 1705 | --------------------------------------------------------------------------------