rsimd

Make SIMD instruction sets easier to use
git clone git://git.meso-star.fr/rsimd.git
Log | Files | Refs | README | LICENSE

commit a2774b681f32b51926c00d0f8b1bf774d3c3a497
parent 860475a1353e4d3f2d601417e0311bcad0ccda04
Author: vaplv <vaplv@free.fr>
Date:   Thu, 23 Oct 2014 14:49:49 +0200

Implement and test the SIMD SoA Float2 functions

Diffstat:
MREADME.md | 9++++++---
Mcmake/CMakeLists.txt | 1+
Msrc/rsimd.h | 2+-
Asrc/soa4f2.h | 30++++++++++++++++++++++++++++++
Asrc/soa4fX.h | 318+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/sse/ssef.h | 10+++-------
Asrc/test_soa4f2.c | 132+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 491 insertions(+), 11 deletions(-)

diff --git a/README.md b/README.md @@ -1,9 +1,12 @@ # RSIMD This C89 library defines an interface that encapsulates and make easier the -manipulation of SIMD instruction sets. It also provide a SIMD implementation -of linear algebrae operations for 3x3 and 4x4 matrices arranged in an `Array of -Structure`. Currently only the SSE2 instruction set is supported. +manipulation of SIMD instruction sets. It also provides a SIMD implementation +of linear algebrae operations for 3x3 and 4x4 matrices as well as quaternions +arranged in an `Array of Structures` SIMD layout. Linear algebrae functions on +two dimensionnal `Structure of Arrays` vectors are also implemented. + +Note that currently only the SSE2 instruction set is supported. ## How to build diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt @@ -85,6 +85,7 @@ new_test(test_v4i) new_test(test_aosf33) new_test(test_aosf44) new_test(test_aosq) +new_test(test_soa4f2) ################################################################################ # Install directives diff --git a/src/rsimd.h b/src/rsimd.h @@ -26,7 +26,7 @@ #define RSIMD_API extern IMPORT_SYM #endif -#ifdef SIMD_SSE +#ifdef SIMD_SSE2 #include "sse/sse.h" #else #error Unsupported_Platform diff --git a/src/soa4f2.h b/src/soa4f2.h @@ -0,0 +1,30 @@ +/* Copyright (C) 2014 Vincent Forest (vaplv@free.fr) + * + * The RSIMD library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The RSIMD library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the RSIMD library. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef SOA4F2_H +#define SOA4F2_H + +/* Generate the common soa4fX funcs */ +#define SOA4FX_DIMENSION__ 2 +#include "soa4fX.h" + +static FINLINE v4f_T +soa4f2_cross(const v4f_T a[2], const v4f_T b[2]) +{ + ASSERT(a && b); + return v4f_sub(v4f_mul(a[0], b[1]), v4f_mul(a[1], b[0])); +} + +#endif /* SOA4F2_H */ diff --git a/src/soa4fX.h b/src/soa4fX.h @@ -0,0 +1,318 @@ +/* Copyright (C) 2014 Vincent Forest (vaplv@free.fr) + * + * The RSIMD library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The RSIMD library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the RSIMD library. If not, see <http://www.gnu.org/licenses/>. */ + +/* + * Header used to generate funcs on SoA SIMD float vectors of X dimensions + */ +#if !defined(SOA4FX_DIMENSION__) + #error Missing arguments +#endif + +#if defined(SOA4FX_FUNC__) + #error Unexpected SOA4FX_FUNC__ macro defintion +#endif + +#include "rsimd.h" + +STATIC_ASSERT(SOA4FX_DIMENSION__ > 1, Unexpected_value); + +#define SOA4FX_FUNC__(Func) \ + CONCAT(CONCAT(CONCAT(soa4f, SOA4FX_DIMENSION__), _), Func) + +/* Helper macro */ +#define SIZEOF_SOA4FX__ sizeof(v4f_T[SOA4FX_DIMENSION__]) + +#if SOA4FX_DIMENSION__ <= 4 +static FINLINE v4f_T* +CONCAT(soa4f, SOA4FX_DIMENSION__) + (v4f_T* dst + ,const v4f_T x + ,const v4f_T y +#if SOA4FX_DIMENSION__ > 2 + ,const v4f_T z +#endif +#if SOA4FX_DIMENSION__ > 3 + ,const v4f_T w +#endif + ) +{ + ASSERT(dst); + dst[0] = x; + dst[1] = y; +#if SOA4FX_DIMENSION__ > 2 + dst[2] = z; +#endif +#if SOA4FX_DIMENSION__ > 3 + dst[3] = w; +#endif + return dst; +} +#endif + +static FINLINE v4f_T* +SOA4FX_FUNC__(splat)(v4f_T* dst, const v4f_T val) +{ + int i; + ASSERT(dst); + FOR_EACH(i, 0, SOA4FX_DIMENSION__) + dst[i] = val; + return dst; +} + +static FINLINE v4f_T* +SOA4FX_FUNC__(set__)(v4f_T* dst, const v4f_T* src) +{ + int i; + ASSERT(dst && src); + ASSERT(!MEM_AREA_OVERLAP(dst, SIZEOF_SOA4FX__, src, SIZEOF_SOA4FX__)); + FOR_EACH(i, 0, SOA4FX_DIMENSION__) + dst[i] = src[i]; + return dst; +} + +static FINLINE v4f_T* +SOA4FX_FUNC__(set)(v4f_T* dst, const v4f_T* src) +{ + ASSERT(dst && src); + if(!MEM_AREA_OVERLAP(dst, SIZEOF_SOA4FX__, src, SIZEOF_SOA4FX__)) { + return SOA4FX_FUNC__(set__)(dst, src); + } else { + v4f_T tmp[SOA4FX_DIMENSION__]; + return SOA4FX_FUNC__(set__)(dst, SOA4FX_FUNC__(set__)(tmp, src)); + } +} + +static FINLINE v4f_T +SOA4FX_FUNC__(dot)(const v4f_T* a, const v4f_T* b) +{ + v4f_T dot; + int i; + ASSERT(a && b); + dot = v4f_mul(a[0], b[0]); + FOR_EACH(i, 1, SOA4FX_DIMENSION__) { + dot = v4f_add(dot, v4f_mul(a[i], b[i])); + } + return dot; +} + +static FINLINE v4f_T +SOA4FX_FUNC__(len)(const v4f_T* a) +{ + ASSERT(a); + return v4f_sqrt(SOA4FX_FUNC__(dot)(a, a)); +} + +static FINLINE v4f_T +SOA4FX_FUNC__(normalize)(v4f_T* dst, const v4f_T* a) +{ + v4f_T tmp[SOA4FX_DIMENSION__]; + v4f_T sqr_len, rcp_len; + v4f_T mask; + int i; + ASSERT(dst && a); + + sqr_len = SOA4FX_FUNC__(dot)(a, a); + mask = v4f_neq(sqr_len, v4f_zero()); + rcp_len = v4f_rsqrt(sqr_len); + FOR_EACH(i, 0, SOA4FX_DIMENSION__) + tmp[i] = v4f_and(mask, v4f_mul(a[i], rcp_len)); + SOA4FX_FUNC__(set__)(dst, tmp); + return v4f_mul(sqr_len, rcp_len); +} + +static FINLINE v4f_T +SOA4FX_FUNC__(is_normalized)(const v4f_T* a) +{ + return v4f_eq_eps(SOA4FX_FUNC__(len)(a), v4f_set1(1.f), v4f_set1(1.e-6f)); +} + +static FINLINE v4f_T* +SOA4FX_FUNC__(add)(v4f_T* dst, const v4f_T* a, const v4f_T* b) +{ + v4f_T tmp[SOA4FX_DIMENSION__]; + int i; + ASSERT(dst && a && b); + FOR_EACH(i, 0, SOA4FX_DIMENSION__) + tmp[i] = v4f_add(a[i], b[i]); + return SOA4FX_FUNC__(set__)(dst, tmp); +} + +static FINLINE v4f_T* +SOA4FX_FUNC__(addf)(v4f_T* dst, const v4f_T* a, const v4f_T f) +{ + v4f_T tmp[SOA4FX_DIMENSION__]; + int i; + ASSERT(dst && a); + FOR_EACH(i, 0, SOA4FX_DIMENSION__) + tmp[i] = v4f_add(a[i], f); + return SOA4FX_FUNC__(set__)(dst, tmp); +} + +static FINLINE v4f_T* +SOA4FX_FUNC__(sub)(v4f_T* dst, const v4f_T* a, const v4f_T* b) +{ + v4f_T tmp[SOA4FX_DIMENSION__]; + int i; + ASSERT(dst && a && b); + FOR_EACH(i, 0, SOA4FX_DIMENSION__) + tmp[i] = v4f_sub(a[i], b[i]); + return SOA4FX_FUNC__(set__)(dst, tmp); +} + +static FINLINE v4f_T* +SOA4FX_FUNC__(subf)(v4f_T* dst, const v4f_T* a, const v4f_T f) +{ + v4f_T tmp[SOA4FX_DIMENSION__]; + int i; + ASSERT(dst && a); + FOR_EACH(i, 0, SOA4FX_DIMENSION__) + tmp[i] = v4f_sub(a[i], f); + return SOA4FX_FUNC__(set__)(dst, tmp); +} + +static FINLINE v4f_T* +SOA4FX_FUNC__(mul)(v4f_T* dst, const v4f_T* a, const v4f_T* b) +{ + v4f_T tmp[SOA4FX_DIMENSION__]; + int i; + ASSERT(dst && a && b); + FOR_EACH(i, 0, SOA4FX_DIMENSION__) + tmp[i] = v4f_mul(a[i], b[i]); + return SOA4FX_FUNC__(set__)(dst, tmp); +} + +static FINLINE v4f_T* +SOA4FX_FUNC__(mulf)(v4f_T* dst, const v4f_T* a, const v4f_T f) +{ + v4f_T tmp[SOA4FX_DIMENSION__]; + int i; + ASSERT(dst && a); + FOR_EACH(i, 0, SOA4FX_DIMENSION__) + tmp[i] = v4f_mul(a[i], f); + return SOA4FX_FUNC__(set__)(dst, tmp); +} + +static FINLINE v4f_T* +SOA4FX_FUNC__(div)(v4f_T* dst, const v4f_T* a, const v4f_T* b) +{ + v4f_T tmp[SOA4FX_DIMENSION__]; + int i; + ASSERT(dst && a && b); + FOR_EACH(i, 0, SOA4FX_DIMENSION__) + tmp[i] = v4f_div(a[i], b[i]); + return SOA4FX_FUNC__(set__)(dst, tmp); +} + +static FINLINE v4f_T* +SOA4FX_FUNC__(divf)(v4f_T* dst, const v4f_T* a, const v4f_T f) +{ + v4f_T tmp[SOA4FX_DIMENSION__]; + int i; + ASSERT(dst && a); + FOR_EACH(i, 0, SOA4FX_DIMENSION__) + tmp[i] = v4f_div(a[i], f); + return SOA4FX_FUNC__(set__)(dst, tmp); +} + +static FINLINE v4f_T* +SOA4FX_FUNC__(minus)(v4f_T* dst, const v4f_T* a) +{ + v4f_T tmp[SOA4FX_DIMENSION__]; + int i; + ASSERT(dst && a); + FOR_EACH(i, 0, SOA4FX_DIMENSION__) + tmp[i] = v4f_minus(a[i]); + return SOA4FX_FUNC__(set__)(dst, tmp); +} + +static FINLINE v4f_T +SOA4FX_FUNC__(sum)(const v4f_T* a) +{ + v4f_T f; + int i = 0; + ASSERT(a); + f = a[i]; + FOR_EACH(i, 1, SOA4FX_DIMENSION__) + f = v4f_add(f, a[i]); + return f; +} + +static FINLINE v4f_T* +SOA4FX_FUNC__(lerp) + (v4f_T* dst, + const v4f_T* from, + const v4f_T* to, + const v4f_T t) +{ + v4f_T tmp[SOA4FX_DIMENSION__]; + v4f_T t_adjusted; + int i; + ASSERT(dst && from && to); + t_adjusted = v4f_min(v4f_max(t, v4f_zero()), v4f_set1(1.f)); + FOR_EACH(i, 0, SOA4FX_DIMENSION__) + tmp[i] = v4f_add(from[i], v4f_mul(t_adjusted, v4f_sub(to[i], from[i]))); + SOA4FX_FUNC__(set__)(dst, tmp); + return dst; +} + +static FINLINE v4f_T +SOA4FX_FUNC__(eq)(const v4f_T* a, const v4f_T* b) +{ + v4f_T is_eq; + int i = 0; + ASSERT(a && b); + is_eq = v4f_eq(a[0], b[0]); + FOR_EACH(i, 1, SOA4FX_DIMENSION__) + is_eq = v4f_and(is_eq, v4f_eq(a[i], b[i])); + return is_eq; +} + +static FINLINE v4f_T +SOA4FX_FUNC__(eq_eps)(const v4f_T* a, const v4f_T* b, const v4f_T eps) +{ + v4f_T is_eq; + int i = 0; + ASSERT(a && b); + is_eq = v4f_eq_eps(a[0], b[0], eps); + FOR_EACH(i, 1, SOA4FX_DIMENSION__) + is_eq = v4f_and(is_eq, v4f_eq_eps(a[i], b[i], eps)); + return is_eq; +} + +static FINLINE v4f_T* +SOA4FX_FUNC__(max)(v4f_T* dst, const v4f_T* a, const v4f_T* b) +{ + v4f_T tmp[SOA4FX_DIMENSION__]; + int i; + ASSERT(dst && a && b); + FOR_EACH(i, 0, SOA4FX_DIMENSION__) + tmp[i] = v4f_max(a[i], b[i]); + return SOA4FX_FUNC__(set__)(dst, tmp); +} + +static FINLINE v4f_T* +SOA4FX_FUNC__(min)(v4f_T* dst, const v4f_T* a, const v4f_T* b) +{ + v4f_T tmp[SOA4FX_DIMENSION__]; + int i; + ASSERT(dst && a && b); + FOR_EACH(i, 0, SOA4FX_DIMENSION__) + tmp[i] = v4f_min(a[i], b[i]); + return SOA4FX_FUNC__(set__)(dst, tmp); +} + +#undef SIZEOF_SOA4FX__ +#undef SOA4FX_DIMENSION__ +#undef SOA4FX_FUNC__ diff --git a/src/sse/ssef.h b/src/sse/ssef.h @@ -23,12 +23,8 @@ #include "sse_swz.h" #include <rsys/math.h> -#ifdef SIMD_SSE - #include <xmmintrin.h> -#endif -#ifdef SIMD_SSE2 - #include <emmintrin.h> -#endif +#include <xmmintrin.h> +#include <emmintrin.h> typedef __m128 v4f_T; #define V4F_AT__(Vec, Id) __builtin_ia32_vec_ext_v4sf(Vec, Id) @@ -544,7 +540,7 @@ v4f_lt(const v4f_T v0, const v4f_T v1) static FINLINE v4f_T v4f_eq_eps(const v4f_T v0, const v4f_T v1, const v4f_T eps) { - return v4f_lt(v4f_abs(v4f_sub(v0, v1)), eps); + return v4f_le(v4f_abs(v4f_sub(v0, v1)), eps); } static FINLINE v4f_T diff --git a/src/test_soa4f2.c b/src/test_soa4f2.c @@ -0,0 +1,132 @@ +/* Copyright (C) 2014 Vincent Forest (vaplv@free.fr) + * + * The RSIMD library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * The RSIMD library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with the RSIMD library. If not, see <http://www.gnu.org/licenses/>. */ + +#include "soa4f2.h" + +#define V4TRUE ~0, ~0, ~0, ~0 +#define V4FALSE 0, 0, 0, 0 +#define CHECK_V4MASK__(Mask, A, B, C, D) \ + { \ + const v4f_T mask__ = (Mask); \ + CHECK(v4f_mask_x(mask__), (A)); \ + CHECK(v4f_mask_y(mask__), (B)); \ + CHECK(v4f_mask_z(mask__), (C)); \ + CHECK(v4f_mask_w(mask__), (D)); \ + } (void)0 +#define CHECK_V4MASK(Mask, Vec) CHECK_V4MASK__(Mask, Vec) + +#define CHECK_SOA4F2(V, A, B, C, D, E, F, G, H) \ + { \ + const v4f_T* v__ = (V); \ + CHECK_V4MASK(v4f_eq(v__[0], v4f_set((A), (B), (C), (D))), V4TRUE); \ + CHECK_V4MASK(v4f_eq(v__[1], v4f_set((E), (F), (G), (H))), V4TRUE); \ + } (void)0 + +int +main(int argc, char** argv) +{ + v4f_T a[2], b[2], c[2], dst[2], f; + (void)argc, (void)argv; + + CHECK(soa4f2_set(a, soa4f2_splat(c, v4f_set1(-1.f))), a); + CHECK_V4MASK(v4f_eq(a[0], v4f_set1(-1.f)), V4TRUE); + CHECK_V4MASK(v4f_eq(a[1], v4f_set1(-1.f)), V4TRUE); + + CHECK(soa4f2(c, v4f_set(0.f, 1.f, 2.f, 3.f), v4f_set(5.f, 6.f, 7.f, 8.f)), c); + CHECK(soa4f2_set(a, c), a); + CHECK_V4MASK(v4f_eq(c[0], v4f_set(0.f, 1.f, 2.f, 3.f)), V4TRUE); + CHECK_V4MASK(v4f_eq(c[1], v4f_set(5.f, 6.f, 7.f, 8.f)), V4TRUE); + CHECK_V4MASK(v4f_eq(a[0], v4f_set(0.f, 1.f, 2.f, 3.f)), V4TRUE); + CHECK_V4MASK(v4f_eq(a[1], v4f_set(5.f, 6.f, 7.f, 8.f)), V4TRUE); + + CHECK(soa4f2(a, v4f_set(-1.f, 2.f, 3.f,-4.f),v4f_set(5.f,-6.f,-7.f, 8.f)), a); + CHECK(soa4f2_minus(b, a), b); + CHECK_SOA4F2(b, 1.f,-2.f,-3.f, 4.f, -5.f, 6.f, 7.f,-8.f); + + CHECK(soa4f2_addf(dst, a, v4f_set1(1.f)), dst); + CHECK_SOA4F2(dst, 0.f, 3.f, 4.f, -3.f, 6.f, -5.f, -6.f, 9.f); + CHECK(soa4f2_addf(dst, a, v4f_set(1.f, 2.f, 0.f, 3.f)), dst); + CHECK_SOA4F2(dst, 0.f, 4.f, 3.f, -1.f, 6.f, -4.f, -7.f, 11.f); + CHECK(soa4f2_add(dst, a, b), dst); + CHECK_SOA4F2(dst, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f); + + CHECK(soa4f2_subf(dst, a, v4f_set1(1.f)), dst); + CHECK_SOA4F2(dst, -2.f, 1.f, 2.f, -5.f, 4.f, -7.f, -8.f, 7.f); + CHECK(soa4f2_subf(dst, a, v4f_set(1.f, 2.f, 0.f, 3.f)), dst); + CHECK_SOA4F2(dst, -2.f, 0.f, 3.f, -7.f, 4.f, -8.f, -7.f, 5.f); + CHECK(soa4f2_sub(dst, a, b), dst); + CHECK_SOA4F2(dst, -2.f, 4.f, 6.f, -8.f, 10.f, -12.f, -14.f, 16.f); + + CHECK(soa4f2_mulf(dst, a, v4f_set1(2.f)), dst); + CHECK_SOA4F2(dst, -2.f, 4.f, 6.f, -8.f, 10.f, -12.f, -14.f, 16.f); + CHECK(soa4f2_mulf(dst, a, v4f_set(2.f, 3.f, 0.f, -1.f)), dst); + CHECK_SOA4F2(dst, -2.f, 6.f, 0.f, 4.f, 10.f, -18.f, 0.f, -8.f); + CHECK(soa4f2_mul(dst, a, b), dst); + CHECK_SOA4F2(dst, -1.f, -4.f, -9.f, -16.f, -25.f, -36.f, -49.f, -64.f); + + CHECK(soa4f2_divf(dst, a, v4f_set1(2.f)), dst); + CHECK_SOA4F2(dst, -0.5f, 1.f, 1.5f, -2.f, 2.5f, -3.f, -3.5f, 4.f); + CHECK(soa4f2_divf(dst, a, v4f_set(2.f, 0.5f, 1.f, 4.f)), dst); + CHECK_SOA4F2(dst, -0.5f, 4.f, 3.f, -1.f, 2.5f, -12.f, -7.f, 2.f); + CHECK(soa4f2_div(dst, a, b), dst); + CHECK_SOA4F2(dst, -1.f, -1.f, -1.f, -1.f, -1.f, -1.f, -1.f, -1.f); + + soa4f2(a, v4f_set1(0.f), v4f_set1(1.f)); + soa4f2(b, v4f_set1(1.f), v4f_set1(2.f)); + CHECK(soa4f2_lerp(dst, a, b, v4f_set1(0.5f)), dst); + CHECK_SOA4F2(dst, 0.5f, 0.5f, 0.5f, 0.5f, 1.5f, 1.5f, 1.5f, 1.5f); + soa4f2(a, v4f_set(-1.f, 2.f, 3.f,-4.f), v4f_set(5.f,-6.f,-7.f, 8.f)); + soa4f2_minus(b, a); + CHECK(soa4f2_lerp(dst, a, b, v4f_set(-0.5f, 1.f, 0.5f, 4.f)), dst); + CHECK_SOA4F2(dst, -1.f, -2.f, 0.f, 4.f, 5.f, 6.f, 0.f, -8.f); + + f = soa4f2_sum(b); + CHECK_V4MASK(v4f_eq(f, v4f_set(-4.f, 4.f, 4.f, -4.f)), V4TRUE); + f = soa4f2_dot(a, b); + CHECK_V4MASK(v4f_eq(f, v4f_set(-26.f, -40.f, -58.f, -80.f)), V4TRUE); + f = soa4f2_len(a); + CHECK_V4MASK + (v4f_eq_eps(f, v4f_sqrt(soa4f2_dot(a, a)), v4f_set1(1.e-6f)), V4TRUE); + + CHECK_V4MASK(soa4f2_is_normalized(b), V4FALSE); + f = soa4f2_normalize(dst, b); + CHECK_V4MASK(v4f_eq_eps(f, soa4f2_len(b), v4f_set1(1.e-6f)), V4TRUE); + CHECK_V4MASK(soa4f2_is_normalized(b), V4FALSE); + CHECK_V4MASK(soa4f2_is_normalized(dst), V4TRUE); + soa4f2_divf(b, b, f); + CHECK_V4MASK(v4f_eq_eps(dst[0], b[0], v4f_set1(1.e-6f)), V4TRUE); + CHECK_V4MASK(v4f_eq_eps(dst[1], b[1], v4f_set1(1.e-6f)), V4TRUE); + + CHECK_V4MASK(soa4f2_eq(a, a), V4TRUE); + CHECK_V4MASK(soa4f2_eq(a, b), V4FALSE); + soa4f2(a, v4f_set(-1.f, 2.f, 3.f,-4.f), v4f_set(5.f,-6.f,-7.f, 8.f)); + soa4f2(b, v4f_set(-1.f,-2.f, 5.f,-4.001f), v4f_set(5.f,-6.f, 7.f, 8.001f)); + CHECK_V4MASK__(soa4f2_eq(a, b), ~0, 0, 0, 0); + CHECK_V4MASK__(soa4f2_eq_eps(a, b, v4f_set1(1.e-6f)), ~0, 0, 0, 0); + CHECK_V4MASK__(soa4f2_eq_eps(a, b, v4f_set(0.f,0.f,0.f,1.e-6f)),~0, 0, 0, 0); + CHECK_V4MASK__(soa4f2_eq_eps(a, b, v4f_set(0.f,0.f,0.f,1.e-2f)),~0, 0, 0,~0); + + soa4f2(a, v4f_set(1.f, 2.f, 3.f,-1.f), v4f_set(-2.f, 0.f,-7.f, 0.f)); + soa4f2(b, v4f_set(3.f, 2.f, 1.f,-2.f), v4f_set(1.f,-6.f, 0.5f, 2.f)); + f = soa4f2_cross(a, b); + CHECK_V4MASK(v4f_eq(f, v4f_set(7.f, -12.f, 8.5f, -2.f)), V4TRUE); + + CHECK(soa4f2_min(dst, a, b), dst); + CHECK_SOA4F2(dst, 1.f, 2.f, 1.f, -2.f, -2.f, -6.f, -7.f, 0.f); + CHECK(soa4f2_max(dst, a, b), dst); + CHECK_SOA4F2(dst, 3.f, 2.f, 3.f, -1.f, 1.f, 0.f, 0.5f, 2.f); + + return 0; +}