Add simd::if_then_else

Elementwise ternary ?: operations on vectors aren't supported until clang 13. This change adds a "simd::if_then_else" method that we will have to use instead, which has a fallback implementation for compilers that don't support the vector ternary.

Diffs=
440512dca Add simd::if_then_else (#4403)
diff --git a/.rive_head b/.rive_head
index b72dae7..4f308ae 100644
--- a/.rive_head
+++ b/.rive_head
@@ -1 +1 @@
-ec9fb5bfcc6442c4e674ddac9a7bfad08ef5151a
+440512dcaee3a07b7643ce9c00d1ebd477556ef2
diff --git a/include/rive/math/simd.hpp b/include/rive/math/simd.hpp
index dfc38fb..3391c11 100644
--- a/include/rive/math/simd.hpp
+++ b/include/rive/math/simd.hpp
@@ -24,6 +24,11 @@
 static_assert(std::numeric_limits<float>::is_iec559,
               "Conformant IEEE 754 behavior for NaN and Inf is required.");
 
+// Recommended in https://clang.llvm.org/docs/LanguageExtensions.html#feature-checking-macros
+#ifndef __has_builtin
+#define __has_builtin(x) 0
+#endif
+
 namespace rive
 {
 namespace simd
@@ -75,6 +80,24 @@
 
 ////// Math //////
 
+// Elementwise ternary expression: "_if ? _then : _else" for each component.
+template <typename T, int N>
+SIMD_ALWAYS_INLINE gvec<T, N> if_then_else(gvec<int32_t, N> _if, gvec<T, N> _then, gvec<T, N> _else)
+{
+#if __clang_major__ >= 13
+    // The '?:' operator supports a vector condition beginning in clang 13.
+    return _if ? _then : _else;
+#else
+#pragma message("performance: vectorized '?:' operator not supported. Consider updating clang.")
+    gvec<T, N> ret{};
+    for (int i = 0; i < N; ++i)
+    {
+        ret[i] = _if[i] ? _then[i] : _else[i];
+    }
+    return ret;
+#endif
+}
+
 // Similar to std::min(), with a noteworthy difference:
 // If a[i] or b[i] is NaN and the other is not, returns whichever is _not_ NaN.
 template <typename T, int N> SIMD_ALWAYS_INLINE gvec<T, N> min(gvec<T, N> a, gvec<T, N> b)
@@ -84,7 +107,7 @@
 #else
 #pragma message("performance: __builtin_elementwise_min() not supported. Consider updating clang.")
     // Generate the same behavior for NaN as the SIMD builtins. (isnan() is a no-op for int types.)
-    return b < a || isnan(a) ? b : a;
+    return if_then_else(b < a || isnan(a), b, a);
 #endif
 }
 
@@ -97,7 +120,7 @@
 #else
 #pragma message("performance: __builtin_elementwise_max() not supported. Consider updating clang.")
     // Generate the same behavior for NaN as the SIMD builtins. (isnan() is a no-op for int types.)
-    return a < b || isnan(a) ? b : a;
+    return if_then_else(a < b || isnan(a), b, a);
 #endif
 }
 
@@ -121,7 +144,7 @@
     return __builtin_elementwise_abs(x);
 #else
 #pragma message("performance: __builtin_elementwise_abs() not supported. Consider updating clang.")
-    return x < 0 ? -x : x; // But the negation in the "true" side so we never negate NaN.
+    return if_then_else(x < 0, -x, x); // Do the negation on the "true" side so we never negate NaN.
 #endif
 }
 
diff --git a/test/simd_test.cpp b/test/simd_test.cpp
index 20ed98d..7517267 100644
--- a/test/simd_test.cpp
+++ b/test/simd_test.cpp
@@ -51,15 +51,15 @@
     CHECK(!simd::any(test.zw == test.zw)); // NaN
 }
 
-// Check that ?: works on vector and scalar conditions.
+// Check simd::if_then_else.
 TEST_CASE("ternary-operator", "[simd]")
 {
     // Vector condition.
-    float4 f4 = int4{1, 2, 3, 4} < int4{4, 3, 2, 1} ? float4(-1) : 1.f;
+    float4 f4 = simd::if_then_else(int4{1, 2, 3, 4} < int4{4, 3, 2, 1}, float4(-1), float4(1));
     CHECK(simd::all(f4 == float4{-1, -1, 1, 1}));
 
     // In vector, -1 is true, 0 is false.
-    uint2 u2 = int2{0, -1} ? uint2{1, 2} : uint2{3, 4};
+    uint2 u2 = simd::if_then_else(int2{0, -1}, uint2{1, 2}, uint2{3, 4});
     CHECK(simd::all(u2 == uint2{3, 2}));
 
     // Scalar condition.
@@ -211,7 +211,7 @@
 static float frand()
 {
     float kMaxBelow1 = math::bit_cast<float>(math::bit_cast<uint32_t>(1.f) - 1);
-    float f = static_cast<float>(rand()) / RAND_MAX;
+    float f = static_cast<float>(rand()) / static_cast<float>(RAND_MAX);
     return std::min(kMaxBelow1, f);
 }
 template <int N> vec<N> vrand()