diff --git a/UnitTest/UnitTest.vcxproj b/UnitTest/UnitTest.vcxproj
index 87b0d8302..c3ae9a3af 100644
--- a/UnitTest/UnitTest.vcxproj
+++ b/UnitTest/UnitTest.vcxproj
@@ -328,6 +328,10 @@
true
true
+
+ true
+ true
+
true
true
diff --git a/UnitTest/UnitTest.vcxproj.filters b/UnitTest/UnitTest.vcxproj.filters
index f838a0433..6bb95e389 100644
--- a/UnitTest/UnitTest.vcxproj.filters
+++ b/UnitTest/UnitTest.vcxproj.filters
@@ -258,6 +258,9 @@
hdf5.lite
+
+ sys
+
diff --git a/UnitTest/pch.h b/UnitTest/pch.h
index fec9e020b..38fc7e221 100644
--- a/UnitTest/pch.h
+++ b/UnitTest/pch.h
@@ -32,6 +32,7 @@
#include
#include
#include
+#include
#include "CppUnitTest.h"
diff --git a/UnitTest/sys.cpp b/UnitTest/sys.cpp
index 6561d2002..73c9e99f4 100644
--- a/UnitTest/sys.cpp
+++ b/UnitTest/sys.cpp
@@ -2,6 +2,7 @@
#include "CppUnitTest.h"
#include
+#include
#include
#include
@@ -58,4 +59,8 @@ TEST_CLASS(test_path){ public:
#include "sys/unittests/test_path.cpp"
};
+TEST_CLASS(test_simd){ public:
+#include "sys/unittests/test_simd.cpp"
+};
+
}
\ No newline at end of file
diff --git a/modules/c++/coda-oss.vcxproj b/modules/c++/coda-oss.vcxproj
index 14e428c45..af6ae54fd 100644
--- a/modules/c++/coda-oss.vcxproj
+++ b/modules/c++/coda-oss.vcxproj
@@ -25,6 +25,7 @@
+
@@ -517,6 +518,7 @@
+
@@ -572,7 +574,7 @@
true
_DEBUG;%(PreprocessorDefinitions);MT_DEFAULT_PINNING=0;RE_ENABLE_STD_REGEX=1;CODA_OSS_EXPORTS;CODA_OSS_LIBRARY_SHARED=1
pch.h
- cli\include;coda_oss\include;config\include;dbi\include;except\include;gsl\include;hdf5.lite\include;io\include;logging\include;math\include;math.linear\include;math.poly\include;mem\include;mt\include;net\include;net.ssl\include;plugin\include;polygon\include;re\include;sio.lite\include;std\include;str\include;sys\include;tiff\include;types\include;unique\include;units\include;xml.lite\include;zip\include;$(ProjectDir)include;$(SolutionDir)out\install\$(Platform)-$(Configuration)\include;$(SolutionDir)externals\$(ProjectName)\out\install\$(Platform)-$(Configuration)\include
+ cli\include;coda_oss\include;config\include;dbi\include;except\include;gsl\include;hdf5.lite\include;io\include;logging\include;math\include;math.linear\include;math.poly\include;mem\include;mt\include;net\include;net.ssl\include;plugin\include;polygon\include;re\include;sio.lite\include;std\include;str\include;sys\include;tiff\include;types\include;unique\include;units\include;xml.lite\include;vectorclass\include;zip\include;$(ProjectDir)include;$(SolutionDir)out\install\$(Platform)-$(Configuration)\include;$(SolutionDir)externals\$(ProjectName)\out\install\$(Platform)-$(Configuration)\include
Use
pch.h
true
@@ -605,7 +607,7 @@
true
NDEBUG;%(PreprocessorDefinitions);MT_DEFAULT_PINNING=0;RE_ENABLE_STD_REGEX=1;CODA_OSS_EXPORTS;CODA_OSS_LIBRARY_SHARED=1
pch.h
- cli\include;coda_oss\include;config\include;dbi\include;except\include;gsl\include;hdf5.lite\include;io\include;logging\include;math\include;math.linear\include;math.poly\include;mem\include;mt\include;net\include;net.ssl\include;plugin\include;polygon\include;re\include;sio.lite\include;std\include;str\include;sys\include;tiff\include;types\include;unique\include;units\include;xml.lite\include;zip\include;$(ProjectDir)include;$(SolutionDir)out\install\$(Platform)-$(Configuration)\include;$(SolutionDir)externals\$(ProjectName)\out\install\$(Platform)-$(Configuration)\include
+ cli\include;coda_oss\include;config\include;dbi\include;except\include;gsl\include;hdf5.lite\include;io\include;logging\include;math\include;math.linear\include;math.poly\include;mem\include;mt\include;net\include;net.ssl\include;plugin\include;polygon\include;re\include;sio.lite\include;std\include;str\include;sys\include;tiff\include;types\include;unique\include;units\include;xml.lite\include;vectorclass\include;zip\include;$(ProjectDir)include;$(SolutionDir)out\install\$(Platform)-$(Configuration)\include;$(SolutionDir)externals\$(ProjectName)\out\install\$(Platform)-$(Configuration)\include
Use
pch.h
true
diff --git a/modules/c++/coda-oss.vcxproj.filters b/modules/c++/coda-oss.vcxproj.filters
index 8046ff207..f28258cf3 100644
--- a/modules/c++/coda-oss.vcxproj.filters
+++ b/modules/c++/coda-oss.vcxproj.filters
@@ -960,6 +960,9 @@
tiff
+
+ coda_oss
+
@@ -1574,5 +1577,8 @@
std
+
+ std
+
\ No newline at end of file
diff --git a/modules/c++/coda_oss/CMakeLists.txt b/modules/c++/coda_oss/CMakeLists.txt
index 2c03e44a4..b096fbb6f 100644
--- a/modules/c++/coda_oss/CMakeLists.txt
+++ b/modules/c++/coda_oss/CMakeLists.txt
@@ -5,7 +5,7 @@ coda_generate_module_config_header(${MODULE_NAME})
coda_add_module(
${MODULE_NAME}
VERSION 1.0
- DEPS config-c++ gsl-c++)
+ DEPS config-c++ gsl-c++ vectorclass-c++)
coda_add_tests(
MODULE_NAME ${MODULE_NAME}
diff --git a/modules/c++/coda_oss/include/coda_oss/CPlusPlus.h b/modules/c++/coda_oss/include/coda_oss/CPlusPlus.h
index 887579a5c..dc3ad0620 100644
--- a/modules/c++/coda_oss/include/coda_oss/CPlusPlus.h
+++ b/modules/c++/coda_oss/include/coda_oss/CPlusPlus.h
@@ -40,6 +40,7 @@
#define CODA_OSS_cplusplus17 201703L
#define CODA_OSS_cplusplus20 202002L
#define CODA_OSS_cplusplus23 202302L
+#define CODA_OSS_cplusplus26 202699L // TODO: real version number when C++26 is official
#if CODA_OSS_cplusplus < CODA_OSS_cplusplus11
#undef CODA_OSS_cplusplus // oops...try to fix
@@ -78,6 +79,7 @@
#define CODA_OSS_cpp17 (CODA_OSS_cplusplus >= CODA_OSS_cplusplus17)
#define CODA_OSS_cpp20 (CODA_OSS_cplusplus >= CODA_OSS_cplusplus20)
#define CODA_OSS_cpp23 (CODA_OSS_cplusplus >= CODA_OSS_cplusplus23)
+#define CODA_OSS_cpp26 0 // TODO: (CODA_OSS_cplusplus >= CODA_OSS_cplusplus26)
#if !CODA_OSS_cpp17
#error "Must compile with C++17 or greater."
diff --git a/modules/c++/coda_oss/include/coda_oss/simd.h b/modules/c++/coda_oss/include/coda_oss/simd.h
new file mode 100644
index 000000000..e2cc4fafb
--- /dev/null
+++ b/modules/c++/coda_oss/include/coda_oss/simd.h
@@ -0,0 +1,122 @@
+/* =========================================================================
+ * This file is part of coda_oss-c++
+ * =========================================================================
+ *
+ * (C) Copyright 2004 - 2014, MDA Information Systems LLC
+ * © Copyright 2024, Maxar Technologies, Inc.
+ *
+ * coda_oss-c++ 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.
+ *
+ * This program 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 this program; If not,
+ * see .
+ *
+ */
+#pragma once
+
+#include "coda_oss/CPlusPlus.h"
+#include "config/disable_compiler_warnings.h"
+
+// This logic needs to be here rather than so that `coda_oss::simd` will
+// be the same as `std::simd`.
+#ifndef CODA_OSS_HAVE_std_simd_
+ #define CODA_OSS_HAVE_std_simd_ 0 // assume no
+#endif
+#ifndef CODA_OSS_HAVE_experimental_simd_
+ #define CODA_OSS_HAVE_experimental_simd_ 0 // assume no std::experimental::simd
+#endif
+#ifndef CODA_OSS_HAVE_vcl_simd_
+ #define CODA_OSS_HAVE_vcl_simd_ 0 // assume no vcl::simd
+#endif
+#if CODA_OSS_cpp17 // __has_include
+ #if __has_include() // not until C++26 (hopefully!)
+ #include
+ #undef CODA_OSS_HAVE_std_simd_
+ #define CODA_OSS_HAVE_std_simd_ 1 // provided by the implementation, probably C++26
+ #endif
+
+ /* not quite ready for experimental::simd
+ #if __has_include() // G++11
+ #include
+ #undef CODA_OSS_HAVE_experimental_simd_
+ #define CODA_OSS_HAVE_experimental_simd_ 1 // provided by
+ #endif
+ */
+
+ #if __has_include("vectorclass/simd/simd") // our own implementation using VCL
+ #ifdef VCL_NAMESPACE
+ #error "VCL_NAMESPACE already #define'd"
+ #endif
+
+ #define VCL_NAMESPACE vcl
+
+ // The vectorclass headers #pragma-away some warnings; be sure those don't persist
+ CODA_OSS_disable_warning_push
+ #if _MSC_VER
+ #pragma warning(disable: 4100) // '...': unreferenced formal parameter
+ #pragma warning(disable: 4127) // conditional expression is constant
+ #pragma warning(disable: 4244) // '...': conversion from '...' to '...', possible loss of data
+ #pragma warning(disable: 4723) // potential divide by 0
+ #pragma warning(disable: 6001) // Using uninitialized memory '...'.
+ #pragma warning(disable: 26485) // Expression '...': No array to pointer decay (bounds.3).
+ #pragma warning(disable: 26440) // Function '...' can be declared 'noexcept' (f.6).
+ #pragma warning(disable: 26493) // Don't use C-style casts (type.4).
+ #pragma warning(disable: 26814) // The const variable '...' can be computed at compile-time. Consider using constexpr (con.5).
+ #pragma warning(disable: 26434) // Function '... hides a non-virtual function '...'.
+ #pragma warning(disable: 26429) // Symbol '...' is never tested for nullness, it can be marked as not_null (f.23).
+ #pragma warning(disable: 26482) // Only index into arrays using constant expressions (bounds.2).
+ #pragma warning(disable: 26481) // Don't use pointer arithmetic. Use span instead (bounds.1).
+ #pragma warning(disable: 26446) // Prefer to use gsl::at() instead of unchecked subscript operator (bounds.4).
+ #pragma warning(disable: 26823) // Dereferencing a possibly null pointer '...' (lifetime.1).
+ #pragma warning(disable: 26496) // The variable '...' does not change after construction, mark it as const (con.4).
+ #pragma warning(disable: 26472) // Don't use a static_cast for arithmetic conversions. Use brace initialization, gsl::narrow_cast or gsl::narrow (type.1).
+ #pragma warning(disable: 26494) // Variable '...' is uninitialized. Always initialize an object (type.5).
+ #pragma warning(disable: 26458) // Prefer to use gsl::at() instead of unchecked subscript operator (bounds.4).
+ #pragma warning(disable: 26497) // You can attempt to make '...' constexpr unless it contains any undefined behavior (f.4).
+ #pragma warning(disable: 26818) // Switch statement does not cover all cases. Consider adding a '...' label (es.79).
+ #pragma warning(disable: 26475) // Do not use function style casts (es.49). Prefer '...' over '...'.
+ #pragma warning(disable: 26477) // Use '...' rather than 0 or NULL (es.47).
+
+ #elif defined(__GNUC__) || defined(__clang__)
+
+ CODA_OSS_disable_warning(-Wzero-as-null-pointer-constant)
+ CODA_OSS_disable_warning(-Wshadow)
+
+ #endif
+
+ #include "vectorclass/version2/vectorclass.h"
+ #include "vectorclass/version2/vectormath_trig.h"
+ CODA_OSS_disable_warning_pop
+
+ CODA_OSS_disable_warning_push
+ #if _MSC_VER
+ #pragma warning(disable: 26472) // Don't use a static_cast for arithmetic conversions. Use brace initialization, gsl::narrow_cast or gsl::narrow (type.1).
+ #endif
+ #include "vectorclass/simd/simd"
+ CODA_OSS_disable_warning_pop
+
+ #undef CODA_OSS_HAVE_vcl_simd_
+ #define CODA_OSS_HAVE_vcl_simd_ 1 // provided by vectorclass/simd/simd.h
+ #endif
+
+#endif // CODA_OSS_cpp17
+
+namespace coda_oss
+{
+ #if CODA_OSS_HAVE_std_simd_
+ namespace simd = std::simd;
+ #elif CODA_OSS_HAVE_experimental_simd_
+ namespace simd = std::experimental;
+ #elif CODA_OSS_HAVE_vcl_simd_
+ namespace simd = vcl::simd;
+ #endif
+}
+
diff --git a/modules/c++/coda_oss/wscript b/modules/c++/coda_oss/wscript
index e907e3364..203ade82b 100644
--- a/modules/c++/coda_oss/wscript
+++ b/modules/c++/coda_oss/wscript
@@ -1,6 +1,6 @@
NAME = 'coda_oss'
VERSION = '1.0'
-MODULE_DEPS = 'config gsl'
+MODULE_DEPS = 'config gsl vectorclass'
USELIB = 'THREAD DL RT SOCKET'
UNITTEST_DEPS = ''
diff --git a/modules/c++/std/include/import/std.h b/modules/c++/std/include/import/std.h
index 5c41ee61b..4332e7b9b 100644
--- a/modules/c++/std/include/import/std.h
+++ b/modules/c++/std/include/import/std.h
@@ -103,6 +103,7 @@ CODA_OSS_disable_warning_system_header_push
#include
#include
#include
+#include
CODA_OSS_disable_warning_pop
diff --git a/modules/c++/std/include/std/simd b/modules/c++/std/include/std/simd
new file mode 100644
index 000000000..706e1b8fc
--- /dev/null
+++ b/modules/c++/std/include/std/simd
@@ -0,0 +1,47 @@
+/* =========================================================================
+ * This file is part of std-c++
+ * =========================================================================
+ *
+ * © Copyright 2023, Maxar Technologies, Inc.
+ *
+ * std-c++ 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.
+ *
+ * This program 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 this program; If not, http://www.gnu.org/licenses/.
+ *
+ */
+#pragma once
+
+#include "coda_oss/simd.h"
+#include "coda_oss/CPlusPlus.h"
+
+// Calling this **stdx** (instead of *std*) for now as it hasn't yet been voted into C++26
+
+// Make it (too?) easy for clients to get our various std:: implementations
+#ifndef CODA_OSS_NO_stdx_simd
+ #if CODA_OSS_cpp26
+ #include
+ #define CODA_OSS_NO_stdx_simd 1 // part of C++26 (hopefully)
+ #else
+ #define CODA_OSS_NO_stdx_simd 0 // use our own
+ #endif
+#endif
+
+#if !CODA_OSS_NO_stdx_simd
+namespace stdx // TODO: This is slightly uncouth: we're not supposed to augment "std".
+{
+ namespace simd = coda_oss::simd;
+}
+//#ifndef __cpp_lib_simd
+//#define __cpp_lib_simd 202402L // https://en.cppreference.com/w/cpp/feature_test#cpp_lib_simd
+//#endif
+
+#endif // CODA_OSS_NO_stdx_simd
diff --git a/modules/c++/sys/unittests/test_os.cpp b/modules/c++/sys/unittests/test_os.cpp
index ba1d71b9a..f4bdb5625 100644
--- a/modules/c++/sys/unittests/test_os.cpp
+++ b/modules/c++/sys/unittests/test_os.cpp
@@ -511,17 +511,6 @@ TEST_CASE(test_make_ifstream)
TEST_ASSERT_TRUE(ifs.is_open());
}
-TEST_CASE(test_SIMD_Instructions)
-{
- const sys::OS os;
- const auto simdInstructionSet = os.getSIMDInstructionSet();
-
- const auto isSSE2 = simdInstructionSet == sys::SIMDInstructionSet::SSE2;
- const auto isAVX2 = simdInstructionSet == sys::SIMDInstructionSet::AVX2;
- const auto isAVX512F = simdInstructionSet == sys::SIMDInstructionSet::AVX512F;
- TEST_ASSERT(isSSE2 || isAVX2 || isAVX512F);
-}
-
TEST_MAIN(
//sys::AbstractOS::setArgvPathname(argv[0]);
TEST_CHECK(testRecursiveRemove);
@@ -538,5 +527,4 @@ TEST_MAIN(
TEST_CHECK(test_sys_fopen_failure);
TEST_CHECK(test_sys_open);
TEST_CHECK(test_make_ifstream);
- TEST_CHECK(test_SIMD_Instructions);
)
diff --git a/modules/c++/sys/unittests/test_simd.cpp b/modules/c++/sys/unittests/test_simd.cpp
new file mode 100644
index 000000000..14baa3f3a
--- /dev/null
+++ b/modules/c++/sys/unittests/test_simd.cpp
@@ -0,0 +1,675 @@
+/* =========================================================================
+ * This file is part of sys-c++
+ * =========================================================================
+ *
+ * (C) Copyright 2004 - 2016, MDA Information Systems LLC
+ * © Copyright 2024, Maxar Technologies, Inc.
+ *
+ * sys-c++ 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.
+ *
+ * This program 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 this program; If not,
+ * see .
+ *
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+using zfloat = std::complex;
+
+#include
+#include
+#include "TestCase.h"
+
+TEST_CASE(test_SIMD_Instructions)
+{
+ const sys::OS os;
+ const auto simdInstructionSet = os.getSIMDInstructionSet();
+
+ const auto isSSE2 = simdInstructionSet == sys::SIMDInstructionSet::SSE2;
+ const auto isAVX2 = simdInstructionSet == sys::SIMDInstructionSet::AVX2;
+ const auto isAVX512F = simdInstructionSet == sys::SIMDInstructionSet::AVX512F;
+ TEST_ASSERT(isSSE2 || isAVX2 || isAVX512F);
+}
+
+using intv = stdx::simd::simd;
+using floatv = stdx::simd::rebind_simd_t;
+
+template
+inline int ssize(const vcl::simd::basic_simd& v) noexcept
+{
+ return v.size();
+}
+
+// Manage a SIMD complex as an array of two SIMDs
+using zfloatv = std::array;
+static inline auto& real(zfloatv& z) noexcept
+{
+ return z[0];
+}
+static inline const auto& real(const zfloatv& z) noexcept
+{
+ return z[0];
+}
+static inline auto& imag(zfloatv& z) noexcept
+{
+ return z[1];
+}
+static inline const auto& imag(const zfloatv& z) noexcept
+{
+ return z[1];
+}
+static inline size_t size(const zfloatv& z) noexcept
+{
+ auto retval = real(z).size();
+ assert(retval == imag(z).size());
+ return retval;
+}
+static inline auto ssize(const zfloatv& z) noexcept
+{
+ return static_cast(size(z));
+}
+
+/***********************************************************************************************/
+
+// https://en.cppreference.com/w/cpp/numeric/complex/arg
+static inline auto arg(const zfloatv& z)
+{
+ // > `std::atan2(std::imag(z), std::real(z))`
+ return atan2(imag(z), real(z)); // arg()
+}
+
+template
+static inline auto generate(TGeneratorReal&& generate_real, TGeneratorImag&& generate_imag)
+{
+ zfloatv retval;
+
+ using Vec = floatv::native_type;
+ auto rv = static_cast(real(retval));
+ auto iv = static_cast(imag(retval));
+
+ for (size_t i = 0; i < size(retval); i++)
+ {
+ const auto i_ = gsl::narrow(i);
+ rv.insert(i_, generate_real(i));
+ iv.insert(i_, generate_imag(i));
+ }
+
+ real(retval) = rv;
+ imag(retval) = iv;
+
+ return retval;
+}
+template
+static inline auto copy_from(zfloatv& this_, It first)
+{
+ // "`It` satisfies contiguous_iterator."
+ const auto mem = &(*first);
+
+ const auto generate_real = [&](size_t i) { return mem[i].real(); };
+ const auto generate_imag = [&](size_t i) { return mem[i].imag(); };
+ this_ = generate(generate_real, generate_imag);
+}
+
+template
+static inline auto lookup(intv const indexv, std::span floats)
+{
+ assert(N == floats.size());
+ return floatv{ vcl::lookup(static_cast(indexv), floats.data()) };
+}
+
+/***********************************************************************************************/
+
+template
+static auto simd_select_(const TMask& m, const TSimd& t, const TSimd& f)
+{
+ auto retval = simd_select(m, t, f);
+
+ for (int i = 0; i < retval.size(); i++)
+ {
+ const auto result = m[i] ? t[i] : f[i];
+
+ const auto& retval_ = retval;
+ const auto r = retval_[i];
+ static const std::string testName("simd_select_");
+ TEST_ASSERT_EQ(r, result);
+ }
+
+ return retval;
+}
+
+/***********************************************************************************************/
+
+// Sample code from SIX
+static constexpr size_t AmplitudeTableSize = 256;
+
+static auto GetPhase(std::complex v)
+{
+ // There's an intentional conversion to zero when we cast 256 -> uint8. That wrap around
+ // handles cases that are close to 2PI.
+ double phase = std::arg(v);
+ if (phase < 0.0) phase += std::numbers::pi * 2.0; // Wrap from [0, 2PI]
+ return phase;
+}
+static auto getPhase(zfloat v, float phase_delta)
+{
+ // Phase is determined via arithmetic because it's equally spaced.
+ const auto phase = GetPhase(v);
+ return static_cast(std::round(phase / phase_delta));
+}
+
+static auto getPhase(const zfloatv& v, float phase_delta)
+{
+ // Phase is determined via arithmetic because it's equally spaced.
+ // There's an intentional conversion to zero when we cast 256 -> uint8. That wrap around
+ // handles cases that are close to 2PI.
+ constexpr auto two_pi = std::numbers::pi_v * 2.0f;
+
+ auto phase = arg(v);
+ phase = simd_select_(phase < 0.0f, phase + two_pi, phase);
+ //phase += simd_select(phase < 0.0f, std::numbers::pi_v *2.0f, 0.0f);
+ return lround(phase / phase_delta);
+}
+
+static const auto& cxValues()
+{
+ //static const std::vector retval{/*{0, 0},*/ {1, 0}, {1, 1}, {0, 1}, {-1, 1}, {-1, 0}, {-1, -1}, {0, -1}, {1, -1}};
+ static const std::vector retval{{0.0, 0.0}, {1.0, 1.0}, {10.0, -10.0}, {-100.0, 100.0}, {-1000.0, -1000.0}, // sample data from SIX
+ {-1.0, -1.0}, {1.0, -1.0}, {-1.0, 1.0} // "pad" to multiple of floatv::size()
+ };
+ return retval;
+}
+
+static auto expected_getPhase_values(const std::vector& values, float phase_delta)
+{
+ static const std::string testName("expected_getPhase_values");
+ TEST_ASSERT(values.size() % floatv::size() == 0);
+ std::vector expected;
+ for (auto&& v : values)
+ {
+ expected.push_back(getPhase(v, phase_delta));
+ }
+ return expected;
+}
+
+static auto toComplex_(double A, uint8_t phase)
+{
+ // The phase values should be read in (values 0 to 255) and converted to float by doing:
+ // P = (1 / 256) * input_value
+ const double P = (1.0 / 256.0) * phase;
+
+ // To convert the amplitude and phase values to complex float (i.e. real and imaginary):
+ // S = A * cos(2 * pi * P) + j * A * sin(2 * pi * P)
+ const double angle = 2 * std::numbers::pi * P;
+ const auto sin_angle = sin(angle);
+ const auto cos_angle = cos(angle);
+ zfloat S(static_cast(A * cos_angle), static_cast(A * sin_angle));
+ return S;
+}
+inline static auto toComplex(uint8_t amplitude, uint8_t phase)
+{
+ // A = input_amplitude(i.e. 0 to 255)
+ const double A = amplitude;
+ return toComplex_(A, phase);
+}
+
+static auto phase_delta()
+{
+ static const auto p0 = GetPhase(toComplex(1, 0));
+ static const auto p1 = GetPhase(toComplex(1, 1));
+ assert(p0 == 0.0);
+ assert(p1 > p0);
+ static const auto retval = static_cast(p1 - p0);
+ return retval;
+}
+
+static auto load(const std::vector& values)
+{
+ std::vector retval;
+ constexpr auto sz = floatv::size();
+ std::span pValues(values);
+ std::span p(pValues.data(), sz);
+ for (size_t i = 0; i < values.size() / sz; i++)
+ {
+ zfloatv z;
+ copy_from(z, p.begin());
+ retval.push_back(z);
+ p = std::span(p.data() + sz, p.size());
+ }
+ return retval;
+}
+
+static auto copy_to(intv v)
+{
+ std::vector retval(v.size());
+ v.copy_to(retval.begin());
+ return retval;
+}
+
+static auto getPhase_(const zfloatv& v, float phase_delta)
+{
+ auto retval = getPhase(v, phase_delta);
+
+ for (int i = 0; i < retval.size(); i++)
+ {
+ const auto& r = real(v);
+ const auto& j = imag(v);
+
+ const std::complex v_(r[i], j[i]);
+ const auto result = getPhase(v_, phase_delta);
+
+ const auto& retval_ = retval;
+ static const std::string testName("getPhase_");
+ TEST_ASSERT_EQ(retval_[i], result);
+ }
+
+ return retval;
+}
+
+TEST_CASE(testGetPhase)
+{
+ const auto& expected = expected_getPhase_values(cxValues(), phase_delta());
+
+ const auto valuesv = load(cxValues());
+ std::vector actual;
+ for (auto&& zvaluev : valuesv)
+ {
+ const auto phasev = getPhase_(zvaluev, phase_delta());
+ for (auto&& phase : copy_to(phasev))
+ {
+ actual.push_back(phase);
+ }
+ }
+
+ TEST_ASSERT_EQ(actual.size(), expected.size());
+ for (size_t i = 0; i < actual.size(); i++)
+ {
+ TEST_ASSERT_EQ(actual[i], expected[i]);
+ }
+}
+
+// Again, more sample code from SIX
+struct PhaseDirections final
+{
+ std::array value; // interleaved, std::complex
+ std::array real;
+ std::array imag;
+};
+static auto getPhaseDirections_()
+{
+ //! Unit vector rays that represent each direction that phase can point.
+ PhaseDirections phase_directions;
+
+ const auto p0 = GetPhase(toComplex(1, 0));
+ for (size_t i = 0; i < AmplitudeTableSize; i++)
+ {
+ const float angle = static_cast(p0) + i * phase_delta();
+ const auto y = sin(angle);
+ const auto x = cos(angle);
+ phase_directions.value[i] = { x, y };
+
+ phase_directions.real[i] = phase_directions.value[i].real();
+ phase_directions.imag[i] = phase_directions.value[i].imag();
+ }
+ return phase_directions;
+}
+static inline const auto& getPhaseDirections()
+{
+ static const auto retval = getPhaseDirections_();
+ return retval;
+}
+
+template
+static inline auto lookup_(intv const indexv, std::span floats)
+{
+ auto retval = lookup(indexv, floats);
+
+ for (int i = 0; i < retval.size(); i++)
+ {
+ const auto i_ = static_cast(indexv[i]);
+ const auto result = floats[i_];
+
+ static const std::string testName("lookup_");
+ const auto& retval_ = retval;
+ TEST_ASSERT_EQ(retval_[i], result);
+ }
+
+ return retval;
+}
+
+TEST_CASE(testLookup)
+{
+ const auto& phase_directions = getPhaseDirections();
+
+ static const auto& expected_getPhase = expected_getPhase_values(cxValues(), phase_delta());
+ std::vector expected;
+ for (auto&& phase : expected_getPhase)
+ {
+ expected.push_back(phase_directions.value[phase]);
+ }
+
+ const auto valuesv = load(cxValues());
+ std::vector actual;
+ for (auto&& zvaluev : valuesv)
+ {
+ const auto phase = getPhase_(zvaluev, phase_delta());
+ const auto phase_direction_real = lookup_(phase, phase_directions.real);
+ const auto phase_direction_imag = lookup_(phase, phase_directions.imag);
+
+ assert(phase_direction_real.size() == phase_direction_imag.size());
+ for (int i = 0; i < phase_direction_real.size(); i++)
+ {
+ actual.emplace_back(phase_direction_real[i], phase_direction_imag[i]);
+ }
+ }
+
+ TEST_ASSERT_EQ(actual.size(), expected.size());
+ for (size_t i = 0; i < actual.size(); i++)
+ {
+ TEST_ASSERT_EQ(actual[i], expected[i]);
+ }
+}
+
+
+// And ... more sample code from SIX
+static auto iota_0_256_()
+{
+ static_assert(sizeof(size_t) > sizeof(uint8_t), "size_t can't hold UINT8_MAX!");
+
+ std::vector retval;
+ retval.reserve(UINT8_MAX + 1);
+ for (size_t i = 0; i <= UINT8_MAX; i++) // Be careful with indexing so that we don't wrap-around in the loop.
+ {
+ retval.push_back(static_cast(i));
+ }
+ assert(retval.size() == UINT8_MAX + 1);
+ return retval;
+}
+static inline std::vector iota_0_256()
+{
+ static const auto retval = iota_0_256_();
+ return retval;
+}
+
+static auto make_magnitudes_()
+{
+ std::vector result;
+ result.reserve(AmplitudeTableSize);
+ for (const auto amplitude : iota_0_256())
+ {
+ // AmpPhase -> Complex
+ const auto phase = amplitude;
+ const auto complex = toComplex(amplitude, phase);
+ result.push_back(std::abs(complex));
+ }
+
+ // I don't know if we can guarantee that the amplitude table is non-decreasing.
+ // Check to verify property at runtime.
+ if (!std::is_sorted(result.begin(), result.end()))
+ {
+ throw std::runtime_error("magnitudes must be sorted");
+ }
+
+ std::array retval;
+ std::copy(result.begin(), result.end(), retval.begin());
+ return retval;
+}
+static inline std::span magnitudes()
+{
+ //! The sorted set of possible magnitudes order from small to large.
+ static const auto cached_magnitudes = make_magnitudes_();
+ return std::span(cached_magnitudes);
+}
+
+/*!
+ * Find the nearest element given an iterator range.
+ * @param value query value
+ * @return index of nearest value within the iterator range.
+ */
+static uint8_t nearest(std::span magnitudes, float value)
+{
+ assert(magnitudes.size() == AmplitudeTableSize);
+
+ const auto begin = magnitudes.begin();
+ const auto end = magnitudes.end();
+
+ const auto it = std::lower_bound(begin, end, value);
+ if (it == begin) return 0;
+
+ const auto prev_it = std::prev(it);
+ const auto nearest_it = it == end ? prev_it :
+ (value - *prev_it <= *it - value ? prev_it : it);
+ const auto distance = std::distance(begin, nearest_it);
+ assert(distance <= std::numeric_limits::max());
+ return static_cast(distance);
+}
+static uint8_t find_nearest(zfloat phase_direction, zfloat v)
+{
+ // We have to do a 1D nearest neighbor search for magnitude.
+ // But it's not the magnitude of the input complex value - it's the projection of
+ // the complex value onto the ray of candidate magnitudes at the selected phase.
+ // i.e. dot product.
+ const auto projection = (phase_direction.real() * v.real()) + (phase_direction.imag() * v.imag());
+ //assert(std::abs(projection - std::abs(v)) < 1e-5); // TODO ???
+ return nearest(magnitudes(), projection);
+}
+
+static auto select_(bool c, ptrdiff_t a, ptrdiff_t b)
+{
+ return c ? a : b;
+}
+
+// https://en.cppreference.com/w/cpp/algorithm/lower_bound
+static auto my_lower_bound(std::span magnitudes, const float& value)
+{
+ assert(magnitudes.size() == AmplitudeTableSize);
+ auto first = std::distance(magnitudes.begin(), magnitudes.begin());
+ const auto last = std::distance(magnitudes.begin(), magnitudes.end());
+
+ auto count = last - first;
+ while (count > 0)
+ {
+ auto it = first;
+ const auto step = count / 2;
+ it += step; // std::advance(it, step);
+
+ //if (magnitudes[it] < value)
+ //{
+ // first = ++it;
+ // count -= step + 1;
+ //}
+ //else
+ // count = step;
+
+ auto count_ = count; count_ -= step + 1; // count -= step + 1;
+
+ const auto c = magnitudes[it];
+ const auto test = c < value;
+ first = select_(test, ++it, first); // first = ++it;
+ count = select_(test, count_, step); // count -= step + 1 <...OR...> count = step
+ }
+
+ return first;
+}
+
+static auto lower_boundv(std::span magnitudes, const floatv& v)
+{
+ assert(magnitudes.size() == AmplitudeTableSize);
+ intv first = gsl::narrow(std::distance(magnitudes.begin(), magnitudes.begin()));
+ const intv last = gsl::narrow(std::distance(magnitudes.begin(), magnitudes.end()));
+
+ auto count = last - first;
+ while (any_of(count > 0))
+ {
+ //auto it = first;
+ const auto step = count / 2;
+ auto it = simd_select(count > 0, first + step, first); // std::advance(it, step);
+
+ //if (magnitudes[it] < value)
+ //{
+ // first = ++it;
+ // count -= step + 1;
+ //}
+ //else
+ // count = step;
+ const auto c = lookup_(it, magnitudes); // magnituides[it]
+
+ const auto test = (count > 0) && (c < v);
+ first = simd_select_(test, ++it, first); // first = ++it;
+ auto count_ = count; count_ -= step + 1; // ... -= step + 1;
+ count = simd_select_(test, count_, step); // count -= step + 1 <...OR...> count = step
+ }
+ return first;
+}
+
+static auto lower_bound_(std::span magnitudes, const floatv& value)
+{
+ auto retval = lower_boundv(magnitudes, value);
+
+ for (int i = 0; i < retval.size(); i++)
+ {
+ const auto v = value[i];
+ const auto result = my_lower_bound(magnitudes, v);
+
+ const auto& retval_ = retval;
+ const auto r = retval_[i];
+ static const std::string testName("lower_bound_");
+ TEST_ASSERT_EQ(r, result);
+ }
+
+ return retval;
+}
+
+static auto nearest(std::span magnitudes, const floatv& value)
+{
+ assert(magnitudes.size() == AmplitudeTableSize);
+
+ /*
+ const auto it = std::lower_bound(begin, end, value);
+ if (it == begin) return 0;
+
+ const auto prev_it = std::prev(it);
+ const auto nearest_it = it == end ? prev_it :
+ (value - *prev_it <= *it - value ? prev_it : it);
+ const auto distance = std::distance(begin, nearest_it);
+ assert(distance <= std::numeric_limits::max());
+ return gsl::narrow(distance);
+ */
+ const auto it = lower_bound_(magnitudes, value);
+
+ static const intv begin = 0;
+ static const auto& zero = begin;
+ if (all_of(it == begin))
+ {
+ return zero;
+ }
+
+ const auto prev_it = simd_select(it == begin, zero, it - 1); // const auto prev_it = std::prev(it);
+
+ const intv end = static_cast(magnitudes.size());
+ if (all_of(it == end))
+ {
+ return prev_it;
+ }
+
+ const auto v0 = value - lookup_(prev_it, magnitudes); // value - *prev_it
+ const auto v1 = lookup_(it, magnitudes) - value; // *it - value
+ //const auto nearest_it = select(v0 <= v1, prev_it, it); // (value - *prev_it <= *it - value ? prev_it : it);
+
+ //const auto end_test = select(it == end, prev_it, nearest_it); // it == end ? prev_it : ...
+ auto retval = simd_select(it == begin, zero, // if (it == begin) return 0;
+ simd_select(it == end, prev_it, // it == end ? prev_it : ...
+ simd_select(v0 <= v1, prev_it, it) // (value - *prev_it <= *it - value ? prev_it : it);
+ ));
+ return retval;
+}
+
+static auto nearest_(std::span magnitudes, const floatv& value)
+{
+ auto retval = nearest(magnitudes, value);
+
+ for (int i = 0; i < retval.size(); i++)
+ {
+ const auto v = value[i];
+ const auto result = nearest(magnitudes, v);
+
+ const auto& retval_ = retval;
+ const auto r = retval_[i];
+ static const std::string testName("nearest_");
+ TEST_ASSERT_EQ(r, result);
+ }
+
+ return retval;
+}
+
+static auto find_nearest(std::span magnitudes,
+ const floatv& phase_direction_real, const floatv& phase_direction_imag,
+ const zfloatv& v)
+{
+ // We have to do a 1D nearest neighbor search for magnitude.
+ // But it's not the magnitude of the input complex value - it's the projection of
+ // the complex value onto the ray of candidate magnitudes at the selected phase.
+ // i.e. dot product.
+ const auto projection = (phase_direction_real * real(v)) + (phase_direction_imag * imag(v));
+ //assert(std::abs(projection - std::abs(v)) < 1e-5); // TODO ???
+ return nearest_(magnitudes, projection);
+}
+
+TEST_CASE(testFindNearest)
+{
+ const auto& values = cxValues();
+ const auto& phase_directions = getPhaseDirections();
+
+ static const auto& expected_getPhase = expected_getPhase_values(cxValues(), phase_delta());
+ std::vector expected_phase_directions;
+ for (auto&& phase : expected_getPhase)
+ {
+ expected_phase_directions.push_back(phase_directions.value[phase]);
+ }
+ std::vector expected;
+ for (size_t i = 0; i < values.size(); i++)
+ {
+ auto a = find_nearest(expected_phase_directions[i], values[i]);
+ expected.push_back(a);
+ }
+
+ const auto valuesv = load(cxValues());
+ std::vector actual;
+ for (auto&& v : valuesv)
+ {
+ const auto phase = getPhase_(v, phase_delta());
+ const auto phase_direction_real = lookup_(phase, phase_directions.real);
+ const auto phase_direction_imag = lookup_(phase, phase_directions.imag);
+ const auto amplitude = find_nearest(magnitudes(), phase_direction_real, phase_direction_imag, v);
+
+ for (auto&& a : copy_to(amplitude))
+ {
+ actual.push_back(a);
+ }
+ }
+
+ TEST_ASSERT_EQ(actual.size(), expected.size());
+ for (size_t i = 0; i < actual.size(); i++)
+ {
+ TEST_ASSERT_EQ(actual[i], expected[i]);
+ }
+}
+
+TEST_MAIN(
+ TEST_CHECK(test_SIMD_Instructions);
+ TEST_CHECK(testGetPhase);
+ TEST_CHECK(testLookup);
+ TEST_CHECK(testFindNearest);
+ )
diff --git a/modules/drivers/CMakeLists.txt b/modules/drivers/CMakeLists.txt
index 992f9d61d..132ab09f1 100644
--- a/modules/drivers/CMakeLists.txt
+++ b/modules/drivers/CMakeLists.txt
@@ -79,4 +79,5 @@ if (CODA_ENABLE_HDF5 OR CODA_HDF5_HOME)
add_subdirectory("highfive")
endif()
-add_subdirectory("libtiff")
\ No newline at end of file
+add_subdirectory("libtiff")
+add_subdirectory("vectorclass")
diff --git a/modules/drivers/vectorclass/CMakeLists.txt b/modules/drivers/vectorclass/CMakeLists.txt
new file mode 100644
index 000000000..91ba7a0f9
--- /dev/null
+++ b/modules/drivers/vectorclass/CMakeLists.txt
@@ -0,0 +1,7 @@
+set(MODULE_NAME vectorclass)
+set(TARGET_LANGUAGE c++)
+
+coda_add_module(
+ ${MODULE_NAME}
+ VERSION 2.02.00
+ DEPS ${MODULE_DEPS})
diff --git a/modules/drivers/vectorclass/LICENSE b/modules/drivers/vectorclass/LICENSE
new file mode 100644
index 000000000..fd2deda36
--- /dev/null
+++ b/modules/drivers/vectorclass/LICENSE
@@ -0,0 +1,191 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+
+ Copyright 2012-2019 Agner Fog.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/modules/drivers/vectorclass/README.md b/modules/drivers/vectorclass/README.md
new file mode 100644
index 000000000..d59b92fed
--- /dev/null
+++ b/modules/drivers/vectorclass/README.md
@@ -0,0 +1,14 @@
+# version2
+Vector Class Library, latest version
+
+This is a C++ class library for using the Single Instruction Multiple Data (SIMD) instructions to improve performance on modern microprocessors with the x86 or x86/64 instruction set on Windows, Linux, and Mac platforms. There are no plans to support ARM or other instruction sets.
+
+[Latest release](https://github.com/vectorclass/version2/releases)
+
+[Download manual](https://github.com/vectorclass/manual/raw/master/vcl_manual.pdf)
+
+[Add-on packages for particular applications](https://github.com/vectorclass/add-on)
+
+[Getting-started video.](https://www.youtube.com/watch?v=TKjYdLIMTrI) Video blogger Christopher Rose has made this nice video telling how to get started with the Vector Class Library.
+
+**Help:** You may ask for programming help on [StackOverflow](https://stackoverflow.com) using the tag vector-class-library.
diff --git a/modules/drivers/vectorclass/include/vectorclass/simd/simd b/modules/drivers/vectorclass/include/vectorclass/simd/simd
new file mode 100644
index 000000000..82316041a
--- /dev/null
+++ b/modules/drivers/vectorclass/include/vectorclass/simd/simd
@@ -0,0 +1,19 @@
+/*************************** simd *********************************
+* Author: Agner Fog
+* Date created: 2024-02-02
+* Last modified: 2024-02-02
+* Version: 2.02.00
+* Project: Extension to vector class library
+* Description:
+* Allow client code using the vector class library to look more like it was using `std::simd::simd`.
+* https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p1928r8.pdf
+*
+* © Copyright 2012-2024. Apache License version 2.0 or later
+******************************************************************************/
+#pragma once
+
+#include "simd.h"
+#include "simd_traits.h"
+#include "simd_builtin.h"
+#include "simd_math.h"
+#include "simd_alg.h"
diff --git a/modules/drivers/vectorclass/include/vectorclass/simd/simd.h b/modules/drivers/vectorclass/include/vectorclass/simd/simd.h
new file mode 100644
index 000000000..f9c75dbd0
--- /dev/null
+++ b/modules/drivers/vectorclass/include/vectorclass/simd/simd.h
@@ -0,0 +1,167 @@
+/*************************** simd.h *********************************
+* Author: Agner Fog
+* Date created: 2024-02-02
+* Last modified: 2024-02-02
+* Version: 2.02.00
+* Project: Extension to vector class library
+* Description:
+* Allow client code using the vector class library to look more like it was using `std::simd::simd`.
+* https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p1928r8.pdf
+*
+* © Copyright 2012-2024. Apache License version 2.0 or later
+******************************************************************************/
+#pragma once
+
+#ifndef VECTORCLASS_H
+#include "vectorclass.h"
+#endif
+
+#include "simd_abi.h"
+
+#ifdef VCL_NAMESPACE
+namespace VCL_NAMESPACE {
+#endif
+
+namespace simd
+{
+ template> struct basic_simd;
+
+ template> struct basic_simd_mask
+ {
+ using value_type = bool;
+ using abi_type = Abi;
+ static constexpr auto N = abi_type::N;
+ using VecNt = typename abi_type::type; // e.g., Vec4i
+ using native_type = details::Boolean_vector_class; // e.g., Vec4ib
+
+ static constexpr auto size = basic_simd, Abi>::size();
+ static_assert(size == native_type::size());
+
+ constexpr basic_simd_mask() noexcept = default;
+
+ // [simd.ctor]
+ template constexpr basic_simd_mask(U&& value) noexcept : v_(value) {}
+ template
+ constexpr explicit basic_simd_mask(const basic_simd_mask& other) noexcept : v_(other.v_) {}
+ template constexpr explicit basic_simd_mask(G&& gen, std::nullptr_t /*TODO: remove*/) noexcept;
+ // "Implementations should enable explicit conversion from and to implementation-defined types."
+ constexpr explicit operator native_type() const { return v_; }
+ constexpr explicit basic_simd_mask(const native_type& init) : v_(init) {}
+
+ // [simd.subscr]
+ // §2.5 of https://github.com/vectorclass/manual/raw/master/vcl_manual.pdf
+ // "Note that you can read a vector element with the[] operator, but not write an element."
+ constexpr value_type& operator[](details::size_type) & = delete;
+ constexpr value_type operator[](details::size_type i) const& { return v_[i]; }
+
+ private:
+ native_type v_;
+ };
+
+ template::size()>
+ using simd_mask = basic_simd_mask>;
+
+ template struct basic_simd
+ {
+ using value_type = T;
+ using mask_type = basic_simd_mask;
+ using abi_type = Abi;
+
+ using native_type = typename details::VecNt::Vector_class; // e.g., Vec4i
+
+ //static constexpr std::integral_constant size;
+ static constexpr details::size_type size() { return native_type::size(); }
+ //static_assert(size() == native_type::size());
+
+ constexpr basic_simd() noexcept = default;
+
+ // [simd.ctor]
+ template constexpr basic_simd(U&& value) noexcept : v_(value) {}
+ template
+ constexpr explicit basic_simd(const basic_simd& other) noexcept : v_(other.v_) {}
+ template constexpr explicit basic_simd(G&& gen, std::nullptr_t /*TODO: remove*/) noexcept
+ {
+ for (int i = 0; i < v_.size(); i++)
+ {
+ v_.insert(i, gen(i));
+ }
+ }
+ template
+ constexpr basic_simd(It first, Flags...)
+ {
+ // TODO: look at simd_flags
+ // However, §2.4 states "There is hardly any difference in efficiency
+ // between `load` and `load_a` on newer microprocessors."
+ copy_from(first);
+ }
+ //template
+ //constexpr basic_simd(It first, const mask_type& mask, simd_flags = {});
+
+ // "Implementations should enable explicit conversion from and to implementation-defined types."
+ constexpr explicit operator native_type() const { return v_; }
+ constexpr explicit basic_simd(const native_type& init) : v_(init) {}
+
+ // [simd.copy]
+ template
+ constexpr void copy_from(It first)
+ {
+ // TODO: look at simd_flags
+ // However, §2.4 states "There is hardly any difference in efficiency
+ // between `load` and `load_a` on newer microprocessors."
+
+ // "`It` satisfies contiguous_iterator."
+ const auto mem = &(*first);
+ v_.load(mem);
+ }
+ //template
+ //constexpr void copy_from(It first, const mask_type& mask, simd_flags f = {});
+ template
+ constexpr void copy_to(Out first) const
+ {
+ // TODO: look at simd_flags
+ // However, §2.5 states "There is hardly any difference in efficiency
+ // between `store` and `store_a` on newer microprocessors."
+
+ // "`It` satisfies contiguous_iterator."
+ auto mem = &(*first);
+ v_.store(mem);
+ }
+ //template
+ //constexpr void copy_to(Out first, const mask_type& mask, simd_flags f = {}) const;
+
+ // [simd.subscr]
+ // §2.5 of https://github.com/vectorclass/manual/raw/master/vcl_manual.pdf
+ // "Note that you can read a vector element with the[] operator, but not write an element."
+ constexpr value_type& operator[](details::size_type) & = delete;
+ constexpr value_type operator[](details::size_type i) const& { return v_[i]; }
+
+ // [simd.unary]
+ constexpr basic_simd& operator++() noexcept;
+ constexpr basic_simd operator++(int) noexcept;
+ constexpr basic_simd& operator--() noexcept;
+ constexpr basic_simd operator--(int) noexcept;
+ constexpr mask_type operator!() const noexcept;
+ constexpr basic_simd operator~() const noexcept;
+ constexpr basic_simd operator+() const noexcept;
+ constexpr basic_simd operator-() const noexcept;
+
+ // [simd.cond]
+ friend constexpr basic_simd simd_select_impl(
+ const mask_type& c, const basic_simd& a, const basic_simd& b) noexcept
+ {
+ using Vec_b = typename mask_type::native_type;
+ return select(static_cast(c), static_cast(a), static_cast(b));
+ }
+
+ //private:
+ native_type v_;
+ };
+
+ template::size()>
+ using simd = basic_simd>;
+}
+
+
+#ifdef VCL_NAMESPACE
+}
+#endif
diff --git a/modules/drivers/vectorclass/include/vectorclass/simd/simd_abi.h b/modules/drivers/vectorclass/include/vectorclass/simd/simd_abi.h
new file mode 100644
index 000000000..507701a85
--- /dev/null
+++ b/modules/drivers/vectorclass/include/vectorclass/simd/simd_abi.h
@@ -0,0 +1,167 @@
+/*************************** simd_abi.h *********************************
+* Author: Agner Fog
+* Date created: 2024-02-02
+* Last modified: 2024-02-02
+* Version: 2.02.00
+* Project: Extension to vector class library
+* Description:
+* Allow client code using the vector class library to look more like it was using `std::simd::simd`.
+* https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p1928r8.pdf
+*
+* © Copyright 2012-2024. Apache License version 2.0 or later
+******************************************************************************/
+#pragma once
+
+#include "simd.h"
+
+#ifdef VCL_NAMESPACE
+namespace VCL_NAMESPACE {
+#endif
+
+namespace simd
+{
+
+ namespace details
+ {
+ using size_type = int;
+
+ // See tables 2.1 and 2.2 of https://github.com/vectorclass/manual/raw/master/vcl_manual.pdf
+ constexpr size_type detect_vector_size()
+ {
+ if constexpr (detected_instrset <= instrset::SSE2) return 128;
+ if constexpr (detected_instrset <= instrset::AVX2) return 256;
+ return 512;
+ }
+ constexpr auto detected_vector_size_bytes = detect_vector_size() / 8;
+ constexpr size_type native_vector_elements(size_t sz)
+ {
+ return static_cast(detected_vector_size_bytes / sz);
+ }
+
+ // "a signed integer type T so that sizeof(T) == Bytes."
+ template struct integer_from_;
+ template<> struct integer_from_<1> { using type = std::int8_t; };
+ template<> struct integer_from_<2> { using type = std::int16_t; };
+ template<> struct integer_from_<4> { using type = std::int32_t; };
+ template<> struct integer_from_<8> { using type = std::int64_t; };
+ template
+ using integer_from = typename integer_from_::type;
+
+ // VecNt<4, int32_t> == Vec4i
+ // https://github.com/vectorclass/manual/raw/master/vcl_manual.pdf
+ template struct VecNt;
+
+ // Table 2.1 from https://github.com/vectorclass/manual/raw/master/vcl_manual.pdf
+ // 128 Total bits
+ template<> struct VecNt<16, int8_t> { using Vector_class = Vec16c; };
+ template<> struct VecNt<16, uint8_t> { using Vector_class = Vec16uc; };
+ template<> struct VecNt<8, int16_t> { using Vector_class = Vec8s; };
+ template<> struct VecNt<8, uint16_t> { using Vector_class = Vec8us; };
+ template<> struct VecNt<4, int32_t> { using Vector_class = Vec4i; };
+ template<> struct VecNt<4, uint32_t> { using Vector_class = Vec4ui; };
+ template<> struct VecNt<2, int64_t> { using Vector_class = Vec2q; };
+ template<> struct VecNt<2, uint64_t> { using Vector_class = Vec2uq; };
+ // 256 Total bits
+ template<> struct VecNt<32, int8_t> { using Vector_class = Vec32c; };
+ template<> struct VecNt<32, uint8_t> { using Vector_class = Vec32uc; };
+ template<> struct VecNt<16, int16_t> { using Vector_class = Vec16s; };
+ template<> struct VecNt<16, uint16_t> { using Vector_class = Vec16us; };
+ template<> struct VecNt<8, int32_t> { using Vector_class = Vec8i; };
+ template<> struct VecNt<8, uint32_t> { using Vector_class = Vec8ui; };
+ template<> struct VecNt<4, int64_t> { using Vector_class = Vec4q; };
+ template<> struct VecNt<4, uint64_t> { using Vector_class = Vec4uq; };
+ // 512 Total bits
+ template<> struct VecNt<64, int8_t> { using Vector_class = Vec64c; };
+ template<> struct VecNt<64, uint8_t> { using Vector_class = Vec64uc; };
+ template<> struct VecNt<32, int16_t> { using Vector_class = Vec32s; };
+ template<> struct VecNt<32, uint16_t> { using Vector_class = Vec32us; };
+ template<> struct VecNt<16, int32_t> { using Vector_class = Vec16i; };
+ template<> struct VecNt<16, uint32_t> { using Vector_class = Vec16ui; };
+ template<> struct VecNt<8, int64_t> { using Vector_class = Vec8q; };
+ template<> struct VecNt<8, uint64_t> { using Vector_class = Vec8uq; };
+
+ // Table 2.2 from https://github.com/vectorclass/manual/raw/master/vcl_manual.pdf
+ // 128 Total bits
+ template<> struct VecNt<4, float> { using Vector_class = Vec4f; };
+ template<> struct VecNt<2, double> { using Vector_class = Vec2d; };
+ // 256 Total bits
+ template<> struct VecNt<8, float> { using Vector_class = Vec8f; };
+ template<> struct VecNt<4, double> { using Vector_class = Vec4d; };
+ // 512 Total bits
+ template<> struct VecNt<16, float> { using Vector_class = Vec16f; };
+ template<> struct VecNt<8, double> { using Vector_class = Vec8d; };
+
+ // Vec<4, int32_t> = Vec4i
+ template
+ using Vec = typename VecNt::Vector_class;
+
+
+ // See table 2.3 of https://github.com/vectorclass/manual/raw/master/vcl_manual.pdf
+ template struct VecNb;
+ // 128 Total bits
+ template<> struct VecNb { using Boolean_vector_class = Vec16cb; };
+ template<> struct VecNb { using Boolean_vector_class = Vec16cb; };
+ template<> struct VecNb { using Boolean_vector_class = Vec8sb; };
+ template<> struct VecNb { using Boolean_vector_class = Vec8sb; };
+ template<> struct VecNb { using Boolean_vector_class = Vec4ib; };
+ template<> struct VecNb { using Boolean_vector_class = Vec4ib; };
+ template<> struct VecNb { using Boolean_vector_class = Vec2qb; };
+ template<> struct VecNb { using Boolean_vector_class = Vec2qb; };
+ template<> struct VecNb { using Boolean_vector_class = Vec4fb; };
+ template<> struct VecNb { using Boolean_vector_class = Vec2db; };
+ // 256 Total bits
+ template<> struct VecNb { using Boolean_vector_class = Vec32cb; };
+ template<> struct VecNb { using Boolean_vector_class = Vec32cb; };
+ template<> struct VecNb { using Boolean_vector_class = Vec16sb; };
+ template<> struct VecNb { using Boolean_vector_class = Vec16sb; };
+ template<> struct VecNb { using Boolean_vector_class = Vec8ib; };
+ template<> struct VecNb { using Boolean_vector_class = Vec8ib; };
+ template<> struct VecNb { using Boolean_vector_class = Vec4qb; };
+ template<> struct VecNb { using Boolean_vector_class = Vec4qb; };
+ template<> struct VecNb { using Boolean_vector_class = Vec8fb; };
+ template<> struct VecNb { using Boolean_vector_class = Vec4db; };
+ // 512 Total bits
+ template<> struct VecNb { using Boolean_vector_class = Vec64cb; };
+ template<> struct VecNb { using Boolean_vector_class = Vec64cb; };
+ template<> struct VecNb { using Boolean_vector_class = Vec32sb; };
+ template<> struct VecNb { using Boolean_vector_class = Vec32sb; };
+ template<> struct VecNb { using Boolean_vector_class = Vec16ib; };
+ template<> struct VecNb { using Boolean_vector_class = Vec16ib; };
+ template<> struct VecNb { using Boolean_vector_class = Vec8qb; };
+ template<> struct VecNb { using Boolean_vector_class = Vec8qb; };
+ template<> struct VecNb { using Boolean_vector_class = Vec16fb; };
+ template<> struct VecNb { using Boolean_vector_class = Vec8db; };
+
+ // Boolean_vector_class<4, i> = Vec4ib
+ template
+ using Boolean_vector_class = typename VecNb>::Boolean_vector_class;
+
+ template
+ struct fixed_size
+ {
+ static constexpr auto N = N_;
+ using type = T;
+ };
+ namespace simd_abi
+ {
+ template
+ using fixed_size = details::fixed_size;
+
+ template
+ using native_abi = fixed_size;
+
+ template>
+ struct deduce
+ {
+ using type = Abi;
+ };
+ template>
+ using deduce_t = typename deduce::type;
+ }
+}
+
+}
+
+#ifdef VCL_NAMESPACE
+}
+#endif
diff --git a/modules/drivers/vectorclass/include/vectorclass/simd/simd_alg.h b/modules/drivers/vectorclass/include/vectorclass/simd/simd_alg.h
new file mode 100644
index 000000000..9122d23ea
--- /dev/null
+++ b/modules/drivers/vectorclass/include/vectorclass/simd/simd_alg.h
@@ -0,0 +1,51 @@
+/*************************** simd_alg.h *********************************
+* Author: Agner Fog
+* Date created: 2024-02-02
+* Last modified: 2024-02-02
+* Version: 2.02.00
+* Project: Extension to vector class library
+* Description:
+* Allow client code using the vector class library to look more like it was using `std::simd::simd`.
+* https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p1928r8.pdf
+*
+* © Copyright 2012-2024. Apache License version 2.0 or later
+******************************************************************************/
+#pragma once
+
+#include "simd.h"
+
+#ifdef VCL_NAMESPACE
+namespace VCL_NAMESPACE {
+#endif
+
+namespace simd
+{
+ template
+ constexpr auto simd_select(const basic_simd_mask& c, const basic_simd& a, const basic_simd& b) noexcept
+ {
+ return simd_select_impl(c, a, b);
+ }
+ template
+ constexpr auto simd_select(const basic_simd_mask& c, const T& a, const U& b) noexcept
+ {
+ return simd_select_impl(c, basic_simd(a), basic_simd(b));
+ }
+
+ template
+ constexpr bool any_of(const basic_simd_mask& m) noexcept
+ {
+ using Vec_b = typename basic_simd_mask::native_type;
+ return horizontal_or(static_cast(m));
+ }
+
+ template
+ constexpr bool all_of(const basic_simd_mask& m) noexcept
+ {
+ using Vec_b = typename basic_simd_mask::native_type;
+ return horizontal_and(static_cast(m));
+ }
+}
+
+#ifdef VCL_NAMESPACE
+}
+#endif
\ No newline at end of file
diff --git a/modules/drivers/vectorclass/include/vectorclass/simd/simd_builtin.h b/modules/drivers/vectorclass/include/vectorclass/simd/simd_builtin.h
new file mode 100644
index 000000000..ee767d942
--- /dev/null
+++ b/modules/drivers/vectorclass/include/vectorclass/simd/simd_builtin.h
@@ -0,0 +1,157 @@
+/*************************** simd_builtin.h *********************************
+* Author: Agner Fog
+* Date created: 2024-02-02
+* Last modified: 2024-02-02
+* Version: 2.02.00
+* Project: Extension to vector class library
+* Description:
+* Allow client code using the vector class library to look more like it was using `std::simd::simd`.
+* https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p1928r8.pdf
+*
+* © Copyright 2012-2024. Apache License version 2.0 or later
+******************************************************************************/
+#pragma once
+
+#include "simd.h"
+
+#ifdef VCL_NAMESPACE
+namespace VCL_NAMESPACE {
+#endif
+
+namespace simd
+{
+ template
+ constexpr basic_simd& basic_simd::operator++() noexcept
+ {
+ ++(this->v_);
+ return *this;
+ }
+ template
+ constexpr basic_simd basic_simd::operator++(int) noexcept
+ {
+ auto old = *this; // copy old value
+ operator++(); // prefix increment
+ return old; // return old value
+ }
+ template
+ constexpr basic_simd& basic_simd::operator--() noexcept
+ {
+ --(this->v_);
+ return *this;
+ }
+ template
+ constexpr basic_simd basic_simd::operator--(int) noexcept
+ {
+ auto old = *this; // copy old value
+ operator--(); // prefix decrement
+ return old; // return old value
+ }
+ template
+ constexpr typename basic_simd::mask_type basic_simd::operator!() const noexcept
+ {
+ return !(this->v_);
+ }
+ template
+ constexpr basic_simd basic_simd::operator~() const noexcept
+ {
+ return ~(this->v_);
+ }
+ template
+ constexpr basic_simd basic_simd::operator+() const noexcept
+ {
+ return +(this->v_);
+ }
+ template
+ constexpr basic_simd basic_simd::operator-() const noexcept
+ {
+ return -(this->v_);
+ }
+
+ #define VECTORCLASS_basic_simd_binary(OPERATOR_) \
+ template constexpr basic_simd operator OPERATOR_(const basic_simd& lhs, const basic_simd& rhs) noexcept { \
+ using Vec = typename basic_simd::native_type; return static_cast(lhs) OPERATOR_ static_cast(rhs); } \
+ template constexpr basic_simd operator OPERATOR_(const basic_simd& lhs, const U& rhs) noexcept { \
+ using Vec = typename basic_simd::native_type; return static_cast(lhs) OPERATOR_ rhs; }
+ VECTORCLASS_basic_simd_binary(+);
+ VECTORCLASS_basic_simd_binary(-);
+ VECTORCLASS_basic_simd_binary(*);
+ VECTORCLASS_basic_simd_binary(/);
+ VECTORCLASS_basic_simd_binary(%);
+ VECTORCLASS_basic_simd_binary(&);
+ VECTORCLASS_basic_simd_binary(|);
+ VECTORCLASS_basic_simd_binary(^);
+ VECTORCLASS_basic_simd_binary(<<);
+ VECTORCLASS_basic_simd_binary(>>);
+ template
+ constexpr basic_simd operator<<(const basic_simd& lhs, details::size_type rhs) noexcept
+ {
+ using Vec = typename basic_simd::Vec;
+ return static_cast(lhs) << rhs;
+ }
+ template
+ constexpr basic_simd operator>>(const basic_simd& lhs, details::size_type rhs) noexcept
+ {
+ using Vec = typename basic_simd::Vec;
+ return static_cast(lhs) >> rhs;
+ }
+ #undef VECTORCLASS_basic_simd_binary
+
+ #define VECTORCLASS_basic_simd_cassign(OPERATOR_) \
+ template constexpr basic_simd& operator OPERATOR_(basic_simd& lhs, const basic_simd& rhs) noexcept { \
+ lhs.v_ OPERATOR_ rhs.v_; return lhs; } \
+ template constexpr basic_simd& operator OPERATOR_(basic_simd& lhs, const U& rhs) noexcept { \
+ lhs.v_ OPERATOR_ rhs; return lhs; }
+ VECTORCLASS_basic_simd_cassign(+=);
+ VECTORCLASS_basic_simd_cassign(-=);
+ VECTORCLASS_basic_simd_cassign(*=);
+ VECTORCLASS_basic_simd_cassign(/=);
+ VECTORCLASS_basic_simd_cassign(%=);
+ VECTORCLASS_basic_simd_cassign(&= );
+ VECTORCLASS_basic_simd_cassign(|=);
+ VECTORCLASS_basic_simd_cassign(^= );
+ VECTORCLASS_basic_simd_cassign(<<= );
+ VECTORCLASS_basic_simd_cassign(>>= );
+ #undef VECTORCLASS_basic_simd_cassign
+ template
+ constexpr basic_simd& operator<<=(basic_simd& lhs, details::size_type rhs) noexcept {
+ lhs.v_ <<= rhs;
+ return lhs;
+ }
+ template
+ constexpr basic_simd& operator>>=(basic_simd& lhs, details::size_type rhs) noexcept {
+ lhs.v_ >>= rhs;
+ return lhs;
+ }
+
+ #define VECTORCLASS_basic_simd_comparison(OPERATOR_) \
+ template constexpr typename basic_simd::mask_type operator OPERATOR_(const basic_simd& lhs, const basic_simd