From 24d7efc977914f3029c2c94ad064a7382fcae593 Mon Sep 17 00:00:00 2001 From: Alex Selimov Date: Tue, 15 Apr 2025 17:48:33 -0400 Subject: [PATCH] Add simple Vec3 header file --- .gitignore | 5 + CMakeLists.txt | 30 +--- compile_commands.json | 32 +++++ include/CMakeLists.txt | 10 ++ include/vec3.h | 33 +++++ kernels/CMakeLists.txt | 18 --- kernels/hello_world.cu | 46 ------ kernels/hello_world.h | 10 -- main.cpp | 10 -- src/CMakeLists.txt | 16 --- src/test.cpp | 4 - src/test.h | 2 - tests/unit_tests/CMakeLists.txt | 6 +- tests/unit_tests/test_example.cpp | 5 - tests/unit_tests/vec_test.cpp | 231 ++++++++++++++++++++++++++++++ 15 files changed, 321 insertions(+), 137 deletions(-) create mode 100644 compile_commands.json create mode 100644 include/CMakeLists.txt create mode 100644 include/vec3.h delete mode 100644 kernels/CMakeLists.txt delete mode 100644 kernels/hello_world.cu delete mode 100644 kernels/hello_world.h delete mode 100644 main.cpp delete mode 100644 src/CMakeLists.txt delete mode 100644 src/test.cpp delete mode 100644 src/test.h delete mode 100644 tests/unit_tests/test_example.cpp create mode 100644 tests/unit_tests/vec_test.cpp diff --git a/.gitignore b/.gitignore index fd57df9..de19d1d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ # Builds build/ +Debug/ +Testing/ # Google Tests tests/lib/ @@ -7,3 +9,6 @@ tests/lib/ # Jet Brains .idea/ cmake-build-debug/ + +# Cache dir +.cache diff --git a/CMakeLists.txt b/CMakeLists.txt index f760070..a26b71d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,35 +1,17 @@ cmake_minimum_required(VERSION 3.9) -project(MyProject LANGUAGES CUDA CXX) +project(Vec3) + +enable_testing() +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) add_compile_options(-Wall -Wextra -Wpedantic) set(CMAKE_CXX_STANDARD 17) -set(CMAKE_CUDA_ARCHITECTURES 61) -set(CUDA_SEPARABLE_COMPILATION ON) - - -include_directories(src) -include_directories(kernels) -include_directories(/usr/local/cuda-12.8/include) - - -add_subdirectory(src) -add_subdirectory(kernels) +include_directories(include) +add_subdirectory(include) add_subdirectory(tests) -add_executable(${CMAKE_PROJECT_NAME}_run main.cpp) - - - -target_link_libraries( - ${CMAKE_PROJECT_NAME}_run - PRIVATE - ${CMAKE_PROJECT_NAME}_lib - ${CMAKE_PROJECT_NAME}_cuda_lib - ${CUDA_LIBRARIES} -) - # Doxygen Build option(BUILD_DOC "Build Documentation" ON) diff --git a/compile_commands.json b/compile_commands.json new file mode 100644 index 0000000..53ff38a --- /dev/null +++ b/compile_commands.json @@ -0,0 +1,32 @@ +[ +{ + "directory": "/home/aselimov/projects/Vec3/build/tests/lib/googletest/googlemock", + "command": "/usr/bin/c++ -I/home/aselimov/projects/Vec3/include -I/home/aselimov/projects/Vec3/tests/lib/googletest/googlemock/include -I/home/aselimov/projects/Vec3/tests/lib/googletest/googlemock -isystem /home/aselimov/projects/Vec3/tests/lib/googletest/googletest/include -isystem /home/aselimov/projects/Vec3/tests/lib/googletest/googletest -std=c++17 -Wall -Wshadow -Wundef -Wno-error=dangling-else -DGTEST_HAS_PTHREAD=1 -fexceptions -Wextra -Wno-unused-parameter -Wno-missing-field-initializers -Wall -Wextra -Wpedantic -o CMakeFiles/gmock.dir/src/gmock-all.cc.o -c /home/aselimov/projects/Vec3/tests/lib/googletest/googlemock/src/gmock-all.cc", + "file": "/home/aselimov/projects/Vec3/tests/lib/googletest/googlemock/src/gmock-all.cc", + "output": "tests/lib/googletest/googlemock/CMakeFiles/gmock.dir/src/gmock-all.cc.o" +}, +{ + "directory": "/home/aselimov/projects/Vec3/build/tests/lib/googletest/googlemock", + "command": "/usr/bin/c++ -isystem /home/aselimov/projects/Vec3/include -isystem /home/aselimov/projects/Vec3/tests/lib/googletest/googlemock/include -isystem /home/aselimov/projects/Vec3/tests/lib/googletest/googlemock -isystem /home/aselimov/projects/Vec3/tests/lib/googletest/googletest/include -isystem /home/aselimov/projects/Vec3/tests/lib/googletest/googletest -std=c++17 -Wall -Wshadow -Wundef -Wno-error=dangling-else -DGTEST_HAS_PTHREAD=1 -fexceptions -Wextra -Wno-unused-parameter -Wno-missing-field-initializers -Wall -Wextra -Wpedantic -o CMakeFiles/gmock_main.dir/src/gmock_main.cc.o -c /home/aselimov/projects/Vec3/tests/lib/googletest/googlemock/src/gmock_main.cc", + "file": "/home/aselimov/projects/Vec3/tests/lib/googletest/googlemock/src/gmock_main.cc", + "output": "tests/lib/googletest/googlemock/CMakeFiles/gmock_main.dir/src/gmock_main.cc.o" +}, +{ + "directory": "/home/aselimov/projects/Vec3/build/tests/lib/googletest/googletest", + "command": "/usr/bin/c++ -I/home/aselimov/projects/Vec3/include -I/home/aselimov/projects/Vec3/tests/lib/googletest/googletest/include -I/home/aselimov/projects/Vec3/tests/lib/googletest/googletest -std=c++17 -Wall -Wshadow -Wundef -Wno-error=dangling-else -DGTEST_HAS_PTHREAD=1 -fexceptions -Wextra -Wno-unused-parameter -Wno-missing-field-initializers -Wall -Wextra -Wpedantic -o CMakeFiles/gtest.dir/src/gtest-all.cc.o -c /home/aselimov/projects/Vec3/tests/lib/googletest/googletest/src/gtest-all.cc", + "file": "/home/aselimov/projects/Vec3/tests/lib/googletest/googletest/src/gtest-all.cc", + "output": "tests/lib/googletest/googletest/CMakeFiles/gtest.dir/src/gtest-all.cc.o" +}, +{ + "directory": "/home/aselimov/projects/Vec3/build/tests/lib/googletest/googletest", + "command": "/usr/bin/c++ -I/home/aselimov/projects/Vec3/include -isystem /home/aselimov/projects/Vec3/tests/lib/googletest/googletest/include -isystem /home/aselimov/projects/Vec3/tests/lib/googletest/googletest -std=c++17 -Wall -Wshadow -Wundef -Wno-error=dangling-else -DGTEST_HAS_PTHREAD=1 -fexceptions -Wextra -Wno-unused-parameter -Wno-missing-field-initializers -Wall -Wextra -Wpedantic -o CMakeFiles/gtest_main.dir/src/gtest_main.cc.o -c /home/aselimov/projects/Vec3/tests/lib/googletest/googletest/src/gtest_main.cc", + "file": "/home/aselimov/projects/Vec3/tests/lib/googletest/googletest/src/gtest_main.cc", + "output": "tests/lib/googletest/googletest/CMakeFiles/gtest_main.dir/src/gtest_main.cc.o" +}, +{ + "directory": "/home/aselimov/projects/Vec3/build/tests/unit_tests", + "command": "/usr/bin/c++ -I/home/aselimov/projects/Vec3/include -isystem /home/aselimov/projects/Vec3/tests/lib/googletest/googletest/include -isystem /home/aselimov/projects/Vec3/tests/lib/googletest/googletest -std=gnu++17 -Wall -Wextra -Wpedantic -o CMakeFiles/Unit_Tests_run.dir/vec_test.cpp.o -c /home/aselimov/projects/Vec3/tests/unit_tests/vec_test.cpp", + "file": "/home/aselimov/projects/Vec3/tests/unit_tests/vec_test.cpp", + "output": "tests/unit_tests/CMakeFiles/Unit_Tests_run.dir/vec_test.cpp.o" +} +] \ No newline at end of file diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt new file mode 100644 index 0000000..e711010 --- /dev/null +++ b/include/CMakeLists.txt @@ -0,0 +1,10 @@ +project(${CMAKE_PROJECT_NAME}_lib) + +set(HEADER_FILES + vec3.h +) + +# The library contains header and source files. +add_library(${CMAKE_PROJECT_NAME}_lib + INTERFACE +) diff --git a/include/vec3.h b/include/vec3.h new file mode 100644 index 0000000..0a2e948 --- /dev/null +++ b/include/vec3.h @@ -0,0 +1,33 @@ +#ifndef VEC3_H +#define VEC3_H + +template struct Vec3 { + T x; + T y; + T z; + + inline Vec3 operator+(Vec3 other) const { + return {x + other.x, y + other.y, z + other.z}; + }; + + inline Vec3 operator-(Vec3 other) const { + return {x - other.x, y - other.y, z - other.z}; + }; + + inline void scale(T scalar) { + x *= scalar; + y *= scalar; + z *= scalar; + }; + + inline T dot(Vec3 other) { + return x * other.x + y * other.y + z * other.z; + } + + inline Vec3 cross(Vec3 other) { + return {y * other.z - z * other.y, z * other.x - x * other.z, + x * other.y - y * other.x}; + } +}; + +#endif diff --git a/kernels/CMakeLists.txt b/kernels/CMakeLists.txt deleted file mode 100644 index f9a1174..0000000 --- a/kernels/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ -project(${CMAKE_PROJECT_NAME}_cuda_lib CUDA CXX) - -set(HEADER_FILES - hello_world.h -) -set(SOURCE_FILES - hello_world.cu -) - -# The library contains header and source files. -add_library(${CMAKE_PROJECT_NAME}_cuda_lib STATIC - ${SOURCE_FILES} - ${HEADER_FILES} -) - -if(CMAKE_COMPILER_IS_GNUCXX) - target_compile_options(${CMAKE_PROJECT_NAME}_cuda_lib PRIVATE -Wno-gnu-line-marker) -endif() diff --git a/kernels/hello_world.cu b/kernels/hello_world.cu deleted file mode 100644 index 7c65115..0000000 --- a/kernels/hello_world.cu +++ /dev/null @@ -1,46 +0,0 @@ -#include -#include - -__global__ void hello_cuda() { - printf("Hello CUDA from thread %d\n", threadIdx.x); -} - -extern "C" void launch_hello_cuda() { - // First check device properties - cudaDeviceProp prop; - cudaGetDeviceProperties(&prop, 1); - printf("Using device: %s with compute capability %d.%d\n", prop.name, - prop.major, prop.minor); - - hello_cuda<<<1, 10>>>(); - cudaDeviceSynchronize(); - fflush(stdout); -} - -extern "C" void check_cuda() { - int deviceCount = 0; - cudaError_t error = cudaGetDeviceCount(&deviceCount); - - if (error != cudaSuccess) { - printf("CUDA error: %s\n", cudaGetErrorString(error)); - } - - printf("Found %d CUDA devices\n", deviceCount); - - for (int i = 0; i < deviceCount; i++) { - cudaDeviceProp prop; - cudaGetDeviceProperties(&prop, i); - - printf("Device %d: %s\n", i, prop.name); - printf(" Compute capability: %d.%d\n", prop.major, prop.minor); - printf(" Total global memory: %.2f GB\n", - static_cast(prop.totalGlobalMem) / (1024 * 1024 * 1024)); - printf(" Multiprocessors: %d\n", prop.multiProcessorCount); - printf(" Max threads per block: %d\n", prop.maxThreadsPerBlock); - printf(" Max threads dimensions: (%d, %d, %d)\n", prop.maxThreadsDim[0], - prop.maxThreadsDim[1], prop.maxThreadsDim[2]); - printf(" Max grid dimensions: (%d, %d, %d)\n", prop.maxGridSize[0], - prop.maxGridSize[1], prop.maxGridSize[2]); - printf("\n"); - } -} diff --git a/kernels/hello_world.h b/kernels/hello_world.h deleted file mode 100644 index 4024e2e..0000000 --- a/kernels/hello_world.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef HELLO_WORLD_CU_H -#define HELLO_WORLD_CU_H - -extern "C" { -// Declaration of the CUDA function that will be called from C++ -void launch_hello_cuda(); -void check_cuda(); -} - -#endif // HELLO_WORLD_CU_H diff --git a/main.cpp b/main.cpp deleted file mode 100644 index e3c8734..0000000 --- a/main.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include "hello_world.h" -#include - -int main() { - std::cout << "Starting CUDA example..." << std::endl; // Using endl to flush - check_cuda(); - launch_hello_cuda(); - std::cout << "Ending CUDA example" << std::endl; // Using endl to flush - return 0; -} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt deleted file mode 100644 index 6c6cd8f..0000000 --- a/src/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -project(${CMAKE_PROJECT_NAME}_lib CUDA CXX) - -set(HEADER_FILES - ./test.h -) -set(SOURCE_FILES - ./test.cpp -) - -# The library contains header and source files. -add_library(${CMAKE_PROJECT_NAME}_lib - ${SOURCE_FILES} - ${HEADER_FILES} -) - -target_include_directories(${CMAKE_PROJECT_NAME}_lib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/src/test.cpp b/src/test.cpp deleted file mode 100644 index eec7934..0000000 --- a/src/test.cpp +++ /dev/null @@ -1,4 +0,0 @@ -#include "test.h" -#include - -void test_hello() { std::cout << "Hello!"; } diff --git a/src/test.h b/src/test.h deleted file mode 100644 index b9c7ab1..0000000 --- a/src/test.h +++ /dev/null @@ -1,2 +0,0 @@ -#include -void test_hello(); diff --git a/tests/unit_tests/CMakeLists.txt b/tests/unit_tests/CMakeLists.txt index e7273a3..e840129 100644 --- a/tests/unit_tests/CMakeLists.txt +++ b/tests/unit_tests/CMakeLists.txt @@ -1,8 +1,10 @@ include_directories(${gtest_SOURCE_DIR}/include ${gtest_SOURCE_DIR}) add_executable(Unit_Tests_run - test_example.cpp + vec_test.cpp ) target_link_libraries(Unit_Tests_run gtest gtest_main) -target_link_libraries(Unit_Tests_run ${CMAKE_PROJECT_NAME}_lib) \ No newline at end of file +target_link_libraries(Unit_Tests_run ${CMAKE_PROJECT_NAME}_lib) + +add_test(NAME Vec3Tests COMMAND ${CMAKE_BINARY_DIR}/tests/unit_tests/Unit_Tests_run) diff --git a/tests/unit_tests/test_example.cpp b/tests/unit_tests/test_example.cpp deleted file mode 100644 index bde73e6..0000000 --- a/tests/unit_tests/test_example.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "gtest/gtest.h" - -TEST(Example, Equals) { - EXPECT_EQ(1, 1); -} \ No newline at end of file diff --git a/tests/unit_tests/vec_test.cpp b/tests/unit_tests/vec_test.cpp new file mode 100644 index 0000000..1b18dd6 --- /dev/null +++ b/tests/unit_tests/vec_test.cpp @@ -0,0 +1,231 @@ +#include "vec3.h" +#include +#include +#include + +// Test fixture for Vec3 tests +class Vec3Test : public ::testing::Test { +protected: + // Test vectors that will be used across multiple tests + Vec3 intVec1, intVec2; + Vec3 floatVec1, floatVec2; + Vec3 doubleVec1, doubleVec2; + + void SetUp() override { + // Initialize test vectors + intVec1 = {1, 2, 3}; + intVec2 = {4, 5, 6}; + + floatVec1 = {1.5f, 2.5f, 3.5f}; + floatVec2 = {4.5f, 5.5f, 6.5f}; + + doubleVec1 = {1.5, 2.5, 3.5}; + doubleVec2 = {4.5, 5.5, 6.5}; + } +}; + +// Test vector addition +TEST_F(Vec3Test, Addition) { + // Test integer vectors + auto intResult = intVec1 + intVec2; + EXPECT_EQ(intResult.x, 5); + EXPECT_EQ(intResult.y, 7); + EXPECT_EQ(intResult.z, 9); + + // Test float vectors + auto floatResult = floatVec1 + floatVec2; + EXPECT_FLOAT_EQ(floatResult.x, 6.0f); + EXPECT_FLOAT_EQ(floatResult.y, 8.0f); + EXPECT_FLOAT_EQ(floatResult.z, 10.0f); + + // Test double vectors + auto doubleResult = doubleVec1 + doubleVec2; + EXPECT_DOUBLE_EQ(doubleResult.x, 6.0); + EXPECT_DOUBLE_EQ(doubleResult.y, 8.0); + EXPECT_DOUBLE_EQ(doubleResult.z, 10.0); +} + +// Test vector subtraction +TEST_F(Vec3Test, Subtraction) { + // Test integer vectors + auto intResult = intVec2 - intVec1; + EXPECT_EQ(intResult.x, 3); + EXPECT_EQ(intResult.y, 3); + EXPECT_EQ(intResult.z, 3); + + // Test float vectors + auto floatResult = floatVec2 - floatVec1; + EXPECT_FLOAT_EQ(floatResult.x, 3.0f); + EXPECT_FLOAT_EQ(floatResult.y, 3.0f); + EXPECT_FLOAT_EQ(floatResult.z, 3.0f); + + // Test double vectors + auto doubleResult = doubleVec2 - doubleVec1; + EXPECT_DOUBLE_EQ(doubleResult.x, 3.0); + EXPECT_DOUBLE_EQ(doubleResult.y, 3.0); + EXPECT_DOUBLE_EQ(doubleResult.z, 3.0); +} + +// Test vector scaling +TEST_F(Vec3Test, Scale) { + // Test integer scaling + Vec3 intVecScaled = intVec1; + intVecScaled.scale(2); + EXPECT_EQ(intVecScaled.x, 2); + EXPECT_EQ(intVecScaled.y, 4); + EXPECT_EQ(intVecScaled.z, 6); + + // Test float scaling + Vec3 floatVecScaled = floatVec1; + floatVecScaled.scale(2.0f); + EXPECT_FLOAT_EQ(floatVecScaled.x, 3.0f); + EXPECT_FLOAT_EQ(floatVecScaled.y, 5.0f); + EXPECT_FLOAT_EQ(floatVecScaled.z, 7.0f); + + // Test double scaling + Vec3 doubleVecScaled = doubleVec1; + doubleVecScaled.scale(2.0); + EXPECT_DOUBLE_EQ(doubleVecScaled.x, 3.0); + EXPECT_DOUBLE_EQ(doubleVecScaled.y, 5.0); + EXPECT_DOUBLE_EQ(doubleVecScaled.z, 7.0); + + // Test scaling by zero + Vec3 zeroScaled = floatVec1; + zeroScaled.scale(0.0f); + EXPECT_FLOAT_EQ(zeroScaled.x, 0.0f); + EXPECT_FLOAT_EQ(zeroScaled.y, 0.0f); + EXPECT_FLOAT_EQ(zeroScaled.z, 0.0f); + + // Test scaling by negative number + Vec3 negScaled = intVec1; + negScaled.scale(-1); + EXPECT_EQ(negScaled.x, -1); + EXPECT_EQ(negScaled.y, -2); + EXPECT_EQ(negScaled.z, -3); +} + +// Test dot product +TEST_F(Vec3Test, DotProduct) { + // Test integer dot product + int intDot = intVec1.dot(intVec2); + EXPECT_EQ(intDot, 32); // 1*4 + 2*5 + 3*6 = 4 + 10 + 18 = 32 + + // Test float dot product + float floatDot = floatVec1.dot(floatVec2); + EXPECT_FLOAT_EQ( + floatDot, + 43.25f); // 1.5*4.5 + 2.5*5.5 + 3.5*6.5 = 6.75 + 13.75 + 22.75 = 43.25 + + // Test double dot product + double doubleDot = doubleVec1.dot(doubleVec2); + EXPECT_DOUBLE_EQ(doubleDot, 43.25); // Same calculation as float + + // Test dot product with self (should equal squared length) + int selfDot = intVec1.dot(intVec1); + EXPECT_EQ(selfDot, 14); // 1*1 + 2*2 + 3*3 = 1 + 4 + 9 = 14 + + // Test dot product with zero vector + Vec3 zeroVec = {0, 0, 0}; + EXPECT_EQ(intVec1.dot(zeroVec), 0); +} + +// Test cross product +TEST_F(Vec3Test, CrossProduct) { + // Test integer cross product + auto intCross = intVec1.cross(intVec2); + EXPECT_EQ(intCross.x, -3); // (2*6 - 3*5) = 12 - 15 = -3 + EXPECT_EQ(intCross.y, 6); // (3*4 - 1*6) = 12 - 6 = 6 + EXPECT_EQ(intCross.z, -3); // (1*5 - 2*4) = 5 - 8 = -3 + + // Test float cross product + auto floatCross = floatVec1.cross(floatVec2); + EXPECT_FLOAT_EQ(floatCross.x, + -3.0f); // (2.5*6.5 - 3.5*5.5) = 16.25 - 19.25 = -3 + EXPECT_FLOAT_EQ(floatCross.y, + 6.0f); // (3.5*4.5 - 1.5*6.5) = 15.75 - 9.75 = 6 + EXPECT_FLOAT_EQ(floatCross.z, + -3.0f); // (1.5*5.5 - 2.5*4.5) = 8.25 - 11.25 = -3 + + // Test double cross product + auto doubleCross = doubleVec1.cross(doubleVec2); + EXPECT_DOUBLE_EQ(doubleCross.x, -3.0); + EXPECT_DOUBLE_EQ(doubleCross.y, 6.0); + EXPECT_DOUBLE_EQ(doubleCross.z, -3.0); + + // Test cross product with self (should be zero) + auto selfCross = intVec1.cross(intVec1); + EXPECT_EQ(selfCross.x, 0); + EXPECT_EQ(selfCross.y, 0); + EXPECT_EQ(selfCross.z, 0); + + // Test cross product of standard basis vectors (i × j = k) + Vec3 i = {1, 0, 0}; + Vec3 j = {0, 1, 0}; + Vec3 k = {0, 0, 1}; + + auto i_cross_j = i.cross(j); + EXPECT_EQ(i_cross_j.x, 0); + EXPECT_EQ(i_cross_j.y, 0); + EXPECT_EQ(i_cross_j.z, 1); + + auto j_cross_k = j.cross(k); + EXPECT_EQ(j_cross_k.x, 1); + EXPECT_EQ(j_cross_k.y, 0); + EXPECT_EQ(j_cross_k.z, 0); + + auto k_cross_i = k.cross(i); + EXPECT_EQ(k_cross_i.x, 0); + EXPECT_EQ(k_cross_i.y, 1); + EXPECT_EQ(k_cross_i.z, 0); +} + +// Test with edge cases +TEST_F(Vec3Test, EdgeCases) { + // Test with max values + Vec3 maxVec = {std::numeric_limits::max(), + std::numeric_limits::max(), + std::numeric_limits::max()}; + + // Addition with max values may overflow, but we want to test the operation + // works + auto maxAddition = maxVec + intVec1; + + // Test with min values + Vec3 minVec = {std::numeric_limits::min(), + std::numeric_limits::min(), + std::numeric_limits::min()}; + + // Subtraction with min values may underflow, but we want to test the + // operation works + auto minSubtraction = minVec - intVec1; + + // Test with mixed values + Vec3 mixedVec1 = {0.0, -1.0, std::numeric_limits::infinity()}; + Vec3 mixedVec2 = {-0.0, 1.0, + -std::numeric_limits::infinity()}; + + auto mixedAddition = mixedVec1 + mixedVec2; + // 0.0 + (-0.0) = 0.0 + EXPECT_DOUBLE_EQ(mixedAddition.x, 0.0); + // -1.0 + 1.0 = 0.0 + EXPECT_DOUBLE_EQ(mixedAddition.y, 0.0); + // inf + (-inf) = NaN + EXPECT_TRUE(std::isnan(mixedAddition.z)); + + // Test with NaN + Vec3 nanVec = {std::numeric_limits::quiet_NaN(), + std::numeric_limits::quiet_NaN(), + std::numeric_limits::quiet_NaN()}; + + // Any operation with NaN should result in NaN + auto nanResult = doubleVec1 + nanVec; + EXPECT_TRUE(std::isnan(nanResult.x)); + EXPECT_TRUE(std::isnan(nanResult.y)); + EXPECT_TRUE(std::isnan(nanResult.z)); +} + +// Main function that runs the tests +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +}