From 0e17c84eb9b54e79eee6dd7723b407bda2ef0633 Mon Sep 17 00:00:00 2001 From: Jan Mrna Date: Thu, 2 Oct 2025 19:29:08 +0200 Subject: [PATCH] WIP new vec class and tests --- cpp/src/math.hpp | 204 ++++++++++++++++++++ cpp/test/test.cpp | 468 +++++++++++++++++++++++++++++++++++----------- 2 files changed, 558 insertions(+), 114 deletions(-) diff --git a/cpp/src/math.hpp b/cpp/src/math.hpp index f7e5f5e..ad5e5c4 100644 --- a/cpp/src/math.hpp +++ b/cpp/src/math.hpp @@ -6,6 +6,209 @@ #include #include #include +#include +#include +#include + + +template +requires std::floating_point +static inline bool equalEpsilon(const T& a, const T& b) +{ + constexpr auto epsilon = [](){ + if constexpr (std::is_same_v) { + return T{1e-5}; + } else { + return T{1e-12}; // double, long double + } + }(); + if (a == b) { + // handle special cases: bit equality, Inf... + return true; + } + return std::abs(a - b) < epsilon; +} + + +template +class vec { +public: + vec() : m_Array{} {} + + template + requires (std::same_as && ...) && (sizeof...(ArgsT) == N) + vec(ArgsT... args) : m_Array{args...} {} + + const T& operator[](size_t index) const + { + // we leave run-time checks to the underlying std::array + return m_Array[index]; + } + + T& operator[](size_t index) + { + // we leave run-time checks to the underlying std::array + return m_Array[index]; + } + + friend std::ostream &operator<<(std::ostream &os, const vec &obj) + { + os << "( "; + for (const auto& element : obj.m_Array) { + os << element << " "; + } + os << ")"; + return os; + } + + + // + // binary operators + // + + friend bool operator==(const vec& a, const vec& b) + { + for (const auto& [u, v] : std::views::zip(a.m_Array,b.m_Array)) { + if (!equalEpsilon(u, v)) { + return false; + } + } + return true; + } + + friend bool operator!=(const vec& a, const vec& b) + { + return !(a == b); + } + + friend vec operator+(const vec& a, const vec& b) + { + vec c; + std::ranges::transform(a.m_Array, b.m_Array, c.m_Array.begin(), std::plus{}); + return c; + } + + friend vec operator-(const vec& a, const vec& b) + { + vec c; + std::ranges::transform(a.m_Array, b.m_Array, c.m_Array.begin(), std::minus{}); + return c; + } + + friend vec operator*(const vec& a, const T& scalar) + { + vec c; + std::ranges::transform(a.m_Array, std::views::repeat(scalar), c.m_Array.begin(), std::multiplies{}); + return c; + } + + friend vec operator*(const T& scalar, const vec& a) { + return a * scalar; + } + + friend vec operator/(const vec& a, const T& scalar) + { + vec c; + std::ranges::transform(a.m_Array, std::views::repeat(scalar), c.m_Array.begin(), std::divides{}); + return c; + } + + // + // compound-assignment operators + // + + vec& operator+=(const vec& b) + { + vec& a = *this; + std::ranges::transform(a.m_Array, b.m_Array, a.m_Array.begin(), std::plus{}); + return a; + } + + vec& operator-=(const vec& b) + { + vec& a = *this; + std::ranges::transform(a.m_Array, b.m_Array, a.m_Array.begin(), std::minus{}); + return a; + } + + + // + // Utility functions + // + + T LengthSquared() const + { + return std::transform_reduce( + m_Array.begin(), m_Array.end(), + T{0.0}, + std::plus{}, + [](T x){ + return x*x; + } + ); + } + + T Length() const + { + return std::sqrt(LengthSquared()); + } + + // + // In-place vector operations + // + + void Normalize() + { + T length = Length(); + if (equalEpsilon(length, T{0})) + return; + std::ranges::transform(m_Array, std::views::repeat(length), m_Array.begin(), std::divides{}); + } + + + // + // Methods returning new object + // + + vec GetNormalized() const + { + vec tmp = *this; + tmp.Normalize(); + return tmp; + } + + vec GetOrthogonal() const requires (N == 2) + { + vec tmp = *this; + + std::swap(tmp.m_Array[0], tmp.m_Array[1]); + tmp.m_Array[0] *= -1; + + return tmp; + } + +// const T& x = m_Array[0]; +// const T& y = m_Array[1]; +// const T& z = m_Array[2]; + + +private: + std::array m_Array; +}; + + +using vec2 = vec; +using vec3 = vec; +using vec4 = vec; +using dvec2 = vec; +using dvec3 = vec; +using dvec4 = vec; +using ivec2 = vec; +using ivec3 = vec; +using ivec4 = vec; +using uvec2 = vec; +using uvec3 = vec; +using uvec4 = vec; constexpr double EQUALITY_LIMIT = 1e-6; @@ -132,6 +335,7 @@ public: using TilePos = Vec2D; using WorldPos = Vec2D; +using WindowPos = Vec2D; struct TilePosHash { std::size_t operator()(const TilePos& p) const noexcept { diff --git a/cpp/test/test.cpp b/cpp/test/test.cpp index 403ed83..b4b2ae5 100644 --- a/cpp/test/test.cpp +++ b/cpp/test/test.cpp @@ -5,151 +5,391 @@ #include #include -#include "array.hpp" #include "log.hpp" #include "math.hpp" -// Vec2D Tests -TEST(Vec2D, DefaultConstruction) { - Vec2D v; - // Default values are uninitialized, but we can test basic functionality - v.x = 0; - v.y = 0; - ASSERT_EQ(v.x, 0); - ASSERT_EQ(v.y, 0); + +TEST(vec, DefaultConstruction) { + // Test that default-constucted vector + // has all elements equal to zero + vec3 v1; + ASSERT_EQ(v1[0], 0.0); + ASSERT_EQ(v1[1], 0.0); + ASSERT_EQ(v1[2], 0.0); } -TEST(Vec2D, InitializerListConstruction) { - Vec2D v{3, 4}; - ASSERT_EQ(v.x, 3); - ASSERT_EQ(v.y, 4); - - Vec2D vf{1.5f, 2.5f}; - ASSERT_FLOAT_EQ(vf.x, 1.5f); - ASSERT_FLOAT_EQ(vf.y, 2.5f); - - // Test type conversion - Vec2D vd{1, 2}; // int to float - ASSERT_FLOAT_EQ(vd.x, 1.0f); - ASSERT_FLOAT_EQ(vd.y, 2.0f); +TEST(vec, GetElements) { + // Test operator[] + ivec3 v1{12, 34, 56}; + ASSERT_EQ(v1[0], 12); + ASSERT_EQ(v1[1], 34); + ASSERT_EQ(v1[2], 56); } -TEST(Vec2D, Addition) { - Vec2D a{1, 2}; - Vec2D b{3, 4}; - Vec2D c = a + b; - ASSERT_EQ(c.x, 4); - ASSERT_EQ(c.y, 6); +TEST(vec, equalEpsilon) { + // Test equalEpsilon + // TODO just an ad-hoc test, + // can possibly fail for other machines. + // This needs some work + vec3 v1{1.0f, 2.0f, 3.0f}; + vec3 v2{0.999999f, 1.9999999f, 2.9999999f}; + ASSERT_EQ(v1, v2); +} +TEST(vec, nonEqualEpsilon) { + // Test operator!= + vec3 v1{1.0f, 2.0f, 3.0f}; + vec3 v2{2.0f, 4.0f, 6.0f}; + ASSERT_NE(v1, v2); +} + +TEST(vec, LogPrint) { + // Test that logger can print the vector of different types + // and sizes + vec2 v2(1.2f, 3.4f); + vec3 v3(1.2f, 3.4f, 5.6f); + vec4 v4(1.2f, 3.4f, 5.6f, 7.8f); + dvec2 dv2(1.2, 3.4); + dvec3 dv3(1.2, 3.4, 5.6); + dvec4 dv4(1.2, 3.4, 5.6, 7.8); + ivec2 iv2(1, 3); + ivec3 iv3(1, 3, 5); + ivec4 iv4(1, 3, 5, 7); + uvec2 uv2(1u, 3u); + uvec3 uv3(1u, 3u, 5u); + uvec4 uv4(1u, 3u, 5u, 7u); + + LOG_DEBUG("vec2 ", v2); + LOG_DEBUG("vec3 ", v3); + LOG_DEBUG("vec4 ", v4); + LOG_DEBUG("dvec2 ", dv2); + LOG_DEBUG("dvec3 ", dv3); + LOG_DEBUG("dvec4 ", dv4); + LOG_DEBUG("ivec2 ", iv2); + LOG_DEBUG("ivec3 ", iv3); + LOG_DEBUG("ivec4 ", iv4); + LOG_DEBUG("uvec2 ", uv2); + LOG_DEBUG("uvec3 ", uv3); + LOG_DEBUG("uvec4 ", uv4); +} + +TEST(vec, Add) +{ + // Test operator+ with float vectors + vec3 v1{1.0f, 2.0f, 3.0f}; + vec3 v2{4.0f, 5.0f, 6.0f}; + vec3 result = v1 + v2; + + ASSERT_FLOAT_EQ(result[0], 5.0f); + ASSERT_FLOAT_EQ(result[1], 7.0f); + ASSERT_FLOAT_EQ(result[2], 9.0f); + + // Test operator+ with integer vectors + ivec3 iv1{1, 2, 3}; + ivec3 iv2{10, 20, 30}; + ivec3 iresult = iv1 + iv2; + + ASSERT_EQ(iresult[0], 11); + ASSERT_EQ(iresult[1], 22); + ASSERT_EQ(iresult[2], 33); + // Test that original vectors are unchanged - ASSERT_EQ(a.x, 1); - ASSERT_EQ(a.y, 2); - ASSERT_EQ(b.x, 3); - ASSERT_EQ(b.y, 4); + ASSERT_FLOAT_EQ(v1[0], 1.0f); + ASSERT_FLOAT_EQ(v1[1], 2.0f); + ASSERT_FLOAT_EQ(v1[2], 3.0f); } -TEST(Vec2D, AdditionAssignment) { - Vec2D a{1, 2}; - Vec2D b{3, 4}; - - a += b; - ASSERT_EQ(a.x, 4); - ASSERT_EQ(a.y, 6); - - // Test that b is unchanged - ASSERT_EQ(b.x, 3); - ASSERT_EQ(b.y, 4); +TEST(vec, Sub) +{ + // Test operator- with float vectors + vec3 v1{5.0f, 7.0f, 9.0f}; + vec3 v2{1.0f, 2.0f, 3.0f}; + vec3 result = v1 - v2; + + ASSERT_FLOAT_EQ(result[0], 4.0f); + ASSERT_FLOAT_EQ(result[1], 5.0f); + ASSERT_FLOAT_EQ(result[2], 6.0f); + + // Test operator- with integer vectors + ivec3 iv1{30, 20, 10}; + ivec3 iv2{5, 3, 1}; + ivec3 iresult = iv1 - iv2; + + ASSERT_EQ(iresult[0], 25); + ASSERT_EQ(iresult[1], 17); + ASSERT_EQ(iresult[2], 9); + + // Test that original vectors are unchanged + ASSERT_FLOAT_EQ(v1[0], 5.0f); + ASSERT_FLOAT_EQ(v1[1], 7.0f); + ASSERT_FLOAT_EQ(v1[2], 9.0f); + + // Test subtraction resulting in negative values + vec3 v3{1.0f, 2.0f, 3.0f}; + vec3 v4{4.0f, 5.0f, 6.0f}; + vec3 negative_result = v3 - v4; + + ASSERT_FLOAT_EQ(negative_result[0], -3.0f); + ASSERT_FLOAT_EQ(negative_result[1], -3.0f); + ASSERT_FLOAT_EQ(negative_result[2], -3.0f); } -TEST(Vec2D, ScalarMultiplication) { - Vec2D v{2, 3}; - - Vec2D result = v * 2.0f; - ASSERT_EQ(result.x, 4); - ASSERT_EQ(result.y, 6); - - // Test with float vector - Vec2D vf{1.5f, 2.5f}; - Vec2D resultf = vf * 2.0f; - ASSERT_FLOAT_EQ(resultf.x, 3.0f); - ASSERT_FLOAT_EQ(resultf.y, 5.0f); +TEST(vec, ScalarMultiplication) +{ + // Test scalar * vector with float vectors + vec3 v1{2.0f, 3.0f, 4.0f}; + vec3 result = v1 * 2.5f; + + ASSERT_FLOAT_EQ(result[0], 5.0f); + ASSERT_FLOAT_EQ(result[1], 7.5f); + ASSERT_FLOAT_EQ(result[2], 10.0f); + + // Test scalar * vector with integer vectors + ivec3 iv1{3, 5, 7}; + ivec3 iresult = iv1 * 2; + + ASSERT_EQ(iresult[0], 6); + ASSERT_EQ(iresult[1], 10); + ASSERT_EQ(iresult[2], 14); + + // Test that original vector is unchanged + ASSERT_FLOAT_EQ(v1[0], 2.0f); + ASSERT_FLOAT_EQ(v1[1], 3.0f); + ASSERT_FLOAT_EQ(v1[2], 4.0f); + + // Test multiplication by zero + vec3 v2{1.0f, 2.0f, 3.0f}; + vec3 zero_result = v2 * 0.0f; + + ASSERT_FLOAT_EQ(zero_result[0], 0.0f); + ASSERT_FLOAT_EQ(zero_result[1], 0.0f); + ASSERT_FLOAT_EQ(zero_result[2], 0.0f); + + // Test multiplication by negative scalar (and different ordering) + vec3 v3{1.0f, -2.0f, 3.0f}; + vec3 negative_result = -2.0f * v3; + + ASSERT_FLOAT_EQ(negative_result[0], -2.0f); + ASSERT_FLOAT_EQ(negative_result[1], 4.0f); + ASSERT_FLOAT_EQ(negative_result[2], -6.0f); } -TEST(Vec2D, Normalization) { - Vec2D v{3.0f, 4.0f}; // Length = 5 - - v.normalize(); - ASSERT_FLOAT_EQ(v.x, 0.6f); - ASSERT_FLOAT_EQ(v.y, 0.8f); - - // Check that length is approximately 1 - float length = sqrt(v.x * v.x + v.y * v.y); - ASSERT_NEAR(length, 1.0f, 1e-6f); +TEST(vec, ScalarDivision) +{ + // Test vector / scalar with float vectors + vec3 v1{10.0f, 15.0f, 20.0f}; + vec3 result = v1 / 2.5f; + + ASSERT_FLOAT_EQ(result[0], 4.0f); + ASSERT_FLOAT_EQ(result[1], 6.0f); + ASSERT_FLOAT_EQ(result[2], 8.0f); + + // Test vector / scalar with integer vectors + ivec3 iv1{12, 18, 24}; + ivec3 iresult = iv1 / 2; + + ASSERT_EQ(iresult[0], 6); + ASSERT_EQ(iresult[1], 9); + ASSERT_EQ(iresult[2], 12); + + // Test that original vector is unchanged + ASSERT_FLOAT_EQ(v1[0], 10.0f); + ASSERT_FLOAT_EQ(v1[1], 15.0f); + ASSERT_FLOAT_EQ(v1[2], 20.0f); + + // Test division by negative scalar + vec3 v2{6.0f, -9.0f, 12.0f}; + vec3 negative_result = v2 / -3.0f; + + ASSERT_FLOAT_EQ(negative_result[0], -2.0f); + ASSERT_FLOAT_EQ(negative_result[1], 3.0f); + ASSERT_FLOAT_EQ(negative_result[2], -4.0f); + + // Test division by fractional scalar + vec3 v3{1.0f, 2.0f, 3.0f}; + vec3 fractional_result = v3 / 0.5f; + + ASSERT_FLOAT_EQ(fractional_result[0], 2.0f); + ASSERT_FLOAT_EQ(fractional_result[1], 4.0f); + ASSERT_FLOAT_EQ(fractional_result[2], 6.0f); } -TEST(Vec2D, NormalizedCopy) { - Vec2D v{3.0f, 4.0f}; - Vec2D normalized = v.normalized(); +TEST(vec, AdditionAssignment) +{ + // Test operator+= with float vectors + vec3 v1{1.0f, 2.0f, 3.0f}; + vec3 v2{4.0f, 5.0f, 6.0f}; + v1 += v2; + + ASSERT_FLOAT_EQ(v1[0], 5.0f); + ASSERT_FLOAT_EQ(v1[1], 7.0f); + ASSERT_FLOAT_EQ(v1[2], 9.0f); + + // Test that v2 is unchanged + ASSERT_FLOAT_EQ(v2[0], 4.0f); + ASSERT_FLOAT_EQ(v2[1], 5.0f); + ASSERT_FLOAT_EQ(v2[2], 6.0f); + + // Test operator+= with integer vectors + ivec3 iv1{10, 20, 30}; + ivec3 iv2{1, 2, 3}; + iv1 += iv2; + + ASSERT_EQ(iv1[0], 11); + ASSERT_EQ(iv1[1], 22); + ASSERT_EQ(iv1[2], 33); + + // Test chaining + vec3 v3{1.0f, 1.0f, 1.0f}; + vec3 v4{2.0f, 2.0f, 2.0f}; + vec3 v5{3.0f, 3.0f, 3.0f}; + v3 += v4 += v5; + + ASSERT_FLOAT_EQ(v3[0], 6.0f); + ASSERT_FLOAT_EQ(v3[1], 6.0f); + ASSERT_FLOAT_EQ(v3[2], 6.0f); + ASSERT_FLOAT_EQ(v4[0], 5.0f); + ASSERT_FLOAT_EQ(v4[1], 5.0f); + ASSERT_FLOAT_EQ(v4[2], 5.0f); +} - // Original should be unchanged - ASSERT_FLOAT_EQ(v.x, 3.0f); - ASSERT_FLOAT_EQ(v.y, 4.0f); +TEST(vec, SubtractionAssignment) +{ + // Test operator-= with float vectors + vec3 v1{10.0f, 15.0f, 20.0f}; + vec3 v2{3.0f, 5.0f, 7.0f}; + v1 -= v2; + + ASSERT_FLOAT_EQ(v1[0], 7.0f); + ASSERT_FLOAT_EQ(v1[1], 10.0f); + ASSERT_FLOAT_EQ(v1[2], 13.0f); + + // Test that v2 is unchanged + ASSERT_FLOAT_EQ(v2[0], 3.0f); + ASSERT_FLOAT_EQ(v2[1], 5.0f); + ASSERT_FLOAT_EQ(v2[2], 7.0f); + + // Test operator-= with integer vectors + ivec3 iv1{50, 40, 30}; + ivec3 iv2{5, 10, 15}; + iv1 -= iv2; + + ASSERT_EQ(iv1[0], 45); + ASSERT_EQ(iv1[1], 30); + ASSERT_EQ(iv1[2], 15); + + // Test subtraction resulting in negative values + vec3 v3{1.0f, 2.0f, 3.0f}; + vec3 v4{4.0f, 5.0f, 6.0f}; + v3 -= v4; + + ASSERT_FLOAT_EQ(v3[0], -3.0f); + ASSERT_FLOAT_EQ(v3[1], -3.0f); + ASSERT_FLOAT_EQ(v3[2], -3.0f); +} +TEST(vec, LengthSquared) +{ + // Test LengthSquared with float vectors + vec3 v1{3.0f, 4.0f, 0.0f}; + ASSERT_FLOAT_EQ(v1.LengthSquared(), 25.0f); // 3² + 4² + 0² = 25 + + vec2 v2{1.0f, 1.0f}; + ASSERT_FLOAT_EQ(v2.LengthSquared(), 2.0f); // 1² + 1² = 2 + + // Test with zero vector + vec3 zero{0.0f, 0.0f, 0.0f}; + ASSERT_FLOAT_EQ(zero.LengthSquared(), 0.0f); +} + +TEST(vec, Length) +{ + // Test Length with float vectors + vec3 v1{3.0f, 4.0f, 0.0f}; + ASSERT_FLOAT_EQ(v1.Length(), 5.0f); // sqrt(3² + 4² + 0²) = 5 + + vec2 v2{1.0f, 1.0f}; + ASSERT_NEAR(v2.Length(), 1.414213f, 1e-5f); // sqrt(2) ≈ 1.414213 + + // Test with zero vector + vec3 zero{0.0f, 0.0f, 0.0f}; + ASSERT_FLOAT_EQ(zero.Length(), 0.0f); +} + +TEST(vec, Normalize) +{ + // Test Normalize with float vectors + vec3 v1{3.0f, 4.0f, 0.0f}; + v1.Normalize(); + + ASSERT_FLOAT_EQ(v1[0], 0.6f); // 3/5 + ASSERT_FLOAT_EQ(v1[1], 0.8f); // 4/5 + ASSERT_FLOAT_EQ(v1[2], 0.0f); + ASSERT_NEAR(v1.Length(), 1.0f, 1e-6f); + + // Test with zero vector (may produce NaN - implementation dependent) + vec3 zero{0.0f, 0.0f, 0.0f}; + zero.Normalize(); + // Check if result is NaN (which is expected for zero vector normalization) + ASSERT_TRUE(zero[0] == 0.0f); + ASSERT_TRUE(zero[1] == 0.0f); + ASSERT_TRUE(zero[2] == 0.0f); +} + +TEST(vec, GetNormalized) +{ + // Test GetNormalized with float vectors + const vec3 v1{3.0f, 4.0f, 0.0f}; + vec3 normalized = v1.GetNormalized(); + + // Original vector should be unchanged + ASSERT_FLOAT_EQ(v1[0], 3.0f); + ASSERT_FLOAT_EQ(v1[1], 4.0f); + ASSERT_FLOAT_EQ(v1[2], 0.0f); + // Normalized copy should be unit length - ASSERT_FLOAT_EQ(normalized.x, 0.6f); - ASSERT_FLOAT_EQ(normalized.y, 0.8f); - - float length = - sqrt(normalized.x * normalized.x + normalized.y * normalized.y); - ASSERT_NEAR(length, 1.0f, 1e-6f); + ASSERT_FLOAT_EQ(normalized[0], 0.6f); // 3/5 + ASSERT_FLOAT_EQ(normalized[1], 0.8f); // 4/5 + ASSERT_FLOAT_EQ(normalized[2], 0.0f); + ASSERT_NEAR(normalized.Length(), 1.0f, 1e-6f); + + // Test with zero vector + vec3 zero{0.0f, 0.0f, 0.0f}; + vec3 zero_normalized = zero.GetNormalized(); + + ASSERT_FLOAT_EQ(zero_normalized[0], 0.0f); + ASSERT_FLOAT_EQ(zero_normalized[1], 0.0f); + ASSERT_FLOAT_EQ(zero_normalized[2], 0.0f); + + // Original zero vector should be unchanged + ASSERT_FLOAT_EQ(zero[0], 0.0f); + ASSERT_FLOAT_EQ(zero[1], 0.0f); + ASSERT_FLOAT_EQ(zero[2], 0.0f); } -TEST(Vec2D, ZeroVectorNormalization) { - Vec2D v{0.0f, 0.0f}; - - v.normalize(); - ASSERT_FLOAT_EQ(v.x, 0.0f); - ASSERT_FLOAT_EQ(v.y, 0.0f); - - // Test normalized() as well - Vec2D v2{0.0f, 0.0f}; - Vec2D normalized = v2.normalized(); - ASSERT_FLOAT_EQ(normalized.x, 0.0f); - ASSERT_FLOAT_EQ(normalized.y, 0.0f); +TEST(vec, GetOrthogonal) +{ + const vec2 v1{5.0f, 1.0f}; + auto v2 = v1.GetOrthogonal(); + ASSERT_FLOAT_EQ(v2[0], -1.0f); + ASSERT_FLOAT_EQ(v2[1], 5.0f); } -TEST(Vec2D, VerySmallVectorNormalization) { - Vec2D v{1e-7f, 1e-7f}; // Very small vector - - v.normalize(); - // Should be treated as zero vector - ASSERT_FLOAT_EQ(v.x, 0.0f); - ASSERT_FLOAT_EQ(v.y, 0.0f); -} - -TEST(Vec2D, OutputOperator) { - Vec2D v{42, 24}; - - std::ostringstream oss; - oss << v; - ASSERT_EQ(oss.str(), "( 42, 24)"); -} - -TEST(Vec2D, ChainedOperations) { - Vec2D a{1.0f, 2.0f}; - Vec2D b{3.0f, 4.0f}; +TEST(vec, ChainedOperations) { + vec2 a{1.0f, 2.0f}; + vec2 b{3.0f, 4.0f}; // Test chaining: (a + b) * 2.0f - Vec2D result = (a + b) * 2.0f; - ASSERT_FLOAT_EQ(result.x, 8.0f); - ASSERT_FLOAT_EQ(result.y, 12.0f); + auto result = (a + b) * 2.0f; + ASSERT_FLOAT_EQ(result[0], 8.0f); + ASSERT_FLOAT_EQ(result[1], 12.0f); // Test chaining with assignment a += b; a = a * 0.5f; - ASSERT_FLOAT_EQ(a.x, 2.0f); - ASSERT_FLOAT_EQ(a.y, 3.0f); + ASSERT_FLOAT_EQ(a[0], 2.0f); + ASSERT_FLOAT_EQ(a[1], 3.0f); } int main(int argc, char **argv) {