libstdc++-v3/ChangeLog:

        * include/bits/simd_complex.h: New file.

Signed-off-by: Matthias Kretz <[email protected]>
---
 libstdc++-v3/include/bits/simd_complex.h | 1342 ++++++++++++++++++++++
 1 file changed, 1342 insertions(+)
 create mode 100644 libstdc++-v3/include/bits/simd_complex.h


--
──────────────────────────────────────────────────────────────────────────
 Dr. Matthias Kretz                           https://mattkretz.github.io
 GSI Helmholtz Center for Heavy Ion Research               https://gsi.de
 std::simd
──────────────────────────────────────────────────────────────────────────
diff --git a/libstdc++-v3/include/bits/simd_complex.h b/libstdc++-v3/include/bits/simd_complex.h
new file mode 100644
index 00000000000..17bfedb4f51
--- /dev/null
+++ b/libstdc++-v3/include/bits/simd_complex.h
@@ -0,0 +1,1342 @@
+/* SPDX-License-Identifier: GPL-3.0-or-later WITH GCC-exception-3.1 */
+/* Copyright © 2025      GSI Helmholtzzentrum fuer Schwerionenforschung GmbH
+ *                       Matthias Kretz <[email protected]>
+ */
+
+#ifndef _GLIBCXX_SIMD_COMPLEX_H
+#define _GLIBCXX_SIMD_COMPLEX_H 1
+
+#ifdef _GLIBCXX_SYSHDR
+#pragma GCC system_header
+#endif
+
+#if __cplusplus >= 202400L
+
+#include "simd_vec.h"
+
+// psabi warnings are bogus because the ABI of the internal types never leaks into user code
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wpsabi"
+
+namespace std::simd
+{
+  /** \internal
+   * The mask type, whose _DataType member is \p _Mp.
+   */
+  template <__simd_mask_type _Mp>
+    requires (_Mp::size() % 2 == 0)
+      and ((_Mp::abi_type::_S_variant & _AbiVariant::_CxVariants) == 0)
+    using __cx_ileav_mask
+      = basic_mask<__mask_element_size<_Mp> * 2,
+                   _Abi<_Mp::size() / 2, _Mp::abi_type::_S_nreg,
+                        _Mp::abi_type::_S_variant | _AbiVariant::_CxIleav>>;
+
+  template <__simd_mask_type _Mp>
+    [[__gnu__::__always_inline__]]
+    constexpr __cx_ileav_mask<_Mp>
+    __to_cx_ileav(const _Mp& __k)
+    { return __cx_ileav_mask<_Mp>::_S_init(__k); }
+
+  constexpr void
+  __check_hi_bits_for_zero(unsigned_integral auto __x)
+  {
+    __glibcxx_simd_precondition(__x == 0,
+                                "to_ullong called on mask with 'true' elements at indices"
+                                "higher than 64");
+  }
+
+  template <typename _T0, typename _T1>
+    constexpr void
+    __check_hi_bits_for_zero(const __trivial_pair<_T0, _T1>& __p)
+    {
+      __check_hi_bits_for_zero(__p._M_first);
+      __check_hi_bits_for_zero(__p._M_second);
+    }
+
+  constexpr unsigned long long
+  __unwrap_pairs_to_ullong(unsigned_integral auto __x)
+  { return __x; }
+
+  template <typename _T0, typename _T1>
+    constexpr unsigned long long
+    __unwrap_pairs_to_ullong(const __trivial_pair<_T0, _T1>& __p)
+    {
+      __check_hi_bits_for_zero(__p._M_second);
+      return __unwrap_pairs_to_ullong(__p._M_first);
+    }
+
+  template <int _Np>
+    constexpr bitset<_Np>
+    __unwrap_pairs_to_bitset(unsigned_integral auto __x)
+    {
+      static_assert(_Np <= 64);
+      return __x;
+    }
+
+  template <size_t _Np, typename _T0, typename _T1>
+    constexpr bitset<_Np>
+    __unwrap_pairs_to_bitset(const __trivial_pair<_T0, _T1>& __p)
+    {
+      constexpr size_t _N0 = __bit_floor(_Np);
+      constexpr size_t _N1 = _Np - _N0;
+      static_assert(_N0 % 64 == 0);
+      struct _Tmp
+      {
+        bitset<__bit_floor(_Np)> _M_lo;
+        bitset<_Np - __bit_floor(_Np)> _M_hi;
+      };
+      _Tmp __tmp = {__unwrap_pairs_to_bitset<_N0>(__p._M_first),
+                    __unwrap_pairs_to_bitset<_N1>(__p._M_second)};
+      return __builtin_bit_cast(bitset<_Np>, __tmp);
+    }
+
+  template <size_t _Bytes>
+    consteval auto
+    __tree_of_ulong()
+    {
+      static constexpr size_t _N0 = __bit_floor(_Bytes - 1);
+      static constexpr size_t _N1 = _Bytes - _N0;
+      if constexpr (_Bytes <= sizeof(unsigned long))
+        return 0ul;
+      else
+        return __trivial_pair {__tree_of_ulong<_N0>(), __tree_of_ulong<_N1>()};
+    }
+
+  template <size_t _Bytes>
+    using __tree_of_ulong_t = decltype(__tree_of_ulong<_Bytes>());
+
+  template <size_t _Np>
+    constexpr auto
+    __bitset_to_pairs(const bitset<_Np>& __b) noexcept
+    {
+      if constexpr (_Np <= 64)
+        return __b.to_ullong();
+      else
+        return __builtin_bit_cast(__tree_of_ulong_t<__div_ceil(_Np, size_t(__CHAR_BIT__))>, __b);
+    }
+
+  template <size_t _Bytes, __abi_tag _Ap>
+    requires _Ap::_S_is_cx_ileav
+    class basic_mask<_Bytes, _Ap>
+    {
+      template <size_t, typename>
+        friend class basic_mask;
+
+      template <typename, typename>
+        friend class basic_vec;
+
+      static constexpr int _S_size = _Ap::_S_size;
+
+      using _DataType = basic_mask<_Bytes / 2, _Abi<_S_size * 2, _Ap::_S_nreg,
+                                                    _Ap::_S_variant & ~_AbiVariant::_CxVariants>>;
+
+      static_assert(_DataType::abi_type::_S_nreg == _Ap::_S_nreg);
+
+      static_assert(is_same_v<__cx_ileav_mask<_DataType>, basic_mask>);
+
+      using _VecType = __similar_vec<__integer_from<_Bytes>, _S_size, _Ap>;
+
+      static constexpr bool _S_is_scalar = _DataType::_S_is_scalar;
+
+      // Interleaved storage requires at least two vector elements and therefore cannot ever be
+      // scalar.
+      static_assert(not _S_is_scalar);
+
+      static constexpr bool _S_use_bitmask = _DataType::_S_use_bitmask;
+
+      static constexpr int _S_full_size = _DataType::_S_full_size / 2;
+
+      static constexpr bool _S_is_partial = _DataType::_S_is_partial;
+
+      static constexpr bool _S_has_bool_member = _DataType::_S_has_bool_member;
+
+      // same as for _S_is_scalar
+      static_assert(not _S_has_bool_member);
+
+      static constexpr size_t _S_padding_bytes = _DataType::_S_padding_bytes;
+
+      _DataType _M_data;
+
+    public:
+      using value_type = bool;
+
+      using abi_type = _Ap;
+
+      using iterator = __iterator<basic_mask>;
+
+      using const_iterator = __iterator<const basic_mask>;
+
+      constexpr iterator
+      begin() noexcept
+      { return {*this, 0}; }
+
+      constexpr const_iterator
+      begin() const noexcept
+      { return {*this, 0}; }
+
+      constexpr const_iterator
+      cbegin() const noexcept
+      { return {*this, 0}; }
+
+      constexpr default_sentinel_t
+      end() const noexcept
+      { return {}; }
+
+      constexpr default_sentinel_t
+      cend() const noexcept
+      { return {}; }
+
+      static constexpr auto size = __simd_size_constant<_S_size>;
+
+      // internal but public API ----------------------------------------------
+      [[__gnu__::__always_inline__]]
+      static constexpr basic_mask
+      _S_init(const _DataType& __x)
+      {
+        basic_mask __r;
+        __r._M_data = __x;
+        return __r;
+      }
+
+      [[__gnu__::__always_inline__]]
+      constexpr auto
+      _M_concat_data() const
+      { return _M_data._M_concat_data(); }
+
+      template <_ArchTraits _Traits = {}>
+        [[__gnu__::__always_inline__]]
+        static constexpr basic_mask
+        _S_partial_mask_of_n(int __n)
+        { return _S_init(_DataType::_S_partial_mask_of_n(__n * 2)); }
+
+      [[__gnu__::__always_inline__]]
+      static constexpr basic_mask
+      _S_and_neighbors(_DataType __k)
+      { return _S_init(__k._M_and_neighbors()); }
+
+      [[__gnu__::__always_inline__]]
+      static constexpr basic_mask
+      _S_or_neighbors(_DataType __k)
+      { return _S_init(__k._M_or_neighbors()); }
+
+      template <typename _Mp>
+        [[__gnu__::__always_inline__]]
+        constexpr auto
+        _M_chunk() const noexcept
+        {
+          if constexpr (_Mp::abi_type::_S_variant != _Ap::_S_variant)
+            return resize_t<_S_size, _Mp>(*this).template _M_chunk<_Mp>();
+          else // _Mp is the same partial specialization
+            {
+              constexpr int __rem = _S_size % _Mp::_S_size;
+              const auto [...__xs] = _M_data.template _M_chunk<typename _Mp::_DataType>();
+              static_assert(is_same_v<decltype(__to_cx_ileav(__xs...[0])), _Mp>);
+              if constexpr (__rem == 0)
+                return array {__to_cx_ileav(__xs)...};
+              else
+                return tuple {__to_cx_ileav(__xs)...};
+            }
+        }
+
+      // [simd.mask.overview] default constructor -----------------------------
+      basic_mask() = default;
+
+      // [simd.mask.overview] conversion extensions ---------------------------
+      template <__vec_builtin _TV>
+        [[__gnu__::__always_inline__]]
+        constexpr
+        basic_mask(const _TV& __x) requires convertible_to<_TV, _DataType>
+        : _M_data(__x)
+        {}
+
+      template <__vec_builtin _TV>
+        [[__gnu__::__always_inline__]]
+        constexpr
+        operator _TV() requires convertible_to<_DataType, _TV>
+        { return _M_data; }
+
+      // [simd.mask.ctor] broadcast constructor -------------------------------
+      [[__gnu__::__always_inline__]]
+      constexpr explicit
+      basic_mask(same_as<bool> auto __x) noexcept
+        : _M_data(__x)
+      {}
+
+      // [simd.mask.ctor] conversion constructor ------------------------------
+      template <size_t _UBytes, typename _UAbi>
+        requires (_S_size == _UAbi::_S_size)
+        [[__gnu__::__always_inline__]]
+        constexpr explicit(__is_mask_conversion_explicit<_Ap, _UAbi>(_Bytes, _UBytes))
+        basic_mask(const basic_mask<_UBytes, _UAbi>& __x) noexcept
+          : _M_data([&] {
+              using _UV = basic_mask<_UBytes, _UAbi>;
+              if constexpr (_UV::_S_is_scalar)
+                return _DataType([&](int __i) { return __x[__i / 2]; });
+              else if constexpr (__flags_test(_UAbi::_S_variant, _AbiVariant::_CxIleav))
+                return __x._M_data; // calls conversion ctor on _DataType
+              else if constexpr (_S_use_bitmask or _UV::_S_use_bitmask)
+                return _DataType::_S_init(__duplicate_each_bit<_S_size>(__x._M_to_uint()));
+              else if constexpr (sizeof(__x) == sizeof(_M_data) and _Bytes == _UBytes
+                                   and _UV::_S_padding_bytes == 0)
+                {
+                  static_assert(not _S_has_bool_member and not _UV::_S_has_bool_member);
+                  return __builtin_bit_cast(_DataType, __x);
+                }
+              else if constexpr (_Bytes <= 8)
+                {
+                  using _U2 = __similar_mask<__integer_from<_Bytes>, _S_size, _UAbi>;
+                  if constexpr (_U2::_S_has_bool_member)
+                    return _DataType::_S_recursive_bit_cast(_U2(__x));
+                  else if constexpr (sizeof(_DataType) == sizeof(_U2))
+                    return __builtin_bit_cast(_DataType, _U2(__x));
+                  else
+                    { // the sizeof can differ e.g. on mask<float, 18> -> mask<complex<float>, 18>
+                      using _U3 = __similar_mask<__integer_from<_Bytes / 2>, _S_size * 2,
+                                                 typename _U2::abi_type>;
+                      static_assert(sizeof(_U3) == sizeof(_U2));
+                      return __builtin_bit_cast(_U3, _U2(__x));
+                    }
+                }
+              else if constexpr (_UBytes > 1) // call conversion ctor on _DataType
+                {
+                  using _U2 = __similar_mask<__integer_from<_UBytes / 2>, _S_size * 2, _UAbi>;
+                  return _U2::_S_recursive_bit_cast(__x);
+                }
+              else if constexpr (_Bytes >= 4)
+                {
+                  // 1. convert to larger mask type
+                  auto __y = __similar_mask<__integer_from<_Bytes / 2>, _S_size, _UAbi>(__x);
+                  // 2. reinterpret to pass to conversion constructor of _DataType
+                  using _U2 = __similar_mask<__integer_from<_Bytes / 2 / 2>, _S_size * 2, _UAbi>;
+                  return _U2::_S_recursive_bit_cast(__y);
+                }
+              else
+                static_assert(false);
+          }())
+        {}
+
+      // [simd.mask.ctor] generator constructor -------------------------------
+      template <__simd_generator_invokable<bool, _S_size> _Fp>
+        [[__gnu__::__always_inline__]]
+        constexpr explicit
+        basic_mask(_Fp&& __gen)
+        : _M_data([&] [[__gnu__::__always_inline__]] {
+            // for _CxIleav, the results of each __gen call need to initialize two
+            // neighboring elements
+            constexpr auto [...__is] = __iota<int[_S_size]>;
+            bool __tmp[_S_size] = {__gen(__simd_size_constant<__is>)...};
+            return _DataType([&] [[__gnu__::__always_inline__]] (size_t __i) {
+                     return __tmp[__i / 2];
+                   });
+          }())
+        {}
+
+      template <__almost_simd_generator_invokable<bool, _S_size> _Fp>
+        constexpr explicit
+        basic_mask(_Fp&&)
+          = _GLIBCXX_DELETE_MSG("Invalid return type of the mask generator function: "
+                                "Needs to be 'bool'.");
+
+      // [simd.mask.ctor] bitset constructor ----------------------------------
+      [[__gnu__::__always_inline__]]
+      constexpr
+      basic_mask(const same_as<bitset<_S_size>> auto& __b) noexcept
+      : _M_data(_DataType::_S_init(__duplicate_each_bit<_S_size>(__bitset_to_pairs(__b))))
+      {}
+
+      // [simd.mask.ctor] uint constructor ------------------------------------
+      template <unsigned_integral _Tp>
+        requires (not same_as<_Tp, bool>)
+        [[__gnu__::__always_inline__]]
+        constexpr explicit
+        basic_mask(_Tp __val) noexcept
+        : _M_data(__duplicate_each_bit<_S_size>(__val))
+        {}
+
+      // [simd.mask.subscr] ---------------------------------------------------
+      [[__gnu__::__always_inline__]]
+      constexpr value_type
+      operator[](__simd_size_type __i) const
+      { return _M_data[__i * 2]; }
+
+      // [simd.mask.unary] ----------------------------------------------------
+      [[__gnu__::__always_inline__]]
+      constexpr basic_mask
+      operator!() const noexcept
+      { return _S_init(!_M_data); }
+
+      [[__gnu__::__always_inline__]]
+      constexpr _VecType
+      operator+() const noexcept requires destructible<_VecType>
+      { return operator _VecType(); }
+
+      constexpr _VecType
+      operator+() const noexcept = delete;
+
+      [[__gnu__::__always_inline__]]
+      constexpr _VecType
+      operator-() const noexcept requires destructible<_VecType>
+      {
+        using _Ip = typename _VecType::value_type;
+        if constexpr (_S_use_bitmask)
+          return __select_impl(*this, _Ip(-1), _Ip());
+        else
+          return __builtin_bit_cast(_VecType, _M_data);
+      }
+
+      constexpr _VecType
+      operator-() const noexcept = delete;
+
+      [[__gnu__::__always_inline__]]
+      constexpr _VecType
+      operator~() const noexcept requires destructible<_VecType>
+      {
+        using _Ip = typename _VecType::value_type;
+        if constexpr (_S_use_bitmask)
+          return __select_impl(*this, _Ip(-2), _Ip(-1));
+        else
+          return __builtin_bit_cast(_VecType, _M_data) - _Ip(1);
+      }
+
+      constexpr _VecType
+      operator~() const noexcept = delete;
+
+      // [simd.mask.conv] -----------------------------------------------------
+      template <typename _Up, typename _UAbi>
+        requires (__simd_size_v<_Up, _UAbi> == _S_size)
+        [[__gnu__::__always_inline__]]
+        constexpr explicit(sizeof(_Up) != _Bytes)
+        operator basic_vec<_Up, _UAbi>() const noexcept
+        {
+          using _Mp = typename basic_vec<_Up, _UAbi>::mask_type;
+          return __select_impl(_Mp(*this), basic_vec<_Up, _UAbi>(1), basic_vec<_Up, _UAbi>(0));
+        }
+
+      // [simd.mask.namedconv] ------------------------------------------------
+      [[__gnu__::__always_inline__]]
+      constexpr bitset<_S_size>
+      to_bitset() const noexcept
+      { return __unwrap_pairs_to_bitset<_S_size>(_M_to_uint()); }
+
+      template <int _Offset = 0, _ArchTraits _Traits = {}>
+        [[__gnu__::__always_inline__]]
+        constexpr auto
+        _M_to_uint() const
+        { return _M_data.template _M_to_uint<_Offset, true>(); }
+
+      [[__gnu__::__always_inline__]]
+      constexpr unsigned long long
+      to_ullong() const
+      { return __unwrap_pairs_to_ullong(_M_to_uint()); }
+
+      // [simd.mask.binary] ---------------------------------------------------
+      [[__gnu__::__always_inline__]]
+      friend constexpr basic_mask
+      operator&&(const basic_mask& __x, const basic_mask& __y) noexcept
+      { return _S_init(__x._M_data & __y._M_data); }
+
+      [[__gnu__::__always_inline__]]
+      friend constexpr basic_mask
+      operator||(const basic_mask& __x, const basic_mask& __y) noexcept
+      { return _S_init(__x._M_data | __y._M_data); }
+
+      [[__gnu__::__always_inline__]]
+      friend constexpr basic_mask
+      operator&(const basic_mask& __x, const basic_mask& __y) noexcept
+      { return _S_init(__x._M_data & __y._M_data); }
+
+      [[__gnu__::__always_inline__]]
+      friend constexpr basic_mask
+      operator|(const basic_mask& __x, const basic_mask& __y) noexcept
+      { return _S_init(__x._M_data | __y._M_data); }
+
+      [[__gnu__::__always_inline__]]
+      friend constexpr basic_mask
+      operator^(const basic_mask& __x, const basic_mask& __y) noexcept
+      { return _S_init(__x._M_data ^ __y._M_data); }
+
+      // [simd.mask.cassign] --------------------------------------------------
+      [[__gnu__::__always_inline__]]
+      friend constexpr basic_mask&
+      operator&=(basic_mask& __x, const basic_mask& __y) noexcept
+      {
+        __x._M_data &= __y._M_data;
+        return __x;
+      }
+
+      [[__gnu__::__always_inline__]]
+      friend constexpr basic_mask&
+      operator|=(basic_mask& __x, const basic_mask& __y) noexcept
+      {
+        __x._M_data |= __y._M_data;
+        return __x;
+      }
+
+      [[__gnu__::__always_inline__]]
+      friend constexpr basic_mask&
+      operator^=(basic_mask& __x, const basic_mask& __y) noexcept
+      {
+        __x._M_data ^= __y._M_data;
+        return __x;
+      }
+
+      // [simd.mask.comparison] -----------------------------------------------
+      [[__gnu__::__always_inline__]]
+      friend constexpr basic_mask
+      operator==(const basic_mask& __x, const basic_mask& __y) noexcept
+      { return _S_init(__x._M_data == __y._M_data); }
+
+      [[__gnu__::__always_inline__]]
+      friend constexpr basic_mask
+      operator!=(const basic_mask& __x, const basic_mask& __y) noexcept
+      { return _S_init(__x._M_data != __y._M_data); }
+
+      [[__gnu__::__always_inline__]]
+      friend constexpr basic_mask
+      operator>=(const basic_mask& __x, const basic_mask& __y) noexcept
+      { return _S_init(__x._M_data >= __y._M_data); }
+
+      [[__gnu__::__always_inline__]]
+      friend constexpr basic_mask
+      operator<=(const basic_mask& __x, const basic_mask& __y) noexcept
+      { return _S_init(__x._M_data <= __y._M_data); }
+
+      [[__gnu__::__always_inline__]]
+      friend constexpr basic_mask
+      operator>(const basic_mask& __x, const basic_mask& __y) noexcept
+      { return _S_init(__x._M_data > __y._M_data); }
+
+      [[__gnu__::__always_inline__]]
+      friend constexpr basic_mask
+      operator<(const basic_mask& __x, const basic_mask& __y) noexcept
+      { return _S_init(__x._M_data < __y._M_data); }
+
+      // [simd.mask.cond] -----------------------------------------------------
+      [[__gnu__::__always_inline__]]
+      friend constexpr basic_mask
+      __select_impl(const basic_mask& __k, const basic_mask& __t, const basic_mask& __f) noexcept
+      { return __select_impl(__k._M_data, __t._M_data, __f._M_data); }
+
+      [[__gnu__::__always_inline__]]
+      friend constexpr basic_mask
+      __select_impl(const basic_mask& __k, same_as<bool> auto __t, same_as<bool> auto __f) noexcept
+      { return __select_impl(__k._M_data, __t, __f); }
+
+      template <__vectorizable _T0, same_as<_T0> _T1>
+        requires (sizeof(_T0) == _Bytes)
+        [[__gnu__::__always_inline__]]
+        friend constexpr vec<_T0, _S_size>
+        __select_impl(const basic_mask& __k, const _T0& __t, const _T1& __f) noexcept
+        { return __select_impl(__k, vec<_T0, _S_size>(__t), vec<_T0, _S_size>(__f)); }
+
+      // [simd.mask.reductions] implementation --------------------------------
+      [[__gnu__::__always_inline__]]
+      constexpr bool
+      _M_all_of() const noexcept
+      { return _M_data._M_all_of(); }
+
+      [[__gnu__::__always_inline__]]
+      constexpr bool
+      _M_any_of() const noexcept
+      { return _M_data._M_any_of(); }
+
+      [[__gnu__::__always_inline__]]
+      constexpr bool
+      _M_none_of() const noexcept
+      { return _M_data._M_none_of(); }
+
+      [[__gnu__::__always_inline__]]
+      constexpr __simd_size_type
+      _M_reduce_count() const noexcept
+      { return _M_data._M_reduce_count() / 2; }
+
+      [[__gnu__::__always_inline__]]
+      constexpr __simd_size_type
+      _M_reduce_min_index() const
+      { return _M_data._M_reduce_min_index() / 2; }
+
+      [[__gnu__::__always_inline__]]
+      constexpr __simd_size_type
+      _M_reduce_max_index() const
+      { return _M_data._M_reduce_max_index() / 2; }
+
+      [[__gnu__::__always_inline__]]
+      bool
+      _M_is_constprop() const
+      { return _M_data._M_is_constprop(); }
+    };
+
+  template <__vectorizable _Tp, __abi_tag _Ap>
+    requires __complex_like<_Tp>
+      and (__flags_test(_Ap::_S_variant, _AbiVariant::_CxIleav))
+    class basic_vec<_Tp, _Ap>
+    : _BinaryOps<_Tp, _Ap>
+    {
+      template <typename, typename>
+        friend class basic_vec;
+
+      static constexpr int _S_size = _Ap::_S_size;
+
+      static constexpr int _S_full_size = __bit_ceil(unsigned(_S_size));
+
+      using _T0 = typename _Tp::value_type;
+
+      using _TSimd = __similar_vec<_T0, 2 * _S_size, _Ap>;
+
+      using _RealSimd = __similar_vec<_T0, _S_size, _Ap>;
+
+      _TSimd _M_data = {};
+
+      static constexpr bool _S_use_bitmask = __flags_test(_Ap::_S_variant, _AbiVariant::_BitMask);
+
+      static constexpr bool _S_is_partial = sizeof(_M_data) > sizeof(_Tp) * _S_size;
+
+      [[__gnu__::__always_inline__]]
+      static constexpr basic_vec
+      _S_init(const _TSimd& __x)
+      {
+        basic_vec __r;
+        __r._M_data = __x;
+        return __r;
+      }
+
+    public:
+      using value_type = _Tp;
+
+      using abi_type = _Ap;
+
+      using mask_type = basic_mask<sizeof(_Tp), abi_type>;
+
+      using iterator = __iterator<basic_vec>;
+
+      using const_iterator = __iterator<const basic_vec>;
+
+      constexpr iterator
+      begin() noexcept
+      { return {*this, 0}; }
+
+      constexpr const_iterator
+      begin() const noexcept
+      { return {*this, 0}; }
+
+      constexpr const_iterator
+      cbegin() const noexcept
+      { return {*this, 0}; }
+
+      constexpr default_sentinel_t
+      end() const noexcept
+      { return {}; }
+
+      constexpr default_sentinel_t
+      cend() const noexcept
+      { return {}; }
+
+      static constexpr auto size = __simd_size_constant<_S_size>;
+
+      [[__gnu__::__always_inline__]]
+      constexpr bool
+      _M_is_constprop() const
+      { return _M_data._M_is_constprop(); }
+
+      template <typename _Vp>
+        [[__gnu__::__always_inline__]]
+        constexpr auto
+        _M_chunk() const noexcept
+        {
+          constexpr int __n = _S_size / _Vp::_S_size;
+          constexpr int __rem = _S_size % _Vp::_S_size;
+          const auto __chunked = _M_data.template _M_chunk<typename _Vp::_TSimd>();
+          constexpr auto [...__is] = __iota<int[__n]>;
+          if constexpr (__rem == 0)
+            return array<_Vp, __n> {_Vp::_S_init(__chunked[__is])...};
+          else
+            {
+              using _Rest = resize_t<__rem, _Vp>;
+              return tuple {_Vp::_S_init(get<__is>(__chunked))...,
+                            _Rest::_S_init(get<__n>(__chunked))};
+            }
+        }
+
+      template <typename _A0>
+        [[__gnu__::__always_inline__]]
+        static constexpr basic_vec
+        _S_concat(const basic_vec<value_type, _A0>& __x0) noexcept
+        { return static_cast<const basic_vec&>(__x0); }
+
+      template <typename... _As>
+        requires (sizeof...(_As) > 1)
+        [[__gnu__::__always_inline__]]
+        static constexpr basic_vec
+        _S_concat(const basic_vec<value_type, _As>&... __xs) noexcept
+        { return basic_vec::_S_init(_TSimd::_S_concat(__xs._M_data...)); }
+
+      basic_vec() = default;
+
+      // TODO: conversion extensions
+
+      // [simd.ctor] broadcast constructor ------------------------------------
+      template <__simd_vec_bcast<value_type> _Up>
+        [[__gnu__::__always_inline__]]
+        constexpr explicit(not __broadcast_constructible<_Up, value_type>)
+        basic_vec(_Up&& __x) noexcept
+          : _M_data([&](int __i) {
+              if constexpr (__complex_like<_Up>)
+                return (__i & 1) == 0 ? __x.real() : __x.imag();
+              else
+                return (__i & 1) == 0 ? __x : _T0();
+            })
+        {}
+
+#ifdef _GLIBCXX_SIMD_CONSTEVAL_BROADCAST
+      template <__simd_vec_bcast_consteval<value_type> _Up>
+        consteval
+        basic_vec(const _Up& __x)
+        : basic_vec(__value_preserving_cast<value_type>(__x))
+        {
+          static_assert(convertible_to<_Up, value_type>);
+          static_assert(is_arithmetic_v<remove_cvref_t<_Up>>);
+        }
+#endif
+
+      // [simd.ctor] conversion constructor -----------------------------------
+      // FIXME: Handle __flags_test(_Ap::_S_variant, _AbiVariant::_CxCtgus)
+      template <__complex_like _Up, typename _UAbi>
+        requires(__simd_size_v<_Up, _UAbi> == size.value
+                   and std::constructible_from<value_type, _Up>)
+        [[__gnu__::__always_inline__]]
+        constexpr
+        explicit(not convertible_to<_Up, value_type>)
+        basic_vec(const basic_vec<_Up, _UAbi>& __x) noexcept
+        : _M_data(__x._M_data)
+        {}
+
+      template <typename _Up, typename _UAbi> // _Up is not complex!
+        requires (__simd_size_v<_Up, _UAbi> == _S_size)
+          and std::constructible_from<value_type, _Up>
+          and (not is_same_v<_T0, _Up>)
+        [[__gnu__::__always_inline__]]
+        constexpr
+        explicit(not convertible_to<_Up, value_type>)
+        basic_vec(const basic_vec<_Up, _UAbi>& __x) noexcept
+        : basic_vec(__similar_vec<_T0, _S_size, _UAbi>(__x))
+        {}
+
+      // [simd.ctor] generator constructor ------------------------------------
+      template <__simd_generator_invokable<value_type, _S_size> _Fp>
+        [[__gnu__::__always_inline__]]
+        constexpr explicit
+        basic_vec(_Fp&& __gen)
+        : _M_data([&] {
+            using _Arr = std::array<value_type, sizeof(_TSimd) / sizeof(value_type)>;
+            constexpr auto [...__is] = __iota<int[_S_size]>;
+            const _Arr __tmp = { static_cast<value_type>(__gen(__simd_size_constant<__is>))... };
+            return __builtin_bit_cast(_TSimd, __tmp);
+          }())
+        {}
+
+      template <__almost_simd_generator_invokable<value_type, _S_size> _Fp>
+        constexpr explicit
+        basic_vec(_Fp&& )
+          = _GLIBCXX_DELETE_MSG("Invalid return type of the generator function: "
+                                "Requires value-preserving conversion or implicitly "
+                                "convertible user-defined type.");
+
+      // [simd.ctor] load constructor -----------------------------------------
+      template <__complex_like _Up>
+        [[__gnu__::__always_inline__]]
+        constexpr
+        basic_vec(_LoadCtorTag, const _Up* __ptr)
+          : _M_data(_LoadCtorTag(), reinterpret_cast<const typename _Up::value_type*>(__ptr))
+        {}
+
+      template <typename _Up>
+        [[__gnu__::__always_inline__]]
+        constexpr
+        basic_vec(_LoadCtorTag, const _Up* __ptr)
+          : basic_vec(_RealSimd(_LoadCtorTag(), __ptr))
+        {}
+
+      template <__static_sized_range<size.value> _Rg, typename... _Flags>
+        // FIXME: see load ctor above
+        [[__gnu__::__always_inline__]]
+        constexpr
+        basic_vec(_Rg&& __range, flags<_Flags...> __flags = {})
+          : basic_vec(_LoadCtorTag(), __flags.template _S_adjust_pointer<basic_vec>(
+                                        std::ranges::data(__range)))
+        {
+          static_assert(__loadstore_convertible_to<std::ranges::range_value_t<_Rg>, value_type,
+                                                   _Flags...>);
+        }
+
+      // [simd.ctor] complex init ---------------------------------------------
+      // This uses _RealSimd as proposed in LWG4230
+      [[__gnu__::__always_inline__]]
+      constexpr
+      basic_vec(const _RealSimd& __re, const _RealSimd& __im = {}) noexcept
+        : _M_data([&](int __i) { return ((__i & 1) == 0 ? __re : __im)[__i / 2]; })
+      {}
+
+      // [simd.subscr] --------------------------------------------------------
+      [[__gnu__::__always_inline__]]
+      constexpr value_type
+      operator[](__simd_size_type __i) const
+      { return value_type(_M_data[__i * 2], _M_data[__i * 2 + 1]); }
+
+      // [simd.unary] unary operators -----------------------------------------
+      [[__gnu__::__always_inline__]]
+      constexpr basic_vec&
+      operator++() noexcept requires requires(value_type __a) { ++__a; }
+      {
+        _M_data += value_type(_T0(1));
+        return *this;
+      }
+
+      [[__gnu__::__always_inline__]]
+      constexpr basic_vec
+      operator++(int) noexcept requires requires(value_type __a) { __a++; }
+      {
+        basic_vec __r = *this;
+        _M_data += value_type(_T0(1));
+        return __r;
+      }
+
+      [[__gnu__::__always_inline__]]
+      constexpr basic_vec&
+      operator--() noexcept requires requires(value_type __a) { --__a; }
+      {
+        _M_data -= value_type(_T0(1));
+        return *this;
+      }
+
+      [[__gnu__::__always_inline__]]
+      constexpr basic_vec
+      operator--(int) noexcept requires requires(value_type __a) { __a--; }
+      {
+        basic_vec __r = *this;
+        _M_data -= value_type(_T0(1));
+        return __r;
+      }
+
+      [[__gnu__::__always_inline__]]
+      constexpr mask_type
+      operator!() const noexcept requires requires(value_type __a) { !__a; }
+      { return _S_init(!_M_data); }
+
+      [[__gnu__::__always_inline__]]
+      constexpr basic_vec
+      operator+() const noexcept requires requires(value_type __a) { +__a; }
+      { return *this; }
+
+      [[__gnu__::__always_inline__]]
+      constexpr basic_vec
+      operator-() const noexcept requires requires(value_type __a) { -__a; }
+      {
+        basic_vec __r = *this;
+        __r._M_data = -_M_data;
+        return __r;
+      }
+
+      // [simd.cassign] compound assignment -----------------------------------
+      [[__gnu__::__always_inline__]]
+      friend constexpr basic_vec&
+      operator+=(basic_vec& __x, const basic_vec& __y) noexcept
+      requires requires(value_type __a) { __a + __a; }
+      {
+        __x._M_data += __y._M_data;
+        return __x;
+      }
+
+      [[__gnu__::__always_inline__]]
+      friend constexpr basic_vec&
+      operator-=(basic_vec& __x, const basic_vec& __y) noexcept
+      requires requires(value_type __a) { __a - __a; }
+      {
+        __x._M_data -= __y._M_data;
+        return __x;
+      }
+
+      [[__gnu__::__always_inline__]]
+      friend constexpr basic_vec&
+      operator*=(basic_vec& __x, const basic_vec& __y) noexcept
+      requires requires(value_type __a) { __a * __a; }
+      {
+        __x._M_data.template _M_complex_multiply_with<basic_vec>(__y._M_data);
+        return __x;
+      }
+
+      template <int _RemoveMe = 0>
+      [[__gnu__::__always_inline__]]
+      friend constexpr basic_vec&
+      operator/=(basic_vec& __x, const basic_vec& __y) noexcept
+      requires requires(value_type __a) { __a / __a; }
+      {
+        static_assert(false, "TODO");
+      }
+
+      // [simd.comparison] compare operators ----------------------------------
+      [[__gnu__::__always_inline__]]
+      friend constexpr mask_type
+      operator==(const basic_vec& __x, const basic_vec& __y) noexcept
+      { return mask_type::_S_and_neighbors(__x._M_data == __y._M_data); }
+
+      [[__gnu__::__always_inline__]]
+      friend constexpr mask_type
+      operator!=(const basic_vec& __x, const basic_vec& __y) noexcept
+      { return mask_type::_S_or_neighbors(__x._M_data != __y._M_data); }
+
+      // [simd.complex.access] complex-value accessors ------------------------
+      // LWG4230: returns _RealSimd instead of auto
+      [[__gnu__::__always_inline__]]
+      constexpr _RealSimd
+      real() const noexcept
+      { return permute<_S_size>(_M_data, [](int __i) { return __i * 2; }); }
+
+      [[__gnu__::__always_inline__]]
+      constexpr _RealSimd
+      imag() const noexcept
+      { return permute<_S_size>(_M_data, [](int __i) { return __i * 2 + 1; }); }
+
+      [[__gnu__::__always_inline__]]
+      constexpr void
+      real(const _RealSimd& __x) noexcept
+      { _M_data._M_complex_set_real(__x); }
+
+      [[__gnu__::__always_inline__]]
+      constexpr void
+      imag(const _RealSimd& __x) noexcept
+      { _M_data._M_complex_set_imag(__x); }
+
+      // [simd.cond] ---------------------------------------------------------
+      [[__gnu__::__always_inline__]]
+      friend constexpr basic_vec
+      __select_impl(const mask_type& __k, const basic_vec& __t, const basic_vec& __f) noexcept
+      { return _S_init(__select_impl(__k._M_data, __t._M_data, __f._M_data)); }
+
+      // [simd.complex.math] internals ---------------------------------------
+      [[__gnu__::__always_inline__]]
+      constexpr _RealSimd
+      _M_abs() const; // TODO
+
+      // associated functions
+      [[__gnu__::__always_inline__]]
+      constexpr _RealSimd
+      _M_norm() const
+      {
+#if 0
+        return (_M_data * _M_data)._M_hadd();
+#elif 0
+        const auto __squared = _M_data * _M_data;
+        return permute<size.value>(
+                 __squared + __squared._M_swap_neighbors(),
+                 [](unsigned __i) { return __i * 2; });
+#elif 0
+        const auto __squared = _M_data * _M_data;
+        return permute<size.value>(__squared, [](int __i) { return __i * 2; })
+                 + permute<size.value>(__squared, [](int __i) { return __i * 2 + 1; });
+#elif 1
+        auto __re = real();
+        auto __im = imag();
+        return __re * __re + __im * __im;
+#endif
+      }
+
+      [[__gnu__::__always_inline__]]
+      constexpr basic_vec
+      _M_conj() const
+      { return _S_init(_M_data._M_complex_conj()); }
+    };
+
+  template <__vectorizable _Tp, __abi_tag _Ap>
+    requires __complex_like<_Tp>
+      //and (_Ap::_S_nreg == 1)
+      and (__flags_test(_Ap::_S_variant, _AbiVariant::_CxCtgus)
+             or is_same_v<_Ap, _ScalarAbi<_Ap::_S_size>>)
+    class basic_vec<_Tp, _Ap>
+    : _BinaryOps<_Tp, _Ap>
+    {
+      template <typename, typename>
+        friend class basic_vec;
+
+      static constexpr int _S_size = _Ap::_S_size;
+
+      static constexpr int _S_full_size = __bit_ceil(unsigned(_S_size));
+
+      using _T0 = typename _Tp::value_type;
+
+      using _RealSimd = __similar_vec<_T0, _S_size, _Ap>;
+
+      _RealSimd _M_real = {};
+
+      _RealSimd _M_imag = {};
+
+      static constexpr bool _S_is_scalar = is_same_v<_Ap, _ScalarAbi<_S_size>>;
+
+      static_assert(_S_is_scalar == _RealSimd::_S_is_scalar);
+
+      static constexpr bool _S_use_bitmask = _RealSimd::_S_use_bitmask;
+
+      static constexpr bool _S_is_partial = _RealSimd::_S_is_partial;
+
+    public:
+      using value_type = _Tp;
+
+      using abi_type = _Ap;
+
+      // We can't use _RealSimd::mask_type here because that would have the wrong value for _Bytes,
+      // which bites us in __select_impl(basic_mask, T, T) where sizeof(T) is constrained to _Bytes.
+      using mask_type = basic_mask<sizeof(_Tp), abi_type>;
+
+      using iterator = __iterator<basic_vec>;
+
+      using const_iterator = __iterator<const basic_vec>;
+
+      constexpr iterator
+      begin() noexcept
+      { return {*this, 0}; }
+
+      constexpr const_iterator
+      begin() const noexcept
+      { return {*this, 0}; }
+
+      constexpr const_iterator
+      cbegin() const noexcept
+      { return {*this, 0}; }
+
+      constexpr default_sentinel_t
+      end() const noexcept
+      { return {}; }
+
+      constexpr default_sentinel_t
+      cend() const noexcept
+      { return {}; }
+
+      static constexpr auto size = __simd_size_constant<_S_size>;
+
+      [[__gnu__::__always_inline__]]
+      constexpr bool
+      _M_is_constprop() const
+      { return _M_real._M_is_constprop() and _M_imag._M_is_constprop(); }
+
+      template <typename _Vp>
+        [[__gnu__::__always_inline__]]
+        constexpr auto
+        _M_chunk() const noexcept
+        {
+          constexpr int __n = _S_size / _Vp::_S_size;
+          constexpr int __rem = _S_size % _Vp::_S_size;
+          const auto [...__rs, __rN] = _M_real.template _M_chunk<typename _Vp::_RealSimd>();
+          const auto [...__is, __iN] = _M_imag.template _M_chunk<typename _Vp::_RealSimd>();
+          if constexpr (__rem == 0)
+            return array<_Vp, __n> {_Vp(__rs, __is)..., _Vp(__rN, __iN)};
+          else
+            return tuple {_Vp(__rs, __is)..., resize_t<__rem, _Vp>(__rN, __iN)};
+        }
+
+      template <typename _A0>
+        [[__gnu__::__always_inline__]]
+        static constexpr basic_vec
+        _S_concat(const basic_vec<value_type, _A0>& __x0) noexcept
+        { return static_cast<const basic_vec&>(__x0); }
+
+      template <typename... _As>
+        requires (sizeof...(_As) > 1)
+        [[__gnu__::__always_inline__]]
+        static constexpr basic_vec
+        _S_concat(const basic_vec<value_type, _As>&... __xs) noexcept
+        { return {_RealSimd::_S_concat(__xs._M_real...), _RealSimd::_S_concat(__xs._M_imag...) }; }
+
+      basic_vec() = default;
+
+      // TODO: conversion extensions
+
+      // [simd.ctor] broadcast constructor ------------------------------------
+      template <__simd_vec_bcast<value_type> _Up>
+        requires __complex_like<_Up>
+        [[__gnu__::__always_inline__]]
+        constexpr explicit(not __broadcast_constructible<_Up, value_type>)
+        basic_vec(_Up&& __x) noexcept
+          : _M_real(__x.real()), _M_imag(__x.imag())
+        {}
+
+      template <__simd_vec_bcast<value_type> _Up>
+        [[__gnu__::__always_inline__]]
+        constexpr explicit(not __broadcast_constructible<_Up, value_type>)
+        basic_vec(_Up&& __x) noexcept
+          : _M_real(__x), _M_imag()
+        {}
+
+#ifdef _GLIBCXX_SIMD_CONSTEVAL_BROADCAST
+      template <__simd_vec_bcast_consteval<value_type> _Up>
+        consteval
+        basic_vec(const _Up& __x)
+        : _M_real(__x), _M_imag()
+        {
+          static_assert(convertible_to<_Up, value_type>);
+          static_assert(is_arithmetic_v<remove_cvref_t<_Up>>);
+        }
+#endif
+
+      // [simd.ctor] conversion constructor -----------------------------------
+      template <__complex_like _Up, typename _UAbi>
+        requires(__simd_size_v<_Up, _UAbi> == size.value
+                   and std::constructible_from<value_type, _Up>)
+        [[__gnu__::__always_inline__]]
+        constexpr
+        explicit(not convertible_to<_Up, value_type>)
+        basic_vec(const basic_vec<_Up, _UAbi>& __x) noexcept
+        : _M_real(__x._M_real), _M_imag(__x._M_imag)
+        {}
+
+      template <typename _Up, typename _UAbi> // _Up is not complex!
+        requires (__simd_size_v<_Up, _UAbi> == _S_size)
+          and std::constructible_from<value_type, _Up>
+          and (not is_same_v<_T0, _Up>)
+        [[__gnu__::__always_inline__]]
+        constexpr
+        explicit(not convertible_to<_Up, value_type>)
+        basic_vec(const basic_vec<_Up, _UAbi>& __x) noexcept
+        : _M_real(__x), _M_imag()
+        {}
+
+      // [simd.ctor] generator constructor ------------------------------------
+      template <__simd_generator_invokable<value_type, _S_size> _Fp>
+        [[__gnu__::__always_inline__]]
+        constexpr explicit
+        basic_vec(_Fp&& __gen)
+        : _M_real(),
+          _M_imag([&] {
+            _T0 __re[sizeof(_RealSimd) / sizeof(_T0)] = {};
+            _T0 __im[sizeof(_RealSimd) / sizeof(_T0)] = {};
+            template for (constexpr int __i : __iota<int[_S_size]>)
+              {
+                const value_type __c = static_cast<value_type>(__gen(__simd_size_constant<__i>));
+                __re[__i] = __c.real();
+                __im[__i] = __c.imag();
+              }
+            _M_real = __builtin_bit_cast(_RealSimd, __re);
+            return __builtin_bit_cast(_RealSimd, __im);
+          }())
+        {}
+
+      template <__almost_simd_generator_invokable<value_type, _S_size> _Fp>
+        constexpr explicit
+        basic_vec(_Fp&& )
+          = _GLIBCXX_DELETE_MSG("Invalid return type of the generator function: "
+                                "Requires value-preserving conversion or implicitly "
+                                "convertible user-defined type.");
+
+      // [simd.ctor] load constructor -----------------------------------------
+      template <__complex_like _Up>
+        [[__gnu__::__always_inline__]]
+        constexpr
+        basic_vec(_LoadCtorTag, const _Up* __ptr)
+        : _M_real([&](int __i) -> _T0 { return __ptr[__i].real(); }),
+          _M_imag([&](int __i) -> _T0 { return __ptr[__i].imag(); })
+        {}
+
+      template <typename _Up>
+        [[__gnu__::__always_inline__]]
+        constexpr
+        basic_vec(_LoadCtorTag, const _Up* __ptr)
+        : _M_real(_LoadCtorTag(), __ptr), _M_imag()
+        {}
+
+      template <__static_sized_range<size.value> _Rg, typename... _Flags>
+        // FIXME: see load ctor above
+        [[__gnu__::__always_inline__]]
+        constexpr
+        basic_vec(_Rg&& __range, flags<_Flags...> __flags = {})
+        : basic_vec(_LoadCtorTag(), __flags.template _S_adjust_pointer<basic_vec>(
+                                      std::ranges::data(__range)))
+        {
+          static_assert(__loadstore_convertible_to<std::ranges::range_value_t<_Rg>, value_type,
+                                                   _Flags...>);
+        }
+
+      // [simd.ctor] complex init ---------------------------------------------
+      // This uses _RealSimd as proposed in LWG4230
+      [[__gnu__::__always_inline__]]
+      constexpr
+      basic_vec(const _RealSimd& __re, const _RealSimd& __im = {}) noexcept
+      : _M_real(__re), _M_imag(__im)
+      {}
+
+      // [simd.subscr] --------------------------------------------------------
+      [[__gnu__::__always_inline__]]
+      constexpr value_type
+      operator[](__simd_size_type __i) const
+      { return value_type(_M_real[__i], _M_imag[__i]); }
+
+      // [simd.unary] unary operators -----------------------------------------
+      [[__gnu__::__always_inline__]]
+      constexpr basic_vec&
+      operator++() noexcept requires requires(value_type __a) { ++__a; }
+      {
+        ++_M_real;
+        return *this;
+      }
+
+      [[__gnu__::__always_inline__]]
+      constexpr basic_vec
+      operator++(int) noexcept requires requires(value_type __a) { __a++; }
+      {
+        basic_vec __r = *this;
+        ++_M_real;
+        return __r;
+      }
+
+      [[__gnu__::__always_inline__]]
+      constexpr basic_vec&
+      operator--() noexcept requires requires(value_type __a) { --__a; }
+      {
+        --_M_real;
+        return *this;
+      }
+
+      [[__gnu__::__always_inline__]]
+      constexpr basic_vec
+      operator--(int) noexcept requires requires(value_type __a) { __a--; }
+      {
+        basic_vec __r = *this;
+        --_M_real;
+        return __r;
+      }
+
+      [[__gnu__::__always_inline__]]
+      constexpr mask_type
+      operator!() const noexcept requires requires(value_type __a) { !__a; }
+      { return !_M_real and !_M_imag; }
+
+      [[__gnu__::__always_inline__]]
+      constexpr basic_vec
+      operator+() const noexcept requires requires(value_type __a) { +__a; }
+      { return *this; }
+
+      [[__gnu__::__always_inline__]]
+      constexpr basic_vec
+      operator-() const noexcept requires requires(value_type __a) { -__a; }
+      { return basic_vec(-_M_real, -_M_imag); }
+
+      // [simd.cassign] compound assignment -----------------------------------
+      [[__gnu__::__always_inline__]]
+      friend constexpr basic_vec&
+      operator+=(basic_vec& __x, const basic_vec& __y) noexcept
+      requires requires(value_type __a) { __a + __a; }
+      {
+        __x._M_real += __y._M_real;
+        __x._M_imag += __y._M_imag;
+        return __x;
+      }
+
+      [[__gnu__::__always_inline__]]
+      friend constexpr basic_vec&
+      operator-=(basic_vec& __x, const basic_vec& __y) noexcept
+      requires requires(value_type __a) { __a - __a; }
+      {
+        __x._M_real -= __y._M_real;
+        __x._M_imag -= __y._M_imag;
+        return __x;
+      }
+
+
+      template <_TargetTraits _Traits = {}>
+        [[__gnu__::__always_inline__]]
+        friend constexpr basic_vec&
+        operator*=(basic_vec& __x, const basic_vec& __y) noexcept
+        requires requires(value_type __a) { __a * __a; }
+        {
+          _RealSimd::template _S_cxctgus_mul<value_type>(
+            __x._M_real, __x._M_imag, __y._M_real, __y._M_imag);
+          return __x;
+        }
+
+      [[__gnu__::__always_inline__]]
+      friend constexpr basic_vec&
+      operator/=(basic_vec& __x, const basic_vec& __y) noexcept
+      requires requires(value_type __a) { __a / __a; }
+      {
+        const _RealSimd __r =  __x._M_real * __y._M_real + __x._M_imag * __y._M_imag;
+        const _RealSimd __n = __y._M_norm();
+        __x._M_imag = (__x._M_imag * __y._M_real - __x._M_real * __y._M_imag) / __n;
+        __x._M_real = __r / __n;
+        return __x;
+      }
+
+      // [simd.comparison] compare operators ----------------------------------
+      [[__gnu__::__always_inline__]]
+      friend constexpr mask_type
+      operator==(const basic_vec& __x, const basic_vec& __y) noexcept
+      { return mask_type(__x._M_real == __y._M_real and __x._M_imag == __y._M_imag); }
+
+      [[__gnu__::__always_inline__]]
+      friend constexpr mask_type
+      operator!=(const basic_vec& __x, const basic_vec& __y) noexcept
+      { return mask_type(__x._M_real != __y._M_real or __x._M_imag != __y._M_imag); }
+
+      // [simd.complex.access] complex-value accessors ------------------------
+      // LWG4230: returns _RealSimd instead of auto
+      [[__gnu__::__always_inline__]]
+      constexpr _RealSimd
+      real() const noexcept
+      { return _M_real; }
+
+      [[__gnu__::__always_inline__]]
+      constexpr _RealSimd
+      imag() const noexcept
+      { return _M_imag; }
+
+      [[__gnu__::__always_inline__]]
+      constexpr void
+      real(const _RealSimd& __x) noexcept
+      { _M_real = __x; }
+
+      [[__gnu__::__always_inline__]]
+      constexpr void
+      imag(const _RealSimd& __x) noexcept
+      { _M_imag = __x; }
+
+      // [simd.cond] ---------------------------------------------------------
+      [[__gnu__::__always_inline__]]
+      friend constexpr basic_vec
+      __select_impl(const mask_type& __k, const basic_vec& __t, const basic_vec& __f) noexcept
+      {
+        typename basic_vec::_RealSimd::mask_type __kk(__k);
+        return basic_vec(__select_impl(__kk, __t._M_real, __f._M_real),
+                         __select_impl(__kk, __t._M_imag, __f._M_imag));
+      }
+
+      // [simd.complex.math] internals ---------------------------------------
+      [[__gnu__::__always_inline__]]
+      constexpr _RealSimd
+      _M_abs() const
+      {
+        // FIXME: avoid overflow & underflow in _M_norm
+        return sqrt(_M_norm());
+      }
+
+      // associated functions
+      [[__gnu__::__always_inline__]]
+      constexpr _RealSimd
+      _M_norm() const
+      { return _M_real * _M_real + _M_imag * _M_imag; }
+
+      [[__gnu__::__always_inline__]]
+      constexpr basic_vec
+      _M_conj() const
+      { return basic_vec(_M_real, -_M_imag); }
+    };
+
+  // [P3319R5] (extension) ----------------------------------------------------
+  template <__complex_like _Tp, typename _Abi>
+    inline constexpr basic_vec<_Tp, _Abi>
+    __iota<basic_vec<_Tp, _Abi>> = basic_vec<_Tp, _Abi>([](typename _Tp::value_type __i)
+                                                          -> typename _Tp::value_type {
+      static_assert(__simd_size_v<_Tp, _Abi> - 1 <= numeric_limits<typename _Tp::value_type>::max(),
+                    "iota object would overflow");
+      return __i;
+    });
+}
+
+#pragma GCC diagnostic pop
+#endif // C++26
+#endif // _GLIBCXX_SIMD_COMPLEX_H

Reply via email to