diff --git a/src/naive/naive_vec4.h b/src/naive/naive_vec4.h index 910efd8..ae3ff46 100644 --- a/src/naive/naive_vec4.h +++ b/src/naive/naive_vec4.h @@ -89,4 +89,9 @@ inline bool approx_eq(vec4 a, vec4 b, float eps = 1e-6f) noexcept && std::fabs(a.w() - b.w()) <= eps; } +[[nodiscard]] inline vec4 operator-(vec4 v) noexcept +{ + return { -v.x(), -v.y(), -v.z(), -v.w() }; +} + } \ No newline at end of file diff --git a/src/tenvex/expressions/core.h b/src/tenvex/expressions/core.h index 50ef0d7..dee9a82 100644 --- a/src/tenvex/expressions/core.h +++ b/src/tenvex/expressions/core.h @@ -13,6 +13,12 @@ namespace tnvx::detail { +[[nodiscard]] TNVX_INLINE +float to_float(vf4 v) +{ + return _mm_cvtss_f32(v); +} + [[nodiscard]] TNVX_INLINE bool eq(vf4 a, vf4 b) { @@ -39,6 +45,12 @@ float get_lane(vf4 v) noexcept return _mm_cvtss_f32(_mm_shuffle_ps(v, v, _MM_SHUFFLE(0, 0, 0, lane))); } +[[nodiscard]] TNVX_INLINE +vf4 neg(vf4 v) noexcept +{ + return _mm_xor_ps(v, _mm_set1_ps(-0.0f)); +} + [[nodiscard]] TNVX_INLINE vf4 add(vf4 l, vf4 r) noexcept { diff --git a/src/tenvex/expressions/dot3_impl.h b/src/tenvex/expressions/dot3_impl.h index 03cadd6..3c3f864 100644 --- a/src/tenvex/expressions/dot3_impl.h +++ b/src/tenvex/expressions/dot3_impl.h @@ -24,7 +24,7 @@ template TNVX_INLINE Dot3::operator float() const noexcept { - return _mm_cvtss_f32(eval()); + return detail::to_float(eval()); } template diff --git a/src/tenvex/expressions/neg.h b/src/tenvex/expressions/neg.h new file mode 100644 index 0000000..4056851 --- /dev/null +++ b/src/tenvex/expressions/neg.h @@ -0,0 +1,36 @@ +#pragma once + +#include "core.h" +#include "expression.h" +#include "traits.h" +#include "concepts.h" + +namespace tnvx +{ + +template +struct Neg : Expr> +{ + TNVX_INLINE + explicit Neg(const E& TNVX_RESTRICT e) noexcept; + + [[nodiscard]] TNVX_INLINE + vf4 eval() const noexcept; + + [[nodiscard]] TNVX_INLINE + operator float() const noexcept requires scalar_expr; + +private: + tnvx_ref_or_value_t _e; +}; + +template inline constexpr bool is_vec_expr> = true; +template inline constexpr bool is_scalar_expr> = true; + +template +[[nodiscard]] TNVX_INLINE +Neg operator-(const E& e) noexcept; + +} + +#include "neg_impl.hpp" diff --git a/src/tenvex/expressions/neg_impl.hpp b/src/tenvex/expressions/neg_impl.hpp new file mode 100644 index 0000000..ecf30f1 --- /dev/null +++ b/src/tenvex/expressions/neg_impl.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include "neg.h" + +namespace tnvx +{ + +template +TNVX_INLINE +tnvx::Neg::Neg(const E& TNVX_RESTRICT e) noexcept + : _e(e) +{ +} + +template +TNVX_INLINE +vf4 Neg::eval() const noexcept +{ + return detail::neg(_e.eval()); +} + +template +TNVX_INLINE +Neg::operator float() const noexcept requires scalar_expr +{ + return detail::to_float(eval()); +} + +template +TNVX_INLINE +Neg operator-(const E& e) noexcept +{ + return Neg(e); +} + +} diff --git a/src/tenvex/expressions/vec4.h b/src/tenvex/expressions/vec4.h index 82960ec..30be4e7 100644 --- a/src/tenvex/expressions/vec4.h +++ b/src/tenvex/expressions/vec4.h @@ -35,9 +35,6 @@ struct alignas(16) vec4 : Expr [[nodiscard]] TNVX_INLINE vf4 eval() const noexcept; - [[nodiscard]] TNVX_INLINE - vec4 operator-() const noexcept; - private: vf4 _data; diff --git a/src/tenvex/tenvex.h b/src/tenvex/tenvex.h index f87fc6b..ad2e4ba 100644 --- a/src/tenvex/tenvex.h +++ b/src/tenvex/tenvex.h @@ -1,5 +1,6 @@ #pragma once +#include "expressions/neg.h" #include "expressions/add.h" #include "expressions/sub.h" #include "expressions/scalar.h" diff --git a/src/tests/naive_tests.cpp b/src/tests/naive_tests.cpp index d145f25..e78260e 100644 --- a/src/tests/naive_tests.cpp +++ b/src/tests/naive_tests.cpp @@ -253,4 +253,34 @@ TEST(naive_vec4, cross_parallel) EXPECT_TRUE(approx_eq(cross3(a, b), check)); } +TEST(naive_vec4, neg_basic) +{ + vec4 a = { 1.0f, -2.0f, 3.0f, -4.0f }; + vec4 check = { -1.0f, 2.0f, -3.0f, 4.0f }; + + EXPECT_TRUE(approx_eq(-a, check)); +} + +TEST(naive_vec4, neg_zero) +{ + vec4 a = { 0.0f, 0.0f, 0.0f, 0.0f }; + + EXPECT_TRUE(approx_eq(-a, a)); +} + +TEST(naive_vec4, neg_double) +{ + vec4 a = { 1.0f, 2.0f, 3.0f, 4.0f }; + + EXPECT_TRUE(approx_eq(-(-a), a)); +} + +TEST(naive_vec4, neg_compound) +{ + vec4 a = { 5.0f, 3.0f, 1.0f, 0.0f }; + vec4 b = { 2.0f, 1.0f, 4.0f, 0.0f }; + + EXPECT_TRUE(approx_eq(a + (-b), a - b)); +} + } \ No newline at end of file diff --git a/src/tests/vec4_tests.cpp b/src/tests/vec4_tests.cpp index 725353f..10c1a13 100644 --- a/src/tests/vec4_tests.cpp +++ b/src/tests/vec4_tests.cpp @@ -257,8 +257,8 @@ TEST(vec4, dot3_times_literal_collapses) auto r = dot3(a, b) * 2.0f; - static_assert(!std::is_same_v); - static_assert(scalar_expr); + EXPECT_TRUE((!std::same_as)); + EXPECT_TRUE((scalar_expr)); } TEST(vec4, magnitude3_times_literal_collapses) @@ -267,8 +267,74 @@ TEST(vec4, magnitude3_times_literal_collapses) auto r = magnitude3(a) * 2.0f; - static_assert(!std::is_same_v); - static_assert(scalar_expr); + EXPECT_TRUE((!std::same_as)); + EXPECT_TRUE((scalar_expr)); +} + +TEST(vec4, neg_basic) +{ + vec4 a = { 1.0f, -2.0f, 3.0f, -4.0f }; + vec4 check = { -1.0f, 2.0f, -3.0f, 4.0f }; + + EXPECT_TRUE(approx_eq(-a, check)); +} + +TEST(vec4, neg_zero) +{ + vec4 a = { 0.0f, 0.0f, 0.0f, 0.0f }; + + EXPECT_TRUE(approx_eq(-a, a)); +} + +TEST(vec4, neg_double) +{ + vec4 a = { 1.0f, 2.0f, 3.0f, 4.0f }; + + EXPECT_TRUE(approx_eq(-(-a), a)); +} + +TEST(vec4, neg_compound) +{ + vec4 a = { 5.0f, 3.0f, 1.0f, 0.0f }; + vec4 b = { 2.0f, 1.0f, 4.0f, 0.0f }; + vec4 check = { 3.0f, 2.0f, -3.0f, 0.0f }; + vec4 result = a + (-b); + + EXPECT_TRUE(approx_eq(check, result)); +} + +TEST(vec4, neg_expression) +{ + vec4 a = { 1.0f, 2.0f, 3.0f, 0.0f }; + vec4 b = { 4.0f, 5.0f, 6.0f, 0.0f }; + vec4 check = { -5.0f, -7.0f, -9.0f, 0.0f }; + vec4 result = -(a + b); + + + EXPECT_TRUE((vec_expr)); + EXPECT_TRUE(approx_eq(check, result)); +} + +TEST(vec4, neg_scalar_stays_lazy) +{ + vec4 a = { 1.0f, 0.0f, 0.0f, 0.0f }; + vec4 b = { 1.0f, 0.0f, 0.0f, 0.0f }; + float check = -1.0f; + float result = -dot3(a, b); + + EXPECT_TRUE((scalar_expr)); + EXPECT_FLOAT_EQ(check, result); +} + +TEST(vec4, neg_scalar_in_expression) +{ + vec4 a = { 2.0f, 0.0f, 0.0f, 0.0f }; + vec4 b = { 1.0f, 0.0f, 0.0f, 0.0f }; + vec4 c = { 1.0f, 1.0f, 1.0f, 0.0f }; + vec4 check = { -2.0f, -2.0f, -2.0f, 0.0f }; + + vec4 result = c * (-dot3(a, b)); + EXPECT_TRUE(approx_eq(check, result)); } }