#ifndef LMP_INTEL_AIREBO_SCALAR
# ifdef __INTEL_COMPILER
#  if defined(__MIC__) || defined(__AVX512F__)
#   define LMP_INTEL_AIREBO_512
#  elif defined(__AVX__)
#   define LMP_INTEL_AIREBO_256
#  else
#   define LMP_INTEL_AIREBO_SCALAR
#  endif
# else
#  define LMP_INTEL_AIREBO_SCALAR
# endif
#endif

#ifdef LMP_INTEL_AIREBO_512

#include <cassert>
#include <immintrin.h>

#define VEC_INLINE __attribute__((always_inline))


#ifndef FVEC_FIRST_PASS
#  define FVEC_LEN 8
#  define FVEC_SUFFIX(a) a##pd
#  define FVEC_SUFFIX_MASK(a) a##pd_mask
#  define FVEC_MASK_T __mmask8
#  define FVEC_VEC_T __m512d
#  define FVEC_SCAL_T double
#  define IVEC_NAME ivec8
#  define FVEC_NAME fvec8pd
#  define BVEC_NAME bvec8
#  define AVEC_NAME avec8pd
#else
#  undef FVEC_LEN
#  undef FVEC_SUFFIX
#  undef FVEC_SUFFIX_MASK
#  undef FVEC_MASK_T
#  undef FVEC_VEC_T
#  undef FVEC_SCAL_T
#  undef IVEC_NAME
#  undef FVEC_NAME
#  undef BVEC_NAME
#  undef AVEC_NAME

#  define FVEC_LEN 16
#  define FVEC_SUFFIX(a) a##ps
#  define FVEC_SUFFIX_MASK(a) a##ps_mask
#  define FVEC_MASK_T __mmask16
#  define FVEC_VEC_T __m512
#  define FVEC_SCAL_T float
#  define IVEC_NAME ivec16
#  define FVEC_NAME fvec16ps
#  define BVEC_NAME bvec16
#  define AVEC_NAME avec16ps
#endif

namespace mm512 {

#ifndef __AVX512F__

#ifndef FVEC_FIRST_PASS
VEC_INLINE static inline __m512i _mm512_mask_expand_epi32(__m512i src,
                                                          __mmask16 k,
                                                          __m512i a) {
  int buf[16] __attribute__((aligned(64)));
  _mm512_store_epi32(buf, a);
  return _mm512_mask_loadunpacklo_epi32(src, k, buf);
}
VEC_INLINE static inline __m512i _mm512_maskz_expand_epi32(__mmask16 k,
                                                           __m512i a) {
  int buf[16] __attribute__((aligned(64)));
  _mm512_store_epi32(buf, a);
  return _mm512_mask_loadunpacklo_epi32(_mm512_setzero_epi32(), k, buf);
}
VEC_INLINE static inline __m512i _mm512_mask_compress_epi32(__m512i src,
                                                            __mmask16 k,
                                                            __m512i a) {
  int buf[16] __attribute__((aligned(64)));
  _mm512_store_epi32(buf, src);
  _mm512_mask_packstorelo_epi32(buf, k, a);
  return _mm512_load_epi32(buf);
}
VEC_INLINE static inline __m512i _mm512_maskz_compress_epi32(__mmask16 k,
                                                             __m512i a) {
  int buf[16] __attribute__((aligned(64))) = {0};
  _mm512_mask_packstorelo_epi32(buf, k, a);
  return _mm512_load_epi32(buf);
}

VEC_INLINE static inline void _mm512_mask_compressstoreu_epi32(int * dest,
                                                               __mmask16 mask,
                                                               __m512i src) {
  _mm512_mask_packstorelo_epi32(dest, mask, src);
  _mm512_mask_packstorehi_epi32(dest + 16, mask, src);
}

VEC_INLINE static inline __m512i _mm512_mask_loadu_epi32(__m512i src,
                                                         __mmask16 k,
                                                         const int * mem_addr) {
  assert((k & (k + 1)) == 0);
  __m512i ret = _mm512_mask_loadunpacklo_epi32(src, k, mem_addr);
  ret = _mm512_mask_loadunpackhi_epi32(ret, k, mem_addr + 16);
  return ret;
}
VEC_INLINE static inline __m512i _mm512_maskz_loadu_epi32(__mmask16 k,
                                                        const int * mem_addr) {
  assert((k & (k + 1)) == 0);
  __m512i ret = _mm512_mask_loadunpacklo_epi32(_mm512_setzero_epi32(), k,
                                               mem_addr);
  ret = _mm512_mask_loadunpackhi_epi32(ret, k, mem_addr + 16);
  return ret;
}
VEC_INLINE static inline void _mm512_mask_storeu_epi32(int * dest,
                                                       __mmask16 mask,
                                                       __m512i src) {
  assert((mask & (mask + 1)) == 0);
  _mm512_mask_packstorelo_epi32(dest, mask, src);
  _mm512_mask_packstorehi_epi32(dest + 16, mask, src);
}
#endif

VEC_INLINE static inline FVEC_VEC_T FVEC_SUFFIX(_mm512_mask_expand_)
  (FVEC_VEC_T src, __mmask16 k, FVEC_VEC_T a) {
  FVEC_SCAL_T buf[FVEC_LEN] __attribute__((aligned(64)));
  FVEC_SUFFIX(_mm512_store_)(buf, a);
  return FVEC_SUFFIX(_mm512_mask_loadunpacklo_)(src, k, buf);
}
VEC_INLINE static inline FVEC_VEC_T FVEC_SUFFIX(_mm512_maskz_expand_)
  (__mmask16 k, FVEC_VEC_T a) {
  FVEC_SCAL_T buf[FVEC_LEN] __attribute__((aligned(64)));
  FVEC_SUFFIX(_mm512_store_)(buf, a);
  return FVEC_SUFFIX(_mm512_mask_loadunpacklo_)(FVEC_SUFFIX(_mm512_setzero_)(),
                                                k, buf);
}
VEC_INLINE static inline FVEC_VEC_T FVEC_SUFFIX(_mm512_mask_compress_)
  (FVEC_VEC_T src, __mmask16 k, FVEC_VEC_T a) {
  FVEC_SCAL_T buf[FVEC_LEN] __attribute__((aligned(64)));
  FVEC_SUFFIX(_mm512_store_)(buf, src);
  FVEC_SUFFIX(_mm512_mask_packstorelo_)(buf, k, a);
  return FVEC_SUFFIX(_mm512_load_)(buf);
}
VEC_INLINE static inline FVEC_VEC_T FVEC_SUFFIX(_mm512_maskz_compress_)
  (__mmask16 k, FVEC_VEC_T a) {
  FVEC_SCAL_T buf[FVEC_LEN] __attribute__((aligned(64))) = {0};
  FVEC_SUFFIX(_mm512_mask_packstorelo_)(buf, k, a);
  return FVEC_SUFFIX(_mm512_load_)(buf);
}
VEC_INLINE static inline void FVEC_SUFFIX(_mm512_mask_storeu_)
  (FVEC_SCAL_T * dest, FVEC_MASK_T mask, FVEC_VEC_T src) {
  assert((mask & (mask + 1)) == 0);
  FVEC_SUFFIX(_mm512_mask_packstorelo_)(dest, mask, src);
  FVEC_SUFFIX(_mm512_mask_packstorehi_)(dest + FVEC_LEN, mask, src);
}
#endif


class FVEC_NAME;
class IVEC_NAME;
class AVEC_NAME;
class BVEC_NAME {
  friend class FVEC_NAME;
  friend class IVEC_NAME;
  friend class AVEC_NAME;
# if FVEC_LEN==16
  friend class avec16pd;
# endif
  FVEC_MASK_T val_;
  VEC_INLINE BVEC_NAME(const FVEC_MASK_T &v) : val_(v) {}
public:
  VEC_INLINE BVEC_NAME() {}
  VEC_INLINE static BVEC_NAME kand(const BVEC_NAME &a, const BVEC_NAME &b) {
    return _mm512_kand(a.val_, b.val_);
  }
  VEC_INLINE static BVEC_NAME kandn(const BVEC_NAME &a, const BVEC_NAME &b) {
    return _mm512_kandn(a.val_, b.val_);
  }
  VEC_INLINE static BVEC_NAME knot(const BVEC_NAME &a) {
    return _mm512_knot(a.val_);
  }
  VEC_INLINE static int kortestz(const BVEC_NAME &a, const BVEC_NAME &b) {
    return _mm512_kortestz(a.val_, b.val_);
  }
  VEC_INLINE static BVEC_NAME masku_compress(const BVEC_NAME &mask,
                                             const BVEC_NAME &a) {
    const __m512i c_i1 = _mm512_set1_epi32(1);
    __m512i a_int_vec = _mm512_mask_blend_epi32(a.val_, _mm512_setzero_epi32(),
                                                c_i1);
    __m512i compressed = _mm512_mask_compress_epi32(_mm512_undefined_epi32(),
                                                    mask.val_, a_int_vec);
    return _mm512_cmpeq_epi32_mask(compressed, c_i1);
  }
  VEC_INLINE static BVEC_NAME mask_expand(const BVEC_NAME &src,
                                          const BVEC_NAME &mask,
                                          const BVEC_NAME &a) {
    const __m512i c_i1 = _mm512_set1_epi32(1);
    __m512i a_int_vec = _mm512_mask_blend_epi32(a.val_, _mm512_setzero_epi32(),
                                                c_i1);
    __m512i src_int_vec = _mm512_mask_blend_epi32(src.val_,
                                                  _mm512_setzero_epi32(), c_i1);
    __m512i compressed = _mm512_mask_expand_epi32(src_int_vec, mask.val_,
                                                  a_int_vec);
    return _mm512_cmpeq_epi32_mask(compressed, c_i1);
  }
  VEC_INLINE static BVEC_NAME full() {
    return static_cast<FVEC_MASK_T>(0xFFFF);
  }
  VEC_INLINE static BVEC_NAME empty() {
    return 0;
  }
  VEC_INLINE static BVEC_NAME only(int n) {
    return full().val_ >> (FVEC_LEN - n);
  }
  VEC_INLINE static BVEC_NAME after(int n) {
    return full().val_ << n;
  }
  VEC_INLINE static BVEC_NAME onlyafter(int only, int after) {
    return (full().val_ >> (FVEC_LEN - only)) << after;
  }
  VEC_INLINE static int popcnt(const BVEC_NAME &a) {
    return _popcnt32(a.val_);
  }
  VEC_INLINE static bool test_all_unset(const BVEC_NAME &a) {
    return _mm512_kortestz(a.val_, a.val_);
  }
  VEC_INLINE static bool test_any_set(const BVEC_NAME &a) {
    return ! test_all_unset(a);
  }
  VEC_INLINE static bool test_at(const BVEC_NAME &a, int i) {
    assert(i < FVEC_LEN);
    return a.val_ & (1 << i);
  }
  VEC_INLINE BVEC_NAME operator &(const BVEC_NAME &b) const {
    return _mm512_kand(val_, b.val_);
  }
  VEC_INLINE BVEC_NAME operator |(const BVEC_NAME &b) const {
    return _mm512_kor(val_, b.val_);
  }
  VEC_INLINE BVEC_NAME operator ~() const {
    return _mm512_knot(val_);
  }
};

class IVEC_NAME {
  friend class FVEC_NAME;
  friend class AVEC_NAME;
# if FVEC_LEN==16
  friend class avec16pd;
# endif
  __m512i val_;
  VEC_INLINE IVEC_NAME(const __m512i &v) : val_(v) {}
public:
  static const int VL = 16;
  VEC_INLINE IVEC_NAME() {}

  #define IVEC_MASK_BINFN_B(the_name)                                \
    VEC_INLINE static BVEC_NAME the_name(const IVEC_NAME &a,         \
      const IVEC_NAME &b) {                                          \
      return _mm512_##the_name##_epi32_mask(a.val_, b.val_);         \
    }                                                                \
    VEC_INLINE static BVEC_NAME mask_##the_name(                        \
                                                const BVEC_NAME &mask,  \
                                                  const IVEC_NAME &a,   \
                                                  const IVEC_NAME &b    \
                                                  ) {                   \
      return _mm512_mask_##the_name##_epi32_mask(                       \
      mask.val_, a.val_, b.val_);                                       \
    }
  IVEC_MASK_BINFN_B(cmpeq)
  IVEC_MASK_BINFN_B(cmplt)
  IVEC_MASK_BINFN_B(cmpneq)
  IVEC_MASK_BINFN_B(cmpgt)

  #define IVEC_MASK_BINFN_I(the_name)                                   \
    VEC_INLINE static IVEC_NAME mask_##the_name(                        \
        const IVEC_NAME &src, const BVEC_NAME &mask,                    \
        const IVEC_NAME &a, const IVEC_NAME &b                          \
    ) {                                                                 \
       return _mm512_mask_##the_name##_epi32(                           \
        src.val_, mask.val_, a.val_, b.val_);                           \
    }
  IVEC_MASK_BINFN_I(add)
  VEC_INLINE static IVEC_NAME mask_blend(
      const BVEC_NAME &mask, const IVEC_NAME &a, const IVEC_NAME &b
  ) {
    return _mm512_mask_blend_epi32(mask.val_, a.val_, b.val_);
  }

  #define IVEC_BINFN_I(the_name)                                     \
    VEC_INLINE static IVEC_NAME the_name(const IVEC_NAME &a,         \
                                         const IVEC_NAME &b) {       \
      return _mm512_##the_name##_epi32(a.val_, b.val_);              \
    }
  IVEC_BINFN_I(mullo)
  IVEC_BINFN_I(srlv)
  VEC_INLINE static IVEC_NAME the_and(const IVEC_NAME &a, const IVEC_NAME &b) {
    return _mm512_and_epi32(a.val_, b.val_);
  }

  VEC_INLINE static IVEC_NAME mask_expand(
      const IVEC_NAME &src, const BVEC_NAME &a, const IVEC_NAME &b
  ) {
    return _mm512_mask_expand_epi32(src.val_,
      a.val_, b.val_);
  }
  VEC_INLINE static IVEC_NAME masku_compress(
      const BVEC_NAME &a, const IVEC_NAME &b
  ) {
    return _mm512_mask_compress_epi32(_mm512_undefined_epi32(), a.val_, b.val_);
  }

  VEC_INLINE static int at(const IVEC_NAME &a, int b) {
    int data[16] __attribute__((aligned(64)));
    _mm512_store_epi32(data, a.val_);
    return data[b];
  }

  VEC_INLINE static IVEC_NAME load(const int * src) {
    return _mm512_load_epi32(src);
  }
  VEC_INLINE static IVEC_NAME mask_loadu(const BVEC_NAME &mask,
                                         const int * src) {
    assert((mask.val_ & (mask.val_ + 1)) == 0);
    assert(mask.val_ <= BVEC_NAME::full().val_);
    return _mm512_mask_loadu_epi32(_mm512_undefined_epi32(), mask.val_, src);
  }
  VEC_INLINE static IVEC_NAME maskz_loadu(const BVEC_NAME &mask,
                                          const int * src) {
    assert((mask.val_ & (mask.val_ + 1)) == 0);
    assert(mask.val_ <= BVEC_NAME::full().val_);
    return _mm512_maskz_loadu_epi32(mask.val_, src);
  }
  VEC_INLINE static void mask_storeu(const BVEC_NAME &mask, int * dest,
    const IVEC_NAME &src) {
    assert((mask.val_ & (mask.val_ + 1)) == 0);
    assert(mask.val_ <= BVEC_NAME::full().val_);
    _mm512_mask_storeu_epi32(dest, mask.val_, src.val_);
  }
  VEC_INLINE static void store(int * dest, const IVEC_NAME &src) {
    _mm512_store_epi32(dest, src.val_);
  }

  VEC_INLINE static IVEC_NAME mask_gather(
      const IVEC_NAME &src, const BVEC_NAME &mask, const IVEC_NAME &idx,
      const int * mem, const int scale
  ) {
    assert(mask.val_ <= BVEC_NAME::full().val_);
    assert(scale == sizeof(int));
    return _mm512_mask_i32gather_epi32(src.val_, mask.val_, idx.val_, mem,
      sizeof(int));
  }
  VEC_INLINE static void mask_i32scatter(
      int * mem, const BVEC_NAME &mask, const IVEC_NAME &idx,
      const IVEC_NAME &a, const int scale
  ) {
    assert(mask.val_ <= BVEC_NAME::full().val_);
    assert(scale == sizeof(int));
    _mm512_mask_i32scatter_epi32(mem, mask.val_, idx.val_, a.val_, sizeof(int));
  }

  VEC_INLINE static void mask_compressstore(const BVEC_NAME &mask, int * dest,
    const IVEC_NAME &src) {
    _mm512_mask_compressstoreu_epi32(dest, mask.val_, src.val_);
  }

  VEC_INLINE static IVEC_NAME set1(int i) {
    return _mm512_set1_epi32(i);
  }
  VEC_INLINE static IVEC_NAME setzero() {
    return _mm512_setzero_epi32();
  }
  VEC_INLINE static IVEC_NAME undefined() {
    return _mm512_undefined_epi32();
  }

  VEC_INLINE IVEC_NAME operator +(const IVEC_NAME &b) const {
    return _mm512_add_epi32(this->val_, b.val_);
  }
  VEC_INLINE static void print(const char * str, const IVEC_NAME &a) {
    int data[8] __attribute__((aligned(32)));
    store(data, a);
    printf("%s:", str);
    for (int i = 0; i < FVEC_LEN; i++) {
      printf(" %d", data[i]);
    }
    printf("\n");
  }
};

class FVEC_NAME {
  friend class AVEC_NAME;
#if FVEC_LEN==16
  friend class avec16pd;
#endif
  FVEC_VEC_T val_;
  VEC_INLINE FVEC_NAME(const FVEC_VEC_T &v) : val_(v) {}
public:
  static const int VL = FVEC_LEN;
  VEC_INLINE FVEC_NAME() {}
  VEC_INLINE static FVEC_SCAL_T at(const FVEC_NAME &a, int i) {
    assert(i < FVEC_LEN);
    FVEC_SCAL_T data[FVEC_LEN] __attribute__((aligned(64)));
    FVEC_SUFFIX(_mm512_store_)(data, a.val_);
    return data[i];
  }
  VEC_INLINE static bool fast_compress() { return true; }

  #define FVEC_MASK_BINFN_B(the_name)                                \
    VEC_INLINE static BVEC_NAME the_name(const FVEC_NAME &a,         \
                                         const FVEC_NAME &b) {       \
      return FVEC_SUFFIX_MASK(_mm512_##the_name##_)(a.val_, b.val_); \
    }                                                                \
    VEC_INLINE static BVEC_NAME mask_##the_name(                     \
        const BVEC_NAME &mask,                                       \
        const FVEC_NAME &a, const FVEC_NAME &b                       \
    ) {                                                              \
      return FVEC_SUFFIX_MASK(_mm512_mask_##the_name##_)(            \
        mask.val_, a.val_, b.val_);                                  \
    }
  FVEC_MASK_BINFN_B(cmple)
  FVEC_MASK_BINFN_B(cmplt)
  FVEC_MASK_BINFN_B(cmpneq)
  FVEC_MASK_BINFN_B(cmpnle)
  FVEC_MASK_BINFN_B(cmpnlt)

  #define FVEC_UNFN_F(the_name)                                      \
    VEC_INLINE static FVEC_NAME the_name(const FVEC_NAME &a) {       \
      return FVEC_SUFFIX(_mm512_##the_name##_)(a.val_);              \
    }
  FVEC_UNFN_F(abs)
  FVEC_UNFN_F(exp)
  FVEC_UNFN_F(invsqrt)
  FVEC_UNFN_F(recip)
  FVEC_UNFN_F(sqrt)

  #define FVEC_MASK_UNFN_F(the_name)                                 \
    VEC_INLINE static FVEC_NAME mask_##the_name(                     \
        const FVEC_NAME &src, const BVEC_NAME &mask,                 \
        const FVEC_NAME &a                                           \
    ) {                                                              \
      return FVEC_SUFFIX(_mm512_mask_##the_name##_)(                 \
        src.val_, mask.val_, a.val_);                                \
    }
  FVEC_MASK_UNFN_F(cos)
  FVEC_MASK_UNFN_F(recip)
  FVEC_MASK_UNFN_F(sqrt)

  #define FVEC_BINFN_F(the_name)                                     \
    VEC_INLINE static FVEC_NAME the_name(const FVEC_NAME &a,         \
                                         const FVEC_NAME &b) {       \
      return FVEC_SUFFIX(_mm512_##the_name##_)(a.val_, b.val_);      \
    }
  FVEC_BINFN_F(max)
  FVEC_BINFN_F(min)

  #define FVEC_MASK_BINFN_F(the_name)                                \
    VEC_INLINE static FVEC_NAME mask_##the_name(                     \
        const FVEC_NAME &src, const BVEC_NAME &mask,                 \
        const FVEC_NAME &a, const FVEC_NAME &b                       \
    ) {                                                              \
      return FVEC_SUFFIX(_mm512_mask_##the_name##_)(                 \
        src.val_, mask.val_, a.val_, b.val_);                        \
    }
  FVEC_MASK_BINFN_F(add)
  FVEC_MASK_BINFN_F(div)
  FVEC_MASK_BINFN_F(mul)
  FVEC_MASK_BINFN_F(sub)
  VEC_INLINE static FVEC_NAME mask_blend(
      const BVEC_NAME &mask, const FVEC_NAME &a, const FVEC_NAME &b
  ) {
    return FVEC_SUFFIX(_mm512_mask_blend_)(mask.val_, a.val_, b.val_);
  }

  VEC_INLINE static FVEC_NAME mask_expand(
      const FVEC_NAME &src, const BVEC_NAME &a, const FVEC_NAME &b
  ) {
    return FVEC_SUFFIX(_mm512_mask_expand_)(src.val_,
      a.val_, b.val_);
  }
  VEC_INLINE static FVEC_NAME masku_compress(
      const BVEC_NAME &a, const FVEC_NAME &b
  ) {
    return FVEC_SUFFIX(_mm512_mask_compress_)(FVEC_SUFFIX(_mm512_undefined_)(),
                                                a.val_, b.val_);
  }

  VEC_INLINE static FVEC_NAME set1(const FVEC_SCAL_T &a) {
    return FVEC_SUFFIX(_mm512_set1_)(a);
  }
  VEC_INLINE static FVEC_NAME setzero() {
    return FVEC_SUFFIX(_mm512_setzero_)();
  }
  VEC_INLINE static FVEC_NAME undefined() {
    return FVEC_SUFFIX(_mm512_undefined_)();
  }

  VEC_INLINE static FVEC_NAME load(const FVEC_SCAL_T *mem) {
    return FVEC_SUFFIX(_mm512_load_)(mem);
  }
  VEC_INLINE static void mask_storeu(const BVEC_NAME &mask, FVEC_SCAL_T * dest,
                                       const FVEC_NAME &a) {
    FVEC_SUFFIX(_mm512_mask_storeu_)(dest, mask.val_, a.val_);
  }
  VEC_INLINE static void store(FVEC_SCAL_T * dest, const FVEC_NAME &a) {
    FVEC_SUFFIX(_mm512_store_)(dest, a.val_);
  }

  VEC_INLINE static FVEC_NAME gather(const IVEC_NAME &idx,
                                     const FVEC_SCAL_T * mem,
                                     const int scale) {
    assert(scale == sizeof(FVEC_SCAL_T));
#   if FVEC_LEN==8
    return FVEC_SUFFIX(_mm512_i32logather_)(idx.val_, mem, sizeof(FVEC_SCAL_T));
#   else
    return FVEC_SUFFIX(_mm512_i32gather_)(idx.val_, mem, sizeof(FVEC_SCAL_T));
#   endif
  }
  VEC_INLINE static FVEC_NAME mask_gather(
      const FVEC_NAME &src, const BVEC_NAME &mask, const IVEC_NAME &idx,
      const FVEC_SCAL_T * mem, const int scale
  ) {
    assert(scale == sizeof(FVEC_SCAL_T));
#   if FVEC_LEN==8
    return FVEC_SUFFIX(_mm512_mask_i32logather_)(src.val_, mask.val_, idx.val_,
                       mem, sizeof(FVEC_SCAL_T));
#   else
    return FVEC_SUFFIX(_mm512_mask_i32gather_)(src.val_, mask.val_, idx.val_,
                       mem, sizeof(FVEC_SCAL_T));
#   endif
  }

  VEC_INLINE static void gather_3_adjacent(const IVEC_NAME &idx,
                                           const FVEC_SCAL_T * mem,
                                           const int scale,
                                           FVEC_NAME * out_0,
                                           FVEC_NAME * out_1,
                                           FVEC_NAME * out_2) {
    assert(scale == sizeof(FVEC_SCAL_T));
    *out_0 = FVEC_NAME::gather(idx, mem + 0, scale);
    *out_1 = FVEC_NAME::gather(idx, mem + 1, scale);
    *out_2 = FVEC_NAME::gather(idx, mem + 2, scale);
  }
  VEC_INLINE static void gather_4_adjacent(const IVEC_NAME &idx,
                                           const FVEC_SCAL_T * mem,
                                           const int scale, FVEC_NAME * out_0,
                                           FVEC_NAME * out_1,
                                           FVEC_NAME * out_2,
                                           FVEC_NAME * out_3) {
    assert(scale == sizeof(FVEC_SCAL_T));
    *out_0 = FVEC_NAME::gather(idx, mem + 0, scale);
    *out_1 = FVEC_NAME::gather(idx, mem + 1, scale);
    *out_2 = FVEC_NAME::gather(idx, mem + 2, scale);
    *out_3 = FVEC_NAME::gather(idx, mem + 3, scale);
  }

  VEC_INLINE static FVEC_SCAL_T mask_reduce_add(const BVEC_NAME &mask,
                                                const FVEC_NAME &a) {
    return FVEC_SUFFIX(_mm512_mask_reduce_add_)(mask.val_, a.val_);
  }
  VEC_INLINE static FVEC_SCAL_T reduce_add(const FVEC_NAME &a) {
    return FVEC_SUFFIX(_mm512_reduce_add_)(a.val_);
  }

  VEC_INLINE static IVEC_NAME unpackloepi32(const FVEC_NAME &a) {
#   if FVEC_LEN==8
    return _mm512_maskz_compress_epi32(0x5555, _mm512_castpd_si512(a.val_));
#   else
    return _mm512_castps_si512(a.val_);
#   endif
  }

  VEC_INLINE static FVEC_NAME mask_sincos(
      FVEC_NAME * cos, const FVEC_NAME &src_a, const FVEC_NAME &src_b,
      const BVEC_NAME &mask, const FVEC_NAME &arg
  ) {
    return FVEC_SUFFIX(_mm512_mask_sincos_)(&cos->val_, src_a.val_, src_b.val_,
      mask.val_, arg.val_);
  }

  #define FVEC_BINOP(the_sym, the_name)                                      \
    VEC_INLINE inline FVEC_NAME operator the_sym(const FVEC_NAME &b) const { \
    return FVEC_SUFFIX(_mm512_##the_name##_)(this->val_, b.val_);            \
  }
  FVEC_BINOP(+, add)
  FVEC_BINOP(-, sub)
  FVEC_BINOP(*, mul)
  FVEC_BINOP(/, div)

  VEC_INLINE static void gather_prefetch0(const IVEC_NAME &a, void * mem) {
    #ifdef __AVX512PF__
    _mm512_mask_prefetch_i32gather_ps(a.val_, BVEC_NAME::full().val_, mem,
      sizeof(FVEC_SCAL_T), _MM_HINT_T0);
    #endif
  }
};

class AVEC_NAME {
  FVEC_VEC_T val_;
  VEC_INLINE AVEC_NAME(const FVEC_VEC_T &a) : val_(a) {}
public:
  VEC_INLINE AVEC_NAME(const FVEC_NAME &a) : val_(a.val_) {}
  VEC_INLINE static AVEC_NAME undefined() {
    return FVEC_SUFFIX(_mm512_undefined_)();
  }
  VEC_INLINE static AVEC_NAME mask_gather(
      const AVEC_NAME &src, const BVEC_NAME &mask, const IVEC_NAME &idx,
      const FVEC_SCAL_T * mem, const int scale
  ) {
    assert(scale == sizeof(FVEC_SCAL_T));
#   if FVEC_LEN==8
    return FVEC_SUFFIX(_mm512_mask_i32logather_)(src.val_, mask.val_, idx.val_,
                                                 mem, sizeof(FVEC_SCAL_T));
#   else
    return FVEC_SUFFIX(_mm512_mask_i32gather_)(src.val_, mask.val_, idx.val_,
                                               mem, sizeof(FVEC_SCAL_T));
#   endif
  }
  VEC_INLINE static void mask_i32loscatter(
      FVEC_SCAL_T * mem, const BVEC_NAME &mask, const IVEC_NAME &idx,
      const AVEC_NAME &a, const int scale
  ) {
    assert(scale == sizeof(FVEC_SCAL_T));
#   if FVEC_LEN==8
    FVEC_SUFFIX(_mm512_mask_i32loscatter_)(mem, mask.val_, idx.val_, a.val_,
                                           sizeof(FVEC_SCAL_T));
#   else
    FVEC_SUFFIX(_mm512_mask_i32scatter_)(mem, mask.val_, idx.val_, a.val_,
                                         sizeof(FVEC_SCAL_T));
#   endif
  }

  #define AVEC_BINOP(the_sym, the_name)                                      \
    VEC_INLINE inline AVEC_NAME operator the_sym(const AVEC_NAME &b) const { \
    return FVEC_SUFFIX(_mm512_##the_name##_)(this->val_, b.val_);            \
  }
  AVEC_BINOP(-, sub)

  VEC_INLINE static void gather_prefetch0(const IVEC_NAME &a, void * mem) {
    _mm512_mask_prefetch_i32gather_ps(a.val_, BVEC_NAME::full().val_, mem,
                                      sizeof(FVEC_SCAL_T), _MM_HINT_T0);
  }
};

#if FVEC_LEN==16
class avec16pd {
  __m512d lo_, hi_;
  VEC_INLINE avec16pd(const __m512d &lo, const __m512d &hi) : lo_(lo), hi_(hi)
    {}
  VEC_INLINE static __mmask8 get_bvec_hi(__mmask16 a) {
    return a >> 8;
  }
  VEC_INLINE static __m512i get_ivec_hi(__m512i a) {
    return _mm512_permute4f128_epi32(a, _MM_PERM_BADC);
  }
public:
  VEC_INLINE avec16pd(const FVEC_NAME &a) {
    lo_ = _mm512_cvtpslo_pd(a.val_);
    hi_ = _mm512_cvtpslo_pd(_mm512_permute4f128_ps(a.val_, _MM_PERM_BADC));
  }
  VEC_INLINE static avec16pd undefined() {
    return avec16pd(_mm512_undefined_pd(), _mm512_undefined_pd());
  }
  VEC_INLINE static avec16pd mask_gather(
      const avec16pd &src, const BVEC_NAME &mask, const IVEC_NAME &idx,
      const double * mem, const int scale
  ) {
    assert(scale == sizeof(double));
    __m512d lo = _mm512_mask_i32logather_pd(src.lo_, mask.val_, idx.val_, mem,
                                            sizeof(double));
    __m512d hi = _mm512_mask_i32logather_pd(src.hi_, get_bvec_hi(mask.val_),
                                            get_ivec_hi(idx.val_), mem,
                                            sizeof(double));
    return avec16pd(lo, hi);
  }
  VEC_INLINE static void mask_i32loscatter(
      double * mem, const BVEC_NAME &mask, const IVEC_NAME &idx,
      const avec16pd &a, const int scale
  ) {
    assert(scale == sizeof(double));
    _mm512_mask_i32loscatter_pd(mem, mask.val_, idx.val_, a.lo_,
                                sizeof(double));
    _mm512_mask_i32loscatter_pd(mem, get_bvec_hi(mask.val_),
                                get_ivec_hi(idx.val_), a.hi_, sizeof(double));
  }

  #define AVEC2_BINOP(the_sym, the_name)                                    \
    VEC_INLINE inline avec16pd operator the_sym(const avec16pd &b) const {  \
    __m512d lo = _mm512_##the_name##_pd(this->lo_, b.lo_);                  \
    __m512d hi = _mm512_##the_name##_pd(this->hi_, b.hi_);                  \
    return avec16pd(lo, hi);                                                \
  }
  AVEC2_BINOP(-, sub)

  VEC_INLINE static void gather_prefetch0(const IVEC_NAME &a, void * mem) {
    _mm512_mask_prefetch_i32gather_ps(a.val_, BVEC_NAME::full().val_, mem,
                                      sizeof(double), _MM_HINT_T0);
  }
};
#endif

}


#ifdef FVEC_FIRST_PASS

template<typename flt_t, typename acc_t>
struct intr_types;

template<>
struct intr_types<double,double> {
  typedef mm512::fvec8pd fvec;
  typedef mm512::ivec8 ivec;
  typedef mm512::bvec8 bvec;
  typedef mm512::avec8pd avec;
};

template<>
struct intr_types<float,float> {
  typedef mm512::fvec16ps fvec;
  typedef mm512::ivec16 ivec;
  typedef mm512::bvec16 bvec;
  typedef mm512::avec16ps avec;
};

template<>
struct intr_types<float,double> {
  typedef mm512::fvec16ps fvec;
  typedef mm512::ivec16 ivec;
  typedef mm512::bvec16 bvec;
  typedef mm512::avec16pd avec;
};

#endif


#ifndef FVEC_FIRST_PASS
#  define FVEC_FIRST_PASS
#  include "intel_intrinsics_airebo.h"
#endif

#endif

#ifdef LMP_INTEL_AIREBO_256

#include <cassert>
#include <immintrin.h>
#include <stdint.h> // <cstdint> requires C++-11

#define VEC_INLINE __attribute__((always_inline))


#ifndef FVEC_FIRST_PASS
#  define FVEC_LEN 4
#  define FVEC_SUFFIX(a) a##pd
#  define FVEC_MASK_T __m256d
#  define FVEC_VEC_T __m256d
#  define FVEC_SCAL_T double
#  define IVEC_NAME ivec4
#  define FVEC_NAME fvec4pd
#  define BVEC_NAME bvec4
#  define AVEC_NAME avec4pd
#else
#  undef FVEC_LEN
#  undef FVEC_SUFFIX
#  undef FVEC_SUFFIX_MASK
#  undef FVEC_MASK_T
#  undef FVEC_VEC_T
#  undef FVEC_SCAL_T
#  undef IVEC_NAME
#  undef FVEC_NAME
#  undef BVEC_NAME
#  undef AVEC_NAME

#  define FVEC_LEN 8
#  define FVEC_SUFFIX(a) a##ps
#  define FVEC_MASK_T __m256
#  define FVEC_VEC_T __m256
#  define FVEC_SCAL_T float
#  define IVEC_NAME ivec8
#  define FVEC_NAME fvec8ps
#  define BVEC_NAME bvec8
#  define AVEC_NAME avec8ps
#endif



namespace mm256 {

//#define __AVX2__ __AVX2__

#if !defined(__AVX2__) && !defined(FVEC_FIRST_PASS)

#define IVEC_EM_BIN(op) \
  __m128i a_lo = _mm256_castsi256_si128(a);  \
  __m128i b_lo = _mm256_castsi256_si128(b);  \
  __m128i a_hi = _mm256_extractf128_si256(a, 1);  \
  __m128i b_hi = _mm256_extractf128_si256(b, 1);  \
  __m128i c_lo = op(a_lo, b_lo); \
  __m128i c_hi = op(a_hi, b_hi); \
  __m256i ret = _mm256_setr_m128i(c_lo, c_hi); \
  return ret;

VEC_INLINE inline __m256i _cm256_add_epi32(const __m256i &a, const __m256i &b) {
  IVEC_EM_BIN(_mm_add_epi32)
}

VEC_INLINE inline __m256i _cm256_and_si256(const __m256i &a, const __m256i &b) {
  IVEC_EM_BIN(_mm_and_si128)
}

VEC_INLINE inline __m256i _cm256_andnot_si256(const __m256i &a,
                                              const __m256i &b) {
  IVEC_EM_BIN(_mm_andnot_si128)
}

VEC_INLINE inline __m256i _cm256_cmpeq_epi32(const __m256i &a,
                                             const __m256i &b) {
  IVEC_EM_BIN(_mm_cmpeq_epi32)
}

VEC_INLINE inline __m256i _cm256_cmpgt_epi32(const __m256i &a,
                                             const __m256i &b) {
  IVEC_EM_BIN(_mm_cmpgt_epi32)
}

VEC_INLINE inline __m256i _cm256_cvtepu8_epi32(const __m128i &a) {
  __m128i a_hi = _mm_castps_si128(_mm_permute_ps(_mm_castsi128_ps(a), 1));
  __m128i c_lo = _mm_cvtepu8_epi32(a);
  __m128i c_hi = _mm_cvtepu8_epi32(a_hi);
  __m256i ret = _mm256_setr_m128i(c_lo, c_hi);
  return ret;

}

#define IVEC_EM_SCAL(op)                       \
  int buf_a[8] __attribute__((aligned(32)));   \
  int buf_b[8] __attribute__((aligned(32)));   \
  int dest[8] __attribute__((aligned(32)));    \
  _mm256_store_si256((__m256i*)buf_a, a);      \
  _mm256_store_si256((__m256i*)buf_b, b);      \
  for (int i = 0; i < 8; i++) {                \
    dest[i] = op;                              \
  }                                            \
  return _mm256_load_si256((__m256i*) dest);

VEC_INLINE inline __m256i _cm256_permutevar8x32_epi32(const __m256i &a,
                                                      const __m256i &b) {
  IVEC_EM_SCAL(buf_a[buf_b[i]])
}

VEC_INLINE inline __m256i _cm256_mullo_epi32(__m256i a, __m256i b) {
  IVEC_EM_BIN(_mm_mullo_epi32)
}

VEC_INLINE inline __m256i _cm256_srlv_epi32(__m256i a, __m256i b) {
  IVEC_EM_SCAL(buf_a[i] >> buf_b[i])
}


VEC_INLINE inline __m256 _cm256_permutevar8x32_ps(const __m256 &a,
                                                  const __m256i &b) {
  return _mm256_castsi256_ps(_cm256_permutevar8x32_epi32(_mm256_castps_si256(a),
                                                         b));
}

VEC_INLINE inline __m128i _cm_maskload_epi32(int const * mem, __m128i mask) {
  return _mm_castps_si128(_mm_maskload_ps((float const *) mem, mask));
}

VEC_INLINE inline __m256i _cm256_maskload_epi32(int const * mem, __m256i mask) {
  __m128i a_lo = _mm256_castsi256_si128(mask);
  __m128i a_hi = _mm256_extractf128_si256(mask, 1);
  __m128i c_lo = _cm_maskload_epi32(mem, a_lo);
  __m128i c_hi = _cm_maskload_epi32(mem + 4, a_hi);
  __m256i ret = _mm256_setr_m128i(c_lo, c_hi);
  return ret;
}


VEC_INLINE inline __m256i _cm256_mask_i32gather_epi32(__m256i src,
                                                      int const * base_addr,
                                                      __m256i index,
                                                      __m256i mask,
                                                      const int scale) {
  assert(scale == sizeof(int));
  int buf_index[8] __attribute__((aligned(32)));
  int buf_mask[8] __attribute__((aligned(32)));
  int dest[8] __attribute__((aligned(32)));
  _mm256_store_si256((__m256i*)dest, src);
  _mm256_store_si256((__m256i*)buf_index, index);
  _mm256_store_si256((__m256i*)buf_mask, mask);
  for (int i = 0; i < 8; i++) {
    if (buf_mask[i]) dest[i] = base_addr[buf_index[i]];
  }
  return _mm256_load_si256((__m256i*) dest);
}

VEC_INLINE inline __m256 _cm256_mask_i32gather_ps(__m256 src,
                                                  float const * base_addr,
                                                  __m256i index, __m256 mask,
                                                  const int scale) {
  return _mm256_castsi256_ps(_cm256_mask_i32gather_epi32(
    _mm256_castps_si256(src), (const int *) base_addr, index,
    _mm256_castps_si256(mask), scale));
}

VEC_INLINE inline __m256d _cm256_mask_i32gather_pd(__m256d src,
                                                   double const * base_addr,
                                                   __m128i index, __m256d mask,
                                                   const int scale) {
  assert(scale == sizeof(double));
  int buf_index[4] __attribute__((aligned(32)));
  int buf_mask[8] __attribute__((aligned(32)));
  double dest[4] __attribute__((aligned(32)));
  _mm256_store_pd(dest, src);
  _mm_store_si128((__m128i*)buf_index, index);
  _mm256_store_si256((__m256i*)buf_mask, _mm256_castpd_si256(mask));
  for (int i = 0; i < 4; i++) {
    if (buf_mask[2*i]) dest[i] = base_addr[buf_index[i]];
  }
  return _mm256_load_pd(dest);
}

VEC_INLINE inline __m256i _cm256_i32gather_epi32(int const * base_addr,
                                                 __m256i index,
                                                 const int scale) {
  assert(scale == sizeof(int));
  int buf_index[8] __attribute__((aligned(32)));
  int dest[8] __attribute__((aligned(32)));
  _mm256_store_si256((__m256i*)buf_index, index);
  for (int i = 0; i < 8; i++) {
    dest[i] = base_addr[buf_index[i]];
  }
  return _mm256_load_si256((__m256i*) dest);
}

VEC_INLINE inline __m256 _cm256_i32gather_ps(float const * base_addr,
                                             __m256i index, const int scale) {
  return _mm256_castsi256_ps(_cm256_i32gather_epi32((const int *) base_addr,
                                                    index, scale));
}

VEC_INLINE inline __m256d _cm256_i32gather_pd(double const * base_addr,
                                              __m128i index, const int scale) {
  assert(scale == sizeof(double));
  int buf_index[4] __attribute__((aligned(32)));
  double dest[4] __attribute__((aligned(32)));
  _mm_store_si128((__m128i*)buf_index, index);
  for (int i = 0; i < 4; i++) {
    dest[i] = base_addr[buf_index[i]];
  }
  return _mm256_load_pd(dest);
}

VEC_INLINE inline uint64_t _cdep_u64(uint64_t tmp, uint64_t mask) {
  uint64_t dst = 0;
  uint64_t k = 0;
  const uint64_t one = 1;
  const uint64_t zero = 0;
  for (uint64_t m = 0; m < 64; m++) {
    if (mask & (one << m)) {
      dst |= static_cast<uint64_t>((tmp & (one << k)) != zero) << m;
      k += 1;
    }
  }
  return dst;
}

VEC_INLINE inline uint64_t _cext_u64(uint64_t tmp, uint64_t mask) {
  uint64_t dst = 0;
  uint64_t k = 0;
  const uint64_t one = 1;
  const uint64_t zero = 0;
  for (uint64_t m = 0; m < 64; m++) {
    if (mask & (one << m)) {
      dst |= static_cast<uint64_t>((tmp & (one << m)) != zero) << k;
      k += 1;
    }
  }
  return dst;
}

#define _mm256_add_epi32 _cm256_add_epi32
#define _mm256_and_si256 _cm256_and_si256
#define _mm256_andnot_si256 _cm256_andnot_si256
#define _mm256_cmpeq_epi32 _cm256_cmpeq_epi32
#define _mm256_cmpgt_epi32 _cm256_cmpgt_epi32
#define _mm256_permutevar8x32_epi32 _cm256_permutevar8x32_epi32
#define _mm256_permutevar8x32_ps _cm256_permutevar8x32_ps
#define _mm_maskload_epi32 _cm_maskload_epi32
#define _mm256_maskload_epi32 _cm256_maskload_epi32
#define _mm256_mullo_epi32 _cm256_mullo_epi32
#define _mm256_srlv_epi32 _cm256_srlv_epi32
#define _mm256_mask_i32gather_epi32 _cm256_mask_i32gather_epi32
#define _mm256_mask_i32gather_pd _cm256_mask_i32gather_pd
#define _mm256_mask_i32gather_ps _cm256_mask_i32gather_ps
#define _mm256_i32gather_epi32 _cm256_i32gather_epi32
#define _mm256_i32gather_pd _cm256_i32gather_pd
#define _mm256_i32gather_ps _cm256_i32gather_ps
#define _pdep_u64 _cdep_u64
#define _pext_u64 _cext_u64
#define _mm256_cvtepu8_epi32 _cm256_cvtepu8_epi32

#endif

#ifndef FVEC_FIRST_PASS

VEC_INLINE inline __m256 _mm256_compress_ps(__m256 mask, __m256 a) {
# ifdef __AVX2__
  uint64_t expanded_mask = _pdep_u64(_mm256_movemask_ps(mask),
                                     0x0101010101010101);
  // unpack each bit to a byte
  expanded_mask *= 0xFF;   // mask |= mask<<1 | mask<<2 | ... | mask<<7;
  // the identity shuffle for vpermps, packed to one index per byte
  const uint64_t identity_indices = 0x0706050403020100;
  uint64_t wanted_indices = _pext_u64(identity_indices, expanded_mask);

  __m128i bytevec = _mm_cvtsi64_si128(wanted_indices);
  __m256i shufmask = _mm256_cvtepu8_epi32(bytevec);

  return _mm256_permutevar8x32_ps(a, shufmask);
# else
  int mask_buf[8] __attribute__((aligned(32)));
  float a_buf[8] __attribute__((aligned(32)));
  float dst_buf[8] __attribute__((aligned(32)));
  _mm256_store_si256((__m256i*) mask_buf, _mm256_castps_si256(mask));
  _mm256_store_ps(a_buf, a);
  int k = 0;
  for (int i = 0; i < 8; i++) {
    if (mask[i]) {
      dst_buf[k++] = a_buf[i];
    }
  }
  return _mm256_load_ps(dst_buf);
# endif
}
VEC_INLINE inline __m256 _mm256_expand_ps(__m256 mask, __m256 a) {
# ifdef __AVX2__
  uint64_t expanded_mask = _pdep_u64(_mm256_movemask_ps(mask),
                                     0x0101010101010101);
  expanded_mask *= 0xFF;
  const uint64_t identity_indices = 0x0706050403020100;
  uint64_t wanted_indices = _pdep_u64(identity_indices, expanded_mask);
  __m128i bytevec = _mm_cvtsi64_si128(wanted_indices);
  __m256i shufmask = _mm256_cvtepu8_epi32(bytevec);
  return _mm256_permutevar8x32_ps(a, shufmask);
# else
  int mask_buf[8] __attribute__((aligned(32)));
  float a_buf[8] __attribute__((aligned(32)));
  float dst_buf[8] __attribute__((aligned(32))) = {0};
  _mm256_store_si256((__m256i*) mask_buf, _mm256_castps_si256(mask));
  _mm256_store_ps(a_buf, a);
  int k = 0;
  for (int i = 0; i < 8; i++) {
    if (mask[i]) {
      dst_buf[i] = a_buf[k++];
    }
  }
  return _mm256_load_ps(dst_buf);
# endif
}

VEC_INLINE inline __m256d _mm256_compress_pd(__m256d mask, __m256d a) {
  return _mm256_castps_pd(_mm256_compress_ps(_mm256_castpd_ps(mask),
                                             _mm256_castpd_ps(a)));
}
VEC_INLINE inline __m256d _mm256_expand_pd(__m256d mask, __m256d a) {
  return _mm256_castps_pd(_mm256_expand_ps(_mm256_castpd_ps(mask),
                                           _mm256_castpd_ps(a)));
}
#endif


class FVEC_NAME;
class IVEC_NAME;
class AVEC_NAME;
class BVEC_NAME {
  friend class FVEC_NAME;
  friend class IVEC_NAME;
  friend class AVEC_NAME;
# if FVEC_LEN==8
  friend class avec8pd;
# endif
  FVEC_MASK_T val_;
  VEC_INLINE BVEC_NAME(const FVEC_MASK_T &v) : val_(v) {}
  VEC_INLINE BVEC_NAME(const __m256i &v) : val_(FVEC_SUFFIX(_mm256_castsi256_)
                                                (v)) {}
public:
  VEC_INLINE BVEC_NAME() {}
  VEC_INLINE static BVEC_NAME kand(const BVEC_NAME &a, const BVEC_NAME &b) {
    return FVEC_SUFFIX(_mm256_and_)(a.val_, b.val_);
  }
  VEC_INLINE static BVEC_NAME kandn(const BVEC_NAME &a, const BVEC_NAME &b) {
    return FVEC_SUFFIX(_mm256_andnot_)(a.val_, b.val_);
  }
  VEC_INLINE static BVEC_NAME masku_compress(const BVEC_NAME &mask,
                                             const BVEC_NAME &a) {
    return FVEC_SUFFIX(_mm256_compress_)(mask.val_, a.val_);
  }
  VEC_INLINE static BVEC_NAME mask_expand(const BVEC_NAME &src,
                                          const BVEC_NAME &mask,
                                          const BVEC_NAME &a) {
    FVEC_MASK_T ret = FVEC_SUFFIX(_mm256_expand_)(mask.val_, a.val_);
    ret = FVEC_SUFFIX(_mm256_and_)(mask.val_, ret);
    ret = FVEC_SUFFIX(_mm256_or_)(ret, FVEC_SUFFIX(_mm256_andnot_)
                                  (mask.val_, src.val_));
    return ret;
  }
  VEC_INLINE static BVEC_NAME full() {
    __m256i a = _mm256_undefined_si256();
    return FVEC_SUFFIX(_mm256_castsi256_)(_mm256_cmpeq_epi32(a, a));
  }
  VEC_INLINE static BVEC_NAME empty() {
    return FVEC_SUFFIX(_mm256_setzero_)();
  }
  VEC_INLINE static BVEC_NAME only(int n) {
    static const unsigned int FULL_ps = (unsigned int) -1;
    static const unsigned int LUT_ps[9][8] = {
      {0, 0, 0, 0, 0, 0, 0, 0},
      {FULL_ps, 0, 0, 0, 0, 0, 0, 0},
      {FULL_ps, FULL_ps, 0, 0, 0, 0, 0, 0},
      {FULL_ps, FULL_ps, FULL_ps, 0, 0, 0, 0, 0},
      {FULL_ps, FULL_ps, FULL_ps, FULL_ps, 0, 0, 0, 0},
      {FULL_ps, FULL_ps, FULL_ps, FULL_ps, FULL_ps, 0, 0, 0},
      {FULL_ps, FULL_ps, FULL_ps, FULL_ps, FULL_ps, FULL_ps, 0, 0},
      {FULL_ps, FULL_ps, FULL_ps, FULL_ps, FULL_ps, FULL_ps, FULL_ps, 0},
      {FULL_ps, FULL_ps, FULL_ps, FULL_ps, FULL_ps, FULL_ps, FULL_ps, FULL_ps},
    };
    static const unsigned long long FULL_pd = (unsigned long long) -1;
    static const unsigned long long LUT_pd[5][4] = {
      {0, 0, 0, 0},
      {FULL_pd, 0, 0, 0},
      {FULL_pd, FULL_pd, 0, 0},
      {FULL_pd, FULL_pd, FULL_pd, 0},
      {FULL_pd, FULL_pd, FULL_pd, FULL_pd},
    };
    return FVEC_SUFFIX(_mm256_load_)((const FVEC_SCAL_T*) FVEC_SUFFIX(LUT_)[n]);
  }
  VEC_INLINE static BVEC_NAME after(int n) {
    static const unsigned int FULL_ps = (unsigned int) -1;
    static const unsigned int LUT_ps[9][8] = {
      {FULL_ps, FULL_ps, FULL_ps, FULL_ps, FULL_ps, FULL_ps, FULL_ps, FULL_ps},
      {0, FULL_ps, FULL_ps, FULL_ps, FULL_ps, FULL_ps, FULL_ps, FULL_ps},
      {0, 0, FULL_ps, FULL_ps, FULL_ps, FULL_ps, FULL_ps, FULL_ps},
      {0, 0, 0, FULL_ps, FULL_ps, FULL_ps, FULL_ps, FULL_ps},
      {0, 0, 0, 0, FULL_ps, FULL_ps, FULL_ps, FULL_ps},
      {0, 0, 0, 0, 0, FULL_ps, FULL_ps, FULL_ps},
      {0, 0, 0, 0, 0, 0, FULL_ps, FULL_ps},
      {0, 0, 0, 0, 0, 0, 0, FULL_ps},
      {0, 0, 0, 0, 0, 0, 0, 0},
    };
    static const unsigned long long FULL_pd = (unsigned long long) -1;
    static const unsigned long long LUT_pd[5][4] = {
      {FULL_pd, FULL_pd, FULL_pd, FULL_pd},
      {0, FULL_pd, FULL_pd, FULL_pd},
      {0, 0, FULL_pd, FULL_pd},
      {0, 0, 0, FULL_pd},
      {0, 0, 0, 0},
    };
    return FVEC_SUFFIX(_mm256_load_)((const FVEC_SCAL_T*) FVEC_SUFFIX(LUT_)[n]);
  }
  VEC_INLINE static BVEC_NAME onlyafter(int only_, int after_) {
    return kand(after(after_), only(after_ + only_));
  }
  VEC_INLINE static int popcnt(const BVEC_NAME &a) {
    return _popcnt32(FVEC_SUFFIX(_mm256_movemask_)(a.val_));
  }
  VEC_INLINE static bool test_all_unset(const BVEC_NAME &a) {
    return FVEC_SUFFIX(_mm256_testz_)(a.val_, a.val_);
  }
  VEC_INLINE static bool test_any_set(const BVEC_NAME &a) {
    return ! test_all_unset(a);
  }
  VEC_INLINE static bool test_at(const BVEC_NAME &a, int i) {
    assert(i < FVEC_LEN);
    return FVEC_SUFFIX(_mm256_movemask_)(a.val_) & (1 << i);
  }
  VEC_INLINE BVEC_NAME operator &(const BVEC_NAME &b) const {
    return FVEC_SUFFIX(_mm256_and_)(val_, b.val_);
  }
  VEC_INLINE BVEC_NAME operator |(const BVEC_NAME &b) const {
    return FVEC_SUFFIX(_mm256_or_)(val_, b.val_);
  }
  VEC_INLINE BVEC_NAME operator ~() const {
    return FVEC_SUFFIX(_mm256_andnot_)(val_, full().val_);
  }
};

class IVEC_NAME {
  friend class FVEC_NAME;
  friend class AVEC_NAME;
# if FVEC_LEN==8
  friend class avec8pd;
# endif
  __m256i val_;
  VEC_INLINE IVEC_NAME(const __m256i &v) : val_(v) {}
  VEC_INLINE static __m256i to(const FVEC_VEC_T &a) {
#   if FVEC_LEN==4
    return _mm256_castpd_si256(a);
#   else
    return _mm256_castps_si256(a);
#   endif
  }
  VEC_INLINE static FVEC_VEC_T from(const __m256i &a) {
    return FVEC_SUFFIX(_mm256_castsi256_)(a);
  }
public:
  static const int VL = 8;
  VEC_INLINE IVEC_NAME() {}

  #define IVEC_MASK_BINFN_B(the_name)                                \
    VEC_INLINE static BVEC_NAME the_name(const IVEC_NAME &a,         \
                                         const IVEC_NAME &b) {       \
      return _mm256_##the_name##_epi32(a.val_, b.val_);              \
    }                                                                \
    VEC_INLINE static BVEC_NAME mask_##the_name(                     \
        const BVEC_NAME &mask,                                       \
        const IVEC_NAME &a, const IVEC_NAME &b                       \
    ) {                                                              \
      BVEC_NAME ret = _mm256_##the_name##_epi32(                     \
        a.val_, b.val_);                                             \
      return mask & ret;                                             \
    }
  IVEC_MASK_BINFN_B(cmpeq)
  IVEC_MASK_BINFN_B(cmpgt)

  VEC_INLINE static __m256i _mm256_cmplt_epi32(__m256i a, __m256i b) {
    __m256i le = _mm256_cmpgt_epi32(b, a);
    __m256i eq = _mm256_cmpeq_epi32(a, b);
    return _mm256_andnot_si256(eq, le);
  }

  VEC_INLINE static __m256i _mm256_cmpneq_epi32(__m256i a, __m256i b) {
    __m256i eq = _mm256_cmpeq_epi32(a, b);
    __m256i t = _mm256_undefined_si256();
    __m256i f = _mm256_cmpeq_epi32(t, t);
    return _mm256_andnot_si256(eq, f);
  }

  IVEC_MASK_BINFN_B(cmplt)
  IVEC_MASK_BINFN_B(cmpneq)
  #undef IVEC_MASK_BINFN_B

  VEC_INLINE static IVEC_NAME mask_blend(
      const BVEC_NAME &mask, const IVEC_NAME &a, const IVEC_NAME &b
  ) {
    return to(FVEC_SUFFIX(_mm256_blendv_)(from(a.val_), from(b.val_),
              mask.val_));
  }
  #define IVEC_MASK_BINFN_I(the_name)                                \
    VEC_INLINE static IVEC_NAME mask_##the_name(                     \
        const IVEC_NAME &src, const BVEC_NAME &mask,                 \
        const IVEC_NAME &a, const IVEC_NAME &b                       \
    ) {                                                              \
      IVEC_NAME ret = _mm256_##the_name##_epi32(                     \
                                                a.val_, b.val_);     \
        return mask_blend(mask, src, ret);                           \
    }
  IVEC_MASK_BINFN_I(add)
  #undef IVEC_MASK_BINFN_I

  #define IVEC_BINFN_I(the_name)                                     \
    VEC_INLINE static IVEC_NAME the_name(const IVEC_NAME &a,         \
                                         const IVEC_NAME &b) {       \
      return _mm256_##the_name##_epi32(a.val_, b.val_);              \
    }
  IVEC_BINFN_I(mullo)
  IVEC_BINFN_I(srlv)
  #undef IVEC_BINFN_I
  VEC_INLINE static IVEC_NAME the_and(const IVEC_NAME &a, const IVEC_NAME &b) {
    return _mm256_and_si256(a.val_, b.val_);
  }

  VEC_INLINE static IVEC_NAME masku_compress(const BVEC_NAME &mask,
                                             const IVEC_NAME &b) {
    return to(FVEC_SUFFIX(_mm256_compress_)(mask.val_, from(b.val_)));
  }
  VEC_INLINE static IVEC_NAME mask_expand(
      const IVEC_NAME &src, const BVEC_NAME &mask, const IVEC_NAME &b
  ) {
    FVEC_VEC_T ret = FVEC_SUFFIX(_mm256_expand_)(mask.val_, from(b.val_));
    ret = FVEC_SUFFIX(_mm256_and_)(mask.val_, ret);
    ret = FVEC_SUFFIX(_mm256_or_)(ret, FVEC_SUFFIX(_mm256_andnot_)
                                    (mask.val_, from(src.val_)));
    return to(ret);
  }

  VEC_INLINE static void store(int * dest, const IVEC_NAME &src) {
    _mm256_store_si256((__m256i*)dest, src.val_);
#   if FVEC_LEN==4
    dest[1] = dest[2];
    dest[2] = dest[4];
    dest[3] = dest[6];
#   endif
  }

  VEC_INLINE static int at(const IVEC_NAME &a, int b) {
    int data[8] __attribute__((aligned(32)));
    store(data, a);
    return data[b];
  }

  VEC_INLINE static void print(const char * str, const IVEC_NAME &a) {
    int data[8] __attribute__((aligned(32)));
    store(data, a);
    printf("%s:", str);
    for (int i = 0; i < FVEC_LEN; i++) {
      printf(" %d", data[i]);
    }
    printf("\n");
  }

  VEC_INLINE static IVEC_NAME maskz_loadu(const BVEC_NAME &mask,
                                          const int * src) {
    FVEC_VEC_T mask_val = mask.val_;
#   if FVEC_LEN==4
#    ifdef __AVX2__
    static const unsigned int mask_shuffle[8] __attribute__((aligned(32))) =
      {0, 2, 4, 6, 0, 0, 0, 0};
    __m256 m = _mm256_castpd_ps(mask_val);
    m = _mm256_permutevar8x32_ps(m, _mm256_load_si256((__m256i*)mask_shuffle));
    __m128i ret = _mm_maskload_epi32(src,
       _mm256_castsi256_si128(_mm256_castps_si256(m)));
    static const unsigned int load_shuffle[8] __attribute__((aligned(32))) =
      {0, 0, 1, 1, 2, 2, 3, 3};
    return _mm256_permutevar8x32_epi32(_mm256_castsi128_si256(ret),
      _mm256_load_si256((__m256i*)load_shuffle));
#    else
    int dest[8] __attribute__((aligned(32))) = {0};
    int mask_buf[8] __attribute__((aligned(32)));
    _mm256_store_pd((double*) mask_buf, mask.val_);
    for (int i = 0; i < 4; i++) {
      if (mask_buf[2*i]) {
        int val = src[i];
        dest[2*i+0] = val;
        dest[2*i+1] = val;
      }
    }
    return _mm256_load_si256((__m256i*) dest);
#    endif
#   else
    return _mm256_maskload_epi32(src, to(mask_val));
#   endif
  }

  VEC_INLINE static IVEC_NAME mask_gather(
      const IVEC_NAME &src, const BVEC_NAME &mask, const IVEC_NAME &idx,
      const int * mem, const int scale
  ) {
    assert(scale == sizeof(int));
    return _mm256_mask_i32gather_epi32(src.val_, mem, idx.val_, to(mask.val_),
                                       sizeof(int));
  }

  VEC_INLINE static void mask_compressstore(const BVEC_NAME &mask, int * dest,
                                            const IVEC_NAME &src) {
    int buf[8] __attribute__((aligned(64)));
    const int stride = FVEC_LEN==4 ? 2 : 1;
    _mm256_store_si256((__m256i*)buf, src.val_);
    int mask_val = FVEC_SUFFIX(_mm256_movemask_)(mask.val_);
    int k = 0;
    #pragma unroll
    for (int i = 0; i < FVEC_LEN; i++) {
      if (mask_val & (1 << i))
        dest[k++] = buf[stride*i];
    }
  }

  VEC_INLINE static IVEC_NAME set1(int i) {
    return _mm256_set1_epi32(i);
  }
  VEC_INLINE static IVEC_NAME setzero() {
    return _mm256_setzero_si256();
  }
  VEC_INLINE static IVEC_NAME undefined() {
    return _mm256_undefined_si256();
  }

  VEC_INLINE IVEC_NAME operator +(const IVEC_NAME &b) const {
    return _mm256_add_epi32(this->val_, b.val_);
  }
};

class FVEC_NAME {
  friend class AVEC_NAME;
#if FVEC_LEN==8
  friend class avec8pd;
#endif
  FVEC_VEC_T val_;
  VEC_INLINE FVEC_NAME(const FVEC_VEC_T &v) : val_(v) {}
public:
  static const int VL = FVEC_LEN;
# if defined(__AVX2__) || defined(__MIC__) || defined(__AVX512F__)
  VEC_INLINE static bool fast_compress() { return true; }
# else
  VEC_INLINE static bool fast_compress() { return false; }
# endif
  VEC_INLINE FVEC_NAME() {}
  VEC_INLINE static FVEC_SCAL_T at(const FVEC_NAME &a, int i) {
    assert(i < FVEC_LEN);
    FVEC_SCAL_T data[FVEC_LEN] __attribute__((aligned(64)));
    FVEC_SUFFIX(_mm256_store_)(data, a.val_);
    return data[i];
  }

  #define FVEC_MASK_BINFN_B(the_name, the_imm)                          \
    VEC_INLINE static BVEC_NAME the_name(const FVEC_NAME &a,            \
                                         const FVEC_NAME &b) {          \
      return FVEC_SUFFIX(_mm256_cmp_)(a.val_, b.val_, the_imm);         \
    }                                                                   \
    VEC_INLINE static BVEC_NAME mask_##the_name(                        \
        const BVEC_NAME &mask,                                          \
        const FVEC_NAME &a, const FVEC_NAME &b                          \
    ) {                                                                 \
      BVEC_NAME ret = FVEC_SUFFIX(_mm256_cmp_)(                         \
        a.val_, b.val_, the_imm);                                       \
      return mask & ret;                                                \
    }
  FVEC_MASK_BINFN_B(cmple, _CMP_LE_OS)
  FVEC_MASK_BINFN_B(cmplt, _CMP_LT_OS)
  FVEC_MASK_BINFN_B(cmpneq, _CMP_NEQ_UQ)
  FVEC_MASK_BINFN_B(cmpnle, _CMP_NLE_US)
  FVEC_MASK_BINFN_B(cmpnlt, _CMP_NLT_US)
  #undef FVEC_MASK_BINFN_B

  VEC_INLINE static __m256d _mm256_recip_pd(__m256d a) {
    __m256d c_1 = _mm256_set1_pd(1);
    return _mm256_div_pd(c_1, a);
  }
  VEC_INLINE static __m256 _mm256_recip_ps(__m256 a) {
    return _mm256_rcp_ps(a);
  }
  VEC_INLINE static __m256d _mm256_abs_pd(__m256d a) {
    const unsigned long long abs_mask = 0x7FFFFFFFFFFFFFFF;
    const unsigned long long abs_full[8] =
        {abs_mask, abs_mask, abs_mask, abs_mask, abs_mask, abs_mask, abs_mask,
           abs_mask};
    return _mm256_and_pd(_mm256_load_pd((double*)abs_full), a);
  }
  VEC_INLINE static __m256 _mm256_abs_ps(__m256 a) {
    const unsigned long long abs_mask = 0x7FFFFFFF;
    const unsigned long long abs_full[16] =
        {abs_mask, abs_mask, abs_mask, abs_mask, abs_mask, abs_mask, abs_mask,
           abs_mask, abs_mask, abs_mask, abs_mask, abs_mask, abs_mask,
           abs_mask, abs_mask, abs_mask};
    return _mm256_and_ps(_mm256_load_ps((float*)abs_full), a);
  }

  #define FVEC_UNFN_F(the_name)                                      \
    VEC_INLINE static FVEC_NAME the_name(const FVEC_NAME &a) {       \
      return FVEC_SUFFIX(_mm256_##the_name##_)(a.val_);              \
    }
  FVEC_UNFN_F(abs)
  FVEC_UNFN_F(exp)
  FVEC_UNFN_F(invsqrt)
  FVEC_UNFN_F(recip)
  FVEC_UNFN_F(sqrt)
  #undef FVEC_UNFN_F

  VEC_INLINE static FVEC_NAME mask_blend(
      const BVEC_NAME &mask, const FVEC_NAME &a, const FVEC_NAME &b
  ) {
    return FVEC_SUFFIX(_mm256_blendv_)(a.val_, b.val_, mask.val_);
  }
  #define FVEC_MASK_UNFN_F(the_name)                                 \
    VEC_INLINE static FVEC_NAME mask_##the_name(                     \
        const FVEC_NAME &src, const BVEC_NAME &mask,                 \
        const FVEC_NAME &a                                           \
    ) {                                                              \
      FVEC_NAME ret = FVEC_SUFFIX(_mm256_##the_name##_)(             \
                                                        a.val_);     \
      return mask_blend(mask, src, ret);                             \
    }
  FVEC_MASK_UNFN_F(cos)
  FVEC_MASK_UNFN_F(recip)
  FVEC_MASK_UNFN_F(sqrt)
  #undef FVEC_MASK_UNFN_F

  #define FVEC_BINFN_F(the_name)                                     \
    VEC_INLINE static FVEC_NAME the_name(const FVEC_NAME &a,         \
                                         const FVEC_NAME &b) {       \
      return FVEC_SUFFIX(_mm256_##the_name##_)(a.val_, b.val_);      \
    }
  FVEC_BINFN_F(max)
  FVEC_BINFN_F(min)
  #undef FVEC_BINFN_F

  #define FVEC_MASK_BINFN_F(the_name)                                \
    VEC_INLINE static FVEC_NAME mask_##the_name(                     \
        const FVEC_NAME &src, const BVEC_NAME &mask,                 \
        const FVEC_NAME &a, const FVEC_NAME &b                       \
    ) {                                                              \
      FVEC_NAME ret = FVEC_SUFFIX(_mm256_##the_name##_)(             \
        a.val_, b.val_);                                             \
      return mask_blend(mask, src, ret);                             \
    }
  FVEC_MASK_BINFN_F(add)
  FVEC_MASK_BINFN_F(div)
  FVEC_MASK_BINFN_F(mul)
  FVEC_MASK_BINFN_F(sub)
  #undef FVEC_MASK_BINFN_F

  VEC_INLINE static FVEC_NAME mask_expand(
      const FVEC_NAME &src, const BVEC_NAME &mask, const FVEC_NAME &b
  ) {
    FVEC_VEC_T ret = FVEC_SUFFIX(_mm256_expand_)(mask.val_, b.val_);
    ret = FVEC_SUFFIX(_mm256_and_)(mask.val_, ret);
    ret = FVEC_SUFFIX(_mm256_or_)(ret, FVEC_SUFFIX(_mm256_andnot_)
      (mask.val_, src.val_));
    return ret;
  }
  VEC_INLINE static FVEC_NAME masku_compress(
      const BVEC_NAME &mask, const FVEC_NAME &b
  ) {
    return FVEC_SUFFIX(_mm256_compress_)(mask.val_, b.val_);
  }

  VEC_INLINE static FVEC_NAME set1(const FVEC_SCAL_T &a) {
    return FVEC_SUFFIX(_mm256_set1_)(a);
  }
  VEC_INLINE static FVEC_NAME setzero() {
    return FVEC_SUFFIX(_mm256_setzero_)();
  }
  VEC_INLINE static FVEC_NAME undefined() {
    return FVEC_SUFFIX(_mm256_undefined_)();
  }

  VEC_INLINE static FVEC_NAME load(const FVEC_SCAL_T *mem) {
    return FVEC_SUFFIX(_mm256_load_)(mem);
  }
  VEC_INLINE static void store(FVEC_SCAL_T * dest, const FVEC_NAME &a) {
    FVEC_SUFFIX(_mm256_store_)(dest, a.val_);
  }


  VEC_INLINE static FVEC_NAME gather(const IVEC_NAME &idx,
    const FVEC_SCAL_T * mem, const int scale) {
    assert(scale == sizeof(FVEC_SCAL_T));
#   if FVEC_LEN==4
#    ifdef __AVX2__
    static const unsigned int mask_shuffle[8] __attribute__((aligned(32))) =
      {0, 2, 4, 6, 0, 0, 0, 0};
    __m256i m = _mm256_permutevar8x32_epi32(idx.val_,
      _mm256_load_si256((__m256i*)mask_shuffle));
    __m128i idx_short = _mm256_castsi256_si128(m);
    return FVEC_SUFFIX(_mm256_i32gather_)(mem, idx_short, sizeof(FVEC_SCAL_T));
#    else
    int idx_buf[8] __attribute__((aligned(32)));
    _mm256_store_si256((__m256i*) idx_buf, idx.val_);
    double dest[4] __attribute__((aligned(32)));
    for (int i = 0; i < 4; i++) {
      dest[i] = mem[idx_buf[2*i]];
    }
    return _mm256_load_pd(dest);
#    endif
#   else
    return FVEC_SUFFIX(_mm256_i32gather_)(mem, idx.val_, sizeof(FVEC_SCAL_T));
#   endif
  }
  VEC_INLINE static FVEC_NAME mask_gather(
      const FVEC_NAME &src, const BVEC_NAME &mask, const IVEC_NAME &idx,
      const FVEC_SCAL_T * mem, const int scale
  ) {
    assert(scale == sizeof(FVEC_SCAL_T));
#   if FVEC_LEN==4
#    ifdef __AVX2__
    static const unsigned int mask_shuffle[8] __attribute__((aligned(32))) =
      {0, 2, 4, 6, 0, 0, 0, 0};
    __m256i m = _mm256_permutevar8x32_epi32(idx.val_,
      _mm256_load_si256((__m256i*)mask_shuffle));
    __m128i idx_short = _mm256_castsi256_si128(m);
    return FVEC_SUFFIX(_mm256_mask_i32gather_)(src.val_, mem, idx_short,
      mask.val_, sizeof(FVEC_SCAL_T));
#    else
    int idx_buf[8] __attribute__((aligned(32)));
    int mask_buf[8] __attribute__((aligned(32)));
    _mm256_store_si256((__m256i*) idx_buf, idx.val_);
    _mm256_store_pd((double*) mask_buf, mask.val_);
    double dest[4] __attribute__((aligned(32)));
    _mm256_store_pd((double*) dest, src.val_);
    for (int i = 0; i < 4; i++) {
      if (mask_buf[2*i])
        dest[i] = mem[idx_buf[2*i]];
    }
    return _mm256_load_pd(dest);
#    endif
#   else
    return FVEC_SUFFIX(_mm256_mask_i32gather_)(src.val_, mem, idx.val_,
      mask.val_, sizeof(FVEC_SCAL_T));
#   endif
  }

  VEC_INLINE static void gather_4_adjacent(const IVEC_NAME &idx,
      const FVEC_SCAL_T * mem, const int scale, FVEC_NAME * out_0,
      FVEC_NAME * out_1, FVEC_NAME * out_2, FVEC_NAME * out_3) {
    assert(scale == sizeof(FVEC_SCAL_T));
    int idx_buf[8] __attribute__((aligned(32)));
    _mm256_store_si256((__m256i*) idx_buf, idx.val_);
#   if FVEC_LEN==4
    __m256d a0 = _mm256_load_pd(&mem[idx_buf[0]]);
    __m256d a1 = _mm256_load_pd(&mem[idx_buf[2]]);
    __m256d a2 = _mm256_load_pd(&mem[idx_buf[4]]);
    __m256d a3 = _mm256_load_pd(&mem[idx_buf[6]]);
    __m256d b0 = _mm256_unpacklo_pd(a0, a1);
    __m256d b1 = _mm256_unpackhi_pd(a0, a1);
    __m256d b2 = _mm256_unpacklo_pd(a2, a3);
    __m256d b3 = _mm256_unpackhi_pd(a2, a3);
    *out_0 = _mm256_permute2f128_pd(b0, b2, 0x20);
    *out_1 = _mm256_permute2f128_pd(b1, b3, 0x20);
    *out_2 = _mm256_permute2f128_pd(b0, b2, 0x31);
    *out_3 = _mm256_permute2f128_pd(b1, b3, 0x31);
#   else
    const float *e0 = &mem[idx_buf[0]];
    const float *e1 = &mem[idx_buf[1]];
    const float *e2 = &mem[idx_buf[2]];
    const float *e3 = &mem[idx_buf[3]];
    const float *e4 = &mem[idx_buf[4]];
    const float *e5 = &mem[idx_buf[5]];
    const float *e6 = &mem[idx_buf[6]];
    const float *e7 = &mem[idx_buf[7]];
    __m256 a0 = _mm256_loadu2_m128(e4, e0);
    __m256 a1 = _mm256_loadu2_m128(e5, e1);
    __m256 b0 = _mm256_unpacklo_ps(a0, a1);
    __m256 b1 = _mm256_unpackhi_ps(a0, a1);
    __m256 a2 = _mm256_loadu2_m128(e6, e2);
    __m256 a3 = _mm256_loadu2_m128(e7, e3);
    __m256 b2 = _mm256_unpacklo_ps(a2, a3);
    __m256 b3 = _mm256_unpackhi_ps(a2, a3);
    *out_0 = _mm256_shuffle_ps(b0, b2, 0x44);
    *out_1 = _mm256_shuffle_ps(b0, b2, 0xEE);
    *out_2 = _mm256_shuffle_ps(b1, b3, 0x44);
    *out_3 = _mm256_shuffle_ps(b1, b3, 0xEE);
#   endif
  }
  VEC_INLINE static void gather_3_adjacent(const IVEC_NAME &idx,
                                           const FVEC_SCAL_T * mem,
                                           const int scale,
                                           FVEC_NAME * out_0,
                                           FVEC_NAME * out_1,
                                           FVEC_NAME * out_2) {
    assert(scale == sizeof(FVEC_SCAL_T));
    FVEC_NAME tmp_3;
    gather_4_adjacent(idx, mem, scale, out_0, out_1, out_2, &tmp_3);
  }

  VEC_INLINE static double _mm256_reduce_add_pd(__m256d a) {
    __m256d t1 = _mm256_hadd_pd(a, a);
    __m128d t2 = _mm256_extractf128_pd(t1, 1);
    __m128d t3 = _mm256_castpd256_pd128(t1);
    return _mm_cvtsd_f64(_mm_add_pd(t2, t3));
  }

  VEC_INLINE static float _mm256_reduce_add_ps(__m256 a) {
    __m256 t1 = _mm256_hadd_ps(a, a);
    __m128 t2 = _mm256_extractf128_ps(t1, 1);
    __m128 t3 = _mm256_castps256_ps128(t1);
    __m128 t4 = _mm_add_ps(t2, t3);
    __m128 t5 = _mm_permute_ps(t4, 0x1B); // 0x1B = reverse
    return _mm_cvtss_f32(_mm_add_ps(t4, t5));
  }

  VEC_INLINE static FVEC_SCAL_T reduce_add(const FVEC_NAME &a) {
    return FVEC_SUFFIX(_mm256_reduce_add_)(a.val_);
  }
  VEC_INLINE static FVEC_SCAL_T mask_reduce_add(const BVEC_NAME &mask,
                                                const FVEC_NAME &a) {
    return reduce_add(FVEC_SUFFIX(_mm256_and_)(mask.val_, a.val_));
  }

  VEC_INLINE static IVEC_NAME unpackloepi32(const FVEC_NAME &a) {
#   if FVEC_LEN==4
#    if __AVX2__
    static const unsigned int mask_shuffle[8] __attribute__((aligned(32))) =
      {0, 0, 2, 2, 4, 4, 6, 6};
    __m256 m = _mm256_permutevar8x32_ps(_mm256_castpd_ps(a.val_),
      _mm256_load_si256((__m256i*)mask_shuffle));
    return _mm256_castps_si256(m);
#    else
    __m128i a_lo = _mm256_castsi256_si128(_mm256_castpd_si256(a.val_));
    __m128i a_hi = _mm256_extractf128_si256(_mm256_castpd_si256(a.val_), 1);
    __m128i c_lo = _mm_shuffle_epi32(a_lo, 0xA0); /*1010 0000*/
    __m128i c_hi = _mm_shuffle_epi32(a_hi, 0xA0);
    __m256i ret = _mm256_setr_m128i(c_lo, c_hi);
    return ret;
#    endif
#   else
    return _mm256_castps_si256(a.val_);
#   endif
  }

  VEC_INLINE static FVEC_NAME mask_sincos(
      FVEC_NAME * cos, const FVEC_NAME &src_a, const FVEC_NAME &src_b,
      const BVEC_NAME &mask, const FVEC_NAME &arg
  ) {
    FVEC_VEC_T c, s = FVEC_SUFFIX(_mm256_sincos_)(&c, arg.val_);
    *cos = mask_blend(mask, src_b, c);
    return mask_blend(mask, src_a, s);
  }

  #define FVEC_BINOP(the_sym, the_name)                                      \
    VEC_INLINE inline FVEC_NAME operator the_sym(const FVEC_NAME &b) const { \
    return FVEC_SUFFIX(_mm256_##the_name##_)(this->val_, b.val_);            \
  }
  FVEC_BINOP(+, add)
  FVEC_BINOP(-, sub)
  FVEC_BINOP(*, mul)
  FVEC_BINOP(/, div)
  #undef FVEC_BINOP

  VEC_INLINE static void gather_prefetch0(const IVEC_NAME &a, void * mem) {
    /* NOP */
  }
};

class AVEC_NAME {
  friend class avec8pd;
  FVEC_VEC_T val_;
  VEC_INLINE AVEC_NAME(const FVEC_VEC_T &a) : val_(a) {}
public:
  VEC_INLINE AVEC_NAME(const FVEC_NAME &a) : val_(a.val_) {}
  VEC_INLINE static AVEC_NAME undefined() {
    return FVEC_SUFFIX(_mm256_undefined_)();
  }
  VEC_INLINE static AVEC_NAME mask_gather(
      const AVEC_NAME &src, const BVEC_NAME &mask, const IVEC_NAME &idx,
      const FVEC_SCAL_T * mem, const int scale
  ) {
    assert(scale == sizeof(FVEC_SCAL_T));
    return FVEC_NAME::mask_gather(src.val_, mask, idx, mem, scale);
  }
  VEC_INLINE static void mask_i32loscatter(
      FVEC_SCAL_T * mem, const BVEC_NAME &mask, const IVEC_NAME &idx,
      const AVEC_NAME &a, const int scale
  ) {
    assert(scale == sizeof(FVEC_SCAL_T));
    for (int l = 0; l < FVEC_NAME::VL; l++) {
      if (BVEC_NAME::test_at(mask, l))
        mem[IVEC_NAME::at(idx, l)] = FVEC_NAME::at(a.val_, l);
    }
  }

  #define AVEC_BINOP(the_sym, the_name)                                      \
    VEC_INLINE inline AVEC_NAME operator the_sym(const AVEC_NAME &b) const { \
    return FVEC_SUFFIX(_mm256_##the_name##_)(this->val_, b.val_);            \
  }
  AVEC_BINOP(-, sub)
  #undef AVEC_BINOP
};

#if FVEC_LEN==8
class avec8pd {
  __m256d lo_, hi_;
  VEC_INLINE avec8pd(const __m256d &lo, const __m256d &hi) : lo_(lo), hi_(hi) {}
  VEC_INLINE static __m128 get_ps_hi(__m256 a) {
    return _mm256_extractf128_ps(a, 1);
  }
  VEC_INLINE static __m128 get_ps_lo(__m256 a) {
    return _mm256_castps256_ps128(a);
  }
  VEC_INLINE static __m128i get_si_hi(__m256i a) {
    return _mm_castps_si128(get_ps_hi(_mm256_castsi256_ps(a)));
  }
  VEC_INLINE static __m128i get_si_lo(__m256i a) {
    return _mm_castps_si128(get_ps_lo(_mm256_castsi256_ps(a)));
  }
public:
  VEC_INLINE avec8pd(const FVEC_NAME &a) {
    lo_ = _mm256_cvtps_pd(get_ps_lo(a.val_));
    hi_ = _mm256_cvtps_pd(get_ps_hi(a.val_));
  }
  VEC_INLINE static avec8pd undefined() {
    return avec8pd(_mm256_undefined_pd(), _mm256_undefined_pd());
  }
  VEC_INLINE static avec8pd mask_gather(
      const avec8pd &src, const BVEC_NAME &mask, const IVEC_NAME &idx,
      const double * mem, const int scale
  ) {
#   ifndef __AVX2__
    assert(scale == sizeof(double));
    int idx_buf[8] __attribute__((aligned(32)));
    _mm256_store_si256((__m256i*) idx_buf, idx.val_);
    int mask_val = _mm256_movemask_ps(mask.val_);
    double ret_buf[8] __attribute__((aligned(32)));
    _mm256_store_pd(&ret_buf[0], src.lo_);
    _mm256_store_pd(&ret_buf[4], src.hi_);
    for (int i = 0; i < 8; i++) {
      if (mask_val & (1 << i)) {
        ret_buf[i] = mem[idx_buf[i]];
      }
    }
    __m256d lo = _mm256_load_pd(&ret_buf[0]);
    __m256d hi = _mm256_load_pd(&ret_buf[4]);
#   else
    static const unsigned int lo_shuffle[8] __attribute__((aligned(32))) =
      {0, 0, 1, 1, 2, 2, 3, 3};
    static const unsigned int hi_shuffle[8] __attribute__((aligned(32))) =
      {4, 4, 5, 5, 6, 6, 7, 7};
    __m256d lo_mask = _mm256_castps_pd(_mm256_permutevar8x32_ps(mask.val_,
      _mm256_load_si256((__m256i*) lo_shuffle)));
    __m256d hi_mask = _mm256_castps_pd(_mm256_permutevar8x32_ps(mask.val_,
      _mm256_load_si256((__m256i*) hi_shuffle)));
    __m256d lo = _mm256_mask_i32gather_pd(src.lo_, mem, get_si_lo(idx.val_),
                                          lo_mask, sizeof(double));
    __m256d hi = _mm256_mask_i32gather_pd(src.hi_, mem, get_si_hi(idx.val_),
                                          hi_mask, sizeof(double));
#   endif
    return avec8pd(lo, hi);
  }
  VEC_INLINE static void mask_i32loscatter(
      double * mem, const BVEC_NAME &mask, const IVEC_NAME &idx,
      const avec8pd &a, const int scale
  ) {
    assert(scale == sizeof(double));
    double a_buf[8] __attribute__((aligned(32)));
    _mm256_store_pd(a_buf, a.lo_);
    _mm256_store_pd(&a_buf[4], a.hi_);
    int idx_buf[8] __attribute__((aligned(32)));
    _mm256_store_si256((__m256i*)idx_buf, idx.val_);
    int mask_val = _mm256_movemask_ps(mask.val_);
    for (int i = 0; i < 8; i++) {
      if (mask_val & (1 << i))
        mem[idx_buf[i]] = a_buf[i];
    }
  }

  #define AVEC2_BINOP(the_sym, the_name)                                    \
    VEC_INLINE inline avec8pd operator the_sym(const avec8pd &b) const {    \
    __m256d lo = _mm256_##the_name##_pd(this->lo_, b.lo_);                  \
    __m256d hi = _mm256_##the_name##_pd(this->hi_, b.hi_);                  \
    return avec8pd(lo, hi);                                                 \
  }
  AVEC2_BINOP(-, sub)
};
#endif

}


#ifdef FVEC_FIRST_PASS

template<typename flt_t, typename acc_t>
struct intr_types;

template<>
struct intr_types<double,double> {
  typedef mm256::fvec4pd fvec;
  typedef mm256::ivec4 ivec;
  typedef mm256::bvec4 bvec;
  typedef mm256::avec4pd avec;
};

template<>
struct intr_types<float,float> {
  typedef mm256::fvec8ps fvec;
  typedef mm256::ivec8 ivec;
  typedef mm256::bvec8 bvec;
  typedef mm256::avec8ps avec;
};

template<>
struct intr_types<float,double> {
  typedef mm256::fvec8ps fvec;
  typedef mm256::ivec8 ivec;
  typedef mm256::bvec8 bvec;
  typedef mm256::avec8pd avec;
};

#endif

#ifndef FVEC_FIRST_PASS
#  define FVEC_FIRST_PASS
#  include "intel_intrinsics_airebo.h"
#endif

#endif

#ifdef LMP_INTEL_AIREBO_SCALAR

#include <cassert>
#include <cmath>
#include <immintrin.h>

#define VEC_INLINE __attribute__((always_inline))

template<typename flt_t, typename acc_t>
struct intr_types {

class fvec;
class ivec;
class avec;
class bvec {
  friend class fvec;
  friend class ivec;
  friend class avec;
  bool val_;
  VEC_INLINE bvec(const bool &v) : val_(v) {}
public:
  VEC_INLINE bvec() {}
  VEC_INLINE static bvec kand(const bvec &a, const bvec &b) {
    return a.val_ && b.val_;
  }
  VEC_INLINE static bvec kandn(const bvec &a, const bvec &b) {
    return (! a.val_) && b.val_;
  }
  VEC_INLINE static bvec knot(const bvec &a) {
    return ! a.val_;
  }
  VEC_INLINE static int kortestz(const bvec &a, const bvec &b) {
    return (! a.val_) && (! b.val_) ? true : false;
  }
  VEC_INLINE static bvec masku_compress(const bvec &mask, const bvec &a) {
    return mask.val_ ? a.val_ : false;
  }
  VEC_INLINE static bvec mask_expand(const bvec &src, const bvec &mask,
                                     const bvec &a) {
    return mask.val_ ? a.val_ : src.val_;
  }
  VEC_INLINE static bvec full() {
    return true;
  }
  VEC_INLINE static bvec empty() {
    return false;
  }
  VEC_INLINE static bvec only(int n) {
    return n == 1 ? true : false;
  }
  VEC_INLINE static bvec after(int n) {
    return n == 0 ? true : false;
  }
  VEC_INLINE static bvec onlyafter(int only, int after) {
    return after == 0 && only == 1 ? true : false;
  }
  VEC_INLINE static int popcnt(const bvec &a) {
    return static_cast<int>(a.val_);
  }
  VEC_INLINE static bool test_all_unset(const bvec &a) {
    return kortestz(a, a);
  }
  VEC_INLINE static bool test_any_set(const bvec &a) {
    return ! test_all_unset(a);
  }
  VEC_INLINE static bool test_at(const bvec &a, int i) {
    assert(i < 1);
    return a.val_;
  }
  VEC_INLINE bvec operator &(const bvec &b) const {
    return val_ && b.val_;
  }
  VEC_INLINE bvec operator |(const bvec &b) const {
    return val_ || b.val_;
  }
  VEC_INLINE bvec operator ~() const {
    return ! val_;
  }
};

class ivec {
  friend class fvec;
  friend class avec;
  int val_;
  VEC_INLINE ivec(const int &v) : val_(v) {}
public:
  static const int VL = 1;
  VEC_INLINE ivec() {}

  #define IVEC_MASK_BINFN_B(the_name, the_op)                        \
    VEC_INLINE static bvec the_name(const ivec &a, const ivec &b) {  \
      return a.val_ the_op b.val_;                                   \
    }                                                                \
    VEC_INLINE static bvec mask_##the_name(                          \
        const bvec &mask,                                            \
        const ivec &a, const ivec &b                                 \
    ) {                                                              \
      return mask.val_ && (a.val_ the_op b.val_);                    \
                                                                     \
    }
  IVEC_MASK_BINFN_B(cmpeq, ==)
  IVEC_MASK_BINFN_B(cmplt, <)
  IVEC_MASK_BINFN_B(cmpneq, !=)
  IVEC_MASK_BINFN_B(cmpgt, >)

  #define IVEC_MASK_BINFN_I(the_name, the_op)                        \
    VEC_INLINE static ivec mask_##the_name(                          \
        const ivec &src, const bvec &mask,                           \
        const ivec &a, const ivec &b                                 \
    ) {                                                              \
      return mask.val_ ? a.val_ the_op b.val_ : src.val_;            \
    }
  IVEC_MASK_BINFN_I(add, +)
  VEC_INLINE static ivec mask_blend(
      const bvec &mask, const ivec &a, const ivec &b
  ) {
    return mask.val_ ? b.val_ : a.val_;
  }

  #define IVEC_BINFN_I(the_name, the_op)                             \
    VEC_INLINE static ivec the_name(const ivec &a, const ivec &b) {  \
      return a.val_ the_op b.val_;                                   \
    }
  IVEC_BINFN_I(mullo, *)
  IVEC_BINFN_I(srlv, >>)
  VEC_INLINE static ivec the_and(const ivec &a, const ivec &b) {
    return a.val_ & b.val_;
  }

  VEC_INLINE static ivec mask_expand(
      const ivec &src, const bvec &a, const ivec &b
  ) {
    return a.val_ ? b.val_ : src.val_;
  }
  VEC_INLINE static ivec masku_compress(
      const bvec &a, const ivec &b
  ) {
    return a.val_ ? b.val_ : 0;
  }

  VEC_INLINE static int at(const ivec &a, int b) {
    assert(b == 0);
    return a.val_;
  }

  VEC_INLINE static ivec load(const int * src) {
    return *src;
  }
  VEC_INLINE static ivec mask_loadu(const bvec &mask, const int * src) {
    return mask.val_ ? *src : 0xDEAD;
  }
  VEC_INLINE static ivec maskz_loadu(const bvec &mask, const int * src) {
    return mask.val_ ? *src : 0;
  }
  VEC_INLINE static void mask_storeu(const bvec &mask, int * dest,
    const ivec &src) {
    if (mask.val_) *dest = src.val_;
  }
  VEC_INLINE static void store(int * dest, const ivec &src) {
    *dest = src.val_;
  }

  VEC_INLINE static ivec mask_gather(
      const ivec &src, const bvec &mask, const ivec &idx, const int * mem,
        const int scale
  ) {
    return mask.val_ ? *reinterpret_cast<const int *>
      (reinterpret_cast<const char*>(mem) + scale * idx.val_) : src.val_;
  }
  VEC_INLINE static void mask_i32scatter(
      int * mem, const bvec &mask, const ivec &idx, const ivec &a,
        const int scale
  ) {
    if (mask.val_) *reinterpret_cast<int *>(reinterpret_cast<char*>(mem) +
      scale * idx.val_) = a.val_;
  }

  VEC_INLINE static void mask_compressstore(const bvec &mask, int * dest,
      const ivec &src) {
    if (mask.val_) *dest = src.val_;
  }

  VEC_INLINE static ivec set(
      int i15, int i14, int i13, int i12, int i11, int i10, int i9, int i8,
      int i7, int i6, int i5, int i4, int i3, int i2, int i1, int i0
  ) {
    return i0;
  }
  VEC_INLINE static ivec set1(int i) {
    return i;
  }
  VEC_INLINE static ivec setzero() {
    return 0;
  }
  VEC_INLINE static ivec undefined() {
    return 0xDEAD;
  }

  VEC_INLINE ivec operator +(const ivec &b) const {
    return val_ + b.val_;
  }
};

class fvec {
  friend class avec;
  flt_t val_;
  VEC_INLINE fvec(const flt_t &v) : val_(v) {}
public:
  static const int VL = 1;
  VEC_INLINE fvec() {}
  VEC_INLINE static flt_t at(const fvec &a, int i) {
    assert(i < 1);
    return a.val_;
  }
  VEC_INLINE static bool fast_compress() { return false; }

  #define FVEC_MASK_BINFN_B(the_name, the_op)                        \
    VEC_INLINE static bvec the_name(const fvec &a, const fvec &b) {  \
      return a.val_ the_op b.val_;                                   \
    }                                                                \
    VEC_INLINE static bvec mask_##the_name(                          \
        const bvec &mask,                                            \
        const fvec &a, const fvec &b                                 \
    ) {                                                              \
      return mask.val_ && (a.val_ the_op b.val_);                    \
    }
  FVEC_MASK_BINFN_B(cmple, <=)
  FVEC_MASK_BINFN_B(cmplt, <)
  FVEC_MASK_BINFN_B(cmpneq, !=)
  FVEC_MASK_BINFN_B(cmpnle, >)
  FVEC_MASK_BINFN_B(cmpnlt, >=)

  #define FVEC_UNFN_F(the_name, the_fn)                              \
    VEC_INLINE static fvec the_name(const fvec &a) {                 \
      return the_fn(a.val_);                                         \
    }
  FVEC_UNFN_F(abs, fabs)
  FVEC_UNFN_F(exp, ::exp)
  FVEC_UNFN_F(invsqrt, 1/std::sqrt)
  FVEC_UNFN_F(recip, 1/)
  FVEC_UNFN_F(sqrt, std::sqrt)

  #define FVEC_MASK_UNFN_F(the_name, the_fn)                         \
    VEC_INLINE static fvec mask_##the_name(                          \
        const fvec &src, const bvec &mask,                           \
        const fvec &a                                                \
    ) {                                                              \
      return mask.val_ ? the_fn(a.val_) : src.val_;                  \
    }
  FVEC_MASK_UNFN_F(cos, std::cos)
  FVEC_MASK_UNFN_F(recip, 1/)
  FVEC_MASK_UNFN_F(sqrt, std::sqrt)

  #define FVEC_BINFN_F(the_name, the_fn)                             \
    VEC_INLINE static fvec the_name(const fvec &a, const fvec &b) {  \
      return the_fn(a.val_, b.val_);                                 \
    }
  FVEC_BINFN_F(max, ::fmax)
  FVEC_BINFN_F(min, ::fmin)

  #define FVEC_MASK_BINFN_F(the_name, the_op)                        \
    VEC_INLINE static fvec mask_##the_name(                          \
        const fvec &src, const bvec &mask,                           \
        const fvec &a, const fvec &b                                 \
    ) {                                                              \
      return mask.val_ ? a.val_ the_op b.val_ : src.val_;            \
    }
  FVEC_MASK_BINFN_F(add, +)
  FVEC_MASK_BINFN_F(div, /)
  FVEC_MASK_BINFN_F(mul, *)
  FVEC_MASK_BINFN_F(sub, -)
  VEC_INLINE static fvec mask_blend(
      const bvec &mask, const fvec &a, const fvec &b
  ) {
    return mask.val_ ? b.val_ : a.val_;
  }

  VEC_INLINE static fvec mask_expand(
      const fvec &src, const bvec &a, const fvec &b
  ) {
    return a.val_ ? b.val_ : src.val_;
  }
  VEC_INLINE static fvec masku_compress(
      const bvec &a, const fvec &b
  ) {
    return a.val_ ? b.val_ : 0;
  }

  VEC_INLINE static fvec set1(const flt_t &a) {
    return a;
  }
  VEC_INLINE static fvec setzero() {
    return 0;
  }
  VEC_INLINE static fvec undefined() {
    return 1337.1337;
  }

  VEC_INLINE static fvec load(const flt_t *mem) {
    return *mem;
  }
  VEC_INLINE static void mask_storeu(const bvec &mask, flt_t * dest,
                                     const fvec &a) {
    if (mask.val_) *dest = a.val_;
  }
  VEC_INLINE static void store(flt_t * dest, const fvec &a) {
    *dest = a.val_;
  }

  VEC_INLINE static fvec gather(const ivec &idx, const flt_t * mem,
                                const int scale) {
    return *reinterpret_cast<const flt_t*>(reinterpret_cast<const char*>(mem) +
      scale * idx.val_);
  }
  VEC_INLINE static fvec mask_gather(
      const fvec &src, const bvec &mask, const ivec &idx,
      const flt_t * mem, const int scale
  ) {
    return mask.val_ ? *reinterpret_cast<const flt_t*>
      (reinterpret_cast<const char*>(mem) + scale * idx.val_) : src.val_;
  }

  VEC_INLINE static void gather_3_adjacent(const ivec &idx, const flt_t * mem,
                                           const int scale, fvec * out_0,
                                           fvec * out_1, fvec * out_2) {
    assert(scale == sizeof(flt_t));
    *out_0 = gather(idx, mem + 0, scale);
    *out_1 = gather(idx, mem + 1, scale);
    *out_2 = gather(idx, mem + 2, scale);
  }
  VEC_INLINE static void gather_4_adjacent(const ivec &idx, const flt_t * mem,
                                           const int scale, fvec * out_0,
                                           fvec * out_1, fvec * out_2,
                                           fvec * out_3) {
    assert(scale == sizeof(flt_t));
    *out_0 = gather(idx, mem + 0, scale);
    *out_1 = gather(idx, mem + 1, scale);
    *out_2 = gather(idx, mem + 2, scale);
    *out_3 = gather(idx, mem + 3, scale);
  }

  VEC_INLINE static flt_t mask_reduce_add(const bvec &mask, const fvec &a) {
    return mask.val_ ? a.val_ : 0;
  }
  VEC_INLINE static flt_t reduce_add(const fvec &a) {
    return a.val_;
  }

  VEC_INLINE static ivec unpackloepi32(const fvec &a) {
    return reinterpret_cast<const int*>(&a.val_)[0];
  }

  VEC_INLINE static fvec mask_sincos(
      fvec * cos_out, const fvec &src_a, const fvec &src_b,
      const bvec &mask, const fvec &arg
  ) {
    cos_out->val_ = mask.val_ ? ::cos(arg.val_) : src_b.val_;
    return mask.val_ ? ::sin(arg.val_) : src_a.val_;
  }

  #define FVEC_BINOP(the_sym, the_name)                              \
    VEC_INLINE inline fvec operator the_sym(const fvec &b) const {   \
    return this->val_ the_sym b.val_;                                \
  }
  FVEC_BINOP(+, add)
  FVEC_BINOP(-, sub)
  FVEC_BINOP(*, mul)
  FVEC_BINOP(/, div)

  VEC_INLINE static void gather_prefetch0(const ivec &idx, const void * mem) {}
};

class avec {
  acc_t val_;
  VEC_INLINE avec(const acc_t &a) : val_(a) {}
public:
  VEC_INLINE avec(const fvec &a) : val_(a.val_) {}
  VEC_INLINE static avec undefined() {
    return 1337.1337;
  }
  VEC_INLINE static avec mask_gather(const avec &src, const bvec &mask,
                                     const ivec &idx, const acc_t * mem,
                                     const int scale) {
    return mask.val_ ? *reinterpret_cast<const acc_t*>
      (reinterpret_cast<const char*>(mem) + scale * idx.val_) : src.val_;
  }
  VEC_INLINE static void mask_i32loscatter(acc_t * mem, const bvec &mask,
                                           const ivec &idx, const avec &a,
                                           const int scale) {
    if (mask.val_) *reinterpret_cast<acc_t*>(reinterpret_cast<char*>(mem) +
                                             idx.val_ * scale) = a.val_;
  }

  #define AVEC_BINOP(the_sym, the_name)                              \
    VEC_INLINE inline avec operator the_sym(const avec &b) const {   \
    return this->val_ the_sym b.val_;                                \
  }
  AVEC_BINOP(-, sub)
};

};

#endif
