Task: Implement macros/vectors-4-default.h for CactusUtils/Vectors thorn

thorn_name: CactusUtils/Vectors
src_filename: macros/vectors-4-default.h
interface: # Interface definition for thorn Vectors

IMPLEMENTS: Vectors

INCLUDE HEADER: vectors.h IN vectors.h

schedule: # Schedule definitions for thorn Vectors

schedule Vectors_Startup at CCTK_STARTUP
{
  LANG: C
} "Print startup message"

schedule Vectors_Test at CCTK_PARAMCHECK
{
  LANG: C
} "Run correctness tests."

param: # Parameter definitions for thorn Vectors

BOOLEAN verbose "Verbose output during vector tests"
{
} "no"

configuration: # Configuration definitions for thorn Vectors

PROVIDES Vectors
{
  SCRIPT configure.sh
  LANG bash
  OPTIONS                                \
    VECTORISE                            \
    VECTORISE_ALIGNED_ARRAYS             \
    VECTORISE_ALWAYS_USE_UNALIGNED_LOADS \
    VECTORISE_ALWAYS_USE_ALIGNED_LOADS   \
    VECTORISE_INLINE                     \
    VECTORISE_STREAMING_STORES
}

REQUIRES Vectors

context: vectors-8-VSX.h:
```
// -*-C++-*-
// Vectorise using IBM's Altivec VSX (Power)

// Use the type vector double directly, without introducing a wrapper class
// Use macros instead of inline functions

// See <http://pic.dhe.ibm.com/infocenter/comphelp/v111v131/index.jsp>



#include <altivec.h>
#include <math.h>



#define vec8_architecture "VSX"

// Vector type corresponding to CCTK_REAL
typedef vector double           CCTK_REAL8_VEC;
typedef vector signed long long CCTK_INTEGER8_VEC;
typedef vector bool long long   CCTK_BOOLEAN8_VEC;

// Number of vector elements in a CCTK_REAL_VEC
#define CCTK_REAL8_VEC_SIZE 2

vec_static_assert(sizeof(CCTK_REAL8_VEC) ==
                  sizeof(CCTK_REAL8) * CCTK_REAL8_VEC_SIZE);

// Integer and boolean types corresponding to this real type
typedef long long          CCTK_INTEGER8;
typedef unsigned long long CCTK_BOOLEAN8;



// Create vectors, extract vector elements

#define vec8_set1(a)  (vec_splats(a))
#define vec8_set1i(a) (vec_splats(a))
#define vec8_set(a,b)                           \
  ({                                            \
    CCTK_REAL8_VEC x;                           \
    x[0]=(a);                                   \
    x[1]=(b);                                   \
    x;                                          \
  })

#define vec8_elt0(x)   ((x)[0])
#define vec8_elt1(x)   ((x)[1])
// #define vec8_elt(x,d)  ((x)[d])
// #define vec8_elti(x,d) ((x)[d])
// #define vec8_eltb(x,d) ((x)[d])
static inline CCTK_REAL8 vec8_elt(CCTK_REAL8_VEC x, int d) { return x[d]; }
static inline CCTK_INTEGER8 vec8_elti(CCTK_INTEGER8_VEC x, int d) { return x[d]; }
static inline CCTK_BOOLEAN8 vec8_eltb(CCTK_BOOLEAN8_VEC x,int d) { return x[d]; }



// Load and store vectors

// Load a vector from memory (aligned and unaligned); this loads from
// a reference to a scalar
#define vec8_load(p)  (*(CCTK_REAL8_VEC const*)&(p))
#define vec8_loadu(p) (*(CCTK_REAL8_VEC const*)&(p))

// Load a vector from memory that may or may not be aligned, as
// decided by the offset and the vector size
#define vec8_loadu_maybe(off,p)             (vec8_loadu(p))
#define vec8_loadu_maybe3(off1,off2,off3,p) (vec8_loadu(p))

// Store a vector to memory (aligned and non-temporal); this stores to
// a reference to a scalar
#define vec8_store(p,x)  (*(CCTK_REAL8_VEC*)&(p)=(x))
#define vec8_storeu(p,x) (*(CCTK_REAL8_VEC*)&(p)=(x))
// stvxl instruction doesn't exist for double precision
#define vec8_store_nta(p,x) vec8_store(p,x)

// Store a partial vector (aligned and non-temporal)
#define vec8_store_partial_prepare(i,imin,imax)                 \
  bool const v8stp_lo = (i)>=(imin);                            \
  bool const v8stp_hi = (i)+CCTK_REAL8_VEC_SIZE-1<(imax)
#define vec8_store_nta_partial(p_,x_)                           \
  ({                                                            \
    CCTK_REAL8& p__=(p_);                                       \
    CCTK_REAL8& p=p__;                                          \
    CCTK_REAL8_VEC const x__=(x_);                              \
    CCTK_REAL8_VEC const x=x__;                                 \
    if (CCTK_BUILTIN_EXPECT(v8stp_lo and v8stp_hi, true)) {     \
      vec8_store(p,x);                                          \
    } else if (v8stp_lo) {                                      \
      (&p)[0]=vec8_elt0(x);                                     \
    } else if (v8stp_hi) {                                      \
      (&p)[1]=vec8_elt1(x);                                     \
    }                                                           \
  })

// Store a lower or higher partial vector (aligned and non-temporal);
// the non-temporal hint is probably ignored
#define vec8_store_nta_partial_lo(p,x,n) ((&(p))[0]=(x)[0])
#define vec8_store_nta_partial_hi(p,x,n) ((&(p))[1]=(x)[1])
#define vec8_store_nta_partial_mid(p,x,nlo,nhi) (assert(0))



// Functions and operators

// Operators
#define k8neg(x) (-(x))

#define k8add(x,y) ((x)+(y))
#define k8sub(x,y) ((x)-(y))
#define k8mul(x,y) ((x)*(y))
#define k8div(x,y) ((x)/(y))

// Fused multiply-add, defined as [+-]x*y[+-]z
#define k8madd(x,y,z)  (vec_madd(x,y,z))
#define k8msub(x,y,z)  (vec_msub(x,y,z))
#define k8nmadd(x,y,z) (vec_nmadd(x,y,z))
#define k8nmsub(x,y,z) (vec_nmsub(x,y,z))

// Cheap functions
#define k8copysign(x,y) (vec_cpsgn(y,x))
#define k8fabs(x)       (vec_abs(x))
#define k8fmax(x,y)     (vec_max(x,y))
#define k8fmin(x,y)     (vec_min(x,y))
#define k8fnabs(x)      (vec_nabs(x))
#define k8sgn(x_)                                                       \
  ({                                                                    \
    CCTK_REAL8_VEC x__=(x_);                                            \
    CCTK_REAL8_VEC x=x__;                                               \
    CCTK_BOOLEAN8_VEC iszero = k8cmpeq(x,vec8_set1((CCTK_REAL8)0.0));   \
    CCTK_REAL8_VEC signedone = k8copysign(vec8_set1((CCTK_REAL8)1.0),x); \
    k8ifthen(iszero, vec8_set1((CCTK_REAL8)0.0), signedone);            \
  })
#define k8sqrt(x)       (vec_sqrt(x))

// Expensive functions
#define K8REPL(f,x_)                            \
  ({                                            \
    CCTK_REAL8_VEC const x__=(x_);              \
    CCTK_REAL8_VEC const x=x__;                 \
    vec8_set(f(vec8_elt0(x)),                   \
             f(vec8_elt1(x)));                  \
  })
#define K8REPL2S(f,x_,a_)                       \
  ({                                            \
    CCTK_REAL8_VEC const x__=(x_);              \
    CCTK_REAL8     const a__=(a_);              \
    CCTK_REAL8_VEC const x=x__;                 \
    CCTK_REAL8     const a=a__;                 \
    vec8_set(f(vec8_elt0(x),a),                 \
             f(vec8_elt1(x),a));                \
  })
#define K8REPL2(f,x_,y_)                        \
  ({                                            \
    CCTK_REAL8_VEC const x__=(x_);              \
    CCTK_REAL8_VEC const y__=(y_);              \
    CCTK_REAL8_VEC const x=x__;                 \
    CCTK_REAL8_VEC const y=y__;                 \
    vec8_set(f(vec8_elt0(x),vec8_elt0(y)),      \
             f(vec8_elt1(x),vec8_elt1(y)));     \
  })

#define k8acos(x)    K8REPL(acos,x)
#define k8acosh(x)   K8REPL(acosh,x)
#define k8asin(x)    K8REPL(asin,x)
#define k8asinh(x)   K8REPL(asinh,x)
#define k8atan(x)    K8REPL(atan,x)
#define k8atan2(x,y) K8REPL2(atan2,x,y)
#define k8atanh(x)   K8REPL(atanh,x)
#define k8cos(x)     K8REPL(cos,x)
#define k8cosh(x)    K8REPL(cosh,x)
#define k8exp(x)     K8REPL(exp,x)
#define k8fmod(x,y)  K8REPL2(fmod,x,y)
#define k8log(x)     K8REPL(log,x)
#define k8pow(x,a)   K8REPL2S(pow,x,a)
#define k8sin(x)     K8REPL(sin,x)
#define k8sinh(x)    K8REPL(sinh,x)
#define k8tan(x)     K8REPL(tan,x)
#define k8tanh(x)    K8REPL(tanh,x)

// canonical true is -1LL, canonical false is 0LL
// truth values are interpreted bit-wise
// #define k8lfalse        ({ CCTK_BOOLEAN8_VEC dummy; vec_xor(dummy,dummy); })
// #define k8ltrue         (k8lnot(k8lfalse))
static inline CCTK_BOOLEAN8_VEC k8lfalse1()
{
  CCTK_BOOLEAN8_VEC dummy;
  return vec_xor(dummy,dummy);
}
#define k8lfalse (k8lfalse1())
#define k8ltrue  (k8lnot(k8lfalse))

// #define k8lnot(x_)                              \
//   ({                                            \
//     CCTK_BOOLEAN8_VEC x__=(x_);                 \
//     CCTK_BOOLEAN8_VEC x=x__;                    \
//     vec_nor(x,x);                               \
//   })
static inline CCTK_BOOLEAN8_VEC k8lnot(CCTK_BOOLEAN8_VEC x)
{
  return vec_nor(x,x);
}

#define k8land(x,y)     (vec_and(x,y))
#define k8lor(x,y)      (vec_or(x,y))
#define k8lxor(x,y)     (vec_xor(x,y))
#define k8ifthen(x,y,z) (vec_sel(z,y,x))

#define k8cmpeq(x,y) (vec_cmpeq(x,y))
#define k8cmpne(x,y) (k8lnot(vec_cmpeq(x,y)))
#define k8cmpgt(x,y) (vec_cmpgt(x,y))
#define k8cmpge(x,y) (vec_cmpge(x,y))
#define k8cmplt(x,y) (vec_cmplt(x,y))
#define k8cmple(x,y) (vec_cmple(x,y))

```

vectors-8-DoubleHummer.h:
```
// -*-C++-*-
// Vectorise using IBM's Blue Gene/P Double Hummer (Power)

// Use the type double _Complex directly, without introducing a wrapper class
// Use macros instead of inline functions

// See <http://publib.boulder.ibm.com/infocenter/compbgpl/v9v111/index.jsp>



#include <assert.h>

#ifdef __cplusplus
#  include <builtins.h>
#endif



#define vec8_architecture "Double Hummer"

// Vector type corresponding to CCTK_REAL
#define CCTK_REAL8_VEC    double _Complex
#define CCTK_INTEGER8_VEC CCTK_REAL8_VEC
#define CCTK_BOOLEAN8_VEC CCTK_REAL8_VEC

// Number of vector elements in a CCTK_REAL_VEC
#define CCTK_REAL8_VEC_SIZE 2

// Integer and boolean types corresponding to this real type
#define CCTK_INTEGER8     CCTK_REAL8
#define CCTK_BOOLEAN8     CCTK_REAL8



union k8const_t {
  double             f[2];
  unsigned long long i[2];
  CCTK_REAL8_VEC     vf;
};



// Create vectors, extract vector elements

#define vec8_set1(a)  (__cmplx(a,a))
#define vec8_set(a,b) (__cmplx(a,b))

#define vec8_elt0(x) (__creal(x))
#define vec8_elt1(x) (__cimag(x))
#define vec8_elt(x_,d)                          \
  ({                                            \
    CCTK_REAL8_VEC const x__=(x_);              \
    CCTK_REAL8_VEC const x=x__;                 \
    CCTK_REAL8 a;                               \
    switch (d) {                                \
    case 0: a=vec8_elt0(x); break;              \
    case 1: a=vec8_elt1(x); break;              \
    }                                           \
    a;                                          \
  })
#define vec8_elti(x,d) vec8_elt(x,d)
#define vec8_eltb(x,d) vec8_elt(x,d)



// Load and store vectors

// Load a vector from memory (aligned and unaligned); this loads from
// a reference to a scalar
#define vec8_load(p)  (__lfpd((CCTK_REAL8 *)&(p)))
#if ! VECTORISE_ALWAYS_USE_ALIGNED_LOADS
#  define vec8_load_off1(p_)                    \
  ({                                            \
    CCTK_REAL8 const& p__=(p_);                 \
    CCTK_REAL8 const& p=p__;                    \
    vec8_set((&p)[0],(&p)[1]);                  \
  })
#else
#if 0
#  define vec8_load_off1(p_)                                    \
  ({                                                            \
    CCTK_REAL8 const& p__=(p_);                                 \
    CCTK_REAL8 const& p=p__;                                    \
    CCTK_REAL8_VEC const lo = __lfxd((CCTK_REAL8 *)(&p-1));     \
    CCTK_REAL8_VEC const hi = __lfxd((CCTK_REAL8 *)(&p+1));     \
    __fpsel(vec8_set(-1.0,+1.0),lo,hi);                         \
  })
#endif
#  define vec8_load_off1(p_)                            \
  ({                                                    \
    CCTK_REAL8 const& p__=(p_);                         \
    CCTK_REAL8 const& p=p__;                            \
    CCTK_REAL8_VEC const lo = vec8_load((&p)[-1]);      \
    CCTK_REAL8_VEC const hi = vec8_load((&p)[+1]);      \
    __fxmr(__fpsel(vec8_set(+1.0,-1.0),lo,hi));         \
  })
#endif
#define vec8_loadu(p_)                          \
  ({                                            \
    CCTK_REAL8 const& p__=(p_);                 \
    CCTK_REAL8 const& p=p__;                    \
    int const off = (ptrdiff_t)&p & 0xf;        \
    off==0 ? vec8_load(p) : vec8_load_off1(p);  \
  })

// Load a vector from memory that may or may not be aligned, as
// decided by the offset and the vector size
#if VECTORISE_ALWAYS_USE_UNALIGNED_LOADS
// Implementation: Always use unaligned load
#  define vec8_loadu_maybe(off,p)             vec8_loadu(p)
#  define vec8_loadu_maybe3(off1,off2,off3,p) vec8_loadu(p)
#else
#  define vec8_loadu_maybe(off,p_)              \
  ({                                            \
    CCTK_REAL8 const& p__=(p_);                 \
    CCTK_REAL8 const& p=p__;                    \
    (off) % CCTK_REAL8_VEC_SIZE == 0 ?          \
      vec8_load(p) :                            \
      vec8_load_off1(p);                        \
  })
#  if VECTORISE_ALIGNED_ARRAYS
// Assume all array x sizes are multiples of the vector size
#    define vec8_loadu_maybe3(off1,off2,off3,p) vec8_loadu_maybe(off1,p)
#  else
#    define vec8_loadu_maybe3(off1,off2,off3,p_)        \
  ({                                                    \
    CCTK_REAL8 const& p__=(p_);                         \
    CCTK_REAL8 const& p=p__;                            \
    ((off2) % CCTK_REAL8_VEC_SIZE != 0 or               \
     (off3) % CCTK_REAL8_VEC_SIZE != 0) ?               \
      vec8_loadu(p) :                                   \
      vec8_loadu_maybe(off1,p);                         \
  })
#  endif
#endif

// Store a vector to memory (aligned and non-temporal); this stores to
// a reference to a scalar
#define vec8_store(p,x)     (__stfpd(&(p),x))
#define vec8_storeu(p,x)    (__stfpd(&(p),x)) // this may not work
#define vec8_store_nta(p,x) (__stfpd(&(p),x)) // this doesn't avoid the cache

// Store a partial vector (aligned and non-temporal)
#define vec8_store_partial_prepare(i,imin,imax)                 \
  bool const v8stp_lo = (i)>=(imin);                            \
  bool const v8stp_hi = (i)+CCTK_REAL_VEC_SIZE-1<(imax)
#define vec8_store_nta_partial(p_,x_)                           \
  ({                                                            \
    CCTK_REAL8& p__=(p_);                                       \
    CCTK_REAL8& p=p__;                                          \
    CCTK_REAL8_VEC const x__=(x_);                              \
    CCTK_REAL8_VEC const x=x__;                                 \
    if (CCTK_BUILTIN_EXPECT(v8stp_lo and v8stp_hi, true)) {     \
      vec8_store(p,x);                                          \
    } else if (v8stp_lo) {                                      \
      (&p)[0]=vec8_elt0(x);                                     \
    } else if (v8stp_hi) {                                      \
      (&p)[1]=vec8_elt1(x);                                     \
    }                                                           \
  })

// Store a lower or higher partial vector (aligned and non-temporal);
// the non-temporal hint is probably ignored
#define vec8_store_nta_partial_lo(p,x,n) ((&(p))[0]=vec8_elt0(x))
#define vec8_store_nta_partial_hi(p,x,n) ((&(p))[1]=vec8_elt1(x))
#define vec8_store_nta_partial_mid(p,x,nlo,nhi) assert(0)



// Functions and operators

// Operators
#define k8neg(x) (__fpneg(x))

#define k8add(x,y) (__fpadd(x,y))
#define k8sub(x,y) (__fpsub(x,y))
#define k8mul(x,y) (__fpmul(x,y))
// Estimate for reciprocal
#define k8inv_init(x) (__fpre(x))
// One Newton iteration for reciprocal
#define k8inv_iter(x_,r_)                               \
  ({                                                    \
    CCTK_REAL8_VEC const x__=(x_);                      \
    CCTK_REAL8_VEC const r__=(r_);                      \
    CCTK_REAL8_VEC const x=x__;                         \
    CCTK_REAL8_VEC const r=r__;                         \
    /* r + r * (1 - x*r) */                             \
    k8madd(r, k8nmsub(x, r, vec8_set1(1.0)), r);        \
  })
// Reciprocal: First estimate, then apply two Newton iterations
#define k8inv(x_)                               \
  ({                                            \
    CCTK_REAL8_VEC const x__=(x_);              \
    CCTK_REAL8_VEC const x=x__;                 \
    CCTK_REAL8_VEC const r0 = k8inv_init(x);    \
    CCTK_REAL8_VEC const r1 = k8inv_iter(x,r0); \
    CCTK_REAL8_VEC const r2 = k8inv_iter(x,r1); \
    r2;                                         \
  })
#define k8div(x,y) (__fpmul(x,k8inv(y)))

// Fused multiply-add, defined as [+-]x*y[+-]z
#define k8madd(x,y,z)  (__fpmadd(z,x,y))
#define k8msub(x,y,z)  (__fpmsub(z,x,y))
#define k8nmadd(x,y,z) (__fpnmadd(z,x,y))
#define k8nmsub(x,y,z) (__fpnmsub(z,x,y))

// Cheap functions
// TODO: handle -0 correctly
#define k8copysign(x,y) (k8ifthen(y,k8fabs(x),k8fnabs(x)))
#define k8fabs(x)       (__fpabs(x))
#define k8fmax(x_,y_)                           \
  ({                                            \
    CCTK_REAL8_VEC const x__=(x_);              \
    CCTK_REAL8_VEC const y__=(y_);              \
    CCTK_REAL8_VEC const x=x__;                 \
    CCTK_REAL8_VEC const y=y__;                 \
    __fpsel(k8sub(y,x),x,y);                    \
  })
#define k8fmin(x_,y_)                           \
  ({                                            \
    CCTK_REAL8_VEC const x__=(x_);              \
    CCTK_REAL8_VEC const y__=(y_);              \
    CCTK_REAL8_VEC const x=x__;                 \
    CCTK_REAL8_VEC const y=y__;                 \
    __fpsel(k8sub(x,y),x,y);                    \
  })
#define k8fnabs(x) (__fpnabs(x))
static const k8const_t k8zero = {{  0.0,  0.0, }};
static const k8const_t k8one  = {{ +1.0, +1.0, }};
static const k8const_t k8mone = {{ -1.0, -1.0, }};
#define k8sgn(x_)                                                       \
  ({                                                                    \
    CCTK_REAL_VEC x__=(x_);                                             \
    CCTK_REAL_VEC x=x__;                                                \
    CCTK_REAL_VEC iszero = k8fnabs(x);                                  \
    CCTK_REAL_VEC signedone = k8ifthen(x, k8one.vf, k8mone.vf);         \
    k8ifthen(iszero, k8zero.vf, signedone);                             \
  })
// Estimate for reciprocal square root
#define k8rsqrt_init(x) (__fprsqrte(x))
// One Newton iteration for reciprocal square root
#define k8rsqrt_iter(x_,rs_)                                    \
  ({                                                            \
    CCTK_REAL8_VEC const x__=(x_);                              \
    CCTK_REAL8_VEC const rs__=(rs_);                            \
    CCTK_REAL8_VEC const x=x__;                                 \
    CCTK_REAL8_VEC const rs=rs__;                               \
    /* rs (3/2 - x/2 rs^2) */                                   \
    k8mul(rs, k8msub(vec8_set1(1.5), x2, k8mul(rs, rs)));       \
  })
// Reciprocal square root: First estimate, then apply two Newton iterations
#define k8rsqrt(x_)                                     \
  ({                                                    \
    CCTK_REAL8_VEC const x__=(x_);                      \
    CCTK_REAL8_VEC const x=x__;                         \
    CCTK_REAL8_VEC const rs0 = k8rsqrt_init(x);         \
    CCTK_REAL8_VEC const x2  = k8mul(vec8_set1(0.5),x); \
    CCTK_REAL8_VEC const rs1 = k8rsqrt_iter(x2,rs0);    \
    CCTK_REAL8_VEC const rs2 = k8rsqrt_iter(x2,rs1);    \
    rs2;                                                \
  })
#define k8sqrt(x_)                              \
  ({                                            \
    CCTK_REAL8_VEC const x__=(x_);              \
    CCTK_REAL8_VEC const x=x__;                 \
    k8mul(x, k8rsqrt(x));                       \
  })

// Expensive functions
#define K8REPL(f,x_)                            \
  ({                                            \
    CCTK_REAL8_VEC const x__=(x_);              \
    CCTK_REAL8_VEC const x=x__;                 \
    vec8_set(f(vec8_elt0(x)),                   \
             f(vec8_elt1(x)));                  \
  })
#define K8REPL2S(f,x_,a_)                       \
  ({                                            \
    CCTK_REAL8_VEC const x__=(x_);              \
    CCTK_REAL8     const a__=(a_);              \
    CCTK_REAL8_VEC const x=x__;                 \
    CCTK_REAL8     const a=a__;                 \
    vec8_set(f(vec8_elt0(x),a),                 \
             f(vec8_elt1(x),a));                \
  })
#define K8REPL2(f,x_,y_)                        \
  ({                                            \
    CCTK_REAL8_VEC const x__=(x_);              \
    CCTK_REAL8_VEC const y__=(y_);              \
    CCTK_REAL8_VEC const x=x__;                 \
    CCTK_REAL8_VEC const y=y__;                 \
    vec8_set(f(vec8_elt0(x),vec8_elt0(y)),      \
             f(vec8_elt1(x),vec8_elt1(y)));     \
  })

#define k8acos(x)    K8REPL(acos,x)
#define k8acosh(x)   K8REPL(acosh,x)
#define k8asin(x)    K8REPL(asin,x)
#define k8asinh(x)   K8REPL(asinh,x)
#define k8atan(x)    K8REPL(atan,x)
#define k8atan2(x,y) K8REPL2(atan2,x,y)
#define k8atanh(x)   K8REPL(atanh,x)
#define k8cos(x)     K8REPL(cos,x)
#define k8cosh(x)    K8REPL(cosh,x)
#define k8exp(x)     K8REPL(exp,x)
#define k8fmod(x,y)  K8REPL2(fmod,x,y)
#define k8log(x)     K8REPL(log,x)
#define k8pow(x,a)   K8REPL2S(pow,x,a)
#define k8sin(x)     K8REPL(sin,x)
#define k8sinh(x)    K8REPL(sinh,x)
#define k8tan(x)     K8REPL(tan,x)
#define k8tanh(x)    K8REPL(tanh,x)

// canonical true is +1.0, canonical false is -1.0
// >=0 is true, -0 is true (?), nan is false (?)
static const k8const_t k8lfalse_ = {{ -1.0, -1.0, }};
static const k8const_t k8ltrue_  = {{ +1.0, +1.0, }};
#define k8lfalse        (k8lfalse_.vf)
#define k8ltrue         (k8ltrue_.vf)
#define k8lnot(x)       (k8ifthen(x,k8lfalse,k8ltrue))
#define k8land(x,y)     (k8ifthen(x,y,k8lfalse))
#define k8lor(x,y)      (k8ifthen(x,k8ltrue,y))
#define k8lxor(x,y)     (k8ifthen(x,k8lnot(y),y))
#define k8ifthen(x,y,z) (__fpsel(x,z,y))

#define k8cmpeq(x,y) (__fpnabs((x)-(y)))
#define k8cmpne(x,y) (k8lnot(__fpnabs((x)-(y))))
#define k8cmpgt(x,y) (k8lnot(k8cmple(x,y)))
#define k8cmpge(x,y) ((x)-(y))
#define k8cmplt(x,y) (k8lnot(k8cmpge(x,y)))
#define k8cmple(x,y) ((y)-(x))

```

vectors-8-AVX.h:
```
// -*-C++-*-
// Vectorise using Intel's or AMD's AVX

// Use the type __m256d directly, without introducing a wrapper class



#include <cstdlib>
#include <cstring>



#include <immintrin.h>
#ifdef __FMA4__
#  include <x86intrin.h>
#endif



#ifdef __FMA4__
#  define vec8_architecture_FMA4 "+FMA4"
#else
#  define vec8_architecture_FMA4 ""
#endif
#define vec8_architecture "AVX" vec8_architecture_FMA4 " (64-bit precision)"



// Vector type corresponding to CCTK_REAL
// Note: some boolean masks (e.g. ~0) correspond to nan when
// interpreted as floating point number. gcc 4.8 is clever enough to
// optimize away such constants with fast-math. We therefore need to
// handle this constant as integer number.
typedef __m256d CCTK_REAL8_VEC;
typedef __m256i CCTK_INTEGER8_VEC;
typedef __m256i CCTK_BOOLEAN8_VEC;

// Number of vector elements in a CCTK_REAL_VEC
#define CCTK_REAL8_VEC_SIZE 4

vec_static_assert(sizeof(CCTK_REAL8_VEC) ==
                  sizeof(CCTK_REAL8) * CCTK_REAL8_VEC_SIZE);

// Integer and boolean types corresponding to this real type
typedef CCTK_INT8 CCTK_INTEGER8;
typedef CCTK_INT8 CCTK_BOOLEAN8;



// These macros are undefined at the end of this file -- use them only
// within functions, not within macros that are exported
#define I2R(x) _mm256_castsi256_pd(x)
#define R2I(x) _mm256_castpd_si256(x)



union k8const_t {
  CCTK_INTEGER8     i[CCTK_REAL8_VEC_SIZE];
  CCTK_INTEGER8_VEC vi;
};

#define k8sign    (vec8_set1i(  (CCTK_INTEGER8)(1ULL << 63ULL)))
#define k8notsign (vec8_set1i(~ (CCTK_INTEGER8)(1ULL << 63ULL)))



// Create vectors, extract vector elements

static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC vec8_set1(CCTK_REAL8 const a)
{
  return _mm256_set1_pd(a);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_INTEGER8_VEC vec8_set1i(CCTK_INT8 const a)
{
  return _mm256_set1_epi64x(a);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC vec8_set(CCTK_REAL8 const a,
                        CCTK_REAL8 const b,
                        CCTK_REAL8 const c,
                        CCTK_REAL8 const d)
{
  return _mm256_set_pd(d,c,b,a); // note reversed arguments
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_INTEGER8_VEC vec8_seti(CCTK_INTEGER8 const a,
                            CCTK_INTEGER8 const b,
                            CCTK_INTEGER8 const c,
                            CCTK_INTEGER8 const d)
{
  return _mm256_set_epi64x(d,c,b,a); // note reversed arguments
}

static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8 vec8_elt(CCTK_REAL8_VEC const x, std::ptrdiff_t const d)
{
  CCTK_REAL8 e;
  std::memcpy(&e, &((char const*)&x)[d*sizeof e], sizeof e);
  return e;
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_INTEGER8 vec8_elti(CCTK_INTEGER8_VEC const x, std::ptrdiff_t const d)
{
  CCTK_INTEGER8 e;
  std::memcpy(&e, &((char const*)&x)[d*sizeof e], sizeof e);
  return e;
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN8 vec8_eltb(CCTK_BOOLEAN8_VEC const x, std::ptrdiff_t const d)
{
  CCTK_BOOLEAN8 e;
  std::memcpy(&e, &((char const*)&x)[d*sizeof e], sizeof e);
  return e;
}



// Load and store vectors

// Load a vector from memory (aligned and unaligned); this loads from
// a reference to a scalar
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC vec8_load(CCTK_REAL8 const& p)
{
  return _mm256_load_pd(&p);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC vec8_loadu(CCTK_REAL8 const& p)
{
  return _mm256_loadu_pd(&p);
}
#if VECTORISE_ALWAYS_USE_ALIGNED_LOADS
#  error "VECTORISE_ALWAYS_USE_ALIGNED_LOADS not yet supported"
#endif

// Load a vector from memory that may or may not be aligned, as
// decided by the offset off and the vector size
#if VECTORISE_ALWAYS_USE_UNALIGNED_LOADS
// Implementation: Always use unaligned load
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC vec8_loadu_maybe(std::ptrdiff_t const off, CCTK_REAL8 const& p)
{
  return vec8_loadu(p);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC vec8_loadu_maybe3(std::ptrdiff_t const off1,
                                 std::ptrdiff_t const off2,
                                 std::ptrdiff_t const off3,
                                 CCTK_REAL8 const& p)
{
  return vec8_loadu(p);
}
#else
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC vec8_loadu_maybe(std::ptrdiff_t const off, CCTK_REAL8 const& p)
{
  return off % CCTK_REAL8_VEC_SIZE == 0 ? vec8_load(p) : vec8_loadu(p);
}
#  if VECTORISE_ALIGNED_ARRAYS
// Assume all array x sizes are multiples of the vector size
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC vec8_loadu_maybe3(std::ptrdiff_t const off1,
                                 std::ptrdiff_t const off2,
                                 std::ptrdiff_t const off3,
                                 CCTK_REAL8 const& p)
{
  return vec8_loadu_maybe(off1, p);
}
#  else
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC vec8_loadu_maybe3(std::ptrdiff_t const off1,
                                 std::ptrdiff_t const off2,
                                 std::ptrdiff_t const off3,
                                 CCTK_REAL8 const& p)
{
  return
    off2 % CCTK_REAL8_VEC_SIZE != 0 or off3 % CCTK_REAL8_VEC_SIZE != 0 ?
    vec8_loadu(p) :
    vec8_loadu_maybe(off1, p);
}
#  endif
#endif

// Store a vector to memory (aligned and non-temporal); this stores to
// a reference to a scalar
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec8_store(CCTK_REAL8& p, CCTK_REAL8_VEC const x)
{
  _mm256_store_pd(&p, x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec8_storeu(CCTK_REAL8& p, CCTK_REAL8_VEC const x)
{
  _mm256_storeu_pd(&p, x);
}
#if ! VECTORISE_STREAMING_STORES
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec8_store_nta(CCTK_REAL8& p, CCTK_REAL8_VEC const x)
{
  vec8_store(p, x);
}
#else
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec8_store_nta(CCTK_REAL8& p, CCTK_REAL8_VEC const x)
{
  _mm256_stream_pd(&p, x);
}
#endif

// Store a partial vector (aligned and non-temporal)
#define vec8_store_partial_prepare(i, imin,imax)                        \
  bool v8stp_all;                                                       \
  __m256i v8stp_mask;                                                   \
  vec8_store_partial_prepare_(v8stp_all, v8stp_mask, i, imin, imax)
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec8_store_partial_prepare_(bool& all, __m256i& mask,
                                 std::ptrdiff_t const i,
                                 std::ptrdiff_t const imin,
                                 std::ptrdiff_t const imax)
{
  all = i>=imin and i+CCTK_REAL8_VEC_SIZE-1<imax;
  
  if (not CCTK_BUILTIN_EXPECT(all, true)) {
    /* __m256i const v8stp_mask = */
    /*   _mm256_andnot_pd(_mm256_add_epi64(_mm256_set1_epi64x(i-imin), */
    /*                                     vec_index), */
    /*                    _mm256_add_epi64(_mm256_set1_epi64x(i-imax), */
    /*                                     vec_index)); */
    __m128i const termlo01 =
      _mm_add_epi64(_mm_set1_epi64x(i-imin), _mm_set_epi64x(1, 0));
    __m128i const termup01 =
      _mm_add_epi64(_mm_set1_epi64x(i-imax), _mm_set_epi64x(1, 0));
    __m128i const term01 = _mm_andnot_si128(termlo01, termup01);
    __m128i const termlo23 =
      _mm_add_epi64(_mm_set1_epi64x(i-imin), _mm_set_epi64x(3, 2));
    __m128i const termup23 =
      _mm_add_epi64(_mm_set1_epi64x(i-imax), _mm_set_epi64x(3, 2));
    __m128i const term23 = _mm_andnot_si128(termlo23, termup23);
    mask = _mm256_insertf128_si256(_mm256_castsi128_si256(term01), term23, 1);
  }
}

// Store a partial vector (aligned and non-temporal)
#define vec8_store_partial_prepare_fixed(i, imin,imax)                  \
  bool v8stp_all;                                                       \
  __m256i v8stp_mask;                                                   \
  vec8_store_partial_prepare_fixed_(v8stp_all, v8stp_mask, i, imin, imax)
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec8_store_partial_prepare_fixed_(bool& all, __m256i& mask,
                                       std::ptrdiff_t const i,
                                       std::ptrdiff_t const imin,
                                       std::ptrdiff_t const imax)
{
  all = i>=imin and i+CCTK_REAL8_VEC_SIZE-1<imax;
  
  if (not CCTK_BUILTIN_EXPECT(all, true)) {
    mask = vec8_seti(i+0>=imin and i+0<imax ? ~0 : 0,
                     i+1>=imin and i+1<imax ? ~0 : 0,
                     i+2>=imin and i+2<imax ? ~0 : 0,
                     i+3>=imin and i+3<imax ? ~0 : 0);
  }
}

#define vec8_store_nta_partial(p, x)                    \
  vec8_store_nta_partial_(v8stp_all, v8stp_mask, p, x)
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec8_store_nta_partial_(bool const all, __m256i const mask,
                             CCTK_REAL8& p,
                             CCTK_REAL8_VEC const x)
{
  if (CCTK_BUILTIN_EXPECT(all, true)) {
    vec8_store_nta(p, x);
  } else {
    _mm256_maskstore_pd(&p, mask, x);
  }
}

#define vec8_storeu_partial(p, x)                       \
  vec8_storeu_partial_(v8stp_all, v8stp_mask, p, x)
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec8_storeu_partial_(bool const all, __m256i const mask,
                          CCTK_REAL8& p,
                          CCTK_REAL8_VEC const x)
{
  if (CCTK_BUILTIN_EXPECT(all, true)) {
    vec8_storeu(p, x);
  } else {
    _mm256_maskstore_pd(&p, mask, x);
  }
}

// Store a lower or higher partial vector (aligned and non-temporal);
// the non-temporal hint is probably ignored
// Masks indicating which vector element should be stored:
/*static*/ k8const_t const k8store_lo[5] =
  {
    {{  0,  0,  0,  0, }},
    {{ ~0,  0,  0,  0, }},
    {{ ~0, ~0,  0,  0, }},
    {{ ~0, ~0, ~0,  0, }},
    {{ ~0, ~0, ~0, ~0, }},
  };
/*static*/ k8const_t const k8store_hi[5] =
  {
    {{  0,  0,  0,  0, }},
    {{  0,  0,  0, ~0, }},
    {{  0,  0, ~0, ~0, }},
    {{  0, ~0, ~0, ~0, }},
    {{ ~0, ~0, ~0, ~0, }},
  };
#if !defined(__INTEL_COMPILER) && defined(__GNUC__) && __GNUC__==4 && __GNUC_MINOR__<=4
// gcc 4.4 uses a wrong prototype for _mm256_maskstore_pd
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec8_store_nta_partial_lo(CCTK_REAL8& p,
                               CCTK_REAL8_VEC const x,
                               ptrdiff_t const n)
{
  _mm256_maskstore_pd(&p, I2R(k8store_lo[n].vi), x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec8_store_nta_partial_hi(CCTK_REAL8& p,
                               CCTK_REAL8_VEC const x,
                               ptrdiff_t const n)
{
  _mm256_maskstore_pd(&p, I2R(k8store_hi[n].vi), x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec8_store_nta_partial_mid(CCTK_REAL8& p,
                                CCTK_REAL8_VEC const x,
                                ptrdiff_t const nlo,
                                ptrdiff_t const nhi)
{
  _mm256_maskstore_pd(&p, I2R(k8store_lo[nlo].vi & k8store_hi[nhi].vi), x);
}
#else
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec8_store_nta_partial_lo(CCTK_REAL8& p,
                               CCTK_REAL8_VEC const x,
                               ptrdiff_t const n)
{
  _mm256_maskstore_pd(&p, k8store_lo[n].vi, x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec8_store_nta_partial_hi(CCTK_REAL8& p,
                               CCTK_REAL8_VEC const x,
                               ptrdiff_t const n)
{
  _mm256_maskstore_pd(&p, k8store_hi[n].vi, x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec8_store_nta_partial_mid(CCTK_REAL8& p,
                                CCTK_REAL8_VEC const x,
                                ptrdiff_t const nlo,
                                ptrdiff_t const nhi)
{
  _mm256_maskstore_pd
    (&p,
     R2I(_mm256_and_pd(I2R(k8store_lo[nlo].vi), I2R(k8store_hi[nhi].vi))),
     x);
}
#endif



// Functions and operators

// Operators
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8neg(CCTK_REAL8_VEC const x)
{
  return _mm256_xor_pd(I2R(k8sign), x);
}

static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8add(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return _mm256_add_pd(x, y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8sub(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return _mm256_sub_pd(x, y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8mul(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return _mm256_mul_pd(x, y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8div(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return _mm256_div_pd(x, y);
}

// Fused multiply-add, defined as [+-]x*y[+-]z
#ifdef __FMA4__
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8madd(CCTK_REAL8_VEC const x,
                      CCTK_REAL8_VEC const y,
                      CCTK_REAL8_VEC const z)
{
  return _mm256_macc_pd(x, y, z);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8msub(CCTK_REAL8_VEC const x,
                      CCTK_REAL8_VEC const y,
                      CCTK_REAL8_VEC const z)
{
  return _mm256_msub_pd(x, y, z);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8nmadd(CCTK_REAL8_VEC const x,
                       CCTK_REAL8_VEC const y,
                       CCTK_REAL8_VEC const z)
{
  return _mm256_nmsub_pd(x, y, z);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8nmsub(CCTK_REAL8_VEC const x,
                       CCTK_REAL8_VEC const y,
                       CCTK_REAL8_VEC const z)
{
  return _mm256_nmacc_pd(x, y, z);
}
#else
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8madd(CCTK_REAL8_VEC const x,
                      CCTK_REAL8_VEC const y,
                      CCTK_REAL8_VEC const z)
{
  return k8add(k8mul(x, y), z);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8msub(CCTK_REAL8_VEC const x,
                      CCTK_REAL8_VEC const y,
                      CCTK_REAL8_VEC const z)
{
  return k8sub(k8mul(x, y), z);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8nmadd(CCTK_REAL8_VEC const x,
                       CCTK_REAL8_VEC const y,
                       CCTK_REAL8_VEC const z)
{
  return k8sub(k8neg(z), k8mul(x, y));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8nmsub(CCTK_REAL8_VEC const x,
                       CCTK_REAL8_VEC const y,
                       CCTK_REAL8_VEC const z)
{
  return k8sub(z, k8mul(x, y));
}
#endif

// Cheap functions
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8copysign(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return _mm256_or_pd(_mm256_and_pd(I2R(k8notsign), x),
                      _mm256_and_pd(I2R(k8sign   ), y));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8fabs(CCTK_REAL8_VEC const x)
{
  return _mm256_and_pd(I2R(k8notsign), x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8fmax(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return _mm256_max_pd(x, y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8fmin(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return _mm256_min_pd(x, y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8fnabs(CCTK_REAL8_VEC const x)
{
  return _mm256_or_pd(I2R(k8sign), x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN8_VEC k8signbit(CCTK_REAL8_VEC const x)
{
  return R2I(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8sqrt(CCTK_REAL8_VEC const x)
{
  return _mm256_sqrt_pd(x);
}



// Expensive functions

#define K8REPL(f,x)                             \
  vec8_set(f(vec8_elt(x,0)),                    \
           f(vec8_elt(x,1)),                    \
           f(vec8_elt(x,2)),                    \
           f(vec8_elt(x,3)));
#define K8REPL2S(f,x,a)                         \
  vec8_set(f(vec8_elt(x,0),a),                  \
           f(vec8_elt(x,1),a),                  \
           f(vec8_elt(x,2),a),                  \
           f(vec8_elt(x,3),a));
#define K8REPL2(f,x,y)                          \
  vec8_set(f(vec8_elt(x,0),vec8_elt(y,0)),      \
           f(vec8_elt(x,1),vec8_elt(y,1)),      \
           f(vec8_elt(x,2),vec8_elt(y,2)),      \
           f(vec8_elt(x,3),vec8_elt(y,3)));

#if defined __ICC
// The Intel compiler provides intrinsics for these

static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8acos(CCTK_REAL8_VEC const x)
{
  return _mm256_acos_pd(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8acosh(CCTK_REAL8_VEC const x)
{
  return _mm256_acosh_pd(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8asin(CCTK_REAL8_VEC const x)
{
  return _mm256_asin_pd(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8asinh(CCTK_REAL8_VEC const x)
{
  return _mm256_asinh_pd(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8atan(CCTK_REAL8_VEC const x)
{
  return _mm256_atan_pd(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8atan2(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return _mm256_atan2_pd(x,y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8atanh(CCTK_REAL8_VEC const x)
{
  return _mm256_atanh_pd(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8cos(CCTK_REAL8_VEC const x)
{
  return _mm256_cos_pd(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8cosh(CCTK_REAL8_VEC const x)
{
  return _mm256_cosh_pd(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8exp(CCTK_REAL8_VEC const x)
{
  return _mm256_exp_pd(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8fmod(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
#if __ICC > 1400
  return _mm256_fmod_pd(x, y);
#else
  return K8REPL2(fmod,x,y);
#endif
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8log(CCTK_REAL8_VEC const x)
{
  return _mm256_log_pd(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8pow(CCTK_REAL8_VEC const x, CCTK_REAL8 const a)
{
  return _mm256_pow_pd(x, _mm256_set1_pd(a));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8sin(CCTK_REAL8_VEC const x)
{
  return _mm256_sin_pd(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8sinh(CCTK_REAL8_VEC const x)
{
  return _mm256_sinh_pd(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8tan(CCTK_REAL8_VEC const x)
{
  return _mm256_tan_pd(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8tanh(CCTK_REAL8_VEC const x)
{
  return _mm256_tanh_pd(x);
}

#else

static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8acos(CCTK_REAL8_VEC const x)
{
  return K8REPL(acos,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8acosh(CCTK_REAL8_VEC const x)
{
  return K8REPL(acosh,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8asin(CCTK_REAL8_VEC const x)
{
  return K8REPL(asin,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8asinh(CCTK_REAL8_VEC const x)
{
  return K8REPL(asinh,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8atan(CCTK_REAL8_VEC const x)
{
  return K8REPL(atan,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8atan2(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return K8REPL2(atan2,x,y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8atanh(CCTK_REAL8_VEC const x)
{
  return K8REPL(atanh,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8cos(CCTK_REAL8_VEC const x)
{
  return K8REPL(cos,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8cosh(CCTK_REAL8_VEC const x)
{
  return K8REPL(cosh,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8exp(CCTK_REAL8_VEC const x)
{
  return K8REPL(exp,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8fmod(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return K8REPL2(fmod,x,y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8log(CCTK_REAL8_VEC const x)
{
  return K8REPL(log,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8pow(CCTK_REAL8_VEC const x, CCTK_REAL8 const a)
{
  return K8REPL2S(pow,x,a);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8sin(CCTK_REAL8_VEC const x)
{
  return K8REPL(sin,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8sinh(CCTK_REAL8_VEC const x)
{
  return K8REPL(sinh,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8tan(CCTK_REAL8_VEC const x)
{
  return K8REPL(tan,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8tanh(CCTK_REAL8_VEC const x)
{
  return K8REPL(tanh,x);
}

#endif



#define k8lfalse (vec8_set1i( 0))
#define k8ltrue  (vec8_set1i(~0))
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN8_VEC k8lnot(CCTK_BOOLEAN8_VEC const x)
{
  return R2I(_mm256_xor_pd(I2R(k8ltrue), I2R(x)));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN8_VEC k8land(CCTK_BOOLEAN8_VEC const x, CCTK_BOOLEAN8_VEC const y)
{
  return R2I(_mm256_and_pd(I2R(x), I2R(y)));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN8_VEC k8lor(CCTK_BOOLEAN8_VEC const x, CCTK_BOOLEAN8_VEC const y)
{
  return R2I(_mm256_or_pd(I2R(x), I2R(y)));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN8_VEC k8lxor(CCTK_BOOLEAN8_VEC const x, CCTK_BOOLEAN8_VEC const y)
{
  return R2I(_mm256_xor_pd(I2R(x), I2R(y)));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8ifthen(CCTK_BOOLEAN8_VEC const x,
                        CCTK_REAL8_VEC const y,
                        CCTK_REAL8_VEC const z)
{
  return _mm256_blendv_pd(z, y, I2R(x));
}

static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN8_VEC k8cmpeq(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return R2I(_mm256_cmp_pd(x, y, _CMP_EQ_OQ));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN8_VEC k8cmpne(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return R2I(_mm256_cmp_pd(x, y, _CMP_NEQ_UQ));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN8_VEC k8cmpgt(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return R2I(_mm256_cmp_pd(x, y, _CMP_GT_OQ));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN8_VEC k8cmpge(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return R2I(_mm256_cmp_pd(x, y, _CMP_GE_OQ));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN8_VEC k8cmplt(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return R2I(_mm256_cmp_pd(x, y, _CMP_LT_OQ));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN8_VEC k8cmple(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return R2I(_mm256_cmp_pd(x, y, _CMP_LE_OQ));
}



static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8sgn(CCTK_REAL8_VEC const x)
{
  CCTK_BOOLEAN8_VEC const iszero    = k8cmpeq(x, vec8_set1(0.0));
  CCTK_REAL8_VEC    const sign      = _mm256_and_pd(I2R(k8sign), x);
  CCTK_REAL8_VEC    const signedone = _mm256_or_pd(sign, vec8_set1(1.0));
  return k8ifthen(iszero, vec8_set1(0.0), signedone);
}



#undef I2R
#undef R2I

```

vectors-4-default.h:
```
// -*-C++-*-
// Fallback vectorisation implementation: Do not vectorise

// We use macros here, so that we are not surprised by compilers which
// don't like to inline functions. This should also make debug builds
// (which may not inline) more efficient.



#include <assert.h>
#include <math.h>



#define vec4_architecture "scalar (no vectorisation, 32-bit precision)"

// Use CCTK_REAL4
#define CCTK_REAL4_VEC CCTK_REAL4

// Number of vector elements in a vector
#define CCTK_REAL4_VEC_SIZE 1

vec_static_assert(sizeof(CCTK_REAL4_VEC) ==
                  sizeof(CCTK_REAL4) * CCTK_REAL4_VEC_SIZE);

// Integer and boolean types corresponding to this real type
#define CCTK_INTEGER4     CCTK_INT4
#define CCTK_BOOLEAN4     CCTK_INT4
#define CCTK_INTEGER4_VEC CCTK_INT4
#define CCTK_BOOLEAN4_VEC CCTK_INT4



// Create a vector replicating a scalar
#define vec4_set1(a) ((CCTK_REAL4)(a))
// Create a vector from N scalars
#define vec4_set(a) ((CCTK_REAL4)(a))

// Access vectors elements
#define vec4_elt0(x) (x)
#define vec4_elt(x,d) (x)
#define vec4_elti(x,d) (x)
#define vec4_eltb(x,d) (x)



// Load an aligned vector from memory
#define vec4_load(p) (p)
// Load an unaligned vector from memory
#define vec4_loadu(p) (p)

// Load a vector from memory that may or may not be aligned, as
// decided by the offset and the vector size. These functions are
// useful e.g. for loading neightbouring grid points while evaluating
// finite differencing stencils.
#define vec4_loadu_maybe(off,p) (p)
#define vec4_loadu_maybe3(off1,off2,off3,p) (p)

// Aligned store
#define vec4_store(p,x) ((p)=(x))
#define vec4_storeu(p,x) ((p)=(x))

// Unaligned store
#define vec4_store_nta(p,x) ((p)=(x))

#define vec4_store_partial_prepare(i,imin,imax) ((void)0)
#define vec4_store_nta_partial(p,x) (vec4_store_nta(p,x))
// Store the n lower elements of a vector to memory
#define vec4_store_nta_partial_lo(p,x,n) (CCTK_BUILTIN_UNREACHABLE())
// Store the n higher elements of a vector into memory. This stores
// the vector elements into memory locations as if element 0 were
// stored at p.
#define vec4_store_nta_partial_hi(p,x,n) (CCTK_BUILTIN_UNREACHABLE())
#define vec4_store_nta_partial_mid(p,x,nlo,nhi) (CCTK_BUILTIN_UNREACHABLE())



// Operators
#define k4neg(x) (-(x))

#define k4add(x,y) ((x)+(y))
#define k4sub(x,y) ((x)-(y))
#define k4mul(x,y) ((x)*(y))
#define k4div(x,y) ((x)/(y))

// Fused multiply-add, defined as [+-]x*y[+-]z
#define k4madd(x,y,z)  (+(x)*(y)+(z))
#define k4msub(x,y,z)  (+(x)*(y)-(z))
#define k4nmadd(x,y,z) (-(x)*(y)-(z))
#define k4nmsub(x,y,z) (-(x)*(y)+(z))

// Functions
#define k4acos(x)       (acosf(x))
#define k4acosh(x)      (acoshf(x))
#define k4asin(x)       (asinf(x))
#define k4asinh(x)      (asinhf(x))
#define k4atan(x)       (atanf(x))
#define k4atan2(x,y)    (atan2f(x,y))
#define k4atanh(x)      (atanhf(x))
#define k4copysign(x,y) (copysign(x,y))
#define k4cos(x)        (cosf(x))
#define k4cosh(x)       (coshf(x))
#define k4exp(x)        (expf(x))
#define k4fabs(x)       (fabsf(x))
#define k4fmax(x,y)     (fmaxf(x,y))
#define k4fmin(x,y)     (fminf(x,y))
#define k4fmod(x,y)     (fmodf(x,y))
#define k4fnabs(x)      (-fabsf(x))
#define k4log(x)        (logf(x))
#define k4pow(x,a)      (powf(x,a))
#define k4sin(x)        (sinf(x))
#define k4sinh(x)       (sinhf(x))
#define k4sqrt(x)       (sqrtf(x))
#define k4tan(x)        (tanf(x))
#define k4tanh(x)       (tanhf(x))

#define k4sgn(x_)                                                       \
  ({                                                                    \
    CCTK_REAL4 x__=(x_);                                                \
    CCTK_REAL4 x=x__;                                                   \
    x==(CCTK_REAL4)0.0 ? (CCTK_REAL4)0.0 : std::copysign((CCTK_REAL4)1.0, x); \
  })
#define k4signbit(x) (std::signbit(x))

#define k4lfalse 0
#define k4ltrue  1
#define k4lnot(x)   (!(x))
#define k4land(x,y) ((x) && (y))
#define k4lor(x,y)  ((x) || (y))
#define k4lxor(x,y) (!(x) != !(y))

#define k4ifthen(x,y,z) ((x)?(y):(z))

#define k4cmpeq(x,y) ((x)==(y))
#define k4cmpne(x,y) ((x)!=(y))
#define k4cmpgt(x,y) ((x)>(y))
#define k4cmpge(x,y) ((x)>=(y))
#define k4cmplt(x,y) ((x)<(y))
#define k4cmple(x,y) ((x)<=(y))

```

vectors-4-SSE.h:
```
// -*-C++-*-
// Vectorise using Intel's or AMD's SSE

// Use the type __m128 directly, without introducing a wrapper class

#include <algorithm>
#include <cassert>
#include <cmath>
#include <cstring>



#include <xmmintrin.h>
#ifdef __SSE4_1__
// Intel's SSE 4.1
#  include <smmintrin.h>
#endif
#ifdef __SSE4A__
// AMD's SSE 4a
#  include <ammintrin.h>
#endif
#ifdef __FMA4__
#  include <x86intrin.h>
#endif



#ifdef __SSE4_1__
#  define vec4_architecture_SSE4_1 "+SSE4.1"
#else
#  define vec4_architecture_SSE4_1 ""
#endif
#ifdef __SSE4A__
#  define vec4_architecture_SSE4a "+SSE4A"
#else
#  define vec4_architecture_SSE4a ""
#endif
#ifdef __FMA4__
#  define vec4_architecture_FMA4 "+FMA4"
#else
#  define vec4_architecture_FMA4 ""
#endif
#define vec4_architecture "SSE" vec4_architecture_SSE4_1 vec4_architecture_SSE4a vec4_architecture_FMA4 " (32-bit precision)"



// Vector type corresponding to CCTK_REAL
// Note: some boolean masks (e.g. ~0) correspond to nan when
// interpreted as floating point number. gcc 4.8 is clever enough to
// optimize away such constants with fast-math. We therefore need to
// handle this constant as integer number.
typedef __m128  CCTK_REAL4_VEC;
typedef __m128i CCTK_INTEGER4_VEC;
typedef __m128i CCTK_BOOLEAN4_VEC;

// Number of vector elements in a CCTK_REAL_VEC
#define CCTK_REAL4_VEC_SIZE 4

vec_static_assert(sizeof(CCTK_REAL4_VEC) ==
                  sizeof(CCTK_REAL4) * CCTK_REAL4_VEC_SIZE);

// Integer and boolean types corresponding to this real type
typedef CCTK_INT4  CCTK_INTEGER4;
typedef CCTK_REAL4 CCTK_BOOLEAN4;



// These macros are undefined at the end of this file -- use them only
// within functions, not within macros that are exported
#define I2R(x) _mm_castsi128_ps(x)
#define R2I(x) _mm_castps_si128(x)



#define k4sign    (vec4_set1i(  (CCTK_INTEGER4)(1UL << 31UL)))
#define k4notsign (vec4_set1i(~ (CCTK_INTEGER4)(1UL << 31UL)))



// Create vectors, extract vector elements

static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC vec4_set1(CCTK_REAL4 const a)
{
  return _mm_set1_ps(a);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_INTEGER4_VEC vec4_set1i(CCTK_INT4 const a)
{
  return _mm_set1_epi32(a);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC vec4_set(CCTK_REAL4 const a,
                        CCTK_REAL4 const b,
                        CCTK_REAL4 const c,
                        CCTK_REAL4 const d)
{
  return _mm_set_ps(d,c,b,a);   // note reversed arguments
}

// original order is 0123
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC vec4_swap1032(CCTK_REAL4_VEC const x)
{
  return _mm_shuffle_ps(x, x, _MM_SHUFFLE(2,3,0,1));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC vec4_swap2301(CCTK_REAL4_VEC const x)
{
  return _mm_shuffle_ps(x, x, _MM_SHUFFLE(1,0,3,2));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC vec4_swap3210(CCTK_REAL4_VEC const x)
{
  return _mm_shuffle_ps(x, x, _MM_SHUFFLE(0,1,2,3));
}

static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4 vec4_elt(CCTK_REAL4_VEC const x, std::ptrdiff_t const d)
{
  CCTK_REAL4 e;
  std::memcpy(&e, &((char const*)&x)[d*sizeof e], sizeof e);
  return e;
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_INTEGER4 vec4_elti(CCTK_INTEGER4_VEC const x, std::ptrdiff_t const d)
{
  CCTK_INTEGER4 e;
  std::memcpy(&e, &((char const*)&x)[d*sizeof e], sizeof e);
  return e;
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN4 vec4_eltb(CCTK_BOOLEAN4_VEC const x, std::ptrdiff_t const d)
{
  CCTK_BOOLEAN4 e;
  std::memcpy(&e, &((char const*)&x)[d*sizeof e], sizeof e);
  return e;
}



// Load and store vectors

// Load a vector from memory (aligned and unaligned); this loads from
// a reference to a scalar
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC vec4_load(CCTK_REAL4 const& p)
{
  return _mm_load_ps(&p);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC vec4_loadu(CCTK_REAL4 const& p)
{
  return _mm_loadu_ps(&p);
}
#if ! VECTORISE_ALWAYS_USE_ALIGNED_LOADS
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC vec4_load_off1(CCTK_REAL4 const& p)
{
  return vec4_loadu(p);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC vec4_load_off2(CCTK_REAL4 const& p)
{
  return vec4_loadu(p);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC vec4_load_off3(CCTK_REAL4 const& p)
{
  return vec4_loadu(p);
}
#else
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC vec4_load_off1(CCTK_REAL4 const& p)
{
  CCTK_REAL4_VEC const lo = vec4_load((&p)[-1]);
  CCTK_REAL4_VEC const hi = vec4_load((&p)[+3]);
  CCTK_REAL4_VEC const hi2 = _mm_shuffle_ps(lo, hi, _MM_SHUFFLE(0,1,2,3));
  return _mm_shuffle_ps(lo, hi2, _MM_SHUFFLE(2,1,3,0));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC vec4_load_off2(CCTK_REAL4 const& p)
{
  CCTK_REAL4_VEC const lo = vec4_load((&p)[-2]);
  CCTK_REAL4_VEC const hi = vec4_load((&p)[+2]);
  return _mm_shuffle_ps(lo, hi, _MM_SHUFFLE(1,0,3,2));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC vec4_load_off3(CCTK_REAL4 const& p)
{
  CCTK_REAL4_VEC const lo = vec4_load((&p)[-1]);
  CCTK_REAL4_VEC const hi = vec4_load((&p)[+3]);
  CCTK_REAL4_VEC const lo2 = _mm_shuffle_ps(lo, hi, _MM_SHUFFLE(0,1,2,3));
  return _mm_shuffle_ps(lo2, hi, _MM_SHUFFLE(3,0,2,1));
}
#endif

// Load a vector from memory that may or may not be aligned, as
// decided by the offset off and the vector size
#if VECTORISE_ALWAYS_USE_UNALIGNED_LOADS
// Implementation: Always use unaligned load
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC vec4_loadu_maybe(std::ptrdiff_t const off, CCTK_REAL4 const& p)
{
  return vec4_loadu(p);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC vec4_loadu_maybe3(std::ptrdiff_t const off1,
                                 std::ptrdiff_t const off2,
                                 std::ptrdiff_t const off3,
                                 CCTK_REAL4 const& p)
{
  return vec4_loadu(p);
}
#else
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC vec4_loadu_maybe(std::ptrdiff_t const off, CCTK_REAL4 const& p)
{
  // The :? operator probably breaks with the Intel compiler
  //return off % CCTK_REAL4_VEC_SIZE == 0 ? vec4_load(p) : vec4_loadu(p);
  if (off % CCTK_REAL4_VEC_SIZE == 0) return vec4_load(p);
  return vec4_loadu(p);
}
#  if VECTORISE_ALIGNED_ARRAYS
// Assume all array x sizes are multiples of the vector size
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC vec4_loadu_maybe3(std::ptrdiff_t const off1,
                                 std::ptrdiff_t const off2,
                                 std::ptrdiff_t const off3,
                                 CCTK_REAL4 const& p)
{
  return vec4_loadu_maybe(off1, p);
}
#  else
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC vec4_loadu_maybe3(std::ptrdiff_t const off1,
                                 std::ptrdiff_t const off2,
                                 std::ptrdiff_t const off3,
                                 CCTK_REAL4 const& p)
{
  return
    off2 % CCTK_REAL4_VEC_SIZE != 0 or
    off3 % CCTK_REAL4_VEC_SIZE != 0 ?
    vec4_loadu(p) :
    vec4_loadu_maybe(off1, p);
}
#  endif
#endif

// Store a vector to memory (aligned and non-temporal); this stores to
// a reference to a scalar
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec4_store(CCTK_REAL4& p, CCTK_REAL4_VEC const x)
{
  return _mm_store_ps(&p, x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec4_storeu(CCTK_REAL4& p, CCTK_REAL4_VEC const x)
{
  return _mm_storeu_ps(&p, x);
}
#if ! VECTORISE_STREAMING_STORES
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec4_store_nta(CCTK_REAL4& p, CCTK_REAL4_VEC const x)
{
  return vec4_store(p, x);
}
#else
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec4_store_nta(CCTK_REAL4& p, CCTK_REAL4_VEC const x)
{
  return _mm_stream_ps(&p, x);
}
#endif

// Store a partial vector (aligned and non-temporal)
// We ignoring VECTORISE_STREAMING_STORES for partial stores
#define vec4_store_partial_prepare(i, imin, imax)                       \
  std::ptrdiff_t v4stp_lo_skip, v4stp_hi_skip;                          \
  vec4_store_partial_prepare_(v4stp_lo_skip, v4stp_hi_skip, i, imin, imax)
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec4_store_partial_prepare_(std::ptrdiff_t& lo_skip,
                                 std::ptrdiff_t& hi_skip,
                                 std::ptrdiff_t const i,
                                 std::ptrdiff_t const imin,
                                 std::ptrdiff_t const imax)
{
  lo_skip = std::max(std::ptrdiff_t(0), imin - i);
  hi_skip = std::max(std::ptrdiff_t(0), i+CCTK_REAL4_VEC_SIZE - imax);
}
#define vec4_store_nta_partial(p, x)                            \
  vec4_store_nta_partial_(v8stp_lo_skip, v8stp_hi_skip, p, x)
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec4_store_nta_partial_(std::ptrdiff_t const lo_skip,
                             std::ptrdiff_t const hi_skip,
                             CCTK_REAL4& p,
                             CCTK_REAL4_VEC const x)
{
  if (CCTK_BUILTIN_EXPECT(lo_skip==0 and hi_skip==0, true)) {
    vec4_store_nta(p, x);
  } else {
    // these cases fall through
    switch (lo_skip) {
    case 0:
      (&p)[0] = vec4_elt(x, 0);
    case 1:
      if (hi_skip>=3) break;
      (&p)[1] = vec4_elt(x, 1);
    case 2:
      if (hi_skip>=2) break;
      (&p)[2] = vec4_elt(x, 2);
    case 3:
      if (hi_skip>=1) break;
      (&p)[3] = vec4_elt(x, 3);
    }
  }
}

static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec4_store_nta_partial_lo(CCTK_REAL4& p,
                               CCTK_REAL4_VEC const x,
                               std::ptrdiff_t const n)
{
  // these cases fall through
  switch (n) {
  case 3: (&p)[2] = vec4_elt(x, 2);
  case 2: (&p)[1] = vec4_elt(x, 1);
  case 1: (&p)[0] = vec4_elt(x, 0);
  }
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec4_store_nta_partial_hi(CCTK_REAL4& p,
                               CCTK_REAL4_VEC const x,
                               std::ptrdiff_t const n)
{
  // these cases fall through
  switch (n) {
  case 3: (&p)[1]=vec4_elt(x, 1);
  case 2: (&p)[2]=vec4_elt(x, 2);
  case 1: (&p)[3]=vec4_elt(x, 3);
  }
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec4_store_nta_partial_hi(CCTK_REAL4& p,
                               CCTK_REAL4_VEC const x,
                               std::ptrdiff_t const nlo,
                               std::ptrdiff_t const nhi)
{
  // these cases fall through
  switch (nhi) {
  case 3:
    if (nlo<2) break;
    (&p)[1] = vec4_elt(x, 1);
  case 2:
    if (nlo<3) break;
    (&p)[2] = vec4_elt(x, 2);
  }
}



// Functions and operators

// Operators
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4neg(CCTK_REAL4_VEC const x)
{
  return _mm_xor_ps(I2R(k4sign), x);
}
// #define k4inv(x)
// TODO: provide k4inv via rcp and Newton-Raphson
// This is described in AMD's publication 47414.
// This should apply to AVX as well.

static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4add(CCTK_REAL4_VEC const x, CCTK_REAL4_VEC const y)
{
  return _mm_add_ps(x, y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4sub(CCTK_REAL4_VEC const x, CCTK_REAL4_VEC const y)
{
  return _mm_sub_ps(x, y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4mul(CCTK_REAL4_VEC const x, CCTK_REAL4_VEC const y)
{
  return _mm_mul_ps(x, y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4div(CCTK_REAL4_VEC const x, CCTK_REAL4_VEC const y)
{
  // TODO: maybe use k4inv and k4mul instead
  return _mm_div_ps(x, y);
}

// Fused multiply-add, defined as [+-]x*y[+-]z
#ifdef __FMA4__
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4madd(CCTK_REAL4_VEC const x,
                      CCTK_REAL4_VEC const y,
                      CCTK_REAL4_VEC const z)
{
  return _mm_macc_ps(x, y, z);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4msub(CCTK_REAL4_VEC const x,
                      CCTK_REAL4_VEC const y,
                      CCTK_REAL4_VEC const z)
{
  return _mm_msub_ps(x, y, z);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4nmadd(CCTK_REAL4_VEC const x,
                       CCTK_REAL4_VEC const y,
                       CCTK_REAL4_VEC const z)
{
  return _mm_nmsub_ps(x, y, z);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4nmsub(CCTK_REAL4_VEC const x,
                       CCTK_REAL4_VEC const y,
                       CCTK_REAL4_VEC const z)
{
  return _mm_nmacc_ps(x, y, z);
}
#else
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4madd(CCTK_REAL4_VEC const x,
                      CCTK_REAL4_VEC const y,
                      CCTK_REAL4_VEC const z)
{
  return k4add(k4mul(x, y), z);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4msub(CCTK_REAL4_VEC const x,
                      CCTK_REAL4_VEC const y,
                      CCTK_REAL4_VEC const z)
{
  return k4sub(k4mul(x, y), z);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4nmadd(CCTK_REAL4_VEC const x,
                       CCTK_REAL4_VEC const y,
                       CCTK_REAL4_VEC const z)
{
  return k4sub(k4neg(z), k4mul(x, y));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4nmsub(CCTK_REAL4_VEC const x,
                       CCTK_REAL4_VEC const y,
                       CCTK_REAL4_VEC const z)
{
  return k4sub(z, k4mul(x, y));
}
#endif

// Cheap functions
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4copysign(CCTK_REAL4_VEC const x, CCTK_REAL4_VEC const y)
{
  return _mm_or_ps(_mm_and_ps(I2R(k4notsign), x),
                   _mm_and_ps(I2R(k4sign   ), y));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4fabs(CCTK_REAL4_VEC const x)
{
  return _mm_and_ps(I2R(k4notsign), x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4fmax(CCTK_REAL4_VEC const x, CCTK_REAL4_VEC const y)
{
  return _mm_max_ps(x, y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4fmin(CCTK_REAL4_VEC const x, CCTK_REAL4_VEC const y)
{
  return _mm_min_ps(x, y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4fnabs(CCTK_REAL4_VEC const x)
{
  return _mm_or_ps(I2R(k4sign), x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN4_VEC k4signbit(CCTK_REAL4_VEC const x)
{
  return R2I(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4sqrt(CCTK_REAL4_VEC const x)
{
  // TODO: maybe use rsqrt and Newton-Raphson
  return _mm_sqrt_ps(x);
}



// Expensive functions

#define K4REPL(f,x)                             \
  vec4_set(f(vec4_elt(x,0)),                    \
           f(vec4_elt(x,1)),                    \
           f(vec4_elt(x,2)),                    \
           f(vec4_elt(x,3)));
#define K4REPL2S(f,x,a)                         \
  vec4_set(f(vec4_elt(x,0),a),                  \
           f(vec4_elt(x,1),a),                  \
           f(vec4_elt(x,2),a),                  \
           f(vec4_elt(x,3),a));
#define K4REPL2(f,x,y)                          \
  vec4_set(f(vec4_elt(x,0),vec4_elt(y,0)),      \
           f(vec4_elt(x,1),vec4_elt(y,1)),      \
           f(vec4_elt(x,2),vec4_elt(y,2)),      \
           f(vec4_elt(x,3),vec4_elt(y,3)));

#if defined __ICC
// The Intel compiler provides intrinsics for these

static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4acos(CCTK_REAL4_VEC const x)
{
  return _mm_acos_ps(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4acosh(CCTK_REAL4_VEC const x)
{
  return _mm_acosh_ps(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4asin(CCTK_REAL4_VEC const x)
{
  return _mm_asin_ps(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4asinh(CCTK_REAL4_VEC const x)
{
  return _mm_asinh_ps(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4atan(CCTK_REAL4_VEC const x)
{
  return _mm_atan_ps(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4atan2(CCTK_REAL4_VEC const x, CCTK_REAL4_VEC const y)
{
  return _mm_atan2_ps(x,y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4atanh(CCTK_REAL4_VEC const x)
{
  return _mm_atanh_ps(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4cos(CCTK_REAL4_VEC const x)
{
  return _mm_cos_ps(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4cosh(CCTK_REAL4_VEC const x)
{
  return _mm_cosh_ps(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4exp(CCTK_REAL4_VEC const x)
{
  return _mm_exp_ps(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4fmod(CCTK_REAL4_VEC const x, CCTK_REAL4_VEC const y)
{
#if __ICC > 1400
  return _mm_fmod_ps(x,y);
#else
  return K4REPL2(fmodf,x,y);
#endif
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4log(CCTK_REAL4_VEC const x)
{
  return _mm_log_ps(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4pow(CCTK_REAL4_VEC const x, CCTK_REAL4 const a)
{
  return _mm_pow_ps(x, _mm_set1_ps(a));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4sin(CCTK_REAL4_VEC const x)
{
  return _mm_sin_ps(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4sinh(CCTK_REAL4_VEC const x)
{
  return _mm_sinh_ps(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4tan(CCTK_REAL4_VEC const x)
{
  return _mm_tan_ps(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4tanh(CCTK_REAL4_VEC const x)
{
  return _mm_tanh_ps(x);
}

#else

static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4acos(CCTK_REAL4_VEC const x)
{
  return K4REPL(acosf,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4acosh(CCTK_REAL4_VEC const x)
{
  return K4REPL(acoshf,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4asin(CCTK_REAL4_VEC const x)
{
  return K4REPL(asinf,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4asinh(CCTK_REAL4_VEC const x)
{
  return K4REPL(asinhf,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4atan(CCTK_REAL4_VEC const x)
{
  return K4REPL(atanf,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4atan2(CCTK_REAL4_VEC const x, CCTK_REAL4_VEC const y)
{
  return K4REPL2(atan2f,x,y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4atanh(CCTK_REAL4_VEC const x)
{
  return K4REPL(atanhf,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4cos(CCTK_REAL4_VEC const x)
{
  return K4REPL(cosf,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4cosh(CCTK_REAL4_VEC const x)
{
  return K4REPL(coshf,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4exp(CCTK_REAL4_VEC const x)
{
  return K4REPL(expf,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4fmod(CCTK_REAL4_VEC const x, CCTK_REAL4_VEC const y)
{
  return K4REPL2(fmodf,x,y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4log(CCTK_REAL4_VEC const x)
{
  return K4REPL(logf,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4pow(CCTK_REAL4_VEC const x, CCTK_REAL4 const a)
{
  return K4REPL2S(powf,x,a);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4sin(CCTK_REAL4_VEC const x)
{
  return K4REPL(sinf,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4sinh(CCTK_REAL4_VEC const x)
{
  return K4REPL(sinhf,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4tan(CCTK_REAL4_VEC const x)
{
  return K4REPL(tanf,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4tanh(CCTK_REAL4_VEC const x)
{
  return K4REPL(tanhf,x);
}

#endif



#define k4lfalse (vec4_set1i( 0))
#define k4ltrue  (vec4_set1i(~0))
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN4_VEC k4lnot(CCTK_BOOLEAN4_VEC const x)
{
  return R2I(_mm_xor_ps(I2R(k4ltrue), I2R(x)));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN4_VEC k4land(CCTK_BOOLEAN4_VEC const x, CCTK_BOOLEAN4_VEC const y)
{
  return R2I(_mm_and_ps(I2R(x), I2R(y)));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN4_VEC k4lor(CCTK_BOOLEAN4_VEC const x, CCTK_BOOLEAN4_VEC const y)
{
  return R2I(_mm_or_ps(I2R(x), I2R(y)));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN4_VEC k4lxor(CCTK_BOOLEAN4_VEC const x, CCTK_BOOLEAN4_VEC const y)
{
  return R2I(_mm_xor_ps(I2R(x), I2R(y)));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4ifthen(CCTK_BOOLEAN4_VEC const x,
                        CCTK_REAL4_VEC const y,
                        CCTK_REAL4_VEC const z)
{
#ifdef __SSE4_1__
  return _mm_blendv_ps(z,y,I2R(x));
#elif 0
  return vec4_set(std::signbit(vec4_elt(x,0)) ? vec4_elt(y,0) : vec4_elt(z,0),
                  std::signbit(vec4_elt(x,1)) ? vec4_elt(y,1) : vec4_elt(z,1),
                  std::signbit(vec4_elt(x,2)) ? vec4_elt(y,2) : vec4_elt(z,2),
                  std::signbit(vec4_elt(x,3)) ? vec4_elt(y,3) : vec4_elt(z,3));
#elif 0
  // We don't need to shift -- the condition (mask) will be either all
  // zeros or all ones
  CCTK_REAL4_VEC const mask = (__m128)_mm_srai_epi32((__m128i)x, 31);
  // (z & ~mask) | (y & mask)
  return _mm_or_ps(_mm_andnot_ps(mask, z), _mm_and_ps(mask, y));
#else
  // This assumes that all logical operations always return either
  // lfalse or ltrue, and nothing "in between"
  // (z & ~mask) | (y & mask)   where imask = ~mask
  return _mm_or_ps(_mm_and_ps(I2R(x), y), _mm_andnot_ps(I2R(x), z));
#endif
}

static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN4_VEC k4cmpeq(CCTK_REAL4_VEC const x, CCTK_REAL4_VEC const y)
{
  return R2I(_mm_cmpeq_ps(x, y));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN4_VEC k4cmpne(CCTK_REAL4_VEC const x, CCTK_REAL4_VEC const y)
{
  return R2I(_mm_cmpneq_ps(x, y));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN4_VEC k4cmpgt(CCTK_REAL4_VEC const x, CCTK_REAL4_VEC const y)
{
  return R2I(_mm_cmpgt_ps(x, y));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN4_VEC k4cmpge(CCTK_REAL4_VEC const x, CCTK_REAL4_VEC const y)
{
  return R2I(_mm_cmpge_ps(x, y));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN4_VEC k4cmplt(CCTK_REAL4_VEC const x, CCTK_REAL4_VEC const y)
{
  return R2I(_mm_cmplt_ps(x, y));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN4_VEC k4cmple(CCTK_REAL4_VEC const x, CCTK_REAL4_VEC const y)
{
  return R2I(_mm_cmple_ps(x, y));
}



static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4sgn(CCTK_REAL4_VEC const x)
{
  CCTK_BOOLEAN4_VEC const iszero    = k4cmpeq(x, vec4_set1(0.0));
  CCTK_REAL4_VEC    const sign      = _mm_and_ps(I2R(k4sign), x);
  CCTK_REAL4_VEC    const signedone = _mm_or_ps(sign, vec4_set1(1.0));
  return k4ifthen(iszero, vec4_set1(0.0), signedone);
}



#undef I2R
#undef R2I

```

vectors-8-MIC.h:
```
// -*-C++-*-
// Vectorise using Intel's MIC

// Use the type __m512d directly, without introducing a wrapper class

// See
// <http://software.intel.com/sites/products/documentation/doclib/stdxe/2013/composerxe/compiler/cpp-lin/index.htm#GUID-B8DF6000-6872-47B4-AA64-D47A38AF21BD.htm>
// and
// <http://software.intel.com/sites/default/files/forum/278102/327364001en.pdf>.



#include <cstdlib>
#include <cstring>

#include <immintrin.h>



#define vec8_architecture "MIC (64-bit precision)"



// Vector type corresponding to CCTK_REAL
typedef __m512d  CCTK_REAL8_VEC;
typedef __m512i  CCTK_INTEGER8_VEC;
typedef __mmask8 CCTK_BOOLEAN8_VEC;

// Number of vector elements in a CCTK_REAL_VEC
#define CCTK_REAL8_VEC_SIZE 8

vec_static_assert(sizeof(CCTK_REAL8_VEC) ==
                  sizeof(CCTK_REAL8) * CCTK_REAL8_VEC_SIZE);

// Integer and boolean types corresponding to this real type
typedef CCTK_INT8     CCTK_INTEGER8;
typedef bool          CCTK_BOOLEAN8;



#define k8sign    (vec8i_set1i(  (CCTK_INTEGER8)(1ULL << 63ULL)))
#define k8notsign (vec8i_set1i(~ (CCTK_INTEGER8)(1ULL << 63ULL)))



// Create vectors, extract vector elements

static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC vec8_set1(CCTK_REAL8 const a)
{
  return _mm512_set1_pd(a);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_INTEGER8_VEC vec8i_set1i(CCTK_INT8 const a)
{
  return _mm512_set1_epi64(a);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC vec8_set(CCTK_REAL8 const a0,
                        CCTK_REAL8 const a1,
                        CCTK_REAL8 const a2,
                        CCTK_REAL8 const a3,
                        CCTK_REAL8 const a4,
                        CCTK_REAL8 const a5,
                        CCTK_REAL8 const a6,
                        CCTK_REAL8 const a7)
{
  return _mm512_set_pd(a7,a6,a5,a4,a3,a2,a1,a0); // note reversed arguments
}

static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8 vec8_elt(CCTK_REAL8_VEC const x, std::ptrdiff_t const d)
{
  CCTK_REAL8 e;
  std::memcpy(&e, &((char const*)&x)[d*sizeof e], sizeof e);
  return e;
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN8 vec8_eltb(CCTK_BOOLEAN8_VEC const x, std::ptrdiff_t const d)
{
  return _mm512_mask2int(x) & (1 << d);
}



// Load and store vectors

// Load a vector from memory (aligned and unaligned); this loads from
// a reference to a scalar
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC vec8_load(CCTK_REAL8 const& p)
{
  return _mm512_load_pd(&p);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC vec8_loadu(CCTK_REAL8 const& p)
{
  CCTK_REAL8_VEC x = _mm512_undefined_pd();
  x = _mm512_loadunpacklo_pd(x, &p);
  x = _mm512_loadunpackhi_pd(x, &p+8);
  return x;
}
#if VECTORISE_ALWAYS_USE_ALIGNED_LOADS
#  error "VECTORISE_ALWAYS_USE_ALIGNED_LOADS is not yet supported"
#endif

// Load a vector from memory that may or may not be aligned, as
// decided by the offset off and the vector size
#if VECTORISE_ALWAYS_USE_UNALIGNED_LOADS
// Implementation: Always use unaligned load
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC vec8_loadu_maybe(std::ptrdiff_t const off, CCTK_REAL8 const& p)
{
  return vec8_loadu(p);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC vec8_loadu_maybe3(std::ptrdiff_t const off1,
                                 std::ptrdiff_t const off2,
                                 std::ptrdiff_t const off3,
                                 CCTK_REAL8 const& p)
{
  return vec8_loadu(p);
}
#else
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC vec8_loadu_maybe(std::ptrdiff_t const off, CCTK_REAL8 const& p)
{
  return off % CCTK_REAL8_VEC_SIZE == 0 ? vec8_load(p) : vec8_loadu(p);
}
#  if VECTORISE_ALIGNED_ARRAYS
// Assume all array x sizes are multiples of the vector size
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC vec8_loadu_maybe3(std::ptrdiff_t const off1,
                                 std::ptrdiff_t const off2,
                                 std::ptrdiff_t const off3,
                                 CCTK_REAL8 const& p)
{
  return vec8_loadu_maybe(off1, p);
}
#  else
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC vec8_loadu_maybe3(std::ptrdiff_t const off1,
                                 std::ptrdiff_t const off2,
                                 std::ptrdiff_t const off3,
                                 CCTK_REAL8 const& p)
{
  return
    off2 % CCTK_REAL8_VEC_SIZE != 0 or
    off3 % CCTK_REAL8_VEC_SIZE != 0 ?
    vec8_loadu(p) :
    vec8_loadu_maybe(off1, p);
}
#  endif
#endif

// Store a vector to memory (aligned and non-temporal); this stores to
// a reference to a scalar
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec8_store(CCTK_REAL8& p, CCTK_REAL8_VEC const x)
{
  _mm512_store_pd(&p, x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec8_storeu(CCTK_REAL8& p, CCTK_REAL8_VEC const x)
{
  // TODO: Intel erratum suggests that hi should come before lo
  _mm512_packstorehi_pd(&p+8, x);
  _mm512_packstorelo_pd(&p  , x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec8_store_nta(CCTK_REAL8& p, CCTK_REAL8_VEC const x)
{
#if VECTORISE_STREAMING_STORES
  // non-temporal hint:
  // _mm512_extstore_pd(&p, x, _MM_DOWNCONV_PD_NONE, _MM_HINT_NT);
  // no-read hint:
  _mm512_storenr_pd(&p, x);
  _mm_clevict(&p, _MM_HINT_T1);
  // no-read hint, not globally ordered (requires fence?):
  // _mm512_storenrngo_pd(&p, x);
  // _mm_clevict(&p, _MM_HINT_T1);

#else
  _mm512_store_pd(&p, x);
#endif
}

// Store a partial vector (aligned and non-temporal)
#define vec8_store_partial_prepare(i, imin,imax)                        \
  __mmask8 v8stp_mask;                                                  \
  vec8_store_partial_prepare_(v8stp_mask, i, imin, imax)
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec8_store_partial_prepare_(__mmask8& mask,
                                 std::ptrdiff_t const i,
                                 std::ptrdiff_t const imin,
                                 std::ptrdiff_t const imax)
{
  unsigned char m = 255;
  if (i < imin) {
    /* clear lower imin-i bits */
    m &= 255 << (imin-i);
  }
  if (i+CCTK_REAL8_VEC_SIZE > imax) {
    /* clear upper i+CCTK_REAL8_VEC_SIZE-imax bits */
    m &= 255 >> (i+CCTK_REAL8_VEC_SIZE-imax);
  }
  mask = _mm512_int2mask(m);
}

#define vec8_store_nta_partial(p, x)            \
  vec8_store_nta_partial_(v8stp_mask, p, x)
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec8_store_nta_partial_(__mmask8 const mask,
                             CCTK_REAL8& p,
                             CCTK_REAL8_VEC const x)
{
  // TODO: use vec8_store_nta(p, x) if all=true?
  _mm512_mask_store_pd(&p, mask, x);
}

static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec8_store_nta_partial_lo(CCTK_REAL8& p,
                               CCTK_REAL8_VEC const x,
                               ptrdiff_t const n)
{
  _mm512_mask_store_pd(&p, _mm512_int2mask(255 >> (8-n)), x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec8_store_nta_partial_hi(CCTK_REAL8& p,
                               CCTK_REAL8_VEC const x,
                               ptrdiff_t const n)
{
  _mm512_mask_store_pd(&p, _mm512_int2mask(255 << (8-n)), x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec8_store_nta_partial_mid(CCTK_REAL8& p,
                                CCTK_REAL8_VEC const x,
                                ptrdiff_t const nlo,
                                ptrdiff_t const nhi)
{
  _mm512_mask_store_pd
    (&p, _mm512_int2mask((255 >> (8-nlo)) & (255 << (8-nhi))), x);
}



// Functions and operators

// Operators
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8neg(CCTK_REAL8_VEC const x)
{
  // Could also multiply by -1
  // Could also invert sign bit
  return _mm512_sub_pd(_mm512_set1_pd(0.0), x);
}

static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8add(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return _mm512_add_pd(x, y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8sub(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return _mm512_sub_pd(x, y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8mul(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return _mm512_mul_pd(x, y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8div(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return _mm512_div_pd(x, y);
}

// Fused multiply-add, defined as [+-]x*y[+-]z
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8madd(CCTK_REAL8_VEC const x,
                      CCTK_REAL8_VEC const y,
                      CCTK_REAL8_VEC const z)
{
  return _mm512_fmadd_pd(x, y, z);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8msub(CCTK_REAL8_VEC const x,
                      CCTK_REAL8_VEC const y,
                      CCTK_REAL8_VEC const z)
{
  return _mm512_fmsub_pd(x, y, z);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8nmadd(CCTK_REAL8_VEC const x,
                       CCTK_REAL8_VEC const y,
                       CCTK_REAL8_VEC const z)
{
  return _mm512_fnmsub_pd(x, y, z);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8nmsub(CCTK_REAL8_VEC const x,
                       CCTK_REAL8_VEC const y,
                       CCTK_REAL8_VEC const z)
{
  return _mm512_fnmadd_pd(x, y, z);
}

// Cheap functions
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8copysign(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  CCTK_INTEGER8_VEC ix = _mm512_castpd_si512(x);
  CCTK_INTEGER8_VEC iy = _mm512_castpd_si512(y);
  CCTK_INTEGER8_VEC ir = _mm512_or_epi64(_mm512_and_epi64(k8notsign, ix),
                                         _mm512_and_epi64(k8sign   , iy));
  return _mm512_castsi512_pd(ir);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8fabs(CCTK_REAL8_VEC const x)
{
  // Could also do k8fmax(x, k8neg(x))
  CCTK_INTEGER8_VEC ix = _mm512_castpd_si512(x);
  CCTK_INTEGER8_VEC ir = _mm512_and_epi64(k8notsign, ix);
  return _mm512_castsi512_pd(ir);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8fmax(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return _mm512_gmax_pd(x, y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8fmin(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return _mm512_gmin_pd(x, y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8fnabs(CCTK_REAL8_VEC const x)
{
  // Could also do k8fmin(x, k8neg(x))
  CCTK_INTEGER8_VEC ix = _mm512_castpd_si512(x);
  CCTK_INTEGER8_VEC ir = _mm512_or_epi64(k8sign, ix);
  return _mm512_castsi512_pd(ir);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8sqrt(CCTK_REAL8_VEC const x)
{
  return _mm512_sqrt_pd(x);
}



// Expensive functions

// These implementations are very expensive
#define K8REPL(f,x)                             \
  vec8_set(f(vec8_elt(x,0)),                    \
           f(vec8_elt(x,1)),                    \
           f(vec8_elt(x,2)),                    \
           f(vec8_elt(x,3)),                    \
           f(vec8_elt(x,4)),                    \
           f(vec8_elt(x,5)),                    \
           f(vec8_elt(x,6)),                    \
           f(vec8_elt(x,7)));
#define K8REPL2S(f,x,a)                         \
  vec8_set(f(vec8_elt(x,0),a),                  \
           f(vec8_elt(x,1),a),                  \
           f(vec8_elt(x,2),a),                  \
           f(vec8_elt(x,3),a),                  \
           f(vec8_elt(x,4),a),                  \
           f(vec8_elt(x,5),a),                  \
           f(vec8_elt(x,6),a),                  \
           f(vec8_elt(x,7),a));
#define K8REPL2(f,x,y)                          \
  vec8_set(f(vec8_elt(x,0),vec8_elt(y,0)),      \
           f(vec8_elt(x,1),vec8_elt(y,1)),      \
           f(vec8_elt(x,2),vec8_elt(y,2)),      \
           f(vec8_elt(x,3),vec8_elt(y,3)),      \
           f(vec8_elt(x,4),vec8_elt(y,4)),      \
           f(vec8_elt(x,5),vec8_elt(y,5)),      \
           f(vec8_elt(x,6),vec8_elt(y,6)),      \
           f(vec8_elt(x,7),vec8_elt(y,7)));

#if defined __ICC
// The Intel compiler provides intrinsics for these

// These implementations lead to an ICE with icpc 13.0.1
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8acos(CCTK_REAL8_VEC const x)
{
  return _mm512_acos_pd(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8acosh(CCTK_REAL8_VEC const x)
{
  return _mm512_acosh_pd(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8asin(CCTK_REAL8_VEC const x)
{
  return _mm512_asin_pd(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8asinh(CCTK_REAL8_VEC const x)
{
  return _mm512_asinh_pd(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8atan(CCTK_REAL8_VEC const x)
{
  return _mm512_atan_pd(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8atan2(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return _mm512_atan2_pd(x,y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8atanh(CCTK_REAL8_VEC const x)
{
  return _mm512_atanh_pd(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8cos(CCTK_REAL8_VEC const x)
{
  return _mm512_cos_pd(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8cosh(CCTK_REAL8_VEC const x)
{
  return _mm512_cosh_pd(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8exp(CCTK_REAL8_VEC const x)
{
  return _mm512_exp_pd(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8fmod(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return K8REPL2(fmod,x,y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8log(CCTK_REAL8_VEC const x)
{
  return _mm512_log_pd(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8pow(CCTK_REAL8_VEC const x, CCTK_REAL8 const a)
{
  return _mm512_pow_pd(x, _mm512_set1_pd(a));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8sin(CCTK_REAL8_VEC const x)
{
  return _mm512_sin_pd(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8sinh(CCTK_REAL8_VEC const x)
{
  return _mm512_sinh_pd(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8tan(CCTK_REAL8_VEC const x)
{
  return _mm512_tan_pd(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8tanh(CCTK_REAL8_VEC const x)
{
  return _mm512_tanh_pd(x);
}

#else

static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8acos(CCTK_REAL8_VEC const x)
{
  return K8REPL(acos,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8acosh(CCTK_REAL8_VEC const x)
{
  return K8REPL(acosh,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8asin(CCTK_REAL8_VEC const x)
{
  return K8REPL(asin,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8asinh(CCTK_REAL8_VEC const x)
{
  return K8REPL(asinh,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8atan(CCTK_REAL8_VEC const x)
{
  return K8REPL(atan,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8atan2(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return K8REPL2(atan2,x,y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8atanh(CCTK_REAL8_VEC const x)
{
  return K8REPL(atanh,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8cos(CCTK_REAL8_VEC const x)
{
  return K8REPL(cos,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8cosh(CCTK_REAL8_VEC const x)
{
  return K8REPL(cosh,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8exp(CCTK_REAL8_VEC const x)
{
  return K8REPL(exp,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8fmod(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return K8REPL2(fmod,x,y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8log(CCTK_REAL8_VEC const x)
{
  return K8REPL(log,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8pow(CCTK_REAL8_VEC const x, CCTK_REAL8 const a)
{
  return K8REPL2S(pow,x,a);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8sin(CCTK_REAL8_VEC const x)
{
  return K8REPL(sin,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8sinh(CCTK_REAL8_VEC const x)
{
  return K8REPL(sinh,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8tan(CCTK_REAL8_VEC const x)
{
  return K8REPL(tan,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8tanh(CCTK_REAL8_VEC const x)
{
  return K8REPL(tanh,x);
}

#endif



// TODO: try k8lxor(x,x) and k8lxnor(x,x)
#define k8lfalse (_mm512_int2mask( 0))
#define k8ltrue  (_mm512_int2mask(~0))
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN8_VEC k8lnot(CCTK_BOOLEAN8_VEC const x)
{
  return _mm512_knot(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN8_VEC k8land(CCTK_BOOLEAN8_VEC const x, CCTK_BOOLEAN8_VEC const y)
{
  return _mm512_kand(x, y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN8_VEC k8lor(CCTK_BOOLEAN8_VEC const x, CCTK_BOOLEAN8_VEC const y)
{
  return _mm512_kor(x, y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN8_VEC k8lxor(CCTK_BOOLEAN8_VEC const x, CCTK_BOOLEAN8_VEC const y)
{
  return _mm512_kxor(x, y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8ifthen(CCTK_BOOLEAN8_VEC const x,
                        CCTK_REAL8_VEC const y,
                        CCTK_REAL8_VEC const z)
{
  // This leads to an ICE
  // return _mm512_mask_blend_pd(x, z, y);
#if 0
  // This works:
  return _mm512_mask_mov_pd(z, x, y);
#endif
  // Intel suggests this:
  return x==0 ? z : _mm512_mask_blend_pd(x, z, y);
}

static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN8_VEC k8cmpeq(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return _mm512_cmpeq_pd_mask(x, y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN8_VEC k8cmpne(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return _mm512_cmpneq_pd_mask(x, y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN8_VEC k8cmpgt(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return _mm512_cmpnle_pd_mask(x, y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN8_VEC k8cmpge(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return _mm512_cmpnlt_pd_mask(x, y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN8_VEC k8cmplt(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return _mm512_cmplt_pd_mask(x, y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN8_VEC k8cmple(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return _mm512_cmple_pd_mask(x, y);
}



static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8sgn(CCTK_REAL8_VEC const x)
{
  CCTK_BOOLEAN8_VEC const iszero = k8cmpeq(x, vec8_set1(0.0));
  CCTK_BOOLEAN8_VEC const isneg  = k8cmplt(x, vec8_set1(0.0));
  return k8ifthen(iszero, vec8_set1(0.0),
                  k8ifthen(isneg, vec8_set1(-1.0), vec8_set1(+1.0)));
}

```

vectors.h:
```
// -*-C++-*-

#ifndef VECTORS_H
#define VECTORS_H

#include <cctk.h>



#define vec_static_assert(x) namespace { typedef int vsa[(x) ? 1 : -1]; }



#if VECTORISE

#  if defined __AVX__ && !defined DISABLE_AVX // Intel AVX
#    include "vectors-4-AVX.h"
#  elif defined __SSE__         // Intel SSE
#    include "vectors-4-SSE.h"
#  elif defined __ALTIVEC__     // Power Altivec
#    include "vectors-4-Altivec.h"
#  endif

#  if defined __MIC__           // Intel MIC
#    include "vectors-8-MIC.h"
#  elif defined __AVX__ && !defined DISABLE_AVX // Intel AVX
#    include "vectors-8-AVX.h"
#  elif defined __SSE2__        // Intel SSE2
#    include "vectors-8-SSE2.h"
#  elif defined __bgq__ && defined __VECTOR4DOUBLE__ // Blue Gene/Q QPX
#    include "vectors-8-QPX.h"
#  elif defined __ALTIVEC__ && defined _ARCH_PWR7 // Power VSX
#    include "vectors-8-VSX.h"
#  elif defined _ARCH_450D      // Blue Gene/P Double Hummer
#    include "vectors-8-DoubleHummer.h"
#  endif

#endif

// Default implementation, do not vectorise
#ifndef CCTK_REAL4_VEC_SIZE
#  include "vectors-4-default.h"
#endif
#ifndef CCTK_REAL8_VEC_SIZE
#  include "vectors-8-default.h"
#endif



// Operation counters
#ifndef VEC_COUNT
#  define VEC_COUNT(x)
#endif
// This expects variables declared as
//    ptrdiff_t vec_op_counter, vec_mem_counter;
#define vec_op_inc  ((void)(VEC_COUNT(vec_op_counter+=CCTK_REAL_VEC_SIZE)+0))
#define vec_mem_inc ((void)(VEC_COUNT(vec_mem_counter+=CCTK_REAL_VEC_SIZE)+0))



// Define macros for CCTK_REAL

#if defined CCTK_REAL_PRECISION_4 

#  define vec_architecture vec4_architecture

#  define CCTK_REAL_VEC      CCTK_REAL4_VEC
#  define CCTK_REAL_VEC_SIZE CCTK_REAL4_VEC_SIZE
#  define CCTK_INTEGER       CCTK_INTEGER4
#  define CCTK_BOOLEAN       CCTK_BOOLEAN4
#  define CCTK_INTEGER_VEC   CCTK_INTEGER4_VEC
#  define CCTK_BOOLEAN_VEC   CCTK_BOOLEAN4_VEC

#  define vec_set1 vec4_set1
#  define vec_set  vec4_set

#  define vec_elt  vec4_elt
#  define vec_elti vec4_elti
#  define vec_eltb vec4_eltb

#  define vec_load(p)            (vec_mem_inc, vec4_load(p))
#  define vec_loadu(p)           (vec_mem_inc, vec4_loadu(p))
#  define vec_loadu_maybe(off,p) (vec_mem_inc, vec4_loadu_maybe(off,p))
#  define vec_loadu_maybe3(off1,off2,off3,p)            \
  (vec_mem_inc, vec4_loadu_maybe3(off1,off2,off3,p))
#  define vec_store(p,x)             (vec_mem_inc, vec4_store(p,x))
#  define vec_store_nta(p,x)         (vec_mem_inc, vec4_store_nta(p,x))
#  define vec_store_partial_prepare  vec4_store_partial_prepare
#  define vec_store_nta_partial(p,x) (vec_mem_inc, vec4_store_nta_partial(p,x))
#  define vec_storeu_partial(p,x)    (vec_mem_inc, vec4_storeu_partial(p,x))
#  define vec_store_nta_partial_lo   vec4_store_nta_partial_lo
#  define vec_store_nta_partial_hi   vec4_store_nta_partial_hi
#  define vec_store_nta_partial_mid  vec4_store_nta_partial_mid

#  define kneg(x) (vec_op_inc, k4neg(x))

#  define kadd(x,y) (vec_op_inc, k4add(x,y))
#  define ksub(x,y) (vec_op_inc, k4sub(x,y))
#  define kmul(x,y) (vec_op_inc, k4mul(x,y))
#  define kdiv(x,y) (vec_op_inc, k4div(x,y))

#  define kmadd(x,y,z)  (vec_op_inc, vec_op_inc, k4madd(x,y,z))
#  define kmsub(x,y,z)  (vec_op_inc, vec_op_inc, k4msub(x,y,z))
#  define knmadd(x,y,z) (vec_op_inc, vec_op_inc, k4nmadd(x,y,z))
#  define knmsub(x,y,z) (vec_op_inc, vec_op_inc, k4nmsub(x,y,z))

#  define kacos     k4acos
#  define kacosh    k4acosh
#  define kasin     k4asin
#  define kasinh    k4asinh
#  define katan     k4atan
#  define katan2    k4atan2
#  define katanh    k4atanh
#  define kcopysign(x,y) (vec_op_inc, k4copysign(x,y))
#  define kcos      k4cos
#  define kcosh     k4cosh
#  define kexp      k4exp
#  define kfabs(x)   (vec_op_inc, k4fabs(x))
#  define kfmax(x,y) (vec_op_inc, k4fmax(x,y))
#  define kfmin(x,y) (vec_op_inc, k4fmin(x,y))
#  define kfmod(x,y) (vec_op_inc, k4fmod(x,y))
#  define kfnabs(x)  (vec_op_inc, k4fnabs(x))
#  define klog      k4log
#  define kpow      k4pow
#  define ksignbit(x) (vec_op_inc, k4signbit(x))
#  define ksin      k4sin
#  define ksinh     k4sinh
#  define ksgn      k4sgn
#  define ksqrt     k4sqrt
#  define ktan      k4tan
#  define ktanh     k4tanh

#  define klfalse k4lfalse
#  define kltrue  k4ltrue
#  define klnot   k4lnot
#  define kland   k4land
#  define klor    k4lor
#  define klxor   k4lxor
#  define kifthen k4ifthen

#  define kcmpeq k4cmpeq
#  define kcmpne k4cmpne
#  define kcmpgt k4cmpgt
#  define kcmpge k4cmpge
#  define kcmplt k4cmplt
#  define kcmple k4cmple

#elif defined CCTK_REAL_PRECISION_8 

#  define vec_architecture vec8_architecture

#  define CCTK_REAL_VEC      CCTK_REAL8_VEC
#  define CCTK_REAL_VEC_SIZE CCTK_REAL8_VEC_SIZE
#  define CCTK_INTEGER       CCTK_INTEGER8
#  define CCTK_BOOLEAN       CCTK_BOOLEAN8
#  define CCTK_INTEGER_VEC   CCTK_INTEGER8_VEC
#  define CCTK_BOOLEAN_VEC   CCTK_BOOLEAN8_VEC

#  define vec_set1 vec8_set1
#  define vec_set  vec8_set

#  define vec_elt  vec8_elt
#  define vec_elti vec8_elti
#  define vec_eltb vec8_eltb

#  define vec_load(p)                (vec_mem_inc, vec8_load(p))
#  define vec_loadu(p)               (vec_mem_inc, vec8_loadu(p))
#  define vec_loadu_maybe(off,p)     (vec_mem_inc, vec8_loadu_maybe(off,p))
#  define vec_loadu_maybe3(off1,off2,off3,p)            \
  (vec_mem_inc, vec8_loadu_maybe3(off1,off2,off3,p))
#  define vec_store(p,x)             (vec_mem_inc, vec8_store(p,x))
#  define vec_store_nta(p,x)         (vec_mem_inc, vec8_store_nta(p,x))
#  define vec_store_partial_prepare  vec8_store_partial_prepare
#  define vec_store_partial_prepare_fixed       \
  vec8_store_partial_prepare_fixed
#  define vec_store_nta_partial(p,x) (vec_mem_inc, vec8_store_nta_partial(p,x))
#  define vec_storeu_partial(p,x)    (vec_mem_inc, vec8_storeu_partial(p,x))
#  define vec_store_nta_partial_lo   vec8_store_nta_partial_lo
#  define vec_store_nta_partial_hi   vec8_store_nta_partial_hi
#  define vec_store_nta_partial_mid  vec8_store_nta_partial_mid

#  define kneg(x) (vec_op_inc, k8neg(x))

#  define kadd(x,y) (vec_op_inc, k8add(x,y))
#  define ksub(x,y) (vec_op_inc, k8sub(x,y))
#  define kmul(x,y) (vec_op_inc, k8mul(x,y))
#  define kdiv(x,y) (vec_op_inc, k8div(x,y))

#  define kmadd(x,y,z)  (vec_op_inc, vec_op_inc, k8madd(x,y,z))
#  define kmsub(x,y,z)  (vec_op_inc, vec_op_inc, k8msub(x,y,z))
#  define knmadd(x,y,z) (vec_op_inc, vec_op_inc, k8nmadd(x,y,z))
#  define knmsub(x,y,z) (vec_op_inc, vec_op_inc, k8nmsub(x,y,z))

#  define kacos     k8acos
#  define kacosh    k8acosh
#  define kasin     k8asin
#  define kasinh    k8asinh
#  define katan     k8atan
#  define katan2    k8atan2
#  define katanh    k8atanh
#  define kcopysign(x,y) (vec_op_inc, k8copysign(x,y))
#  define kcos      k8cos
#  define kcosh     k8cosh
#  define kexp      k8exp
#  define kfabs(x)   (vec_op_inc, k8fabs(x))
#  define kfmax(x,y) (vec_op_inc, k8fmax(x,y))
#  define kfmin(x,y) (vec_op_inc, k8fmin(x,y))
#  define kfmod(x,y) (vec_op_inc, k8fmod(x,y))
#  define kfnabs(x)  (vec_op_inc, k8fnabs(x))
#  define klog      k8log
#  define kpow      k8pow
#  define ksignbit(x) (vec_op_inc, k8signbit(x))
#  define ksin      k8sin
#  define ksinh     k8sinh
#  define ksgn      k8sgn
#  define ksqrt     k8sqrt
#  define ktan      k8tan
#  define ktanh     k8tanh

#  define klfalse k8lfalse
#  define kltrue  k8ltrue
#  define klnot   k8lnot
#  define kland   k8land
#  define klor    k8lor
#  define klxor   k8lxor
#  define kifthen k8ifthen

#  define kcmpeq k8cmpeq
#  define kcmpne k8cmpne
#  define kcmpgt k8cmpgt
#  define kcmpge k8cmpge
#  define kcmplt k8cmplt
#  define kcmple k8cmple

#else

#  error "Unknown CCTK_REAL_PRECISION"

#endif



// Deprecated
#define kifmsb(a,b,c) kifthen(a,b,c)
#define kifneg(a,b,c) kifmsb(a,b,c)
#define kifpos(a,b,c) kifmsb(a,c,b)

#define kisgn(a) (-42424242)



#if CCTK_REAL_VEC_SIZE == 1
#  define vec_index vec_set(0)
#elif CCTK_REAL_VEC_SIZE == 2
#  define vec_index vec_set(0,1)
#elif CCTK_REAL_VEC_SIZE == 4
#  define vec_index vec_set(0,1,2,3)
#elif CCTK_REAL_VEC_SIZE == 8
#  define vec_index vec_set(0,1,2,3,4,5,6,7)
#elif CCTK_REAL_VEC_SIZE == 16
#  define vec_index vec_set(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15)
#else
#  error "Unsupported vector size"
#endif

  

// Define a class template for easier access from C++

#ifdef __cplusplus

#include <cmath>
#include <cstdlib>

template<typename T>
struct vecprops {
  typedef T scalar_t;
  typedef T vector_t;
  static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  std::size_t size()
  {
    return 1;
  }
  static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  vector_t load (scalar_t const& a)
  {
    return a;
  }
  static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  vector_t loadu (scalar_t const& a)
  {
    return a;
  }
  static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  vector_t set1 (scalar_t const& a)
  {
    return a;
  }
  static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  scalar_t elt (vector_t const& x, std::ptrdiff_t const d)
  {
    return x;
  }
  static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  vector_t neg (vector_t const& x)
  {
    return -x;
  }
  static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  vector_t add (vector_t const& x, vector_t const& y)
  {
    return x+y;
  }
  static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  vector_t sub (vector_t const& x, vector_t const& y)
  {
    return x-y;
  }
  static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  vector_t mul (vector_t const& x, vector_t const& y)
  {
    return x*y;
  }
  static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  vector_t div (vector_t const& x, vector_t const& y)
  {
    return x/y;
  }
  static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  vector_t madd (vector_t const& x, vector_t const& y, vector_t const& z)
  {
    return x*y+z;
  }
  static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  vector_t sqrt (vector_t const& x)
  {
    return std::sqrt(x);
  }
};

template<>
struct vecprops<CCTK_REAL4> {
  typedef CCTK_REAL4     scalar_t;
  typedef CCTK_REAL4_VEC vector_t;
  static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  int size()
  {
    return CCTK_REAL4_VEC_SIZE;
  }
  static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  vector_t load (scalar_t const& a)
  {
    return vec4_load(a);
  }
  static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
   vector_t loadu (scalar_t const& a)
  {
    return vec4_loadu(a);
  }
  static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  vector_t set1 (scalar_t const& a)
  {
    return vec4_set1(a);
  }
  static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  scalar_t elt (vector_t const& x, int const d)
  {
    return vec4_elt(x,d);
  }
  static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  vector_t neg (vector_t const& x)
  {
    return k4neg(x);
  }
  static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  vector_t add (vector_t const& x, vector_t const& y)
  {
    return k4add(x,y);
  }
  static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  vector_t sub (vector_t const& x, vector_t const& y)
  {
    return k4sub(x,y);
  }
  static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  vector_t mul (vector_t const& x, vector_t const& y)
  {
    return k4mul(x,y);
  }
  static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  vector_t div (vector_t const& x, vector_t const& y)
  {
    return k4div(x,y);
  }
  static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  vector_t madd (vector_t const& x, vector_t const& y, vector_t const& z)
  {
    return k4madd(x,y,z);
  }
  static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  vector_t sqrt (vector_t const& x)
  {
    return k4sqrt(x);
  }
};

template<>
struct vecprops<CCTK_REAL8> {
  typedef CCTK_REAL8     scalar_t;
  typedef CCTK_REAL8_VEC vector_t;
  static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  int size()
  {
    return CCTK_REAL8_VEC_SIZE;
  }
  static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  vector_t load (scalar_t const& a)
  {
    return vec8_load(a);
  }
  static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  vector_t loadu (scalar_t const& a)
  {
    return vec8_loadu(a);
  }
  static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  vector_t set1 (scalar_t const& a)
  {
    return vec8_set1(a);
  }
  static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  scalar_t elt (vector_t const& x, int const d)
  {
    return vec8_elt(x,d);
  }
  static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  vector_t neg (vector_t const& x)
  {
    return k8neg(x);
  }
  static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  vector_t add (vector_t const& x, vector_t const& y)
  {
    return k8add(x,y);
  }
  static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  vector_t sub (vector_t const& x, vector_t const& y)
  {
    return k8sub(x,y);
  }
  static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  vector_t mul (vector_t const& x, vector_t const& y)
  {
    return k8mul(x,y);
  }
  static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  vector_t div (vector_t const& x, vector_t const& y)
  {
    return k8div(x,y);
  }
  static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  vector_t madd (vector_t const& x, vector_t const& y, vector_t const& z)
  {
    return k8madd(x,y,z);
  }
  static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  vector_t sqrt (vector_t const& x)
  {
    return k8sqrt(x);
  }
};

template<typename T>
class vectype {
  typedef vecprops<T> props;
public:
  typedef typename props::vector_t vector_t;
  typedef typename props::scalar_t scalar_t;
  vector_t v;
  vectype() { }
  vectype(vectype const& x): v(x.v) { }
  vectype(vector_t const& x): v(x) { }
  explicit vectype(scalar_t const& a): v(props::set1(a)) { }
  operator vector_t() const { return v; }
  vectype& operator=(vectype const& x) { v=x.v; return *this; }
  
  inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  std::size_t size() const {
    return props::size();
  }
  
  static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  vectype load(scalar_t const& a)
  {
    return props::load(a);
  }
  static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  vectype loadu(scalar_t const& a)
  {
    return props::loadu(a);
  }
  
  inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  scalar_t elt(std::ptrdiff_t const d) const
  {
    return props::elt(*this, d);
  }
  
  inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  vectype operator+() const
  {
    return *this;
  }
  inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  vectype operator-() const
  {
    return props::neg(*this);
  }
  
  inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  vectype operator+(vectype const& x) const
  {
    return props::add(*this, x);
  }
  inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  vectype operator-(vectype const& x) const
  {
    return props::sub(*this, x);
  }
  inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  vectype operator*(vectype const& x) const
  {
    return props::mul(*this, x);
  }
  inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  vectype operator/(vectype const& x) const
  {
    return props::div(*this, x);
  }
  
  inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  vectype& operator+=(vectype const& x)
  {
    return *this = *this+x;
  }
  inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  vectype& operator-=(vectype const& x)
  {
    return *this = *this-x;
  }
  inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  vectype& operator*=(vectype const& x)
  {
    return *this = *this*x;
  }
  inline CCTK_ATTRIBUTE_ALWAYS_INLINE
  vectype& operator/=(vectype const& x)
  {
    return *this = *this/x;
  }
};

template<typename T>
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
vectype<T> sqrt(vectype<T> const& x)
{
  return vecprops<T>::sqrt(x);
}

#endif



// Cache information

// Size of a a cache line in bytes
#ifndef CCTK_CACHELINE_SIZE
// TODO: Determine this properly
#  define CCTK_CACHELINE_SIZE 64
#endif

// Number of CCTK_REALs in a cache line
#define CCTK_REAL_CACHELINE_SIZE (CCTK_CACHELINE_SIZE / CCTK_REAL_PRECISION)
// If this fails, something is most likely wrong -- this would be a
// very weird (and inefficient?) architecture indeed
#if CCTK_REAL_CACHELINE_SIZE % CCTK_REAL_VEC_SIZE != 0
#  error "The cache line size is not a multiple of sizeof(CCTK_REAL_VEC)"
#endif



// For Kranc

#ifdef KRANC_C

#  undef KRANC_DIFF_FUNCTIONS
#  if ! VECTORISE_INLINE
#    define KRANC_DIFF_FUNCTIONS
#  endif

#  undef ToReal
#  define ToReal(x) (vec_set1(CCTK_REAL(x)))

#  undef IfThen
#  if (defined __PGI ||                                                 \
       defined _ARCH_450D ||                                            \
       (defined __ALTIVEC__ && defined _ARCH_PWR7))
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL_VEC vec_IfThen(CCTK_BOOLEAN x, CCTK_REAL_VEC y, CCTK_REAL_VEC z)
{
  if (x) return y; else return z;
}
#    define IfThen(x,y,z) vec_IfThen(x,y,z)
#  else
#    define IfThen(x,y,z) ((x) ? CCTK_REAL_VEC(y) : CCTK_REAL_VEC(z))
#  endif

#  undef KRANC_GFOFFSET3D
#  define KRANC_GFOFFSET3D(var,i,j,k)                                   \
  vec_loadu_maybe3((i),(j),(k),                                         \
                   *(CCTK_REAL const*)&                                 \
                   ((char const*)(var))[cdi*(i)+cdj*(j)+cdk*(k)])

#endif  // KRANC_C

#endif  // #ifndef VECTORS_H

```

vectors-8-default.h:
```
// -*-C++-*-
// Fallback vectorisation implementation: Do not vectorise



#include <cassert>
#include <cmath>



#define vec8_architecture "scalar (no vectorisation, 64-bit precision)"

// Use CCTK_REAL8
#define CCTK_REAL8_VEC CCTK_REAL8

// Number of vector elements in a vector
#define CCTK_REAL8_VEC_SIZE 1

vec_static_assert(sizeof(CCTK_REAL8_VEC) ==
                  sizeof(CCTK_REAL8) * CCTK_REAL8_VEC_SIZE);

// Integer and boolean types corresponding to this real type
#define CCTK_INTEGER8     CCTK_INT8
#define CCTK_BOOLEAN8     CCTK_INT8
#define CCTK_INTEGER8_VEC CCTK_INT8
#define CCTK_BOOLEAN8_VEC CCTK_INT8



// Create a vector replicating a scalar
#define vec8_set1(a) ((CCTK_REAL8)(a))
// Create a vector from N scalars
#define vec8_set(a) ((CCTK_REAL8)(a))

// Access vectors elements
#define vec8_elt0(x) (x)
#define vec8_elt(x,d) (x)
#define vec8_elti(x,d) (x)
#define vec8_eltb(x,d) (x)



// Load an aligned vector from memory
#define vec8_load(p) (p)
// Load an unaligned vector from memory
#define vec8_loadu(p) (p)

// Load a vector from memory that may or may not be aligned, as
// decided by the offset and the vector size. These functions are
// useful e.g. for loading neightbouring grid points while evaluating
// finite differencing stencils.
#define vec8_loadu_maybe(off,p) (p)
#define vec8_loadu_maybe3(off1,off2,off3,p) (p)

// Aligned store
#define vec8_store(p,x) ((p)=(x))
// Unaligned store
#define vec8_store_nta(p,x) ((p)=(x))

#define vec8_store_partial_prepare(i,imin,imax) ((void)0)
#define vec8_store_nta_partial(p,x) (vec8_store_nta(p,x))
// Store the n lower elements of a vector to memory
#define vec8_store_nta_partial_lo(p,x,n) (CCTK_BUILTIN_UNREACHABLE())
// Store the n higher elements of a vector into memory. This stores
// the vector elements into memory locations as if element 0 were
// stored at p.
#define vec8_store_nta_partial_hi(p,x,n) (CCTK_BUILTIN_UNREACHABLE())
#define vec8_store_nta_partial_mid(p,x,nlo,nhi) (CCTK_BUILTIN_UNREACHABLE())



// Operators
#define k8neg(x) (-(x))

#define k8add(x,y) ((x)+(y))
#define k8sub(x,y) ((x)-(y))
#define k8mul(x,y) ((x)*(y))
#define k8div(x,y) ((x)/(y))

// Fused multiply-add, defined as [+-]x*y[+-]z
#define k8madd(x,y,z)  (+(x)*(y)+(z))
#define k8msub(x,y,z)  (+(x)*(y)-(z))
#define k8nmadd(x,y,z) (-(x)*(y)-(z))
#define k8nmsub(x,y,z) (-(x)*(y)+(z))

// Functions
#define k8acos(x)       (acos(x))
#define k8acosh(x)      (acosh(x))
#define k8asin(x)       (asin(x))
#define k8asinh(x)      (asinh(x))
#define k8atan(x)       (atan(x))
#define k8atan2(x,y)    (atan2(x,y))
#define k8atanh(x)      (atanh(x))
#define k8copysign(x,y) (std::copysign(x,y))
#define k8cos(x)        (cos(x))
#define k8cosh(x)       (cosh(x))
#define k8exp(x)        (exp(x))
#define k8fabs(x)       (fabs(x))
#define k8fmax(x,y)     (fmax(x,y))
#define k8fmin(x,y)     (fmin(x,y))
#define k8fmod(x,y)     (fmod(x,y))
#define k8fnabs(x)      (-fabs(x))
#define k8log(x)        (log(x))
#define k8pow(x,a)      (pow(x,a))
#define k8sin(x)        (sin(x))
#define k8sinh(x)       (sinh(x))
#define k8sqrt(x)       (sqrt(x))
#define k8tan(x)        (tan(x))
#define k8tanh(x)       (tanh(x))
#define k8signbit(x)    (std::signbit(x))

#define k8lfalse 0
#define k8ltrue  1
#define k8lnot(x)   (!(x))
#define k8land(x,y) ((x) && (y))
#define k8lor(x,y)  ((x) || (y))
#define k8lxor(x,y) (!(x) != !(y))

#define k8ifthen(x,y,z) ((x)?(y):(z))

#define k8cmpeq(x,y) ((x)==(y))
#define k8cmpne(x,y) ((x)!=(y))
#define k8cmpgt(x,y) ((x)>(y))
#define k8cmpge(x,y) ((x)>=(y))
#define k8cmplt(x,y) ((x)<(y))
#define k8cmple(x,y) ((x)<=(y))

static inline CCTK_REAL8_VEC k8sgn(CCTK_REAL8_VEC const x)
{
  return x==(CCTK_REAL8)0.0 ? (CCTK_REAL8)0.0 : k8copysign((CCTK_REAL8)1.0, x);
}

```

vectors-4-Altivec.h:
```
// -*-C++-*-
// Vectorise using IBM's Altivec (Power)

// Use the type vector double directly, without introducing a wrapper class
// Use macros instead of inline functions



#include <altivec.h>



#define vec4_architecture "Altivec"

// Vector type corresponding to CCTK_REAL
#define CCTK_REAL4_VEC vector float

// Number of vector elements in a CCTK_REAL_VEC
#define CCTK_REAL4_VEC_SIZE 4

// Integer and boolean types corresponding to this real type
#define CCTK_INTEGER4     CCTK_REAL4
#define CCTK_BOOLEAN4     CCTK_REAL4
#define CCTK_INTEGER4_VEC CCTK_REAL4_VEC
#define CCTK_BOOLEAN4_VEC CCTK_REAL4_VEC



// Create vectors, extract vector elements

#define vec4_set1(a)  (vec_splats(a))
#define vec4_set(a,b,c,d)                       \
  ({                                            \
    CCTK_REAL4_VEC x;                           \
    x[0]=(a);                                   \
    x[1]=(b);                                   \
    x[2]=(c);                                   \
    x[3]=(d);                                   \
    x;                                          \
  })

#define vec4_elt0(x) ((x)[0])
#define vec4_elt1(x) ((x)[1])
#define vec4_elt2(x) ((x)[2])
#define vec4_elt3(x) ((x)[3])
#define vec4_elt(x,d) ((x)[d])



// Load and store vectors

// Load a vector from memory (aligned and unaligned); this loads from
// a reference to a scalar
#define vec4_load(p)  (*(CCTK_REAL4_VEC const*)&(p))
#define vec4_loadu(p) (*(CCTK_REAL4_VEC const*)&(p))

// Load a vector from memory that may or may not be aligned, as
// decided by the offset and the vector size
#define vec4_loadu_maybe(off,p)             (vec4_loadu(p))
#define vec4_loadu_maybe3(off1,off2,off3,p) (vec4_loadu(p))

// Store a vector to memory (aligned and non-temporal); this stores to
// a reference to a scalar
#define vec4_store(p,x)  (*(CCTK_REAL4_VEC*)&(p)=(x))
#define vec4_storeu(p,x) (*(CCTK_REAL4_VEC*)&(p)=(x))
#if ! VECTORISE_STREAMING_STORES
#  define vec4_store_nta(p,x) (vec4_store(p,x))
#else
// use stvxl instruction
#  define vec4_store_nta(p,x) (vec_stl(x,0,(CCTK_REAL4_VEC*)&(p)))
#endif

// Store a lower or higher partial vector (aligned and non-temporal);
// the non-temporal hint is probably ignored
#define vec4_store_nta_partial_lo(p_,x_,n)      \
  ({                                            \
    CCTK_REAL4     const& p__=(p_);             \
    CCTK_REAL4_VEC const  x__=(x_);             \
    CCTK_REAL4     const& p=p__;                \
    CCTK_REAL4_VEC const  x=x__;                \
    switch (n) {                                \
    case 3: (&p)[2]=x[2];                       \
    case 2: (&p)[1]=x[1];                       \
    case 1: (&p)[0]=x[0];                       \
    }                                           \
  })
#define vec4_store_nta_partial_hi(p_,x_,n)      \
  ({                                            \
    CCTK_REAL4     const& p__=(p_);             \
    CCTK_REAL4_VEC const  x__=(x_);             \
    CCTK_REAL4     const& p=p__;                \
    CCTK_REAL4_VEC const  x=x__;                \
    switch (n) {                                \
    case 3: (&p)[1]=x[1];                       \
    case 2: (&p)[2]=x[2];                       \
    case 1: (&p)[3]=x[3];                       \
    }                                           \
  })
#define vec4_store_nta_partial_mid(p_,x_,nlo_,nhi_)     \
  ({                                                    \
    CCTK_REAL4     const& p__  =(p_);                   \
    CCTK_REAL4_VEC const  x__  =(x_);                   \
    int            const  nlo__=(nlo_);                 \
    int            const  nhi__=(nhi_);                 \
    CCTK_REAL4     const& p  =p__;                      \
    CCTK_REAL4_VEC const  x  =x__;                      \
    int            const  nlo=nlo__;                    \
    int            const  nhi=nhi__;                    \
    if (nlo==3 and nhi==3) {                            \
      (&p)[1]=x[1];                                     \
      (&p)[2]=x[2];                                     \
    } else if (nlo==2 and nhi==3) {                     \
      (&p)[1]=x[1];                                     \
    } else if (nlo==3 and nhi==2) {                     \
      (&p)[2]=x[2];                                     \
    }                                                   \
  })



// Functions and operators

// Operators
#define k4neg(x) (-(x))

#define k4add(x,y) ((x)+(y))
#define k4sub(x,y) ((x)-(y))
#define k4mul(x,y) ((x)*(y))
#define k4div(x,y) ((x)/(y))

// Fused multiply-add, defined as [+-]x*y[+-]z
#define k4madd(x,y,z)  (vec_madd(x,y,z))
#define k4msub(x,y,z)  (vec_msub(x,y,z))
#define k4nmadd(x,y,z) (vec_nmadd(x,y,z))
#define k4nmsub(x,y,z) (vec_nmsub(x,y,z))

// Cheap functions
#define k4fabs(x)   (vec_abs(x))
#define k4fmax(x,y) (vec_max(x,y))
#define k4fmin(x,y) (vec_min(x,y))
#define k4fnabs(x)  (vec_nabs(x))

// Expensive functions
#define K4REPL(f,x_)                            \
  ({                                            \
    CCTK_REAL4_VEC const x__=(x_);              \
    CCTK_REAL4_VEC const x=x__;                 \
    vec4_set(f(vec4_elt0(x)),                   \
             f(vec4_elt1(x)),                   \
             f(vec4_elt2(x)),                   \
             f(vec4_elt3(x)));                  \
  })
#define K4REPL2S(f,x_,a_)                       \
  ({                                            \
    CCTK_REAL4_VEC const x__=(x_);              \
    CCTK_REAL4     const a__=(a_);              \
    CCTK_REAL4_VEC const x=x__;                 \
    CCTK_REAL4     const a=a__;                 \
    vec4_set(f(vec4_elt0(x),a),                 \
             f(vec4_elt1(x),a),                 \
             f(vec4_elt2(x),a),                 \
             f(vec4_elt3(x),a));                \
  })
#define K4REPL2(f,x_,y_)                        \
  ({                                            \
    CCTK_REAL4_VEC const x__=(x_);              \
    CCTK_REAL4_VEC const y__=(y_);              \
    CCTK_REAL4_VEC const x=x__;                 \
    CCTK_REAL4_VEC const y=y__;                 \
    vec4_set(f(vec4_elt0(x),vec4_elt0(y)),      \
             f(vec4_elt1(x),vec4_elt1(y)),      \
             f(vec4_elt2(x),vec4_elt2(y)),      \
             f(vec4_elt3(x),vec4_elt3(y)));     \
  })

#define k4acos(x)    K4REPL(acosf,x)
#define k4acosh(x)   K4REPL(acoshf,x)
#define k4asin(x)    K4REPL(asinf,x)
#define k4asinh(x)   K4REPL(asinhf,x)
#define k4atan(x)    K4REPL(atanf,x)
#define k4atan2(x,y) K4REPL2(atan2f,x,y)
#define k4atanh(x)   K4REPL(atanhf,x)
#define k4cos(x)     K4REPL(cosf,x)
#define k4cosh(x)    K4REPL(coshf,x)
#define k4exp(x)     K4REPL(expf,x)
#define k4fmod(x,y)  K4REPL2(fmodf,x,y)
#define k4log(x)     K4REPL(logf,x)
#define k4pow(x,a)   K4REPL2S(powf,x,a)
#define k4sin(x)     K4REPL(sinf,x)
#define k4sinh(x)    K4REPL(sinhf,x)
#define k4tan(x)     K4REPL(tanf,x)
#define k4tanh(x)    K4REPL(tanhf,x)

#define k4ifmsb(x,y,z)                                                  \
  (vec_sel((z), (y), vec_sra(vec_convert((x), &(vector int*)0), 31)))

```

make.code.defn:
```
# Main make.code.defn file for thorn Vectors

# Source files in this directory
SRCS = vectors.cc test.cc

# Subdirectories containing source files
SUBDIRS = 

```

test.cc:
```
#include "vectors.h"

#include <cctk.h>
#include <cctk_Arguments.h>
#include <cctk_Parameters.h>

#include <cassert>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <limits>

using namespace std;

inline CCTK_REAL my_sgn(CCTK_REAL const x) {
  return x == (CCTK_REAL)0.0 ? (CCTK_REAL)0.0 : copysign((CCTK_REAL)1.0, x);
}

#define SCALARTEST(testname, vecexpr, scalarexpr)                              \
  do {                                                                         \
    if (verbose) {                                                             \
      CCTK_VInfo(CCTK_THORNSTRING, "Test %s...", testname);                    \
      fflush(stdout);                                                          \
    }                                                                          \
    CCTK_REAL const res = (scalarexpr);                                        \
    CCTK_REAL const vecres = (vecexpr);                                        \
    CCTK_REAL const eps = numeric_limits<CCTK_REAL>::epsilon();                \
    if ((fabs(vecres - res) <= 10 * eps) or                                    \
        (CCTK_isnan(vecres) and CCTK_isnan(res))) {                            \
      passed++;                                                                \
    } else {                                                                   \
      CCTK_VParamWarn(CCTK_THORNSTRING,                                        \
                      "Failed test %s: expected %.17g, received %.17g",        \
                      testname, (double)res, (double)vecres);                  \
    }                                                                          \
    numtests++;                                                                \
  } while (0)

#define VECTEST(testname, vecexpr, scalarexpr)                                 \
  do {                                                                         \
    if (verbose) {                                                             \
      CCTK_VInfo(CCTK_THORNSTRING, "Test %s...", testname);                    \
      fflush(stdout);                                                          \
    }                                                                          \
    CCTK_REAL_VEC rv = (vecexpr);                                              \
    for (int i = 0; i < CCTK_REAL_VEC_SIZE; i++) {                             \
      CCTK_REAL res = (scalarexpr);                                            \
      CCTK_REAL vecres = vec_elt(rv, i);                                       \
      CCTK_REAL eps = numeric_limits<CCTK_REAL>::epsilon();                    \
      if ((fabs(vecres - res) <= 10 * eps) or                                  \
          (CCTK_isnan(vecres) and CCTK_isnan(res))) {                          \
        passed++;                                                              \
      } else {                                                                 \
        CCTK_VParamWarn(CCTK_THORNSTRING,                                      \
                        "Failed test %s: "                                     \
                        "for element %d, expected %.17g, received %.17g",      \
                        testname, i, (double)res, (double)vecres);             \
      }                                                                        \
      numtests++;                                                              \
    }                                                                          \
  } while (0)

#define VECBITTEST(testname, vecexpr, scalarexpr)                              \
  do {                                                                         \
    if (verbose) {                                                             \
      CCTK_VInfo(CCTK_THORNSTRING, "Test %s...", testname);                    \
      fflush(stdout);                                                          \
    }                                                                          \
    CCTK_BOOLEAN_VEC rv = (vecexpr);                                           \
    for (int i = 0; i < CCTK_REAL_VEC_SIZE; i++) {                             \
      CCTK_BOOLEAN res = (scalarexpr);                                         \
      CCTK_BOOLEAN vecres = vec_eltb(rv, i);                                   \
      if (memcmp(&vecres, &res, sizeof vecres) == 0) {                         \
        passed++;                                                              \
      } else {                                                                 \
        CCTK_INTEGER ires, ivecres;                                            \
        memcpy(&ires, &res, sizeof ires);                                      \
        memcpy(&ivecres, &vecres, sizeof ivecres);                             \
        CCTK_VParamWarn(CCTK_THORNSTRING,                                      \
                        "Failed test %s: "                                     \
                        "for element %d, expected %lld, received %lld",        \
                        testname, i, (long long)ires, (long long)ivecres);     \
      }                                                                        \
      numtests++;                                                              \
    }                                                                          \
  } while (0)

extern "C" void Vectors_Test(CCTK_ARGUMENTS) {
  DECLARE_CCTK_ARGUMENTS;
  DECLARE_CCTK_PARAMETERS;

  CCTK_INFO("Testing vectorisation... [errors may result in segfaults]");
  fflush(stdout);

  char testname[100];

  int passed = 0, numtests = 0;

  CCTK_REAL a[CCTK_REAL_VEC_SIZE];
  CCTK_REAL b[CCTK_REAL_VEC_SIZE];
  CCTK_REAL c[CCTK_REAL_VEC_SIZE];
  CCTK_REAL x[CCTK_REAL_VEC_SIZE]; // small
  CCTK_REAL y[CCTK_REAL_VEC_SIZE]; // small and positive
  CCTK_REAL z[CCTK_REAL_VEC_SIZE]; // >=1

  for (int i = 0; i < CCTK_REAL_VEC_SIZE; i++) {
    a[i] = (i + 1) * 1.23456789;
    b[i] = -(i + 1) * 9.87654321;
    c[i] = (i + 1) * 1.01010101;
    x[i] = (i + 1) * 0.00123 * (i % 2 ? +1 : -1);
    y[i] = (i + 1) * 0.00234;
    z[i] = (i)*0.00234 + 1.0;
  }

  CCTK_REAL_VEC av = vec_loadu(a[0]);
  CCTK_REAL_VEC bv = vec_loadu(b[0]);
  CCTK_REAL_VEC cv = vec_loadu(c[0]);
  CCTK_REAL_VEC xv = vec_loadu(x[0]);
  CCTK_REAL_VEC yv = vec_loadu(y[0]);
  CCTK_REAL_VEC zv = vec_loadu(z[0]);

  /* l and lv are similar to a and av, except that it is larger, and
     guaranteed to be aligned */
  CCTK_REAL_VEC lv[4];
  lv[0] = vec_loadu(a[0]);
  lv[1] = vec_loadu(b[0]);
  lv[2] = vec_loadu(c[0]);
  lv[3] = vec_loadu(a[0]);
  CCTK_REAL *const l = (CCTK_REAL *)&lv[0];

  /* s and sv are similar to a and av, but are aligned and not
     initialised */
  CCTK_REAL_VEC sv;
  CCTK_REAL *const s = (CCTK_REAL *)&sv;

  VECTEST("vec_set1", vec_set1(a[0]), a[0]);
#if CCTK_REAL_VEC_SIZE == 1
  VECTEST("vec_set", vec_set(a[0]), a[i]);
#elif CCTK_REAL_VEC_SIZE == 2
  VECTEST("vec_set", vec_set(a[0], a[1]), a[i]);
#elif CCTK_REAL_VEC_SIZE == 4
  VECTEST("vec_set", vec_set(a[0], a[1], a[2], a[3]), a[i]);
#elif CCTK_REAL_VEC_SIZE == 8
  VECTEST("vec_set", vec_set(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7]),
          a[i]);
#else
#error "Unsupported vector size"
#endif
  for (int d = 0; d < CCTK_REAL_VEC_SIZE; ++d) {
    snprintf(testname, sizeof testname, "vec_elt[%d]", d);
    SCALARTEST(testname, vec_elt(av, d), a[d]);
  }

  /* These tests will probably fail with a segfault, if they fail */
  VECTEST("vec_load", vec_load(*l), l[i]);
  for (int d = 0; d < CCTK_REAL_VEC_SIZE; ++d) {
    snprintf(testname, sizeof testname, "vec_loadu[%d]", d);
    VECTEST(testname, vec_loadu(l[d]), l[i + d]);
  }
  for (int d = 0; d < CCTK_REAL_VEC_SIZE; ++d) {
    snprintf(testname, sizeof testname, "vec_loadu_maybe[%d]", d);
    VECTEST(testname, vec_loadu_maybe(d, l[d]), l[i + d]);
  }
  for (int d1 = 0; d1 < CCTK_REAL_VEC_SIZE; ++d1) {
    for (int d2 = 0; d2 < CCTK_REAL_VEC_SIZE; ++d2) {
      for (int d3 = 0; d3 < CCTK_REAL_VEC_SIZE; ++d3) {
        if (not(VECTORISE and VECTORISE_ALIGNED_ARRAYS) or
            (d2 == 0 and d3 == 0)) {
          snprintf(testname, sizeof testname, "vec_loadu_maybe3[%d,%d,%d]", d1,
                   d2, d3);
          VECTEST(testname, vec_loadu_maybe3(d1, d2, d3, l[d1 + d2 + d3]),
                  l[i + d1 + d2 + d3]);
        }
      }
    }
  }

  /* These tests may fail with a segfault, if they fail */
  sv = av;
  vec_store(*s, bv);
  VECTEST("vec_store", sv, b[i]);
  sv = av;
  vec_store_nta(*s, bv);
  VECTEST("vec_store_nta", sv, b[i]);
  int const arenaitems = 3;
  int const arenasize = arenaitems * CCTK_REAL_VEC_SIZE;
  for (int ilo = 0; ilo < arenasize; ++ilo) {
    for (int ihi = 0; ihi < arenasize; ++ihi) {
      // Initialise arena to a everywhere
      for (int n = 0; n < arenaitems; ++n) {
        lv[n] = av;
      }
      // Set arena to b in the "interior"
      for (int i = (ilo & -CCTK_REAL_VEC_SIZE); i < ihi;
           i += CCTK_REAL_VEC_SIZE) {
        vec_store_partial_prepare(i, ilo, ihi);
        vec_store_nta_partial(l[i], bv);
      }
      for (int i = 0; i < arenasize; ++i) {
        snprintf(testname, sizeof testname, "vec_store_nta_partial[%d..%d][%d]",
                 ilo, ihi, i);
        SCALARTEST(
            testname, l[i],
            (i >= ilo and i < ihi ? b : a)[i & (CCTK_REAL_VEC_SIZE - 1)]);
      }
    }
  }
  /* The partial stores are not implemented for d==0 and
     d==CCTK_REAL_VEC_SIZE (because these are trivial) */
  for (int d = 1; d < CCTK_REAL_VEC_SIZE - 1; ++d) {
    sv = av;
    vec_store_nta_partial_lo(*s, bv, d);
    snprintf(testname, sizeof testname, "vec_store_nta_partial_lo[%d]", d);
    VECTEST(testname, sv, i < d ? b[i] : a[i]);
  }
  for (int d = 1; d < CCTK_REAL_VEC_SIZE - 1; ++d) {
    sv = av;
    vec_store_nta_partial_hi(*s, bv, d);
    snprintf(testname, sizeof testname, "vec_store_nta_partial_hi[%d]", d);
    VECTEST(testname, sv, i >= CCTK_REAL_VEC_SIZE - d ? b[i] : a[i]);
  }
  for (int dlo = 1; dlo < CCTK_REAL_VEC_SIZE - 1; ++dlo) {
    for (int dhi = 1; dhi < CCTK_REAL_VEC_SIZE - 1; ++dhi) {
      sv = av;
      vec_store_nta_partial_mid(*s, bv, dlo, dhi);
      snprintf(testname, sizeof testname, "vec_store_nta_partial_mid[%d,%d]",
               dlo, dhi);
      VECTEST(testname, sv,
              i < dlo and i >= CCTK_REAL_VEC_SIZE - dhi ? b[i] : a[i]);
    }
  }

  VECTEST("kneg", kneg(av), -a[i]);

  VECTEST("kadd", kadd(av, bv), a[i] + b[i]);
  VECTEST("ksub", ksub(av, bv), a[i] - b[i]);
  VECTEST("kmul", kmul(av, bv), a[i] * b[i]);
  VECTEST("kdiv", kdiv(av, bv), a[i] / b[i]);

  VECTEST("kmadd", kmadd(av, bv, cv), +a[i] * b[i] + c[i]);
  VECTEST("kmsub", kmsub(av, bv, cv), +a[i] * b[i] - c[i]);
  VECTEST("knmadd", knmadd(av, bv, cv), -a[i] * b[i] - c[i]);
  VECTEST("knmsub", knmsub(av, bv, cv), -a[i] * b[i] + c[i]);

  VECTEST("kacos", kacos(xv), acos(x[i]));
  VECTEST("kacosh", kacosh(zv), acosh(z[i]));
  VECTEST("kasin", kasin(xv), asin(x[i]));
  VECTEST("kasinh", kasinh(xv), asinh(x[i]));
  VECTEST("katan", katan(xv), atan(x[i]));
  VECTEST("katan2", katan2(xv, yv), atan2(x[i], y[i]));
  VECTEST("katanh", katanh(xv), atanh(x[i]));
  VECTEST("kcopysign", kcopysign(xv, yv), copysign(x[i], y[i]));
  VECTEST("kcos", kcos(xv), cos(x[i]));
  VECTEST("kcosh", kcosh(xv), cosh(x[i]));
  VECTEST("kexp", kexp(xv), exp(x[i]));
  VECTEST("kfabs", kfabs(xv), fabs(x[i]));
  VECTEST("kfmax", kfmax(xv, yv), fmax(x[i], y[i]));
  VECTEST("kfmin", kfmin(xv, yv), fmin(x[i], y[i]));
  VECTEST("kfmod", kfmod(xv, yv), fmod(x[i], y[i]));
  VECTEST("kfnabs", kfnabs(xv), -fabs(x[i]));
  VECTEST("klog", klog(yv), log(y[i]));
  VECTEST("kpow", kpow(yv, x[0]), pow(y[i], x[0]));
  VECTEST("ksin", ksin(xv), sin(x[i]));
  VECTEST("ksinh", ksinh(xv), sinh(x[i]));
  VECTEST("ksgn", ksgn(xv), my_sgn(x[i]));
  VECTEST("ksqrt", ksqrt(yv), sqrt(y[i]));
  VECTEST("ktan", ktan(xv), tan(x[i]));
  VECTEST("ktanh", ktanh(xv), tanh(x[i]));

#if 0
  VECTEST("kifpos positive",
          kifpos(av, bv, cv), CCTK_SIGNBIT(a[i]) ? c[i] : b[i]);
  VECTEST("kifpos negative",
          kifpos(bv, bv, cv), CCTK_SIGNBIT(b[i]) ? c[i] : b[i]);
  VECTEST("kifpos 0",  kifpos(vec_set1(0.),bv,cv),  b[i]);
  VECTEST("kifpos -0", kifpos(vec_set1(-0.),bv,cv), c[i]);

  VECTEST("kifneg positive",
          kifneg(av, bv, cv), CCTK_SIGNBIT(a[i]) ? b[i] : c[i]);
  VECTEST("kifneg negative",
          kifneg(bv, bv, cv), CCTK_SIGNBIT(b[i]) ? b[i] : c[i]);
  VECTEST("kifneg 0",  kifneg(vec_set1(0.),bv,cv),  c[i]);
  VECTEST("kifneg -0", kifneg(vec_set1(-0.),bv,cv), b[i]);
#endif

  CCTK_BOOLEAN klfalse1 = vec_eltb(klfalse, 0);
  CCTK_BOOLEAN kltrue1 = vec_eltb(kltrue, 0);
  VECBITTEST("constant F", klfalse, klfalse1);
  VECBITTEST("constant T", kltrue, kltrue1);
  VECBITTEST("klnot F", klnot(klfalse), kltrue1);
  VECBITTEST("klnot T", klnot(kltrue), klfalse1);
  VECBITTEST("kland FF", kland(klfalse, klfalse), klfalse1);
  VECBITTEST("kland FT", kland(klfalse, kltrue), klfalse1);
  VECBITTEST("kland TF", kland(kltrue, klfalse), klfalse1);
  VECBITTEST("kland TT", kland(kltrue, kltrue), kltrue1);
  VECBITTEST("klor FF", klor(klfalse, klfalse), klfalse1);
  VECBITTEST("klor FT", klor(klfalse, kltrue), kltrue1);
  VECBITTEST("klor TF", klor(kltrue, klfalse), kltrue1);
  VECBITTEST("klor TT", klor(kltrue, kltrue), kltrue1);
  VECBITTEST("klxor FF", klxor(klfalse, klfalse), klfalse1);
  VECBITTEST("klxor FT", klxor(klfalse, kltrue), kltrue1);
  VECBITTEST("klxor TF", klxor(kltrue, klfalse), kltrue1);
  VECBITTEST("klxor TT", klxor(kltrue, kltrue), klfalse1);
  VECTEST("kifthen F", kifthen(klfalse, av, bv), b[i]);
  VECTEST("kifthen T", kifthen(kltrue, av, bv), a[i]);

  VECBITTEST("kcmpeq", kcmpeq(av, bv), a[i] == b[i] ? kltrue1 : klfalse1);
  VECBITTEST("kcmpne", kcmpne(av, bv), a[i] != b[i] ? kltrue1 : klfalse1);
  VECBITTEST("kcmpgt", kcmpgt(av, bv), a[i] > b[i] ? kltrue1 : klfalse1);
  VECBITTEST("kcmpge", kcmpge(av, bv), a[i] >= b[i] ? kltrue1 : klfalse1);
  VECBITTEST("kcmplt", kcmplt(av, bv), a[i] < b[i] ? kltrue1 : klfalse1);
  VECBITTEST("kcmple", kcmple(av, bv), a[i] <= b[i] ? kltrue1 : klfalse1);

  CCTK_VInfo(CCTK_THORNSTRING, "%d/%d tests passed ", passed, numtests);
  fflush(stdout);
  if (passed != numtests) {
    CCTK_VWarn(CCTK_WARN_ALERT, __LINE__, __FILE__, CCTK_THORNSTRING,
               "Failed %d correctness tests", numtests - passed);
  }
}

```

vectors-8-QPX.h:
```
// -*-C++-*-
// Vectorise using IBM's Blue Gene/Q QPX (Power)

#ifndef __clang__

// Use the type vector4double directly, without introducing a wrapper class
// Use macros instead of inline functions

// Note: bgxlC_r does not like const declarations, so we need to cast
// them away and/or omit them everywhere

// See <http://pic.dhe.ibm.com/infocenter/compbg/v121v141/index.jsp>

#include <assert.h>

#define vec8_assert(x) ((void)0)
// #define vec8_assert(x) assert(x)

// Argonne
#ifdef __cplusplus
#include <builtins.h>
#endif
#include <mass_simd.h>

#define vec8_architecture "QPX"

// Vector type corresponding to CCTK_REAL
// We use a struct to avoid the "const" issue
// #define CCTK_REAL8_VEC vector4double
struct CCTK_REAL8_VEC {
  vector4double v;
  CCTK_REAL8_VEC() {}
  CCTK_REAL8_VEC(CCTK_REAL8_VEC const &x) : v(x.v) {}
  CCTK_REAL8_VEC(vector4double v_) : v(v_) {}
  operator vector4double() const { return v; }
};

// Number of vector elements in a CCTK_REAL_VEC
#define CCTK_REAL8_VEC_SIZE 4

// Integer and boolean types corresponding to this real type
#define CCTK_INTEGER8 CCTK_INT8
#define CCTK_BOOLEAN8 CCTK_REAL8
#define CCTK_INTEGER8_VEC CCTK_REAL8_VEC
#define CCTK_BOOLEAN8_VEC CCTK_REAL8_VEC

// Create vectors, extract vector elements

#define vec8_set1(a) (vec_splats(a))
#if 0
#define vec8_set(a, b, c, d)                                                   \
  (vec_insert(                                                                 \
      d,                                                                       \
      vec_insert(c, vec_insert(b, vec_insert(a, CCTK_REAL8_VEC(), 0), 1), 2),  \
      3))
#endif
#define vec8_set(a_, b_, c_, d_)                                               \
  ({                                                                           \
    CCTK_REAL8 const a__ = (a_);                                               \
    CCTK_REAL8 const b__ = (b_);                                               \
    CCTK_REAL8 const c__ = (c_);                                               \
    CCTK_REAL8 const d__ = (d_);                                               \
    CCTK_REAL8 const a = a__;                                                  \
    CCTK_REAL8 const b = b__;                                                  \
    CCTK_REAL8 const c = c__;                                                  \
    CCTK_REAL8 const d = d__;                                                  \
    CCTK_REAL8_VEC x;                                                          \
    ((CCTK_REAL8 *)&x)[0] = a;                                                 \
    ((CCTK_REAL8 *)&x)[1] = b;                                                 \
    ((CCTK_REAL8 *)&x)[2] = c;                                                 \
    ((CCTK_REAL8 *)&x)[3] = d;                                                 \
    x;                                                                         \
  })

#define vec8_b2r(b) ((b) ? +1.0 : -1.0)
#define vec8_setb(a, b, c, d)                                                  \
  (vec8_set(vec8_b2r(a), vec8_b2r(b), vec8_b2r(c), vec8_b2r(d)))

#define vec8_elt0(x) (vec_extract(x, 0))
#define vec8_elt1(x) (vec_extract(x, 1))
#define vec8_elt2(x) (vec_extract(x, 2))
#define vec8_elt3(x) (vec_extract(x, 3))
#define vec8_elt(x, d) (vec_extract(x, d))
#define vec8_elts(x, a, b, c, d)                                               \
  ({                                                                           \
    CCTK_REAL8_VEC x__ = (x_);                                                 \
    CCTK_REAL8_VEC x = x__;                                                    \
    a = ((CCTK_REAL8 *)&x)[0];                                                 \
    b = ((CCTK_REAL8 *)&x)[1];                                                 \
    c = ((CCTK_REAL8 *)&x)[2];                                                 \
    d = ((CCTK_REAL8 *)&x)[3];                                                 \
  })

#define vec8_r2b(x) ((x) >= 0.0)
#define vec8_eltb(x, d) (vec8_r2b(vec8_elt(x, d)))

// Load and store vectors

// Load a vector from memory (aligned and unaligned); this loads from
// a reference to a scalar
#define vec8_load(p) (vec_lda(0, (CCTK_REAL8 *)&(p)))
#define vec8_loadu(p_)                                                         \
  ({                                                                           \
    CCTK_REAL8 const &p__ = (p_);                                              \
    CCTK_REAL8 &p = *(CCTK_REAL8 *)&p__;                                       \
    vector4double v1, v2, vp;                                                  \
    /* code taken from IBM's compiler documentation */                         \
    v1 = vec_ld(0, &p);   /* load the left part of the vector */               \
    v2 = vec_ld(31, &p);  /* load the right part of the vector */              \
    vp = vec_lvsl(0, &p); /* generate control value */                         \
    vec_perm(v1, v2, vp); /* generate the aligned vector */                    \
  })
#define vec8_loadu_off(off_, p_)                                               \
  ({                                                                           \
    int const off__ = (off_);                                                  \
    CCTK_REAL8 const &p__ = (p_);                                              \
    int off = off__;                                                           \
    CCTK_REAL8 &p = *(CCTK_REAL8 *)&p__;                                       \
    vector4double v1, v2;                                                      \
    off &= CCTK_REAL8_VEC_SIZE - 1;                                            \
    v1 = vec_lda(0, &p - off);                                                 \
    v2 = vec_lda(0, &p - off + CCTK_REAL8_VEC_SIZE);                           \
    off == 1 ? vec_sldw(v1, v2, 1) : off == 2                                  \
                                         ? vec_sldw(v1, v2, 2)                 \
                                         : off == 3 ? vec_sldw(v1, v2, 3)      \
                                                    : (vec8_assert(0), v1);    \
  })

// Load a vector from memory that may or may not be aligned, as
// decided by the offset and the vector size
#if VECTORISE_ALWAYS_USE_UNALIGNED_LOADS
// Implementation: Always use unaligned load
#define vec8_loadu_maybe(off, p) vec8_loadu(p)
#define vec8_loadu_maybe3(off1, off2, off3, p) vec8_loadu(p)
#else
#define vec8_loadu_maybe(off_, p_)                                             \
  ({                                                                           \
    CCTK_REAL8 const &p__ = (p_);                                              \
    int const off__ = (off_);                                                  \
    CCTK_REAL8 const &p = p__;                                                 \
    int const off = off__;                                                     \
    off % CCTK_REAL8_VEC_SIZE == 0 ? vec8_load(p) : vec8_loadu_off(off, p);    \
  })
#if VECTORISE_ALIGNED_ARRAYS
// Assume all array x sizes are multiples of the vector size
#define vec8_loadu_maybe3(off1, off2, off3, p) vec8_loadu_maybe(off1, p)
#else
#define vec8_loadu_maybe3(off1, off2, off3, p_)                                \
  ({                                                                           \
    CCTK_REAL8 const &p__ = (p_);                                              \
    CCTK_REAL8 const &p = p__;                                                 \
    ((off2) % CCTK_REAL8_VEC_SIZE != 0 or (off3) % CCTK_REAL8_VEC_SIZE != 0)   \
        ? vec8_loadu(p)                                                        \
        : vec8_loadu_maybe(off1, p);                                           \
  })
#endif
#endif

// Store a vector to memory (aligned and non-temporal); this stores to
// a reference to a scalar
#define vec8_store(p, x) (vec_sta(x, 0, &(p)))
#define vec8_storeu(p_, x_)                                                    \
  ({                                                                           \
    CCTK_REAL8 &p__ = (p_);                                                    \
    CCTK_REAL8_VEC x__ = (x_);                                                 \
    CCTK_REAL8 &p = p__;                                                       \
    CCTK_REAL8_VEC x = x__;                                                    \
    CCTK_REAL8_VEC v1, v2, v3, vp, m1, m2, m3;                                 \
    /* code taken from IBM's compiler documentation */                         \
    /* generate insert masks */                                                \
    vp = vec_lvsr(0, &p);                                                      \
    m1 = k8lfalse;                                                             \
    m2 = k8ltrue;                                                              \
    m3 = vec_perm(m1, m2, vp);                                                 \
    v3 = vec_perm(x, x, vp);                                                   \
    _Pragma("tm_atomic") {                                                     \
      /* get existing data */                                                  \
      v1 = vec_ld(0, &p);                                                      \
      v2 = vec_ld(31, &p);                                                     \
      /* permute and insert */                                                 \
      v1 = vec_sel(v1, v3, m3);                                                \
      v2 = vec_sel(v3, v2, m3);                                                \
      /* store data back */                                                    \
      vec_st(v1, 0, &p);                                                       \
      vec_st(v2, 31, &p);                                                      \
    }                                                                          \
  })
#define vec8_store_nta(p, x)                                                   \
  (vec_sta(x, 0, &(p))) // this doesn't avoid the cache

#ifdef _OPENMP
#if VECTORISE_ALIGNED_ARRAYS
// Arrays are aligned; wrap-around is not an issue
#define vec8_store_omp
#else
// Need to protect partial stores, as they may wrap around to the
// beginning of the next line in the array
#define vec8_store_omp _Pragma("tm_atomic")
#endif
#else
#define vec8_store_omp
#endif

// Store a partial vector (aligned and non-temporal)
#define vec8_store_partial_prepare(i, imin_, imax_)                            \
  bool v8stp_all;                                                              \
  CCTK_BOOLEAN8_VEC v8stp_mask;                                                \
  bool v8stp_mask0, v8stp_mask1, v8stp_mask2, v8stp_mask3;                     \
  ({                                                                           \
    ptrdiff_t const imin__ = (imin_);                                          \
    ptrdiff_t const imax__ = (imax_);                                          \
    ptrdiff_t const imin = imin__;                                             \
    ptrdiff_t const imax = imax__;                                             \
                                                                               \
    v8stp_all = i >= imin and i + CCTK_REAL8_VEC_SIZE - 1 < imax;              \
                                                                               \
    if (not CCTK_BUILTIN_EXPECT(v8stp_all, true)) {                            \
      CCTK_INTEGER8_VEC vp_lo, vp_hi;                                          \
      CCTK_BOOLEAN8_VEC mask_lo, mask_hi;                                      \
      /* this is correct but slow */                                           \
      /*                                                                       \
      mask_lo = vec8_setb(i+0>=imin, i+1>=imin, i+2>=imin, i+3>=imin);         \
      mask_hi = vec8_setb(i+0<imax, i+1<imax, i+2<imax, i+3<imax);             \
      */                                                                       \
      /* Note: vec_lvsl(i,p) =  &p[i] / 8 % 4                                  \
         Note: vec_lvsr(i,p) = -&p[i] / 8 % 4                                  \
         /8: 8 bytes per double                                                \
         %4: 4 doubles per vector                                              \
      */                                                                       \
      /* We assume p[i] is aligned */                                          \
      /* Ensure at least one vector element is inside the active region */     \
      vec8_assert(i - imin >= -(CCTK_REAL8_VEC_SIZE - 1));                     \
      vp_lo = vec_lvsl(8 * (i - imin), (CCTK_REAL *)0);                        \
      mask_lo =                                                                \
          (i - imin >= 0 ? k8ltrue : vec_perm(k8lfalse, k8ltrue, vp_lo));      \
      /* Ensure at least one vector element is inside the active region */     \
      vec8_assert(i < imax);                                                   \
      vp_hi = vec_lvsl(8 * (i - imax), (CCTK_REAL *)0);                        \
      mask_hi = (i - imax < -(CCTK_REAL8_VEC_SIZE - 1)                         \
                     ? k8ltrue                                                 \
                     : vec_perm(k8ltrue, k8lfalse, vp_hi));                    \
      v8stp_mask = k8land(mask_lo, mask_hi);                                   \
      v8stp_mask0 = vec8_eltb(v8stp_mask, 0);                                  \
      v8stp_mask1 = vec8_eltb(v8stp_mask, 1);                                  \
      v8stp_mask2 = vec8_eltb(v8stp_mask, 2);                                  \
      v8stp_mask3 = vec8_eltb(v8stp_mask, 3);                                  \
    }                                                                          \
  })
#define vec8_store_nta_partial(p_, x_)                                         \
  ({                                                                           \
    CCTK_REAL8 &p__ = (p_);                                                    \
    CCTK_REAL8_VEC x__ = (x_);                                                 \
    CCTK_REAL8 &p = p__;                                                       \
    CCTK_REAL8_VEC x = x__;                                                    \
    if (CCTK_BUILTIN_EXPECT(v8stp_all, true)) {                                \
      vec8_store(p, x);                                                        \
    } else {                                                                   \
      /*                                                                       \
      vec8_store_omp                                                           \
        vec8_store(p, k8ifthen(v8stp_mask, x, vec8_load(p)));                  \
      */                                                                       \
      if (VECTORISE_ALIGNED_ARRAYS) {                                          \
        vec8_store(p, k8ifthen(v8stp_mask, x, vec8_load(p)));                  \
      } else {                                                                 \
        if (v8stp_mask0)                                                       \
          (&p)[0] = vec8_elt0(x);                                              \
        if (v8stp_mask1)                                                       \
          (&p)[1] = vec8_elt1(x);                                              \
        if (v8stp_mask2)                                                       \
          (&p)[2] = vec8_elt2(x);                                              \
        if (v8stp_mask3)                                                       \
          (&p)[3] = vec8_elt3(x);                                              \
      }                                                                        \
    }                                                                          \
  })

// Store a lower or higher partial vector (aligned and non-temporal);
// the non-temporal hint is probably ignored
#define vec8_store_nta_partial_lo(p_, x_, n)                                   \
  ({                                                                           \
    CCTK_REAL8 &p__ = (p_);                                                    \
    CCTK_REAL8_VEC x__ = (x_);                                                 \
    CCTK_REAL8 &p = p__;                                                       \
    CCTK_REAL8_VEC x = x__;                                                    \
    CCTK_REAL8_VEC vp, mask;                                                   \
    /* Ensure at least one and but all vector elements are active */           \
    vec8_assert(n > 0 and n < CCTK_REAL8_VEC_SIZE - 1);                        \
    vp = vec_lvsl(-8 * n, (CCTK_REAL *)0);                                     \
    mask = vec_perm(k8ltrue, k8lfalse, vp);                                    \
    vec8_store_omp vec8_store(p, k8ifthen(mask, x, vec8_load(p)));             \
  })
#define vec8_store_nta_partial_hi(p_, x_, n)                                   \
  ({                                                                           \
    CCTK_REAL8 &p__ = (p_);                                                    \
    CCTK_REAL8_VEC x__ = (x_);                                                 \
    CCTK_REAL8 &p = p__;                                                       \
    CCTK_REAL8_VEC x = x__;                                                    \
    CCTK_REAL8_VEC vp, mask;                                                   \
    /* Ensure at least one but not all vector elements are active */           \
    vec8_assert(n > 0 and n < CCTK_REAL8_VEC_SIZE - 1);                        \
    vp = vec_lvsl(8 * n, (CCTK_REAL *)0);                                      \
    mask = vec_perm(k8lfalse, k8ltrue, vp);                                    \
    vec8_store_omp vec8_store(p, k8ifthen(mask, x, vec8_load(p)));             \
  })
#define vec8_store_nta_partial_mid(p_, x_, nlo, nhi)                           \
  ({                                                                           \
    CCTK_REAL8 &p__ = (p_);                                                    \
    CCTK_REAL8_VEC x__ = (x_);                                                 \
    CCTK_REAL8 p = p__;                                                        \
    CCTK_REAL8_VEC x = x__;                                                    \
    CCTK_REAL8_VEC vp_lo, mask_lo;                                             \
    /* Ensure at least one but not all vector elements are active */           \
    vec8_assert(nlo > 0 and nlo < CCTK_REAL8_VEC_SIZE - 1);                    \
    vp_lo = vec_lvsl(-8 * nlo, (CCTK_REAL *)0);                                \
    mask_lo = vec_perm(k8lfalse, k8ltrue, vp_lo);                              \
    CCTK_REAL8_VEC vp_hi, mask_hi;                                             \
    /* Ensure at least one but not all vector elements are active */           \
    vec8_assert(nhi > 0 and nhi < CCTK_REAL8_VEC_SIZE - 1);                    \
    vp_hi = vec_lvsl(8 * nhi, (CCTK_REAL *)0);                                 \
    mask_hi = vec_perm(k8lfalse, k8ltrue, vp_hi);                              \
    CCTK_REAL8_VEC mask;                                                       \
    mask = vec_and(mask_lo, mask_hi);                                          \
    vec8_store_omp vec8_store(p, k8ifthen(mask, x, vec8_load(p)));             \
  })

// Functions and operators

// Operators
#define k8neg(x) (vec_neg(x))

#define k8add(x, y) (vec_add(x, y))
#define k8sub(x, y) (vec_sub(x, y))
#define k8mul(x, y) (vec_mul(x, y))
#define k8div(x, y) (vec_swdiv_nochk(x, y))

// Fused multiply-add, defined as [+-]x*y[+-]z
#define k8madd(x, y, z) (vec_madd(x, y, z))
#define k8msub(x, y, z) (vec_msub(x, y, z))
#define k8nmadd(x, y, z) (vec_nmadd(x, y, z))
#define k8nmsub(x, y, z) (vec_nmsub(x, y, z))

// Cheap functions
#define k8copysign(x, y) (vec_cpsgn(y, x))
#define k8fabs(x) (vec_abs(x))
#define k8fmax(x_, y_)                                                         \
  ({                                                                           \
    CCTK_REAL8_VEC x__ = (x_);                                                 \
    CCTK_REAL8_VEC y__ = (y_);                                                 \
    CCTK_REAL8_VEC x = x__;                                                    \
    CCTK_REAL8_VEC y = y__;                                                    \
    k8ifthen(k8cmplt(x, y), y, x);                                             \
  })
#define k8fmin(x_, y_)                                                         \
  ({                                                                           \
    CCTK_REAL8_VEC x__ = (x_);                                                 \
    CCTK_REAL8_VEC y__ = (y_);                                                 \
    CCTK_REAL8_VEC x = x__;                                                    \
    CCTK_REAL8_VEC y = y__;                                                    \
    k8ifthen(k8cmpgt(x, y), y, x);                                             \
  })
#define k8fnabs(x) (vec_nabs(x))
#define k8sgn(x_)                                                              \
  ({                                                                           \
    CCTK_REAL8_VEC x__ = (x_);                                                 \
    CCTK_REAL8_VEC x = x__;                                                    \
    CCTK_REAL8_VEC one, zero, iszero;                                          \
    one = k8ltrue;                                                             \
    zero = k8sub(one, one);                                                    \
    iszero = k8cmpeq(x, zero);                                                 \
    k8ifthen(iszero, zero, k8copysign(one, x));                                \
  })
#define k8sqrt(x) (vec_swsqrt_nochk(x))

// Expensive functions

#define K8REPL(f, x_)                                                          \
  ({                                                                           \
    CCTK_REAL8_VEC const x__ = (x_);                                           \
    CCTK_REAL8_VEC const x = x__;                                              \
    vec8_set(f(vec8_elt0(x)), f(vec8_elt1(x)), f(vec8_elt2(x)),                \
             f(vec8_elt3(x)));                                                 \
  })
#define K8REPL2S(f, x_, a_)                                                    \
  ({                                                                           \
    CCTK_REAL8_VEC const x__ = (x_);                                           \
    CCTK_REAL8 const a__ = (a_);                                               \
    CCTK_REAL8_VEC const x = x__;                                              \
    CCTK_REAL8 const a = a__;                                                  \
    vec8_set(f(vec8_elt0(x), a), f(vec8_elt1(x), a), f(vec8_elt2(x), a),       \
             f(vec8_elt3(x), a));                                              \
  })
#define K8REPL2(f, x_, y_)                                                     \
  ({                                                                           \
    CCTK_REAL8_VEC const x__ = (x_);                                           \
    CCTK_REAL8_VEC const y__ = (y_);                                           \
    CCTK_REAL8_VEC const x = x__;                                              \
    CCTK_REAL8_VEC const y = y__;                                              \
    vec8_set(f(vec8_elt0(x), vec8_elt0(y)), f(vec8_elt1(x), vec8_elt1(y)),     \
             f(vec8_elt2(x), vec8_elt2(y)), f(vec8_elt3(x), vec8_elt3(y)));    \
  })

#define k8acos(x) acosd4(x)
#define k8acosh(x) acoshd4(x)
#define k8asin(x) asind4(x)
#define k8asinh(x) asinhd4(x)
#define k8atan(x) atand4(x)
#define k8atan2(x, y) atan2d4(x, y)
#define k8atanh(x) atanhd4(x)
#define k8cos(x) cosd4(x)
#define k8cosh(x) coshd4(x)
#define k8exp(x) expd4(x)
// #define k8fmod(x,y)  fmodd4(x,y)
#define k8fmod(x, y) K8REPL2(fmod, x, y)
#define k8log(x) logd4(x)
#define k8pow(x, a) powd4(x, vec_set1(a))
#define k8sin(x) sind4(x)
#define k8sinh(x) sinhd4(x)
#define k8tan(x) tand4(x)
#define k8tanh(x) tanhd4(x)

// canonical true is +1.0, canonical false is -1.0
// >=0 is true, -0 is true, nan is false
#define k8lfalse                                                               \
  ({                                                                           \
    CCTK_REAL8_VEC dummy;                                                      \
    vec_logical(dummy, dummy, 0x0);                                            \
  })
#define k8ltrue                                                                \
  ({                                                                           \
    CCTK_REAL8_VEC dummy;                                                      \
    vec_logical(dummy, dummy, 0xf);                                            \
  })
#define k8lnot(x) (vec_not(x))
#define k8land(x, y) (vec_and(x, y))
#define k8lor(x, y) (vec_or(x, y))
#define k8lxor(x, y) (vec_xor(x, y))
#define k8ifthen(x, y, z) (vec_sel(z, y, x))

#define k8cmpeq(x, y) (vec_cmpeq(x, y))
#define k8cmpne(x, y) (k8lnot(k8cmpeq(x, y)))
#define k8cmpgt(x, y) (vec_cmpgt(x, y))
#define k8cmpge(x, y) (k8lnot(k8cmplt(x, y)))
#define k8cmplt(x, y) (vec_cmplt(x, y))
#define k8cmple(x, y) (k8lnot(k8cmpgt(x, y)))

#else // #ifdef __clang__

// Use the type vector4double directly, without introducing a wrapper class
// Use macros instead of inline functions

// See <http://pic.dhe.ibm.com/infocenter/compbg/v121v141/index.jsp>

#include <assert.h>

#define vec8_assert(x) ((void)0)
// #define vec8_assert(x) assert(x)

#include <qpxmath.h>

#define vec8_architecture "QPX"

// Vector type corresponding to CCTK_REAL
typedef vector4double CCTK_REAL8_VEC;

// Number of vector elements in a CCTK_REAL_VEC
#define CCTK_REAL8_VEC_SIZE 4

// Integer and boolean types corresponding to this real type
typedef CCTK_INT8 CCTK_INTEGER8;
typedef CCTK_REAL8 CCTK_BOOLEAN8;
typedef CCTK_REAL8_VEC CCTK_INTEGER8_VEC;
typedef CCTK_REAL8_VEC CCTK_BOOLEAN8_VEC;

// canonical true is +1.0, canonical false is -1.0
// >=0 is true, -0 is true, nan is false
inline CCTK_BOOLEAN8_VEC k8lfalse_() {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wuninitialized"
  CCTK_BOOLEAN8_VEC dummy;
  return vec_logical(dummy, dummy, 0x0);
#pragma clang diagnostic pop
}
inline CCTK_BOOLEAN8_VEC k8ltrue_() {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wuninitialized"
  CCTK_BOOLEAN8_VEC dummy;
  return vec_logical(dummy, dummy, 0xf);
#pragma clang diagnostic pop
}
#define k8lfalse k8lfalse_()
#define k8ltrue k8ltrue_()

// Create vectors, extract vector elements

inline CCTK_REAL8_VEC vec8_set1(CCTK_REAL8 a) { return vec_splats(a); }
inline CCTK_REAL8_VEC vec8_set(CCTK_REAL8 a, CCTK_REAL8 b, CCTK_REAL8 c,
                               CCTK_REAL8 d) {
  return (CCTK_REAL8_VEC){a, b, c, d};
}

inline CCTK_REAL8 vec8_b2r(bool b) { return b ? +1.0 : -1.0; }
inline CCTK_BOOLEAN8_VEC vec8_setb(bool a, bool b, bool c, bool d) {
  return vec8_set(vec8_b2r(a), vec8_b2r(b), vec8_b2r(c), vec8_b2r(d));
}

inline CCTK_REAL8 vec8_elt0(CCTK_REAL8_VEC x) { return vec_extract(x, 0); }
inline CCTK_REAL8 vec8_elt1(CCTK_REAL8_VEC x) { return vec_extract(x, 1); }
inline CCTK_REAL8 vec8_elt2(CCTK_REAL8_VEC x) { return vec_extract(x, 2); }
inline CCTK_REAL8 vec8_elt3(CCTK_REAL8_VEC x) { return vec_extract(x, 3); }
inline CCTK_REAL8 vec8_elt(CCTK_REAL8_VEC x, int d) {
  return vec_extract(x, d);
}
/*
inline void vec8_elts(CCTK_REAL8_VEC x, CCTK_REAL8 &a, CCTK_REAL8 &b,
                      CCTK_REAL8 &c, CCTK_REAL8 &d) {
  a = x[0];
  b = x[1];
  c = x[2];
  d = x[3];
}
*/

inline bool vec8_r2b(CCTK_REAL8 x) { return x >= 0.0; }
inline bool vec8_eltb(CCTK_BOOLEAN8_VEC x, int d) {
  return vec8_r2b(vec8_elt(x, d));
}

// Load and store vectors

// Load a vector from memory (aligned and unaligned); this loads from
// a reference to a scalar
inline CCTK_REAL8_VEC vec8_load(const CCTK_REAL8 &p) {
  return vec_lda(0, (CCTK_REAL8 *)&p);
}
inline CCTK_REAL8_VEC vec8_loadu(const CCTK_REAL8 &p) {
  vector4double v1, v2, vp; // code taken from IBM's compiler documentation
  v1 = vec_ld(0, (CCTK_REAL8 *)&p);   // load the left part of the vector
  v2 = vec_ld(31, (CCTK_REAL8 *)&p);  // load the right part of the vector
  vp = vec_lvsl(0, (CCTK_REAL8 *)&p); // generate control value
  return vec_perm(v1, v2, vp);        // generate the aligned vector
}
inline CCTK_REAL8_VEC vec8_loadu_off(ptrdiff_t off, const CCTK_REAL8 &p) {
  vector4double v1, v2;
  off &= CCTK_REAL8_VEC_SIZE - 1;
  v1 = vec_lda(0, (CCTK_REAL8 *)&p - off);
  v2 = vec_lda(0, (CCTK_REAL8 *)&p - off + CCTK_REAL8_VEC_SIZE);
  return off == 1 ? vec_sldw(v1, v2, 1) : off == 2
                                              ? vec_sldw(v1, v2, 2)
                                              : off == 3 ? vec_sldw(v1, v2, 3)
                                                         : (vec8_assert(0), v1);
}

// Load a vector from memory that may or may not be aligned, as
// decided by the offset and the vector size
#if VECTORISE_ALWAYS_USE_UNALIGNED_LOADS
// Implementation: Always use unaligned load
inline CCTK_REAL8_VEC vec8_loadu_maybe(ptrdiff_t off, const CCTK_REAL8 &p) {
  return vec8_loadu(p);
}
inline CCTK_REAL8_VEC vec8_loadu_maybe3(ptrdiff_t off1, ptrdiff_t off2,
                                        ptrdiff_t off3, const CCTK_REAL8 &p) {
  return vec8_loadu(p);
}
#else
inline CCTK_REAL8_VEC vec8_loadu_maybe(ptrdiff_t off, const CCTK_REAL8 &p) {
  return off % CCTK_REAL8_VEC_SIZE == 0 ? vec8_load(p) : vec8_loadu_off(off, p);
}
#if VECTORISE_ALIGNED_ARRAYS
// Assume all array x sizes are multiples of the vector size
inline CCTK_REAL8_VEC vec8_loadu_maybe3(ptrdiff_t off1, ptrdiff_t off2,
                                        ptrdiff_t off3, const CCTK_REAL8 &p) {
  return vec8_loadu_maybe(off1, p);
}
#else
inline CCTK_REAL8_VEC vec8_loadu_maybe3(ptrdiff_t off1, ptrdiff_t off2,
                                        ptrdiff_t off3, const CCTK_REAL8 &p) {
  return off2 % CCTK_REAL8_VEC_SIZE != 0 or off3 % CCTK_REAL8_VEC_SIZE != 0
             ? vec8_loadu(p)
             : vec8_loadu_maybe(off1, p);
}
#endif
#endif

// Store a vector to memory (aligned and non-temporal); this stores to
// a reference to a scalar
inline void vec8_store(CCTK_REAL8 &p, CCTK_REAL8_VEC x) { vec_sta(x, 0, &p); }
inline void vec8_storeu(CCTK_REAL8 &p, CCTK_REAL8_VEC x) {
  CCTK_REAL8_VEC v1, v2, v3, vp, m1, m2, m3;
  // code taken from IBM's compiler documentation
  // generate insert masks
  vp = vec_lvsr(0, &p);
  m1 = k8lfalse;
  m2 = k8ltrue;
  m3 = vec_perm(m1, m2, vp);
  v3 = vec_perm(x, x, vp);
#pragma tm_atomic
  {
    // get existing data
    v1 = vec_ld(0, &p);
    v2 = vec_ld(31, &p);
    // permute and insert
    v1 = vec_sel(v1, v3, m3);
    v2 = vec_sel(v3, v2, m3);
    // store data back
    vec_st(v1, 0, &p);
    vec_st(v2, 31, &p);
  }
}
inline void vec8_store_nta(CCTK_REAL8 &p, CCTK_REAL8_VEC x) {
  // this doesn't avoid the cache
  return vec_sta(x, 0, &p);
}

#ifdef _OPENMP
#if VECTORISE_ALIGNED_ARRAYS
// Arrays are aligned; wrap-around is not an issue
#define vec8_store_omp
#else
// Need to protect partial stores, as they may wrap around to the
// beginning of the next line in the array
#define vec8_store_omp _Pragma("tm_atomic")
#endif
#else
#define vec8_store_omp
#endif

// Store a partial vector (aligned and non-temporal)
#define vec8_store_partial_prepare(i, imin_, imax_)                            \
  bool v8stp_all;                                                              \
  CCTK_BOOLEAN8_VEC v8stp_mask;                                                \
  bool v8stp_mask0, v8stp_mask1, v8stp_mask2, v8stp_mask3;                     \
  ({                                                                           \
    ptrdiff_t const imin__ = (imin_);                                          \
    ptrdiff_t const imax__ = (imax_);                                          \
    ptrdiff_t const imin = imin__;                                             \
    ptrdiff_t const imax = imax__;                                             \
                                                                               \
    v8stp_all = i >= imin and i + CCTK_REAL8_VEC_SIZE - 1 < imax;              \
                                                                               \
    if (not CCTK_BUILTIN_EXPECT(v8stp_all, true)) {                            \
      CCTK_INTEGER8_VEC vp_lo, vp_hi;                                          \
      CCTK_BOOLEAN8_VEC mask_lo, mask_hi;                                      \
      /* this is correct but slow */                                           \
      /*                                                                       \
      mask_lo = vec8_setb(i+0>=imin, i+1>=imin, i+2>=imin, i+3>=imin);         \
      mask_hi = vec8_setb(i+0<imax, i+1<imax, i+2<imax, i+3<imax);             \
      */                                                                       \
      /* Note: vec_lvsl(i,p) =  &p[i] / 8 % 4                                  \
         Note: vec_lvsr(i,p) = -&p[i] / 8 % 4                                  \
         /8: 8 bytes per double                                                \
         %4: 4 doubles per vector                                              \
      */                                                                       \
      /* We assume p[i] is aligned */                                          \
      /* Ensure at least one vector element is inside the active region */     \
      vec8_assert(i - imin >= -(CCTK_REAL8_VEC_SIZE - 1));                     \
      vp_lo = vec_lvsl(8 * (i - imin), (CCTK_REAL *)0);                        \
      mask_lo =                                                                \
          (i - imin >= 0 ? k8ltrue : vec_perm(k8lfalse, k8ltrue, vp_lo));      \
      /* Ensure at least one vector element is inside the active region */     \
      vec8_assert(i < imax);                                                   \
      vp_hi = vec_lvsl(8 * (i - imax), (CCTK_REAL *)0);                        \
      mask_hi = (i - imax < -(CCTK_REAL8_VEC_SIZE - 1)                         \
                     ? k8ltrue                                                 \
                     : vec_perm(k8ltrue, k8lfalse, vp_hi));                    \
      v8stp_mask = k8land(mask_lo, mask_hi);                                   \
      v8stp_mask0 = vec8_eltb(v8stp_mask, 0);                                  \
      v8stp_mask1 = vec8_eltb(v8stp_mask, 1);                                  \
      v8stp_mask2 = vec8_eltb(v8stp_mask, 2);                                  \
      v8stp_mask3 = vec8_eltb(v8stp_mask, 3);                                  \
    }                                                                          \
  })
#define vec8_store_nta_partial(p_, x_)                                         \
  ({                                                                           \
    CCTK_REAL8 &p__ = (p_);                                                    \
    CCTK_REAL8_VEC x__ = (x_);                                                 \
    CCTK_REAL8 &p = p__;                                                       \
    CCTK_REAL8_VEC x = x__;                                                    \
    if (CCTK_BUILTIN_EXPECT(v8stp_all, true)) {                                \
      vec8_store(p, x);                                                        \
    } else {                                                                   \
      /*                                                                       \
      vec8_store_omp                                                           \
        vec8_store(p, k8ifthen(v8stp_mask, x, vec8_load(p)));                  \
      */                                                                       \
      if (VECTORISE_ALIGNED_ARRAYS) {                                          \
        vec8_store(p, k8ifthen(v8stp_mask, x, vec8_load(p)));                  \
      } else {                                                                 \
        if (v8stp_mask0)                                                       \
          (&p)[0] = vec8_elt0(x);                                              \
        if (v8stp_mask1)                                                       \
          (&p)[1] = vec8_elt1(x);                                              \
        if (v8stp_mask2)                                                       \
          (&p)[2] = vec8_elt2(x);                                              \
        if (v8stp_mask3)                                                       \
          (&p)[3] = vec8_elt3(x);                                              \
      }                                                                        \
    }                                                                          \
  })

// Store a lower or higher partial vector (aligned and non-temporal);
// the non-temporal hint is probably ignored
#define vec8_store_nta_partial_lo(p_, x_, n)                                   \
  ({                                                                           \
    CCTK_REAL8 &p__ = (p_);                                                    \
    CCTK_REAL8_VEC x__ = (x_);                                                 \
    CCTK_REAL8 &p = p__;                                                       \
    CCTK_REAL8_VEC x = x__;                                                    \
    CCTK_REAL8_VEC vp, mask;                                                   \
    /* Ensure at least one and but all vector elements are active */           \
    vec8_assert(n > 0 and n < CCTK_REAL8_VEC_SIZE - 1);                        \
    vp = vec_lvsl(-8 * n, (CCTK_REAL *)0);                                     \
    mask = vec_perm(k8ltrue, k8lfalse, vp);                                    \
    vec8_store_omp vec8_store(p, k8ifthen(mask, x, vec8_load(p)));             \
  })
#define vec8_store_nta_partial_hi(p_, x_, n)                                   \
  ({                                                                           \
    CCTK_REAL8 &p__ = (p_);                                                    \
    CCTK_REAL8_VEC x__ = (x_);                                                 \
    CCTK_REAL8 &p = p__;                                                       \
    CCTK_REAL8_VEC x = x__;                                                    \
    CCTK_REAL8_VEC vp, mask;                                                   \
    /* Ensure at least one but not all vector elements are active */           \
    vec8_assert(n > 0 and n < CCTK_REAL8_VEC_SIZE - 1);                        \
    vp = vec_lvsl(8 * n, (CCTK_REAL *)0);                                      \
    mask = vec_perm(k8lfalse, k8ltrue, vp);                                    \
    vec8_store_omp vec8_store(p, k8ifthen(mask, x, vec8_load(p)));             \
  })
#define vec8_store_nta_partial_mid(p_, x_, nlo, nhi)                           \
  ({                                                                           \
    CCTK_REAL8 &p__ = (p_);                                                    \
    CCTK_REAL8_VEC x__ = (x_);                                                 \
    CCTK_REAL8 p = p__;                                                        \
    CCTK_REAL8_VEC x = x__;                                                    \
    CCTK_REAL8_VEC vp_lo, mask_lo;                                             \
    /* Ensure at least one but not all vector elements are active */           \
    vec8_assert(nlo > 0 and nlo < CCTK_REAL8_VEC_SIZE - 1);                    \
    vp_lo = vec_lvsl(-8 * nlo, (CCTK_REAL *)0);                                \
    mask_lo = vec_perm(k8lfalse, k8ltrue, vp_lo);                              \
    CCTK_REAL8_VEC vp_hi, mask_hi;                                             \
    /* Ensure at least one but not all vector elements are active */           \
    vec8_assert(nhi > 0 and nhi < CCTK_REAL8_VEC_SIZE - 1);                    \
    vp_hi = vec_lvsl(8 * nhi, (CCTK_REAL *)0);                                 \
    mask_hi = vec_perm(k8lfalse, k8ltrue, vp_hi);                              \
    CCTK_REAL8_VEC mask;                                                       \
    mask = vec_and(mask_lo, mask_hi);                                          \
    vec8_store_omp vec8_store(p, k8ifthen(mask, x, vec8_load(p)));             \
  })

// Functions and operators

// Operators
#define k8neg(x) (vec_neg(x))

#define k8add(x, y) (vec_add(x, y))
#define k8sub(x, y) (vec_sub(x, y))
#define k8mul(x, y) (vec_mul(x, y))
#define k8div(x, y) (vec_swdiv_nochk(x, y))

// Fused multiply-add, defined as [+-]x*y[+-]z
#define k8madd(x, y, z) (vec_madd(x, y, z))
#define k8msub(x, y, z) (vec_msub(x, y, z))
#define k8nmadd(x, y, z) (vec_nmadd(x, y, z))
#define k8nmsub(x, y, z) (vec_nmsub(x, y, z))

// Cheap functions
#define k8copysign(x, y) (vec_cpsgn(y, x))
#define k8fabs(x) (vec_abs(x))
#define k8fmax(x_, y_)                                                         \
  ({                                                                           \
    CCTK_REAL8_VEC x__ = (x_);                                                 \
    CCTK_REAL8_VEC y__ = (y_);                                                 \
    CCTK_REAL8_VEC x = x__;                                                    \
    CCTK_REAL8_VEC y = y__;                                                    \
    k8ifthen(k8cmplt(x, y), y, x);                                             \
  })
#define k8fmin(x_, y_)                                                         \
  ({                                                                           \
    CCTK_REAL8_VEC x__ = (x_);                                                 \
    CCTK_REAL8_VEC y__ = (y_);                                                 \
    CCTK_REAL8_VEC x = x__;                                                    \
    CCTK_REAL8_VEC y = y__;                                                    \
    k8ifthen(k8cmpgt(x, y), y, x);                                             \
  })
#define k8fnabs(x) (vec_nabs(x))
#define k8sgn(x_)                                                              \
  ({                                                                           \
    CCTK_REAL8_VEC x__ = (x_);                                                 \
    CCTK_REAL8_VEC x = x__;                                                    \
    CCTK_REAL8_VEC one, zero, iszero;                                          \
    one = k8ltrue;                                                             \
    zero = k8sub(one, one);                                                    \
    iszero = k8cmpeq(x, zero);                                                 \
    k8ifthen(iszero, zero, k8copysign(one, x));                                \
  })
#define k8sqrt(x) (vec_swsqrt_nochk(x))

// Expensive functions

#define K8REPL(f, x_)                                                          \
  ({                                                                           \
    CCTK_REAL8_VEC const x__ = (x_);                                           \
    CCTK_REAL8_VEC const x = x__;                                              \
    vec8_set(f(vec8_elt0(x)), f(vec8_elt1(x)), f(vec8_elt2(x)),                \
             f(vec8_elt3(x)));                                                 \
  })
#define K8REPL2S(f, x_, a_)                                                    \
  ({                                                                           \
    CCTK_REAL8_VEC const x__ = (x_);                                           \
    CCTK_REAL8 const a__ = (a_);                                               \
    CCTK_REAL8_VEC const x = x__;                                              \
    CCTK_REAL8 const a = a__;                                                  \
    vec8_set(f(vec8_elt0(x), a), f(vec8_elt1(x), a), f(vec8_elt2(x), a),       \
             f(vec8_elt3(x), a));                                              \
  })
#define K8REPL2(f, x_, y_)                                                     \
  ({                                                                           \
    CCTK_REAL8_VEC const x__ = (x_);                                           \
    CCTK_REAL8_VEC const y__ = (y_);                                           \
    CCTK_REAL8_VEC const x = x__;                                              \
    CCTK_REAL8_VEC const y = y__;                                              \
    vec8_set(f(vec8_elt0(x), vec8_elt0(y)), f(vec8_elt1(x), vec8_elt1(y)),     \
             f(vec8_elt2(x), vec8_elt2(y)), f(vec8_elt3(x), vec8_elt3(y)));    \
  })

#define k8acos(x) xacos(x)
#define k8acosh(x) xacosh(x)
#define k8asin(x) xasin(x)
#define k8asinh(x) xasinh(x)
#define k8atan(x) xatan(x)
#define k8atan2(x, y) xatan2(x, y)
#define k8atanh(x) xatanh(x)
#define k8cos(x) xcos(x)
#define k8cosh(x) xcosh(x)
#define k8exp(x) xexp(x)
#define k8fmod(x, y) K8REPL2(fmod, x, y)
#define k8log(x) xlog(x)
#define k8pow(x, a) xpow(x, vec_set1(a))
#define k8sin(x) xsin(x)
#define k8sinh(x) xsinh(x)
#define k8tan(x) xtan(x)
#define k8tanh(x) xtanh(x)

#define k8lnot(x) (vec_not(x))
#define k8land(x, y) (vec_and(x, y))
#define k8lor(x, y) (vec_or(x, y))
#define k8lxor(x, y) (vec_xor(x, y))
#define k8ifthen(x, y, z) (vec_sel(z, y, x))

#define k8cmpeq(x, y) (vec_cmpeq(x, y))
#define k8cmpne(x, y) (k8lnot(k8cmpeq(x, y)))
#define k8cmpgt(x, y) (vec_cmpgt(x, y))
#define k8cmpge(x, y) (k8lnot(k8cmplt(x, y)))
#define k8cmplt(x, y) (vec_cmplt(x, y))
#define k8cmple(x, y) (k8lnot(k8cmpgt(x, y)))

#endif

```

vectors-4-AVX.h:
```
// -*-C++-*-
// Vectorise using Intel's or AMD's AVX

// Use the type __m256 directly, without introducing a wrapper class



#include <cstdlib>
#include <cstring>



#include <immintrin.h>
#ifdef __FMA4__
#  include <x86intrin.h>
#endif



#ifdef __FMA4__
#  define vec4_architecture_FMA4 "+FMA4"
#else
#  define vec4_architecture_FMA4 ""
#endif
#define vec4_architecture "AVX" vec4_architecture_FMA4 " (32-bit precision)"



// Vector type corresponding to CCTK_REAL
// Note: some boolean masks (e.g. ~0) correspond to nan when
// interpreted as floating point number. gcc 4.8 is clever enough to
// optimize away such constants with fast-math. We therefore need to
// handle this constant as integer number.
typedef __m256  CCTK_REAL4_VEC;
typedef __m256i CCTK_INTEGER4_VEC;
typedef __m256i CCTK_BOOLEAN4_VEC;

// Number of vector elements in a CCTK_REAL_VEC
#define CCTK_REAL4_VEC_SIZE 8

vec_static_assert(sizeof(CCTK_REAL4_VEC) ==
                  sizeof(CCTK_REAL4) * CCTK_REAL4_VEC_SIZE);

// Integer and boolean types corresponding to this real type
typedef CCTK_INT4 CCTK_INTEGER4;
typedef CCTK_INT4 CCTK_BOOLEAN4;



// These macros are undefined at the end of this file -- use them only
// within functions, not within macros that are exported
#define I2R(x) _mm256_castsi256_ps(x)
#define R2I(x) _mm256_castps_si256(x)



union k4const_t {
  CCTK_INTEGER4     i[CCTK_REAL4_VEC_SIZE];
  CCTK_INTEGER4_VEC vi;
};

#define k4sign    (vec4_set1i(  (CCTK_INTEGER4)(1UL << 31UL)))
#define k4notsign (vec4_set1i(~ (CCTK_INTEGER4)(1UL << 31UL)))



// Create vectors, extract vector elements

static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC vec4_set1(CCTK_REAL4 const a)
{
  return _mm256_set1_ps(a);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_INTEGER4_VEC vec4_set1i(CCTK_INT4 const a)
{
  return _mm256_set1_epi32(a);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC vec4_set(CCTK_REAL4 const a,
                        CCTK_REAL4 const b,
                        CCTK_REAL4 const c,
                        CCTK_REAL4 const d,
                        CCTK_REAL4 const e,
                        CCTK_REAL4 const f,
                        CCTK_REAL4 const g,
                        CCTK_REAL4 const h)
{
  return _mm256_set_ps(h,g,f,e,d,c,b,a); // note reversed arguments
}

static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4 vec4_elt(CCTK_REAL4_VEC const x, std::ptrdiff_t const d)
{
  CCTK_REAL4 e;
  std::memcpy(&e, &((char const*)&x)[d*sizeof e], sizeof e);
  return e;
}

static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_INTEGER4 vec4_elti(CCTK_INTEGER4_VEC const x, std::ptrdiff_t const d)
{
  CCTK_INTEGER4 e;
  std::memcpy(&e, &((char const*)&x)[d*sizeof e], sizeof e);
  return e;
}

static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN4 vec4_eltb(CCTK_BOOLEAN4_VEC const x, std::ptrdiff_t const d)
{
  CCTK_BOOLEAN4 e;
  std::memcpy(&e, &((char const*)&x)[d*sizeof e], sizeof e);
  return e;
}



// Load and store vectors

// Load a vector from memory (aligned and unaligned); this loads from
// a reference to a scalar
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC vec4_load(CCTK_REAL4 const& p)
{
  return _mm256_load_ps(&p);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC vec4_loadu(CCTK_REAL4 const& p)
{
  return _mm256_loadu_ps(&p);
}
#if VECTORISE_ALWAYS_USE_ALIGNED_LOADS
#  error "VECTORISE_ALWAYS_USE_ALIGNED_LOADS not yet supported"
#endif

// Load a vector from memory that may or may not be aligned, as
// decided by the offset off and the vector size
#if VECTORISE_ALWAYS_USE_UNALIGNED_LOADS
// Implementation: Always use unaligned load
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC vec4_loadu_maybe(std::ptrdiff_t const off, CCTK_REAL4 const& p)
{
  return vec4_loadu(p);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC vec4_loadu_maybe3(std::ptrdiff_t const off1,
                                 std::ptrdiff_t const off2,
                                 std::ptrdiff_t const off3,
                                 CCTK_REAL4 const& p)
{
  return vec4_loadu(p);
}
#else
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC vec4_loadu_maybe(std::ptrdiff_t const off, CCTK_REAL4 const& p)
{
  return off % CCTK_REAL4_VEC_SIZE == 0 ? vec4_load(p) : vec4_loadu(p);
}
#  if VECTORISE_ALIGNED_ARRAYS
// Assume all array x sizes are multiples of the vector size
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC vec4_loadu_maybe3(std::ptrdiff_t const off1,
                                 std::ptrdiff_t const off2,
                                 std::ptrdiff_t const off3,
                                 CCTK_REAL4 const& p)
{
  return vec4_loadu_maybe(off1, p);
}
#  else
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC vec4_loadu_maybe3(std::ptrdiff_t const off1,
                                 std::ptrdiff_t const off2,
                                 std::ptrdiff_t const off3,
                                 CCTK_REAL4 const& p)
{
  return
    off2 % CCTK_REAL4_VEC_SIZE != 0 or
    off3 % CCTK_REAL4_VEC_SIZE != 0 ?
    vec4_loadu(p) :
    vec4_loadu_maybe(off1, p);
}
#  endif
#endif

// Store a vector to memory (aligned and non-temporal); this stores to
// a reference to a scalar
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec4_store(CCTK_REAL4& p, CCTK_REAL4_VEC const x)
{
  return _mm256_store_ps(&p, x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec4_storeu(CCTK_REAL4& p, CCTK_REAL4_VEC const x)
{
  return _mm256_storeu_ps(&p, x);
}
#if ! VECTORISE_STREAMING_STORES
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec4_store_nta(CCTK_REAL4& p, CCTK_REAL4_VEC const x)
{
  return vec4_store(p, x);
}
#else
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec4_store_nta(CCTK_REAL4& p, CCTK_REAL4_VEC const x)
{
  return _mm256_stream_ps(&p, x);
}
#endif

// Store a partial vector (aligned and non-temporal)
#define vec4_store_partial_prepare(i,imin,imax)                         \
  bool v4stp_all;                                                       \
  __m256i v4stp_mask;                                                   \
  vec4_store_partial_prepare_(v4stp_all, v4stp_mask, i, imin, imax);
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec4_store_partial_prepare_(bool& all, __m256i& mask,
                                 std::ptrdiff_t const i,
                                 std::ptrdiff_t const imin,
                                 std::ptrdiff_t const imax)
{
  all = i>=imin and i+CCTK_REAL4_VEC_SIZE-1<imax;
  
  if (not CCTK_BUILTIN_EXPECT(all, true)) {
    /* __m256i const v4stp_mask = */
    /*   _mm256_andnot_ps(_mm256_add_epi64(_mm256_set1_epi64x(i-imin), */
    /*                                     vec_index), */
    /*                    _mm256_add_epi64(_mm256_set1_epi64x(i-imax), */
    /*                                     vec_index)); */
    __m128i const termlo0123 =
      _mm_add_epi32(_mm_set1_epi32(i-imin), _mm_set_epi32(3, 2, 1, 0));
    __m128i const termup0123 =
      _mm_add_epi32(_mm_set1_epi32(i-imax), _mm_set_epi32(3, 2, 1, 0));
    __m128i const term0123 = _mm_andnot_si128(termlo0123, termup0123);
    __m128i const termlo4567 =
      _mm_add_epi32(_mm_set1_epi32(i-imin), _mm_set_epi32(7, 6, 5, 4));
    __m128i const termup4567 =
      _mm_add_epi32(_mm_set1_epi32(i-imax), _mm_set_epi32(7, 6, 5, 4));
    __m128i const term4567 = _mm_andnot_si128(termlo4567, termup4567);
    mask =
      _mm256_insertf128_si256(_mm256_castsi128_si256(term0123), term4567, 1);
  }
}

#define vec4_store_nta_partial(p, x)                    \
  vec4_store_nta_partial_(v4stp_all, v4stp_mask, p, x)
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec4_store_nta_partial_(bool const all, __m256i const mask,
                             CCTK_REAL4& p, CCTK_REAL4_VEC const x)
{
  if (CCTK_BUILTIN_EXPECT(all, true)) {
    vec4_store_nta(p, x);
  } else {
    _mm256_maskstore_ps(&p, mask, x);
  }
}

// Store a lower or higher partial vector (aligned and non-temporal);
// the non-temporal hint is probably ignored
// Masks indicating which vector element should be stored:
static k4const_t const k4store_lo[9] =
  {
    {{  0,  0,  0,  0,  0,  0,  0,  0, }},
    {{ ~0,  0,  0,  0,  0,  0,  0,  0, }},
    {{ ~0, ~0,  0,  0,  0,  0,  0,  0, }},
    {{ ~0, ~0, ~0,  0,  0,  0,  0,  0, }},
    {{ ~0, ~0, ~0, ~0,  0,  0,  0,  0, }},
    {{ ~0, ~0, ~0, ~0, ~0,  0,  0,  0, }},
    {{ ~0, ~0, ~0, ~0, ~0, ~0,  0,  0, }},
    {{ ~0, ~0, ~0, ~0, ~0, ~0, ~0,  0, }},
    {{ ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, }},
  };
static k4const_t const k4store_hi[9] =
  {
    {{  0,  0,  0,  0,  0,  0,  0,  0, }},
    {{  0,  0,  0,  0,  0,  0,  0, ~0, }},
    {{  0,  0,  0,  0,  0,  0, ~0, ~0, }},
    {{  0,  0,  0,  0,  0, ~0, ~0, ~0, }},
    {{  0,  0,  0,  0, ~0, ~0, ~0, ~0, }},
    {{  0,  0,  0, ~0, ~0, ~0, ~0, ~0, }},
    {{  0,  0, ~0, ~0, ~0, ~0, ~0, ~0, }},
    {{  0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, }},
    {{ ~0, ~0, ~0, ~0, ~0, ~0, ~0, ~0, }},
  };
#if !defined(__INTEL_COMPILER) && defined(__GNUC__) && __GNUC__==4 && __GNUC_MINOR__<=4
// gcc 4.4 uses a wrong prototype for _mm256_maskstore_ps
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec4_store_nta_partial_lo(CCTK_REAL4& p,
                               CCTK_REAL4_VEC const x,
                               ptrdiff_t const n)
{
  _mm256_maskstore_ps(&p, I2R(k4store_lo[n].vi), x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec4_store_nta_partial_hi(CCTK_REAL4& p,
                               CCTK_REAL4_VEC const x,
                               ptrdiff_t const n)
{
  _mm256_maskstore_ps(&p, I2R(k4store_hi[n].vi), x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec4_store_nta_partial_mid(CCTK_REAL4& p,
                                CCTK_REAL4_VEC const x,
                                ptrdiff_t const nlo,
                                ptrdiff_t const nhi)
{
  _mm256_maskstore_ps(&p, I2R(k4store_lo[nlo].vi & k4store_hi[nhi].vi), x);
}
#else
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec4_store_nta_partial_lo(CCTK_REAL4& p,
                               CCTK_REAL4_VEC const x,
                               ptrdiff_t const n)
{
  _mm256_maskstore_ps(&p, k4store_lo[n].vi, x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec4_store_nta_partial_hi(CCTK_REAL4& p,
                               CCTK_REAL4_VEC const x,
                               ptrdiff_t const n)
{
  _mm256_maskstore_ps(&p, k4store_hi[n].vi, x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec4_store_nta_partial_mid(CCTK_REAL4& p,
                                CCTK_REAL4_VEC const x,
                                ptrdiff_t const nlo,
                                ptrdiff_t const nhi)
{
  _mm256_maskstore_ps
    (&p,
     R2I(_mm256_and_ps(I2R(k4store_lo[nlo].vi), I2R(k4store_hi[nhi].vi))),
     x);
}
#endif



// Functions and operators

// Operators
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4neg(CCTK_REAL4_VEC const x)
{
  return _mm256_xor_ps(x, I2R(k4sign));
}

static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4add(CCTK_REAL4_VEC const x, CCTK_REAL4_VEC const y)
{
  return _mm256_add_ps(x, y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4sub(CCTK_REAL4_VEC const x, CCTK_REAL4_VEC const y)
{
  return _mm256_sub_ps(x, y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4mul(CCTK_REAL4_VEC const x, CCTK_REAL4_VEC const y)
{
  return _mm256_mul_ps(x, y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4div(CCTK_REAL4_VEC const x, CCTK_REAL4_VEC const y)
{
  return _mm256_div_ps(x, y);
}

// Fused multiply-add, defined as [+-]x*y[+-]z
#ifdef __FMA4__
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4madd(CCTK_REAL4_VEC const x,
                      CCTK_REAL4_VEC const y,
                      CCTK_REAL4_VEC const z)
{
  return _mm256_macc_ps(x, y, z);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4msub(CCTK_REAL4_VEC const x,
                      CCTK_REAL4_VEC const y,
                      CCTK_REAL4_VEC const z)
{
  return _mm256_msub_ps(x, y, z);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4nmadd(CCTK_REAL4_VEC const x,
                       CCTK_REAL4_VEC const y,
                       CCTK_REAL4_VEC const z)
{
  return _mm256_nmsub_ps(x, y, z);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4nmsub(CCTK_REAL4_VEC const x,
                       CCTK_REAL4_VEC const y,
                       CCTK_REAL4_VEC const z)
{
  return _mm256_nmacc_ps(x, y, z);
}
#else
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4madd(CCTK_REAL4_VEC const x,
                      CCTK_REAL4_VEC const y,
                      CCTK_REAL4_VEC const z)
{
  return k4add(k4mul(x, y), z);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4msub(CCTK_REAL4_VEC const x,
                      CCTK_REAL4_VEC const y,
                      CCTK_REAL4_VEC const z)
{
  return k4sub(k4mul(x, y), z);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4nmadd(CCTK_REAL4_VEC const x,
                       CCTK_REAL4_VEC const y,
                       CCTK_REAL4_VEC const z)
{
  return k4sub(k4neg(z), k4mul(x, y));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4nmsub(CCTK_REAL4_VEC const x,
                       CCTK_REAL4_VEC const y,
                       CCTK_REAL4_VEC const z)
{
  return k4sub(z, k4mul(x, y));
}
#endif

// Cheap functions
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4copysign(CCTK_REAL4_VEC const x, CCTK_REAL4_VEC const y)
{
  return _mm256_or_ps(_mm256_and_ps(I2R(k4notsign), x),
                      _mm256_and_ps(I2R(k4sign   ), y));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4fabs(CCTK_REAL4_VEC const x)
{
  return _mm256_and_ps(I2R(k4notsign), x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4fmax(CCTK_REAL4_VEC const x, CCTK_REAL4_VEC const y)
{
  return _mm256_max_ps(x, y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4fmin(CCTK_REAL4_VEC const x, CCTK_REAL4_VEC const y)
{
  return _mm256_min_ps(x, y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4fnabs(CCTK_REAL4_VEC const x)
{
  return _mm256_or_ps(x, I2R(k4sign));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN4_VEC k4signbit(CCTK_REAL4_VEC const x)
{
  return R2I(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4sqrt(CCTK_REAL4_VEC const x)
{
  return _mm256_sqrt_ps(x);
}



// Expensive functions

#define K4REPL(f,x)                             \
  vec4_set(f(vec4_elt(x,0)),                    \
           f(vec4_elt(x,1)),                    \
           f(vec4_elt(x,2)),                    \
           f(vec4_elt(x,3)),                    \
           f(vec4_elt(x,4)),                    \
           f(vec4_elt(x,5)),                    \
           f(vec4_elt(x,6)),                    \
           f(vec4_elt(x,7)));
#define K4REPL2S(f,x,a)                         \
  vec4_set(f(vec4_elt(x,0),a),                  \
           f(vec4_elt(x,1),a),                  \
           f(vec4_elt(x,2),a),                  \
           f(vec4_elt(x,3),a),                  \
           f(vec4_elt(x,4),a),                  \
           f(vec4_elt(x,5),a),                  \
           f(vec4_elt(x,6),a),                  \
           f(vec4_elt(x,7),a));
#define K4REPL2(f,x,y)                          \
  vec4_set(f(vec4_elt(x,0),vec4_elt(y,0)),      \
           f(vec4_elt(x,1),vec4_elt(y,1)),      \
           f(vec4_elt(x,2),vec4_elt(y,2)),      \
           f(vec4_elt(x,3),vec4_elt(y,3)),      \
           f(vec4_elt(x,4),vec4_elt(y,4)),      \
           f(vec4_elt(x,5),vec4_elt(y,5)),      \
           f(vec4_elt(x,6),vec4_elt(y,6)),      \
           f(vec4_elt(x,7),vec4_elt(y,7)));

#if defined __ICC
// The Intel compiler provides intrinsics for these

static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4acos(CCTK_REAL4_VEC const x)
{
  return _mm256_acos_ps(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4acosh(CCTK_REAL4_VEC const x)
{
  return _mm256_acosh_ps(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4asin(CCTK_REAL4_VEC const x)
{
  return _mm256_asin_ps(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4asinh(CCTK_REAL4_VEC const x)
{
  return _mm256_asinh_ps(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4atan(CCTK_REAL4_VEC const x)
{
  return _mm256_atan_ps(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4atan2(CCTK_REAL4_VEC const x, CCTK_REAL4_VEC const y)
{
  return _mm256_atan2_ps(x,y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4atanh(CCTK_REAL4_VEC const x)
{
  return _mm256_atanh_ps(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4cos(CCTK_REAL4_VEC const x)
{
  return _mm256_cos_ps(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4cosh(CCTK_REAL4_VEC const x)
{
  return _mm256_cosh_ps(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4exp(CCTK_REAL4_VEC const x)
{
  return _mm256_exp_ps(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4fmod(CCTK_REAL4_VEC const x, CCTK_REAL4_VEC const y)
{
#if __ICC > 1400
  return _mm256_fmod_ps(x,y);
#else
  return K4REPL2(fmodf,x,y);
#endif
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4log(CCTK_REAL4_VEC const x)
{
  return _mm256_log_ps(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4pow(CCTK_REAL4_VEC const x, CCTK_REAL4 const a)
{
  return _mm256_pow_ps(x, _mm256_set1_ps(a));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4sin(CCTK_REAL4_VEC const x)
{
  return _mm256_sin_ps(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4sinh(CCTK_REAL4_VEC const x)
{
  return _mm256_sinh_ps(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4tan(CCTK_REAL4_VEC const x)
{
  return _mm256_tan_ps(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4tanh(CCTK_REAL4_VEC const x)
{
  return _mm256_tanh_ps(x);
}

#else

static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4acos(CCTK_REAL4_VEC const x)
{
  return K4REPL(acosf,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4acosh(CCTK_REAL4_VEC const x)
{
  return K4REPL(acoshf,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4asin(CCTK_REAL4_VEC const x)
{
  return K4REPL(asinf,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4asinh(CCTK_REAL4_VEC const x)
{
  return K4REPL(asinhf,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4atan(CCTK_REAL4_VEC const x)
{
  return K4REPL(atanf,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4atan2(CCTK_REAL4_VEC const x, CCTK_REAL4_VEC const y)
{
  return K4REPL2(atan2f,x,y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4atanh(CCTK_REAL4_VEC const x)
{
  return K4REPL(atanhf,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4cos(CCTK_REAL4_VEC const x)
{
  return K4REPL(cosf,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4cosh(CCTK_REAL4_VEC const x)
{
  return K4REPL(coshf,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4exp(CCTK_REAL4_VEC const x)
{
  return K4REPL(expf,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4fmod(CCTK_REAL4_VEC const x, CCTK_REAL4_VEC const y)
{
  return K4REPL2(fmodf,x,y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4log(CCTK_REAL4_VEC const x)
{
  return K4REPL(logf,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4pow(CCTK_REAL4_VEC const x, CCTK_REAL4 const a)
{
  return K4REPL2S(powf,x,a);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4sin(CCTK_REAL4_VEC const x)
{
  return K4REPL(sinf,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4sinh(CCTK_REAL4_VEC const x)
{
  return K4REPL(sinhf,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4tan(CCTK_REAL4_VEC const x)
{
  return K4REPL(tanf,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4tanh(CCTK_REAL4_VEC const x)
{
  return K4REPL(tanhf,x);
}

#endif



#define k4lfalse (vec4_set1i( 0))
#define k4ltrue  (vec4_set1i(~0))
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN4_VEC k4lnot(CCTK_BOOLEAN4_VEC const x)
{
  return R2I(_mm256_xor_ps(I2R(k4ltrue), I2R(x)));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN4_VEC k4land(CCTK_BOOLEAN4_VEC const x, CCTK_BOOLEAN4_VEC const y)
{
  return R2I(_mm256_and_ps(I2R(x), I2R(y)));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN4_VEC k4lor(CCTK_BOOLEAN4_VEC const x, CCTK_BOOLEAN4_VEC const y)
{
  return R2I(_mm256_or_ps(I2R(x), I2R(y)));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN4_VEC k4lxor(CCTK_BOOLEAN4_VEC const x, CCTK_BOOLEAN4_VEC const y)
{
  return R2I(_mm256_xor_ps(I2R(x), I2R(y)));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4ifthen(CCTK_BOOLEAN4_VEC const x,
                        CCTK_REAL4_VEC const y,
                        CCTK_REAL4_VEC const z)
{
  return _mm256_blendv_ps(z, y, I2R(x));
}

static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN4_VEC k4cmpeq(CCTK_REAL4_VEC const x, CCTK_REAL4_VEC const y)
{
  return R2I(_mm256_cmp_ps(x, y, _CMP_EQ_OQ));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN4_VEC k4cmpne(CCTK_REAL4_VEC const x, CCTK_REAL4_VEC const y)
{
  return R2I(_mm256_cmp_ps(x, y, _CMP_NEQ_UQ));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN4_VEC k4cmpgt(CCTK_REAL4_VEC const x, CCTK_REAL4_VEC const y)
{
  return R2I(_mm256_cmp_ps(x, y, _CMP_GT_OQ));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN4_VEC k4cmpge(CCTK_REAL4_VEC const x, CCTK_REAL4_VEC const y)
{
  return R2I(_mm256_cmp_ps(x, y, _CMP_GE_OQ));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN4_VEC k4cmplt(CCTK_REAL4_VEC const x, CCTK_REAL4_VEC const y)
{
  return R2I(_mm256_cmp_ps(x, y, _CMP_LT_OQ));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN4_VEC k4cmple(CCTK_REAL4_VEC const x, CCTK_REAL4_VEC const y)
{
  return R2I(_mm256_cmp_ps(x, y, _CMP_LE_OQ));
}



static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL4_VEC k4sgn(CCTK_REAL4_VEC const x)
{
  CCTK_BOOLEAN4_VEC const iszero    = k4cmpeq(x, vec4_set1(0.0));
  CCTK_REAL4_VEC    const sign      = _mm256_and_ps(I2R(k4sign), x);
  CCTK_REAL4_VEC    const signedone = _mm256_or_ps(sign, vec4_set1(1.0));
  return k4ifthen(iszero, vec4_set1(0.0), signedone);
}



#undef I2R
#undef R2I

```

vectors.cc:
```
#include "vectors.h"

#include <cctk.h>
#include <cctk_Arguments.h>
#include <cctk_Parameters.h>

extern "C"
int Vectors_Startup(void)
{
  CCTK_VInfo(CCTK_THORNSTRING,
             "Using vector size %d for architecture %s",
             CCTK_REAL_VEC_SIZE, vec_architecture);
  return 0;
}

```

vectors-8-SSE2.h:
```
// -*-C++-*-
// Vectorise using Intel's or AMD's SSE2

// Use the type __m128d directly, without introducing a wrapper class

#include <cassert>
#include <cmath>



#include <emmintrin.h>
#ifdef __SSE4_1__
// Intel's SSE 4.1
#  include <smmintrin.h>
#endif
#ifdef __SSE4A__
// AMD's SSE 4a
#  include <ammintrin.h>

// Intel compilers don't support SSE 4a. Here is how we can implement
// these instructions in assembler instead:

// inline void __attribute__((__always_inline__))
//   _mm_stream_sd (double *p, __m128d x)
// {
//   asm ("movntsd %[x],%[p]" : "=m" (*p) : [p] "m" (*p), [x] "x" (x));
// }

#endif
#ifdef __AVX__
#  include <immintrin.h>
#endif
#ifdef __FMA4__
#  include <x86intrin.h>
#endif



#ifdef __SSE4_1__
#  define vec8_architecture_SSE4_1 "+SSE4.1"
#else
#  define vec8_architecture_SSE4_1 ""
#endif
#ifdef __SSE4A__
#  define vec8_architecture_SSE4a "+SSE4A"
#else
#  define vec8_architecture_SSE4a ""
#endif
#ifdef __FMA4__
#  define vec8_architecture_FMA4 "+FMA4"
#else
#  define vec8_architecture_FMA4 ""
#endif
#define vec8_architecture "SSE2" vec8_architecture_SSE4_1 vec8_architecture_SSE4a vec8_architecture_FMA4 " (64-bit precision)"



// Vector type corresponding to CCTK_REAL
// Note: some boolean masks (e.g. ~0) correspond to nan when
// interpreted as floating point number. gcc 4.8 is clever enough to
// optimize away such constants with fast-math. We therefore need to
// handle this constant as integer number.
typedef __m128d CCTK_REAL8_VEC;
typedef __m128i CCTK_INTEGER8_VEC;
typedef __m128i CCTK_BOOLEAN8_VEC;

// Number of vector elements in a CCTK_REAL_VEC
#define CCTK_REAL8_VEC_SIZE 2

vec_static_assert(sizeof(CCTK_REAL8_VEC) ==
                  sizeof(CCTK_REAL8) * CCTK_REAL8_VEC_SIZE);

// Integer and boolean types corresponding to this real type
typedef CCTK_INT8 CCTK_INTEGER8;
typedef CCTK_INT8 CCTK_BOOLEAN8;



// These macros are undefined at the end of this file -- use them only
// within functions, not within macros that are exported
#define I2R(x) _mm_castsi128_pd(x)
#define R2I(x) _mm_castpd_si128(x)



union k8const_t {
  CCTK_INTEGER8     i[CCTK_REAL8_VEC_SIZE];
  CCTK_INTEGER8_VEC vi;
};

#define k8sign    (vec8_set1i(  (CCTK_INTEGER8)(1ULL << 63ULL)))
#define k8notsign (vec8_set1i(~ (CCTK_INTEGER8)(1ULL << 63ULL)))



// Create vectors, extract vector elements

static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC vec8_set1(CCTK_REAL8 const a)
{
  return _mm_set1_pd(a);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_INTEGER8_VEC vec8_set1i(CCTK_INT8 const a)
{
#if defined(__INTEL_COMPILER)
  // Intel 11.1 does not support _mm_set1_epi64x
  return R2I(_mm_set1_pd(*(CCTK_REAL8 const*)&a));
#else
  return _mm_set1_epi64x(a);
#endif
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC vec8_set(CCTK_REAL8 const a, CCTK_REAL8 const b)
{
  return _mm_set_pd(b,a);       // note reversed arguments
}

// original order is 01
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC vec8_swap10(CCTK_REAL8_VEC const x)
{
  return _mm_shuffle_pd(x,x, _MM_SHUFFLE2(0,1));
}

static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8 vec8_elt(CCTK_REAL8_VEC const x, std::ptrdiff_t const d)
{
  CCTK_REAL8 e;
  std::memcpy(&e, &((char const*)&x)[d*sizeof e], sizeof e);
  return e;
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_INTEGER8 vec8_elti(CCTK_INTEGER8_VEC const x, std::ptrdiff_t const d)
{
  CCTK_INTEGER8 e;
  std::memcpy(&e, &((char const*)&x)[d*sizeof e], sizeof e);
  return e;
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN8 vec8_eltb(CCTK_BOOLEAN8_VEC const x, std::ptrdiff_t const d)
{
  CCTK_BOOLEAN8 e;
  std::memcpy(&e, &((char const*)&x)[d*sizeof e], sizeof e);
  return e;
}



// Load and store vectors

// Load a vector from memory (aligned and unaligned); this loads from
// a reference to a scalar
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC vec8_load(CCTK_REAL8 const& p)
{
  return _mm_load_pd(&p);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC vec8_loadu(CCTK_REAL8 const& p)
{
  return _mm_loadu_pd(&p);
}
#if ! VECTORISE_ALWAYS_USE_ALIGNED_LOADS
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC vec8_load_off1(CCTK_REAL8 const& p)
{
  return vec8_loadu(p);
}
#else
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC vec8_load_off1(CCTK_REAL8 const& p)
{
  return _mm_shuffle_pd(vec8_load((&p)[-1]),
                        vec8_load((&p)[+1]), _MM_SHUFFLE2(0,1));
}
#endif

// Load a vector from memory that may or may not be aligned, as
// decided by the offset off and the vector size
#if VECTORISE_ALWAYS_USE_UNALIGNED_LOADS
// Implementation: Always use unaligned load
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC vec8_loadu_maybe(std::ptrdiff_t const off, CCTK_REAL8 const& p)
{
  return vec8_loadu(p);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC vec8_loadu_maybe3(std::ptrdiff_t const off1,
                                 std::ptrdiff_t const off2,
                                 std::ptrdiff_t const off3,
                                 CCTK_REAL8 const& p)
{
  return vec8_loadu(p);
}
#else
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC vec8_loadu_maybe(std::ptrdiff_t const off, CCTK_REAL8 const& p)
{
  // Note: This function is mis-translated by some versions of the
  // Intel compiler. The symptom is a segfault during the Vectors
  // selftest in the vec_loadu_maybe test.
  // return off % CCTK_REAL8_VEC_SIZE == 0 ? vec8_load(p) : vec8_load_off1(p);
  if (off % CCTK_REAL8_VEC_SIZE == 0) return vec8_load(p);
  return vec8_load_off1(p);
}
#  if VECTORISE_ALIGNED_ARRAYS
// Assume all array x sizes are multiples of the vector size
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC vec8_loadu_maybe3(std::ptrdiff_t const off1,
                                 std::ptrdiff_t const off2,
                                 std::ptrdiff_t const off3,
                                 CCTK_REAL8 const& p)
{
  return vec8_loadu_maybe(off1, p);
}
#  else
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC vec8_loadu_maybe3(std::ptrdiff_t const off1,
                                 std::ptrdiff_t const off2,
                                 std::ptrdiff_t const off3,
                                 CCTK_REAL8 const& p)
{
  return
    off2 % CCTK_REAL8_VEC_SIZE != 0 or
    off3 % CCTK_REAL8_VEC_SIZE != 0 ?
    vec8_loadu(p) :
    vec8_loadu_maybe(off1, p);
}
#  endif
#endif

// Store a vector to memory (aligned and non-temporal); this stores to
// a reference to a scalar
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec8_store(CCTK_REAL8& p, CCTK_REAL8_VEC const x)
{
  _mm_store_pd(&p, x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec8_storeu(CCTK_REAL8& p, CCTK_REAL8_VEC const x)
{
  _mm_storeu_pd(&p, x);
}
#if ! VECTORISE_STREAMING_STORES
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec8_store_nta(CCTK_REAL8& p, CCTK_REAL8_VEC const x)
{
  vec8_store(p, x);
}
#else
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec8_store_nta(CCTK_REAL8& p, CCTK_REAL8_VEC const x)
{
  // TODO: requires _mm_sfence() afterwards? requires _mm_lfence() in
  // readers afterwards? maybe better just an _mm_mfence() afterwards?
  _mm_stream_pd(&p, x);
}
#endif

// TODO: Use _mm_maskstore_pd if AVX is available?

// Store a partial vector (aligned and non-temporal)
#define vec8_store_partial_prepare(i, imin,imax)                        \
  bool v8stp_lo, v8stp_hi;                                              \
  vec8_store_partial_prepare_(v8stp_lo, v8stp_hi, i, imin, imax);
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec8_store_partial_prepare_(bool& lo, bool& hi,
                                 std::ptrdiff_t const i,
                                 std::ptrdiff_t const imin,
                                 std::ptrdiff_t const imax)
{
  lo = i >= imin;
  hi = i+CCTK_REAL8_VEC_SIZE-1 < imax;
}

// Store a partial vector (aligned and non-temporal)
#define vec8_store_partial_prepare_fixed(i, imin,imax)  \
  vec8_store_partial_prepare(i, imin,imax)

#define vec8_store_nta_partial(p, x)                    \
  vec8_store_nta_partial_(v8stp_lo, v8stp_hi, p, x)
#if VECTORISE_STREAMING_STORES && defined(__SSE4A__)
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec8_store_nta_partial_(bool const lo, bool const hi,
                             CCTK_REAL8& p,
                             CCTK_REAL8_VEC const x)
{
  if (CCTK_BUILTIN_EXPECT(lo and hi, true)) {
    vec8_store_nta(p, x);
  } else if (lo) {
    _mm_stream_sd(&p, x);
  } else if (hi) {
    _mm_stream_sd(&p+1, vec8_swap10(x));
  }
}
#else
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec8_store_nta_partial_(bool const lo, bool const hi,
                             CCTK_REAL8& p,
                             CCTK_REAL8_VEC const x)
{
  if (CCTK_BUILTIN_EXPECT(lo and hi, true)) {
    vec8_store_nta(p, x);
  } else if (lo) {
    _mm_storel_pd(&p, x);
  } else if (hi) {
    _mm_storeh_pd(&p+1, x);
  }
}
#endif

#define vec8_storeu_partial(p, x)                       \
  vec8_storeu_partial_(v8stp_lo, v8stp_hi, p, x)
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec8_storeu_partial_(bool const lo, bool const hi,
                          CCTK_REAL8& p,
                          CCTK_REAL8_VEC const x)
{
  if (CCTK_BUILTIN_EXPECT(lo and hi, true)) {
    vec8_storeu(p, x);
  } else if (lo) {
    _mm_storel_pd(&p, x);
  } else if (hi) {
    _mm_storeh_pd(&p+1, x);
  }
}

// Store a lower or higher partial vector (aligned and non-temporal)
#if ! VECTORISE_STREAMING_STORES
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec8_store_nta_partial_lo(CCTK_REAL8& p,
                               CCTK_REAL8_VEC const x,
                               ptrdiff_t const n)
{
  _mm_storel_pd(&p, x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec8_store_nta_partial_hi(CCTK_REAL8& p,
                               CCTK_REAL8_VEC const x,
                               ptrdiff_t const n)
{
  _mm_storeh_pd(&p+1, x);
}
#else
#  if defined(__SSE4A__)
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec8_store_nta_partial_lo(CCTK_REAL8& p,
                               CCTK_REAL8_VEC const x,
                               ptrdiff_t const n)
{
  _mm_stream_sd(&p, x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec8_store_nta_partial_hi(CCTK_REAL8& p,
                               CCTK_REAL8_VEC const x,
                               ptrdiff_t const n)
{
  _mm_stream_sd(&p+1, vec8_swap10(x));
}
#  else
// TODO: use clflush once a whole cache line has been written (cache
// lines are usually larger than the CPU vector size)
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec8_store_nta_partial_lo(CCTK_REAL8& p,
                               CCTK_REAL8_VEC const x,
                               ptrdiff_t const n)
{
  _mm_storel_pd(&p, x);
  // _mm_clflush(&p);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec8_store_nta_partial_hi(CCTK_REAL8& p,
                               CCTK_REAL8_VEC const x,
                               ptrdiff_t const n)
{
  _mm_storeh_pd(&p+1, x);
  // _mm_clflush(&p+1);
}
#  endif
#endif
#if 0
// This is slower; we would need a non-temporal read
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec8_store_nta_partial_lo(CCTK_REAL8& p,
                               CCTK_REAL8_VEC const x,
                               ptrdiff_t const n)
{
  vec8_store_nta(p, _mm_loadh_pd(x, &p+1));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec8_store_nta_partial_hi(CCTK_REAL8& p,
                               CCTK_REAL8_VEC const x,
                               ptrdiff_t const n)
{
  vec8_store_nta(p, _mm_loadl_pd(x, &p));
}
#endif
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
void vec8_store_nta_partial_mid(CCTK_REAL8& p,
                                CCTK_REAL8_VEC const x,
                                ptrdiff_t const nlo,
                                ptrdiff_t const nhi)
{
  assert(0);
}



// Functions and operators

// Operators

// #define k8inot(x) (_mm_xor_si128(k8all_mask,x))
// 
// #define k8iand(x,y) (_mm_and_si128(x,y))
// #define k8ior(x,y)  (_mm_or_si128(x,y))
// #define k8ixor(x,y) (_mm_xor_si128(x,y))
// 
// #define k8ineg(x) (_mm_xor_pd(k8sign_mask,x))
// 
// #define k8iadd(x,y) (_mm_add_epi64(x,y))
// #define k8isub(x,y) (_mm_sub_epi64(x,y))
// 
// #define k8not(x) (_mm_xor_pd(k8all_mask,x))
// 
// #define k8and(x,y) (_mm_and_pd(x,y))
// #define k8or(x,y)  (_mm_or_pd(x,y))
// #define k8xor(x,y) (_mm_xor_pd(x,y))

static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8neg(CCTK_REAL8_VEC const x)
{
  return _mm_xor_pd(I2R(k8sign), x);
}

static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8add(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return _mm_add_pd(x, y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8sub(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return _mm_sub_pd(x, y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8mul(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return _mm_mul_pd(x, y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8div(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return _mm_div_pd(x, y);
}

// Fused multiply-add, defined as [+-]x*y[+-]z
#ifdef __FMA4__
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8madd(CCTK_REAL8_VEC const x,
                      CCTK_REAL8_VEC const y,
                      CCTK_REAL8_VEC const z)
{
  return _mm_macc_pd(x, y, z);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8msub(CCTK_REAL8_VEC const x,
                      CCTK_REAL8_VEC const y,
                      CCTK_REAL8_VEC const z)
{
  return _mm_msub_pd(x, y, z);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8nmadd(CCTK_REAL8_VEC const x,
                       CCTK_REAL8_VEC const y,
                       CCTK_REAL8_VEC const z)
{
  return _mm_nmsub_pd(x, y, z);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8nmsub(CCTK_REAL8_VEC const x,
                       CCTK_REAL8_VEC const y,
                       CCTK_REAL8_VEC const z)
{
  return _mm_nmacc_pd(x, y, z);
}
#else
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8madd(CCTK_REAL8_VEC const x,
                      CCTK_REAL8_VEC const y,
                      CCTK_REAL8_VEC const z)
{
  return k8add(k8mul(x, y), z);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8msub(CCTK_REAL8_VEC const x,
                      CCTK_REAL8_VEC const y,
                      CCTK_REAL8_VEC const z)
{
  return k8sub(k8mul(x, y), z);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8nmadd(CCTK_REAL8_VEC const x,
                       CCTK_REAL8_VEC const y,
                       CCTK_REAL8_VEC const z)
{
  return k8sub(k8neg(z), k8mul(x, y));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8nmsub(CCTK_REAL8_VEC const x,
                       CCTK_REAL8_VEC const y,
                       CCTK_REAL8_VEC const z)
{
  return k8sub(z, k8mul(x, y));
}
#endif

// Cheap functions
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8copysign(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return _mm_or_pd(_mm_and_pd(I2R(k8notsign), x),
                   _mm_and_pd(I2R(k8sign   ), y));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8fabs(CCTK_REAL8_VEC const x)
{
  return _mm_and_pd(I2R(k8notsign), x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8fmax(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return _mm_max_pd(x, y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8fmin(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return _mm_min_pd(x, y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8fnabs(CCTK_REAL8_VEC const x)
{
  return _mm_or_pd(I2R(k8sign), x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN8_VEC k8signbit(CCTK_REAL8_VEC const x)
{
  return R2I(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8sqrt(CCTK_REAL8_VEC const x)
{
  return _mm_sqrt_pd(x);
}



// Expensive functions

#define K8REPL(f,x)                             \
  vec8_set(f(vec8_elt(x,0)),                    \
           f(vec8_elt(x,1)));
#define K8REPL2S(f,x,a)                         \
  vec8_set(f(vec8_elt(x,0),a),                  \
           f(vec8_elt(x,1),a));
#define K8REPL2(f,x,y)                          \
  vec8_set(f(vec8_elt(x,0),vec8_elt(y,0)),      \
           f(vec8_elt(x,1),vec8_elt(y,1)));

#if defined __ICC
// The Intel compiler provides intrinsics for these

static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8acos(CCTK_REAL8_VEC const x)
{
  return _mm_acos_pd(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8acosh(CCTK_REAL8_VEC const x)
{
  return _mm_acosh_pd(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8asin(CCTK_REAL8_VEC const x)
{
  return _mm_asin_pd(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8asinh(CCTK_REAL8_VEC const x)
{
  return _mm_asinh_pd(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8atan(CCTK_REAL8_VEC const x)
{
  return _mm_atan_pd(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8atan2(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return _mm_atan2_pd(x,y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8atanh(CCTK_REAL8_VEC const x)
{
  return _mm_atanh_pd(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8cos(CCTK_REAL8_VEC const x)
{
  return _mm_cos_pd(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8cosh(CCTK_REAL8_VEC const x)
{
  return _mm_cosh_pd(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8exp(CCTK_REAL8_VEC const x)
{
  return _mm_exp_pd(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8fmod(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
#if __ICC > 1400
  return _mm_fmod_pd(x,y);
#else
  return K8REPL2(fmod,x,y);
#endif
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8log(CCTK_REAL8_VEC const x)
{
  return _mm_log_pd(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8pow(CCTK_REAL8_VEC const x, CCTK_REAL8 const a)
{
  return _mm_pow_pd(x, _mm_set1_pd(a));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8sin(CCTK_REAL8_VEC const x)
{
  return _mm_sin_pd(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8sinh(CCTK_REAL8_VEC const x)
{
  return _mm_sinh_pd(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8tan(CCTK_REAL8_VEC const x)
{
  return _mm_tan_pd(x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8tanh(CCTK_REAL8_VEC const x)
{
  return _mm_tanh_pd(x);
}

#else

static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8acos(CCTK_REAL8_VEC const x)
{
  return K8REPL(acos,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8acosh(CCTK_REAL8_VEC const x)
{
  return K8REPL(acosh,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8asin(CCTK_REAL8_VEC const x)
{
  return K8REPL(asin,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8asinh(CCTK_REAL8_VEC const x)
{
  return K8REPL(asinh,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8atan(CCTK_REAL8_VEC const x)
{
  return K8REPL(atan,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8atan2(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return K8REPL2(atan2,x,y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8atanh(CCTK_REAL8_VEC const x)
{
  return K8REPL(atanh,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8cos(CCTK_REAL8_VEC const x)
{
  return K8REPL(cos,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8cosh(CCTK_REAL8_VEC const x)
{
  return K8REPL(cosh,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8exp(CCTK_REAL8_VEC const x)
{
  return K8REPL(exp,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8fmod(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return K8REPL2(fmod,x,y);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8log(CCTK_REAL8_VEC const x)
{
  return K8REPL(log,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8pow(CCTK_REAL8_VEC const x, CCTK_REAL8 const a)
{
  return K8REPL2S(pow,x,a);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8sin(CCTK_REAL8_VEC const x)
{
  return K8REPL(sin,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8sinh(CCTK_REAL8_VEC const x)
{
  return K8REPL(sinh,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8tan(CCTK_REAL8_VEC const x)
{
  return K8REPL(tan,x);
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8tanh(CCTK_REAL8_VEC const x)
{
  return K8REPL(tanh,x);
}

#endif



#define k8lfalse (vec8_set1i( 0))
#define k8ltrue  (vec8_set1i(~0))
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN8_VEC k8lnot(CCTK_BOOLEAN8_VEC const x)
{
  return R2I(_mm_xor_pd(I2R(k8ltrue), I2R(x)));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN8_VEC k8land(CCTK_BOOLEAN8_VEC const x, CCTK_BOOLEAN8_VEC const y)
{
  return R2I(_mm_and_pd(I2R(x), I2R(y)));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN8_VEC k8lor(CCTK_BOOLEAN8_VEC const x, CCTK_BOOLEAN8_VEC const y)
{
  return R2I(_mm_or_pd(I2R(x), I2R(y)));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN8_VEC k8lxor(CCTK_BOOLEAN8_VEC const x, CCTK_BOOLEAN8_VEC const y)
{
  return R2I(_mm_xor_pd(I2R(x), I2R(y)));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8ifthen(CCTK_BOOLEAN8_VEC const x,
                        CCTK_REAL8_VEC const y,
                        CCTK_REAL8_VEC const z)
{
#ifdef __SSE4_1__
  return _mm_blendv_pd(z,y,I2R(x));
#elif 0
  // This is slow (but this is what Intel/PGI produce by themselves)
  int const m = _mm_movemask_pd(x);
  switch (m) {
  case 0: return y;
  case 1: return _mm_move_sd(y,z);
  case 2: return _mm_move_sd(z,y);
  }
  return z;
#elif 0
  return vec8_set(std::signbit(vec8_elt(x,0)) ? vec8_elt(y,0) : vec8_elt(z,0),
                  std::signbit(vec8_elt(x,1)) ? vec8_elt(y,1) : vec8_elt(z,1));
#elif 0
  // We don't need to shift -- the condition (mask) will be either all
  // zeros or all ones
  k8const_t const k8ione  = {{ 1, 1, }};
  // there is no _mm_srai_epi64(x, 63); we therefore calculate srli(x)-1
  __m128i const x_int = *(__m128i const*)&x;
  __m128i const imask_int = _mm_sub_epi64(_mm_srli_epi64(x_int, 63), k8ione.vi);
  CCTK_REAL8_VEC const imask = *(CCTK_REAL8_VEC const*)&imask_int;
  // (z & ~mask) | (y & mask)   where imask = ~mask
  return _mm_or_pd(_mm_and_pd(imask, z), _mm_andnot_pd(imask, y));
#else
  // This assumes that all logical operations always return either
  // lfalse or ltrue, and nothing "in between"
  // (z & ~mask) | (y & mask)   where imask = ~mask
  return _mm_or_pd(_mm_and_pd(I2R(x), y), _mm_andnot_pd(I2R(x), z));
#endif
}

static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN8_VEC k8cmpeq(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return R2I(_mm_cmpeq_pd(x, y));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN8_VEC k8cmpne(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return R2I(_mm_cmpneq_pd(x, y));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN8_VEC k8cmpgt(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return R2I(_mm_cmpgt_pd(x, y));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN8_VEC k8cmpge(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return R2I(_mm_cmpge_pd(x, y));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN8_VEC k8cmplt(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return R2I(_mm_cmplt_pd(x, y));
}
static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_BOOLEAN8_VEC k8cmple(CCTK_REAL8_VEC const x, CCTK_REAL8_VEC const y)
{
  return R2I(_mm_cmple_pd(x, y));
}



static inline CCTK_ATTRIBUTE_ALWAYS_INLINE
CCTK_REAL8_VEC k8sgn(CCTK_REAL8_VEC const x)
{
  CCTK_BOOLEAN8_VEC const iszero    = k8cmpeq(x, vec8_set1(0.0));
  CCTK_REAL8_VEC    const sign      = _mm_and_pd(I2R(k8sign), x);
  CCTK_REAL8_VEC    const signedone = _mm_or_pd(sign, vec8_set1(1.0));
  return k8ifthen(iszero, vec8_set1(0.0), signedone);
}



#undef I2R
#undef R2I

```

macros/vectors-8-AVX.h:
```
// Vectorise using Intel's or AMD's AVX

// Use the type __m256d directly, without introducing a wrapper class
// Use macros instead of inline functions



#if VECTORISE_EMULATE_AVX
#  include "avxintrin_emu.h"
#else
#  include <immintrin.h>
#endif
#ifdef __FMA4__
#  include <fma4intrin.h>
#endif



#ifdef __FMA4__
#  define vec8_architecture_FMA4 "+FMA4"
#else
#  define vec8_architecture_FMA4 ""
#endif
#define vec8_architecture "AVX" vec8_architecture_FMA4 " (64-bit precision)"



// Vector type corresponding to CCTK_REAL
#define CCTK_REAL8_VEC __m256d

// Number of vector elements in a CCTK_REAL_VEC
#define CCTK_REAL8_VEC_SIZE 4

// Integer and boolean types corresponding to this real type
#define CCTK_INTEGER8     CCTK_REAL8
#define CCTK_BOOLEAN8     CCTK_REAL8
#define CCTK_INTEGER8_VEC CCTK_REAL8_VEC
#define CCTK_BOOLEAN8_VEC CCTK_REAL8_VEC



union k8const_t {
  unsigned long long i[4];
  double             f[4];
  __m256i            vi;
  __m256d            vf;
};

#define K8_ZERO    0x0000000000000000ULL
#define K8_NOTZERO 0xffffffffffffffffULL
#define K8_IMIN    0x8000000000000000ULL
#define K8_IMAX    0x7fffffffffffffffULL



// Create vectors, extract vector elements

#define vec8_set1(a)      (_mm256_set1_pd(a))
#define vec8_set(a,b,c,d) (_mm256_set_pd(d,c,b,a)) // note reversed arguments

#define vec8_elt0(x) (((CCTK_REAL8 const*)&(x))[0])
#define vec8_elt1(x) (((CCTK_REAL8 const*)&(x))[1])
#define vec8_elt2(x) (((CCTK_REAL8 const*)&(x))[2])
#define vec8_elt3(x) (((CCTK_REAL8 const*)&(x))[3])
#define vec8_elt(x,d) (((CCTK_REAL8 const*)&(x))[d])



// Load and store vectors

// Load a vector from memory (aligned and unaligned); this loads from
// a reference to a scalar
#define vec8_load(p)  (_mm256_load_pd(&(p)))
#define vec8_loadu(p) (_mm256_loadu_pd(&(p)))
#if ! VECTORISE_ALWAYS_USE_ALIGNED_LOADS
#  define vec8_load_off1(p) vec_loadu(p)
#else
#  error "VECTORISE_ALWAYS_USE_ALIGNED_LOADS not yet supported"
#endif

// Load a vector from memory that may or may not be aligned, as
// decided by the offset off and the vector size
#if VECTORISE_ALWAYS_USE_UNALIGNED_LOADS
// Implementation: Always use unaligned load
#  define vec8_loadu_maybe(off,p)             (vec8_loadu(p))
#  define vec8_loadu_maybe3(off1,off2,off3,p) (vec8_loadu(p))
#else
#  define vec8_loadu_maybe(off,p_)              \
  ({                                            \
    CCTK_REAL8 const& p__=(p_);                 \
    CCTK_REAL8 const& p=p__;                    \
    (off) % CCTK_REAL8_VEC_SIZE == 0 ?          \
      vec8_load(p) :                            \
      vec8_load_off1(p);                        \
  })
#  if VECTORISE_ALIGNED_ARRAYS
// Assume all array x sizes are multiples of the vector size
#    define vec8_loadu_maybe3(off1,off2,off3,p) \
  vec8_loadu_maybe(off1,p)
#  else
#    define vec8_loadu_maybe3(off1,off2,off3,p_)        \
  ({                                                    \
    CCTK_REAL8 const& p__=(p_);                         \
    CCTK_REAL8 const& p=p__;                            \
    ((off2) % CCTK_REAL8_VEC_SIZE != 0 or               \
     (off3) % CCTK_REAL8_VEC_SIZE != 0) ?               \
      vec8_loadu(p) :                                   \
      vec8_loadu_maybe(off1,p);                         \
  })
#  endif
#endif

// Store a vector to memory (aligned and non-temporal); this stores to
// a reference to a scalar
#define vec8_store(p,x)  (_mm256_store_pd(&(p),x))
#define vec8_storeu(p,x) (_mm256_storeu_pd(&(p),x))
#if ! VECTORISE_STREAMING_STORES
#  define vec8_store_nta(p,x) (vec8_store(p,x))
#else
#  define vec8_store_nta(p,x) (_mm256_stream_pd(&(p),x))
#endif

// Store a partial vector (aligned and non-temporal)
#define vec8_store_partial_prepare(i,imin_,imax_)                       \
  bool v8stp_all;                                                       \
  __m256i v8stp_mask;                                                   \
  ({                                                                    \
    ptrdiff_t const imin__=(imin_);                                     \
    ptrdiff_t const imin=imin__;                                        \
    ptrdiff_t const imax__=(imax_);                                     \
    ptrdiff_t const imax=imax__;                                        \
                                                                        \
    v8stp_all = i>=imin and i+CCTK_REAL_VEC_SIZE-1<imax;                \
                                                                        \
    if (not CCTK_BUILTIN_EXPECT(v8stp_all, true)) {                     \
      /*                                                                \
        __m256i const v8stp_mask =                                      \
          _mm256_andnot_pd(_mm256_add_epi64(_mm256_set1_epi64x(i-imin), \
                                            vec_index),                 \
                           _mm256_add_epi64(_mm256_set1_epi64x(i-imax), \
                                            vec_index));                \
      */                                                                \
      __m128i const termlo0 =                                           \
        _mm_add_epi64(_mm_set1_epi64x(i-imin), _mm_set_epi64x(1, 0));   \
      __m128i const termup0 =                                           \
        _mm_add_epi64(_mm_set1_epi64x(i-imax), _mm_set_epi64x(1, 0));   \
      __m128i const term0 = _mm_andnot_si128(termlo0, termup0);         \
      __m128i const termlo1 =                                           \
        _mm_add_epi64(_mm_set1_epi64x(i-imin), _mm_set_epi64x(3, 2));   \
      __m128i const termup1 =                                           \
        _mm_add_epi64(_mm_set1_epi64x(i-imax), _mm_set_epi64x(3, 2));   \
      __m128i const term1 = _mm_andnot_si128(termlo1, termup1);         \
      v8stp_mask =                                                      \
        _mm256_insertf128_si256(_mm256_castsi128_si256(term0), term1, 1); \
    }                                                                   \
  })

#define vec8_store_nta_partial(p,x)             \
  ({                                            \
    if (CCTK_BUILTIN_EXPECT(v8stp_all, true)) { \
      vec8_store_nta(p,x);                      \
    } else {                                    \
      _mm256_maskstore_pd(&p,v8stp_mask,x);     \
    }                                           \
  })

// Store a lower or higher partial vector (aligned and non-temporal);
// the non-temporal hint is probably ignored
// Masks indicating which vector element should be stored:
static const k8const_t k8store_lo[5] =
  {
    {{ K8_ZERO   , K8_ZERO   , K8_ZERO   , K8_ZERO   , }},
    {{ K8_NOTZERO, K8_ZERO   , K8_ZERO   , K8_ZERO   , }},
    {{ K8_NOTZERO, K8_NOTZERO, K8_ZERO   , K8_ZERO   , }},
    {{ K8_NOTZERO, K8_NOTZERO, K8_NOTZERO, K8_ZERO   , }},
    {{ K8_NOTZERO, K8_NOTZERO, K8_NOTZERO, K8_NOTZERO, }},
  };
static const k8const_t k8store_hi[5] =
  {
    {{ K8_ZERO   , K8_ZERO   , K8_ZERO   , K8_ZERO   , }},
    {{ K8_ZERO   , K8_ZERO   , K8_ZERO   , K8_NOTZERO, }},
    {{ K8_ZERO   , K8_ZERO   , K8_NOTZERO, K8_NOTZERO, }},
    {{ K8_ZERO   , K8_NOTZERO, K8_NOTZERO, K8_NOTZERO, }},
    {{ K8_NOTZERO, K8_NOTZERO, K8_NOTZERO, K8_NOTZERO, }},
  };
#if !defined(__INTEL_COMPILER) && defined(__GNUC__) && __GNUC__==4 && __GNUC_MINOR__<=4
// gcc 4.4 uses a wrong prototype for _mm256_maskstore_pd
#  define vec8_store_nta_partial_lo(p,x,n)                              \
  (_mm256_maskstore_pd(&(p),_mm256_castsi256_pd(k8store_lo[n].vi),x))
#  define vec8_store_nta_partial_hi(p,x,n)                              \
  (_mm256_maskstore_pd(&(p),_mm256_castsi256_pd(k8store_hi[n].vi),x))
#  define vec8_store_nta_partial_mid(p,x,nlo,nhi)                       \
  (_mm256_maskstore_pd                                                  \
   (&(p),                                                               \
    _mm256_castsi256_pd(k8store_lo[nlo].vi & k8store_hi[nhi].vi),       \
    x))
#else
#  define vec8_store_nta_partial_lo(p,x,n)              \
  (_mm256_maskstore_pd(&(p),k8store_lo[n].vi,x))
#  define vec8_store_nta_partial_hi(p,x,n)              \
  (_mm256_maskstore_pd(&(p),k8store_hi[n].vi,x))
#  define vec8_store_nta_partial_mid(p,x,nlo,nhi)               \
  (_mm256_maskstore_pd                                          \
   (&(p),                                                       \
    _mm256_castpd_si256(_mm256_and_pd(k8store_lo[nlo].vf,       \
                                      k8store_hi[nhi].vf)),     \
    x))
#endif



// Functions and operators

static const k8const_t k8sign_mask = {{ K8_IMIN, K8_IMIN, K8_IMIN, K8_IMIN, }};

// Operators
#define k8neg(x) (_mm256_xor_pd(x,k8sign_mask.vf))

#define k8add(x,y) (_mm256_add_pd(x,y))
#define k8sub(x,y) (_mm256_sub_pd(x,y))
#define k8mul(x,y) (_mm256_mul_pd(x,y))
#define k8div(x,y) (_mm256_div_pd(x,y))

// Fused multiply-add, defined as [+-]x*y[+-]z
#ifdef __FMA4__
#  define k8madd(x,y,z)  (_mm256_macc_pd(x,y,z))
#  define k8msub(x,y,z)  (_mm256_msub_pd(x,y,z))
#  define k8nmadd(x,y,z) (_mm256_nmsub_pd(x,y,z))
#  define k8nmsub(x,y,z) (_mm256_nmacc_pd(x,y,z))
#else
#  define k8madd(x,y,z)  (k8add(k8mul(x,y),z))
#  define k8msub(x,y,z)  (k8sub(k8mul(x,y),z))
#  define k8nmadd(x,y,z) (k8sub(k8neg(z),k8mul(x,y)))
#  define k8nmsub(x,y,z) (k8sub(z,k8mul(x,y)))
#endif

// Cheap functions
#define k8copysign(x,y)                                 \
  (_mm256_or_pd(_mm256_andnot_pd(k8sign_mask.vf,x),     \
                _mm256_and_pd(k8sign_mask.vf,y)))
#define k8fabs(x)   (_mm256_andnot_pd(k8sign_mask.vf,x))
#define k8fmax(x,y) (_mm256_max_pd(x,y))
#define k8fmin(x,y) (_mm256_min_pd(x,y))
#define k8fnabs(x)  (_mm256_or_pd(x,k8sign_mask.vf))
static const k8const_t k8zero = { f: { 0.0, 0.0, 0.0, 0.0, }};
static const k8const_t k8one  = { f: { 1.0, 1.0, 1.0, 1.0, }};
#define k8sgn(x_)                                                       \
  ({                                                                    \
    CCTK_REAL_VEC x__=(x_);                                             \
    CCTK_REAL_VEC x=x__;                                                \
    CCTK_REAL_VEC iszero = _mm256_cmp_pd(x, k8zero.vf, _CMP_EQ_OQ);     \
    CCTK_REAL_VEC sign = _mm256_and_pd(k8sign_mask.vf, x);              \
    CCTK_REAL_VEC signedone = _mm256_or_pd(sign, k8one.vf);             \
    k8ifthen(iszero, k8zero.vf, signedone);                             \
  })
#define k8sqrt(x)   (_mm256_sqrt_pd(x))

// Expensive functions
#define K8REPL(f,x_)                            \
  ({                                            \
    CCTK_REAL8_VEC const x__=(x_);              \
    CCTK_REAL8_VEC const x=x__;                 \
    vec8_set(f(vec8_elt0(x)),                   \
             f(vec8_elt1(x)),                   \
             f(vec8_elt2(x)),                   \
             f(vec8_elt3(x)));                  \
  })
#define K8REPL2S(f,x_,a_)                       \
  ({                                            \
    CCTK_REAL8_VEC const x__=(x_);              \
    CCTK_REAL8     const a__=(a_);              \
    CCTK_REAL8_VEC const x=x__;                 \
    CCTK_REAL8     const a=a__;                 \
    vec8_set(f(vec8_elt0(x),a),                 \
             f(vec8_elt1(x),a),                 \
             f(vec8_elt2(x),a),                 \
             f(vec8_elt3(x),a));                \
  })
#define K8REPL2(f,x_,y_)                        \
  ({                                            \
    CCTK_REAL8_VEC const x__=(x_);              \
    CCTK_REAL8_VEC const y__=(y_);              \
    CCTK_REAL8_VEC const x=x__;                 \
    CCTK_REAL8_VEC const y=y__;                 \
    vec8_set(f(vec8_elt0(x),vec8_elt0(y)),      \
             f(vec8_elt1(x),vec8_elt1(y)),      \
             f(vec8_elt2(x),vec8_elt2(y)),      \
             f(vec8_elt3(x),vec8_elt3(y)));     \
  })

#define k8acos(x)    K8REPL(acos,x)
#define k8acosh(x)   K8REPL(acosh,x)
#define k8asin(x)    K8REPL(asin,x)
#define k8asinh(x)   K8REPL(asinh,x)
#define k8atan(x)    K8REPL(atan,x)
#define k8atan2(x,y) K8REPL2(atan2,x,y)
#define k8atanh(x)   K8REPL(atanh,x)
#define k8cos(x)     K8REPL(cos,x)
#define k8cosh(x)    K8REPL(cosh,x)
#define k8exp(x)     K8REPL(exp,x)
#define k8log(x)     K8REPL(log,x)
#define k8pow(x,a)   K8REPL2S(pow,x,a)
#define k8sin(x)     K8REPL(sin,x)
#define k8sinh(x)    K8REPL(sinh,x)
#define k8tan(x)     K8REPL(tan,x)
#define k8tanh(x)    K8REPL(tanh,x)

static const k8const_t k8lfalse_ =
  {{ K8_ZERO, K8_ZERO, K8_ZERO, K8_ZERO, }};
static const k8const_t k8ltrue_  =
  {{ K8_NOTZERO, K8_NOTZERO, K8_NOTZERO, K8_NOTZERO, }};
#define k8lfalse        (k8lfalse_.vf)
#define k8ltrue         (k8ltrue_.vf)
#define k8lnot(x)       (_mm256_xor_pd(k8ltrue,x))
#define k8land(x,y)     (_mm256_and_pd(x,y))
#define k8lor(x,y)      (_mm256_or_pd(x,y))
#define k8lxor(x,y)     (_mm256_xor_pd(x,y))
#define k8ifthen(x,y,z) (_mm256_blendv_pd(z,y,x))

#define k8cmpeq(x,y) (_mm256_cmp_pd(x,y,_CMP_EQ_OQ))
#define k8cmpne(x,y) (_mm256_cmp_pd(x,y,_CMP_NEQ_OQ))
#define k8cmpgt(x,y) (_mm256_cmp_pd(x,y,_CMP_GT_OQ))
#define k8cmpge(x,y) (_mm256_cmp_pd(x,y,_CMP_GE_OQ))
#define k8cmplt(x,y) (_mm256_cmp_pd(x,y,_CMP_LT_OQ))
#define k8cmple(x,y) (_mm256_cmp_pd(x,y,_CMP_LE_OQ))

```

macros/vectors-4-SSE.h:
```
// Vectorise using Intel's or AMD's SSE

// Use the type __m128 directly, without introducing a wrapper class
// Use macros instead of inline functions



#include <assert.h>
#include <math.h>

#include <xmmintrin.h>
#ifdef __SSE4_1__
// Intel's SSE 4.1
#  include <smmintrin.h>
#endif
#ifdef __SSE4A__
// AMD's SSE 4a
#  include <ammintrin.h>
#endif
#ifdef __FMA4__
#  include <fma4intrin.h>
#endif



#ifdef __SSE4_1__
#  define vec4_architecture_SSE4_1 "+SSE4.1"
#else
#  define vec4_architecture_SSE4_1 ""
#endif
#ifdef __SSE4A__
#  define vec4_architecture_SSE4a "+SSE4A"
#else
#  define vec4_architecture_SSE4a ""
#endif
#ifdef __FMA4__
#  define vec4_architecture_FMA4 "+FMA4"
#else
#  define vec4_architecture_FMA4 ""
#endif
#define vec4_architecture "SSE" vec4_architecture_SSE4_1 vec4_architecture_SSE4a vec4_architecture_FMA4 " (32-bit precision)"



// Vector type corresponding to CCTK_REAL
#define CCTK_REAL4_VEC __m128

// Number of vector elements in a CCTK_REAL_VEC
#define CCTK_REAL4_VEC_SIZE 4

// Integer and boolean types corresponding to this real type
#define CCTK_INTEGER4     CCTK_REAL4
#define CCTK_BOOLEAN4     CCTK_REAL4
#define CCTK_INTEGER4_VEC CCTK_REAL4_VEC
#define CCTK_BOOLEAN4_VEC CCTK_REAL4_VEC



union k4const_t {
  unsigned i[4];
  float    f[4];
  __m128i  vi;
  __m128   vf;
};

#define K4_ZERO 0x00000000UL
#define K4_IMIN 0x80000000UL
#define K4_IMAX 0x7fffffffUL



// Create vectors, extract vector elements

#define vec4_set1(a)      (_mm_set1_ps(a))
#define vec4_set(a,b,c,d) (_mm_set_ps(d,c,b,a)) // note reversed arguments

// original order is 0123
#define vec4_swap1032(x_)                       \
  ({                                            \
    CCTK_REAL4_VEC const x__=(x_);              \
    CCTK_REAL4_VEC const x=x__;                 \
    _mm_shuffle_ps(x,x, _MM_SHUFFLE(2,3,0,1));  \
  })
#define vec4_swap2301(x_)                       \
  ({                                            \
    CCTK_REAL4_VEC const x__=(x_);              \
    CCTK_REAL4_VEC const x=x__;                 \
    _mm_shuffle_ps(x,x, _MM_SHUFFLE(1,0,3,2));  \
  })
#define vec4_swap3210(x_)                       \
  ({                                            \
    CCTK_REAL4_VEC const x__=(x_);              \
    CCTK_REAL4_VEC const x=x__;                 \
    _mm_shuffle_ps(x,x, _MM_SHUFFLE(0,1,2,3));  \
  })

#if defined(__PGI)
// _mm_cvtss_f32 does not exist on PGI compilers
#  define vec4_elt0(x)                          \
  ({                                            \
    CCTK_REAL4 a;                               \
    asm ("" : "=x" (a) : "0" (x));              \
    a;                                          \
  })
#else
#  define vec4_elt0(x) (_mm_cvtss_f32(x)) // this is a no-op
#endif
#define vec4_elt1(x) vec4_elt0(vec4_swap1032(x))
#define vec4_elt2(x) vec4_elt0(vec4_swap2301(x))
#define vec4_elt3(x) vec4_elt0(vec4_swap3210(x))
#if defined(__PGI)
#  define vec4_elt(x_,d)                        \
  ({                                            \
    CCTK_REAL4_VEC const x__=(x_);              \
    CCTK_REAL4_VEC const x=x__;                 \
    CCTK_REAL4 a;                               \
    if (d==0)      a=vec4_elt0(x);              \
    else if (d==1) a=vec4_elt1(x);              \
    else if (d==2) a=vec4_elt2(x);              \
    else if (d==3) a=vec4_elt3(x);              \
    a;                                          \
  })
#else
#  define vec4_elt(x_,d)                        \
  ({                                            \
    CCTK_REAL4_VEC const x__=(x_);              \
    CCTK_REAL4_VEC const x=x__;                 \
    CCTK_REAL4 a;                               \
    switch (d) {                                \
    case 0: a=vec4_elt0(x); break;              \
    case 1: a=vec4_elt1(x); break;              \
    case 2: a=vec4_elt2(x); break;              \
    case 3: a=vec4_elt3(x); break;              \
    }                                           \
    a;                                          \
  })
#endif



// Load and store vectors

// Load a vector from memory (aligned and unaligned); this loads from
// a reference to a scalar
#define vec4_load(p)  (_mm_load_ps(&(p)))
#define vec4_loadu(p) (_mm_loadu_ps(&(p)))
#if ! VECTORISE_ALWAYS_USE_ALIGNED_LOADS
#  define vec4_load_off1(p) vec_loadu(p)
#  define vec4_load_off2(p) vec_loadu(p)
#  define vec4_load_off3(p) vec_loadu(p)
#else
#  define vec4_load_off1(p_)                                            \
  ({                                                                    \
    CCTK_REAL4 const& p__=(p_);                                         \
    CCTK_REAL4 const& p=p__;                                            \
    CCTK_REAL4_VEC const lo=vec4_load((&p)[-1]);                        \
    CCTK_REAL4_VEC const hi=vec4_load((&p)[+3]);                        \
    assert(0);                                                          \
    CCTK_REAL4_VEC const hi2=_mm_shuffle_ps(lo,hi, _MM_SHUFFLE(0,1,2,3)); \
    _mm_shuffle_ps(lo,hi2, _MM_SHUFFLE(2,1,3,0));                       \
  })
#  define vec4_load_off2(p_)                            \
  ({                                                    \
    CCTK_REAL4 const& p__=(p_);                         \
    CCTK_REAL4 const& p=p__;                            \
    CCTK_REAL4_VEC const lo=vec4_load((&p)[-2]);        \
    CCTK_REAL4_VEC const hi=vec4_load((&p)[+2]);        \
    _mm_shuffle_ps(lo,hi, _MM_SHUFFLE(1,0,3,2));        \
  })
#  define vec4_load_off3(p_)                                            \
  ({                                                                    \
    CCTK_REAL4 const& p__=(p_);                                         \
    CCTK_REAL4 const& p=p__;                                            \
    CCTK_REAL4_VEC const lo=vec4_load((&p)[-1]);                        \
    CCTK_REAL4_VEC const hi=vec4_load((&p)[+3]);                        \
    assert(0);                                                          \
    CCTK_REAL4_VEC const lo2=_mm_shuffle_ps(lo,hi, _MM_SHUFFLE(0,1,2,3)); \
    _mm_shuffle_ps(lo2,hi, _MM_SHUFFLE(3,0,2,1));                       \
  })
#endif

// Load a vector from memory that may or may not be aligned, as
// decided by the offset off and the vector size
#if VECTORISE_ALWAYS_USE_UNALIGNED_LOADS
// Implementation: Always use unaligned load
#  define vec4_loadu_maybe(off,p)             vec4_loadu(p)
#  define vec4_loadu_maybe3(off1,off2,off3,p) vec4_loadu(p)
#else
#  define vec4_loadu_maybe(off,p_)              \
  ({                                            \
    CCTK_REAL4 const& p__=(p_);                 \
    CCTK_REAL4 const& p=p__;                    \
    (off) % CCTK_REAL4_VEC_SIZE == 0 ?          \
      vec4_load(p) :                            \
      vec4_loadu(p);                            \
  })
#  if VECTORISE_ALIGNED_ARRAYS
// Assume all array x sizes are multiples of the vector size
#    define vec4_loadu_maybe3(off1,off2,off3,p) \
  vec4_loadu_maybe(off1,p)
#  else
#    define vec4_loadu_maybe3(off1,off2,off3,p) \
  vec4_loadu_maybe((off1)|(off2)|(off3),p)
#  endif
#endif

// Store a vector to memory (aligned and non-temporal); this stores to
// a reference to a scalar
#define vec4_store(p,x)  (_mm_store_ps(&(p),x))
#define vec4_storeu(p,x) (_mm_storeu_ps(&(p),x))
#if ! VECTORISE_STREAMING_STORES
#  define vec4_store_nta(p,x) vec4_store(p,x)
#else
#  define vec4_store_nta(p,x) (_mm_stream_ps(&(p),x))
#endif

// Store a partial vector (aligned and non-temporal)
#define vec4_store_partial_prepare(i,imin,imax)                         \
  int v4stp_lo_skip = (imin)-(i);                                       \
  int v4stp_hi_skip = (i)+CCTK_REAL_VEC_SIZE-(imax);                    \
  if (CCTK_BUILTIN_EXPECT(v4stp_lo_skip < 0, true)) v4stp_lo_skip = 0;  \
  if (CCTK_BUILTIN_EXPECT(v4stp_hi_skip < 0, true)) v4stp_hi_skip = 0;
// Ignoring VECTORISE_STREAMING_STORES for partial stores
#define vec4_store_nta_partial(p_,x_)                                   \
  ({                                                                    \
    CCTK_REAL4& p__=(p_);                                               \
    CCTK_REAL4& p=p__;                                                  \
    CCTK_REAL4_VEC const x__=(x_);                                      \
    CCTK_REAL4_VEC const x=x__;                                         \
    if (CCTK_BUILTIN_EXPECT(v4stp_lo_skip==0 and v4stp_hi_skip==0, true)) { \
      vec4_store_nta(p,x);                                              \
    } else {                                                            \
      /* these cases fall through */                                    \
      switch (v4stp_lo_skip) {                                          \
      case 0:                                                           \
        (&p)[0] = vec4_elt0(x);                                         \
      case 1:                                                           \
        if (v4stp_hi_skip>=3) break;                                    \
        (&p)[1] = vec4_elt1(x);                                         \
      case 2:                                                           \
        if (v4stp_hi_skip>=2) break;                                    \
        (&p)[2] = vec4_elt2(x);                                         \
      case 3:                                                           \
        if (v4stp_hi_skip>=1) break;                                    \
        (&p)[3] = vec4_elt3(x);                                         \
      }                                                                 \
    }                                                                   \
  })

// Ignoring VECTORISE_STREAMING_STORES for partial stores
#define vec4_store_nta_partial_lo(p_,x_,n)      \
  ({                                            \
    CCTK_REAL4          & p__=(p_);             \
    CCTK_REAL4_VEC const  x__=(x_);             \
    CCTK_REAL4          & p=p__;                \
    CCTK_REAL4_VEC const  x=x__;                \
    /* these cases fall through */              \
    switch (n) {                                \
    case 3: (&p)[2] = vec4_elt2(x);             \
    case 2: (&p)[1] = vec4_elt1(x);             \
    case 1: (&p)[0] = vec4_elt0(x);             \
    }                                           \
  })
#define vec4_store_nta_partial_hi(p_,x_,n)      \
  ({                                            \
    CCTK_REAL4          & p__=(p_);             \
    CCTK_REAL4_VEC const  x__=(x_);             \
    CCTK_REAL4          & p=p__;                \
    CCTK_REAL4_VEC const  x=x__;                \
    /* these cases fall through */              \
    switch (n) {                                \
    case 3: (&p)[1]=vec4_elt1(x);               \
    case 2: (&p)[2]=vec4_elt2(x);               \
    case 1: (&p)[3]=vec4_elt3(x);               \
    }                                           \
  })
#define vec4_store_nta_partial_mid(p_,x_,nlo,nhi)               \
  ({                                                            \
    CCTK_REAL4          & p__=(p_);                             \
    CCTK_REAL4_VEC const  x__=(x_);                             \
    CCTK_REAL4          & p=p__;                                \
    CCTK_REAL4_VEC const  x=x__;                                \
    /* these cases fall through */                              \
    switch (nhi) {                                              \
    case 3: if (nlo<2) break; (&p)[1] = vec4_elt1(x);           \
    case 2: if (nlo<3) break; (&p)[2] = vec4_elt2(x);           \
    }                                                           \
  })



// Functions and operators

static const k4const_t k4sign_mask = {{ K4_IMIN, K4_IMIN, K4_IMIN, K4_IMIN, }};

// Operators
#define k4neg(x) (_mm_xor_ps(k4sign_mask.vf,x))
// #define k4inv(x)
// TODO: provide k4inv via rcp and Newton-Raphson
// This is described in AMD's publication 47414.
// This should apply for AVX as well.

#define k4add(x,y) (_mm_add_ps(x,y))
#define k4sub(x,y) (_mm_sub_ps(x,y))
#define k4mul(x,y) (_mm_mul_ps(x,y))
// TODO: use k4inv and k4mul instead
#define k4div(x,y) (_mm_div_ps(x,y))

// Fused multiply-add, defined as [+-]x*y[+-]z
#ifdef __FMA4__
#  define k4madd(x,y,z)  (_mm_macc_ps(x,y,z))
#  define k4msub(x,y,z)  (_mm_msub_ps(x,y,z))
#  define k4nmadd(x,y,z) (_mm_nmsub_ps(x,y,z))
#  define k4nmsub(x,y,z) (_mm_nmacc_ps(x,y,z))
#else
#  define k4madd(x,y,z)  (k4add(k4mul(x,y),z))
#  define k4msub(x,y,z)  (k4sub(k4mul(x,y),z))
#  define k4nmadd(x,y,z) (k4sub(k4neg(z),k4mul(x,y)))
#  define k4nmsub(x,y,z) (k4sub(z,k4mul(x,y)))
#endif

// Cheap functions
#define k4copysign(x,y)                                         \
  (_mm_or_ps(_mm_andnot_ps(k4sign_mask.vf,x),                   \
             _mm_and_ps(k4sign_mask.vf,y)))
#define k4fabs(x)   (_mm_andnot_ps(k4sign_mask.vf,x))
#define k4fmax(x,y) (_mm_max_ps(x,y))
#define k4fmin(x,y) (_mm_min_ps(x,y))
#define k4fnabs(x)  (_mm_or_ps(k4sign_mask.vf,x))
#define k4sgn(x_)                                                       \
  ({                                                                    \
    CCTK_REAL_VEC const x__=(x_);                                       \
    CCTK_REAL_VEC const x=x__;                                          \
    CCTK_REAL_VEC const iszero = _mm_cmpeq_ps(vec4_set1(0.0f), x);      \
    CCTK_REAL_VEC const sign = _mm_and_ps(k4sign_mask.vf, x);           \
    CCTK_REAL_VEC const signedone = _mm_or_ps(vec4_set1(1.0f), sign);   \
    k4ifthen(iszero, vec4_set1(0.0f), signedone);                       \
  })
// TODO: maybe use rsqrt and Newton-Raphson
#define k4sqrt(x)   (_mm_sqrt_ps(x))

// Expensive functions
#define K4REPL(f,x_)                            \
  ({                                            \
    CCTK_REAL4_VEC const x__=(x_);              \
    CCTK_REAL4_VEC const x=x__;                 \
    vec4_set(f(vec4_elt0(x)),                   \
             f(vec4_elt1(x)),                   \
             f(vec4_elt2(x)),                   \
             f(vec4_elt3(x)));                  \
  })
#define K4REPL2S(f,x_,a_)                       \
  ({                                            \
    CCTK_REAL4_VEC const x__=(x_);              \
    CCTK_REAL4     const a__=(a_);              \
    CCTK_REAL4_VEC const x=x__;                 \
    CCTK_REAL4     const a=a__;                 \
    vec4_set(f(vec4_elt0(x),a),                 \
             f(vec4_elt1(x),a),                 \
             f(vec4_elt2(x),a),                 \
             f(vec4_elt3(x),a));                \
  })
#define K4REPL2(f,x_,y_)                        \
  ({                                            \
    CCTK_REAL4_VEC const x__=(x_);              \
    CCTK_REAL4_VEC const y__=(y_);              \
    CCTK_REAL4_VEC const x=x__;                 \
    CCTK_REAL4_VEC const y=y__;                 \
    vec4_set(f(vec4_elt0(x),vec4_elt0(y)),      \
             f(vec4_elt1(x),vec4_elt1(y)),      \
             f(vec4_elt2(x),vec4_elt2(y)),      \
             f(vec4_elt3(x),vec4_elt3(y)));     \
  })

#define k4acos(x)    K4REPL(acosf,x)
#define k4acosh(x)   K4REPL(acoshf,x)
#define k4asin(x)    K4REPL(asinf,x)
#define k4asinh(x)   K4REPL(asinhf,x)
#define k4atan(x)    K4REPL(atanf,x)
#define k4atan2(x,y) K4REPL2(atan2f,x,y)
#define k4atanh(x)   K4REPL(atanhf,x)
#define k4cos(x)     K4REPL(cosf,x)
#define k4cosh(x)    K4REPL(coshf,x)
#define k4exp(x)     K4REPL(expf,x)
#define k4log(x)     K4REPL(logf,x)
#define k4pow(x,a)   K4REPL2S(powf,x,a)
#define k4sin(x)     K4REPL(sinf,x)
#define k4sinh(x)    K4REPL(sinhf,x)
#define k4tan(x)     K4REPL(tanf,x)
#define k4tanh(x)    K4REPL(tanhf,x)

static const k4const_t k4lfalse_ = {{  0U,  0U,  0U,  0U, }};
static const k4const_t k4ltrue_  = {{ ~0U, ~0U, ~0U, ~0U, }};
#define k4lfalse (k4lfalse_.vf)
#define k4ltrue  (k4ltrue_.vf)
#define k4lnot(x)   (_mm_xor_ps(k4ltrue,x))
#define k4land(x,y) (_mm_and_ps(x,y))
#define k4lor(x,y)  (_mm_or_ps(x,y))
#define k4lxor(x,y) (_mm_xor_ps(x,y))

#ifdef __SSE4_1__
#  define k4ifthen(x,y,z) (_mm_blendv_ps(z,y,x))
#elif 0
#  ifdef __cplusplus
#    define k4signbit(x) ({ using namespace std; signbit(x); })
#  else
#    define k4signbit(x) (signbitf(x))
#  endif
#  define k4ifthen(x,y,z)                                               \
  ({                                                                    \
    CCTK_REAL4_VEC const x__=(x_);                                      \
    CCTK_REAL4_VEC const y__=(y_);                                      \
    CCTK_REAL4_VEC const z__=(z_);                                      \
    CCTK_REAL4_VEC const x=x__;                                         \
    CCTK_REAL4_VEC const y=y__;                                         \
    CCTK_REAL4_VEC const z=z__;                                         \
    vec4_set(k4signbit(vec4_elt0(x)) ? vec4_elt0(y) : vec4_elt0(z),     \
             k4signbit(vec4_elt1(x)) ? vec4_elt1(y) : vec4_elt1(z),     \
             k4signbit(vec4_elt2(x)) ? vec4_elt2(y) : vec4_elt2(z),     \
             k4signbit(vec4_elt3(x)) ? vec4_elt3(y) : vec4_elt3(z));    \
  })
#elif 0
// We don't need to shift -- the condition (mask) will be either all
// zeros or all ones
#  define k4ifthen(x_,y_,z_)                                    \
  ({                                                            \
    CCTK_REAL4_VEC const x__=(x_);                              \
    CCTK_REAL4_VEC const y__=(y_);                              \
    CCTK_REAL4_VEC const z__=(z_);                              \
    CCTK_REAL4_VEC const x=x__;                                 \
    CCTK_REAL4_VEC const y=y__;                                 \
    CCTK_REAL4_VEC const z=z__;                                 \
    CCTK_REAL4_VEC const mask =                                 \
      (__m128)_mm_srai_epi32((__m128i)x, 31);                   \
    /* (z & ~mask) | (y & mask) */                              \
    _mm_or_ps(_mm_andnot_ps(mask, z), _mm_and_ps(mask, y));     \
  })
#else
#  define k4ifthen(x_,y_,z_)                                    \
  ({                                                            \
    CCTK_REAL4_VEC const x__=(x_);                              \
    CCTK_REAL4_VEC const y__=(y_);                              \
    CCTK_REAL4_VEC const z__=(z_);                              \
    CCTK_REAL4_VEC const x=x__;                                 \
    CCTK_REAL4_VEC const y=y__;                                 \
    CCTK_REAL4_VEC const z=z__;                                 \
    /* (z & ~mask) | (y & mask)   where imask = ~mask */        \
    _mm_or_ps(_mm_and_ps(x, y), _mm_andnot_ps(x, z));           \
  })
#endif

#define k4cmpeq(x,y) (_mm_cmpeq_ps(x,y))
#define k4cmpne(x,y) (_mm_cmpneq_ps(x,y))
#define k4cmpgt(x,y) (_mm_cmpgt_ps(x,y))
#define k4cmpge(x,y) (_mm_cmpge_ps(x,y))
#define k4cmplt(x,y) (_mm_cmplt_ps(x,y))
#define k4cmple(x,y) (_mm_cmple_ps(x,y))

```

macros/vectors-8-default.h:
```
// Fallback vectorisation implementation: Do not vectorise

// We use macros here, so that we are not surprised by compilers which
// don't like to inline functions. This should also make debug builds
// (which may not inline) more efficient.



#include <assert.h>
#include <math.h>



#define vec8_architecture "scalar (no vectorisation, 64-bit precision)"

// Use CCTK_REAL8
#define CCTK_REAL8_VEC CCTK_REAL8

// Number of vector elements in a vector
#define CCTK_REAL8_VEC_SIZE 1

// Integer and boolean types corresponding to this real type
#define CCTK_INTEGER8     CCTK_REAL8
#define CCTK_BOOLEAN8     CCTK_REAL8
#define CCTK_INTEGER8_VEC CCTK_REAL8_VEC
#define CCTK_BOOLEAN8_VEC CCTK_REAL8_VEC



// Create a vector replicating a scalar
#define vec8_set1(a) (a)
// Create a vector from N scalars
#define vec8_set(a) (a)

// Access vectors elements
#define vec8_elt0(x) (x)
#define vec8_elt(x,d) (x)



// Load an aligned vector from memory
#define vec8_load(p) (p)
// Load an unaligned vector from memory
#define vec8_loadu(p) (p)

// Load a vector from memory that may or may not be aligned, as
// decided by the offset and the vector size. These functions are
// useful e.g. for loading neightbouring grid points while evaluating
// finite differencing stencils.
#define vec8_loadu_maybe(off,p) (p)
#define vec8_loadu_maybe3(off1,off2,off3,p) (p)

// Aligned store
#define vec8_store(p,x) ((p)=(x))
// Unaligned store
#define vec8_store_nta(p,x) ((p)=(x))

#define vec8_store_partial_prepare(i,imin,imax) ((void)0)
#define vec8_store_nta_partial(p,x) (vec8_store_nta(p,x))
// Store the n lower elements of a vector to memory
#define vec8_store_nta_partial_lo(p,x,n) (assert(0))
// Store the n higher elements of a vector into memory. This stores
// the vector elements into memory locations as if element 0 were
// stored at p.
#define vec8_store_nta_partial_hi(p,x,n) (assert(0))
#define vec8_store_nta_partial_mid(p,x,nlo,nhi) (assert(0))



// Operators
#define k8neg(x) (-(x))

#define k8add(x,y) ((x)+(y))
#define k8sub(x,y) ((x)-(y))
#define k8mul(x,y) ((x)*(y))
#define k8div(x,y) ((x)/(y))

// Fused multiply-add, defined as [+-]x*y[+-]z
#define k8madd(x,y,z)  (+(x)*(y)+(z))
#define k8msub(x,y,z)  (+(x)*(y)-(z))
#define k8nmadd(x,y,z) (-(x)*(y)-(z))
#define k8nmsub(x,y,z) (-(x)*(y)+(z))

// Functions
#define k8acos(x)       (acos(x))
#define k8acosh(x)      (acosh(x))
#define k8asin(x)       (asin(x))
#define k8asinh(x)      (asinh(x))
#define k8atan(x)       (atan(x))
#define k8atan2(x,y)    (atan2(x,y))
#define k8atanh(x)      (atanh(x))
#define k8copysign(x,y) (copysign(x,y))
#define k8cos(x)        (cos(x))
#define k8cosh(x)       (cosh(x))
#define k8exp(x)        (exp(x))
#define k8fabs(x)       (fabs(x))
#define k8fmax(x,y)     (fmax(x,y))
#define k8fmin(x,y)     (fmin(x,y))
#define k8fnabs(x)      (-fabs(x))
#define k8log(x)        (log(x))
#define k8pow(x,a)      (pow(x,a))
#define k8sin(x)        (sin(x))
#define k8sinh(x)       (sinh(x))
#define k8sqrt(x)       (sqrt(x))
#define k8tan(x)        (tan(x))
#define k8tanh(x)       (tanh(x))

#define k8sgn(x_)                                                       \
  ({                                                                    \
    CCTK_REAL x__=(x_);                                                 \
    CCTK_REAL x=x__;                                                    \
    x==(CCTK_REAL)0.0 ? (CCTK_REAL)0.0 : std::copysign((CCTK_REAL)1.0, x); \
  })
#define k8signbit(x) (std::signbit(x))

#define k8l2r(x_) ({ CCTK_INT8 x__=(x_); CCTK_INT8 x=x__; *(CCTK_REAL8*)&x; })
#define k8r2l(x_) ({ CCTK_REAL8 x__=(x_); CCTK_REAL8 x=x__; *(CCTK_INT8*)&x; })
#define k8lfalse k8l2r(0)
#define k8ltrue  k8l2r(1)
#define k8lnot(x)   k8l2r(!k8r2l(x))
#define k8land(x,y) k8l2r(k8r2l(x) && k8r2l(y))
#define k8lor(x,y)  k8l2r(k8r2l(x) || k8r2l(y))
#define k8lxor(x,y) k8l2r(!k8r2l(x) != !k8r2l(y))

#define k8ifthen(x,y,z) (k8r2l(x)?(y):(z))

#define k8cmpeq(x,y) k8l2r((x)==(y))
#define k8cmpne(x,y) k8l2r((x)!=(y))
#define k8cmpgt(x,y) k8l2r((x)>(y))
#define k8cmpge(x,y) k8l2r((x)>=(y))
#define k8cmplt(x,y) k8l2r((x)<(y))
#define k8cmple(x,y) k8l2r((x)<=(y))

```

macros/vectors-8-SSE2.h:
```
// Vectorise using Intel's or AMD's SSE2

// Use the type __m128d directly, without introducing a wrapper class
// Use macros instead of inline functions



#include <assert.h>
#include <math.h>

#include <emmintrin.h>
#ifdef __SSE4_1__
// Intel's SSE 4.1
#  include <smmintrin.h>
#endif
#ifdef __SSE4A__
// AMD's SSE 4a
#  include <ammintrin.h>

// Intel compilers don't support SSE 4a. Here is how we can implement
// these instructions in assembler instead:

// inline void __attribute__((__always_inline__))
//   _mm_stream_sd (double *p, __m128d x)
// {
//   asm ("movntsd %[x],%[p]" : "=m" (*p) : [p] "m" (*p), [x] "x" (x));
// }

#endif
#ifdef __FMA4__
#  include <fma4intrin.h>
#endif



#ifdef __SSE4_1__
#  define vec8_architecture_SSE4_1 "+SSE4.1"
#else
#  define vec8_architecture_SSE4_1 ""
#endif
#ifdef __SSE4A__
#  define vec8_architecture_SSE4a "+SSE4A"
#else
#  define vec8_architecture_SSE4a ""
#endif
#ifdef __FMA4__
#  define vec8_architecture_FMA4 "+FMA4"
#else
#  define vec8_architecture_FMA4 ""
#endif
#define vec8_architecture "SSE2" vec8_architecture_SSE4_1 vec8_architecture_SSE4a vec8_architecture_FMA4 " (64-bit precision)"



// Vector type corresponding to CCTK_REAL
#define CCTK_REAL8_VEC __m128d

// Number of vector elements in a CCTK_REAL_VEC
#define CCTK_REAL8_VEC_SIZE 2

// Integer and boolean types corresponding to this real type
#define CCTK_INTEGER8     CCTK_REAL8
#define CCTK_BOOLEAN8     CCTK_REAL8
#define CCTK_INTEGER8_VEC CCTK_REAL8_VEC
#define CCTK_BOOLEAN8_VEC CCTK_REAL8_VEC



union k8const_t {
  long long i[2];
  double    f[2];
  __m128i   vi;
  __m128d   vf;
};

#define K8_IMIN ((long long)0x8000000000000000ULL)



// Create vectors, extract vector elements

#define vec8_set1(a)  (_mm_set1_pd(a))
#define vec8_set(a,b) (_mm_set_pd(b,a)) // note reversed arguments

// original order is 01
#define vec8_swap10(x_)                         \
  ({                                            \
    CCTK_REAL8_VEC const x__=(x_);              \
    CCTK_REAL8_VEC const x=x__;                 \
    _mm_shuffle_pd(x,x, _MM_SHUFFLE2(0,1));     \
  })

#define vec8_elt0(x) (((CCTK_REAL8 const*)&(x))[0])
#define vec8_elt1(x) (((CCTK_REAL8 const*)&(x))[1])
#define vec8_elt(x,d) (((CCTK_REAL8 const*)&(x))[d])



// Load and store vectors

// Load a vector from memory (aligned and unaligned); this loads from
// a reference to a scalar
#define vec8_load(p)  (_mm_load_pd(&(p)))
#define vec8_loadu(p) (_mm_loadu_pd(&(p)))
#if ! VECTORISE_ALWAYS_USE_ALIGNED_LOADS
#  define vec8_load_off1(p) vec_loadu(p)
#else
#  define vec8_load_off1(p_)                                    \
  ({                                                            \
    CCTK_REAL8 const& p__=(p_);                                 \
    CCTK_REAL8 const& p=p__;                                    \
    _mm_shuffle_pd(vec8_load((&p)[-1]),                         \
                   vec8_load((&p)[+1]), _MM_SHUFFLE2(0,1));     \
  })
#endif

// Load a vector from memory that may or may not be aligned, as
// decided by the offset off and the vector size
#if VECTORISE_ALWAYS_USE_UNALIGNED_LOADS
// Implementation: Always use unaligned load
#  define vec8_loadu_maybe(off,p)             vec8_loadu(p)
#  define vec8_loadu_maybe3(off1,off2,off3,p) vec8_loadu(p)
#else
#  define vec8_loadu_maybe(off,p_)              \
  ({                                            \
    CCTK_REAL8 const& p__=(p_);                 \
    CCTK_REAL8 const& p=p__;                    \
    (off) % CCTK_REAL8_VEC_SIZE == 0 ?          \
      vec8_load(p) :                            \
      vec8_load_off1(p);                        \
  })
#  if VECTORISE_ALIGNED_ARRAYS
// Assume all array x sizes are multiples of the vector size
#    define vec8_loadu_maybe3(off1,off2,off3,p) \
  vec8_loadu_maybe(off1,p)
#  else
#    define vec8_loadu_maybe3(off1,off2,off3,p_)        \
  ({                                                    \
    CCTK_REAL8 const& p__=(p_);                         \
    CCTK_REAL8 const& p=p__;                            \
    ((off2) % CCTK_REAL8_VEC_SIZE != 0 or               \
     (off3) % CCTK_REAL8_VEC_SIZE != 0) ?               \
      vec8_loadu(p) :                                   \
      vec8_loadu_maybe(off1,p);                         \
  })
#  endif
#endif

// Store a vector to memory (aligned and non-temporal); this stores to
// a reference to a scalar
#define vec8_store(p,x)  (_mm_store_pd(&(p),x))
#define vec8_storeu(p,x) (_mm_storeu_pd(&(p),x))
#if ! VECTORISE_STREAMING_STORES
#  define vec8_store_nta(p,x) vec8_store(p,x)
#else
#  define vec8_store_nta(p,x) (_mm_stream_pd(&(p),x))
#endif

// Store a partial vector (aligned and non-temporal)
#define vec8_store_partial_prepare(i,imin,imax)                 \
  bool const v8stp_lo = (i)>=(imin);                            \
  bool const v8stp_hi = (i)+CCTK_REAL_VEC_SIZE-1<(imax)
#if VECTORISE_STREAMING_STORES && defined(__SSE4A__)
#  define vec8_store_nta_partial(p_,x_)                         \
  ({                                                            \
    CCTK_REAL8& p__=(p_);                                       \
    CCTK_REAL8& p=p__;                                          \
    CCTK_REAL8_VEC const x__=(x_);                              \
    CCTK_REAL8_VEC const x=x__;                                 \
    if (CCTK_BUILTIN_EXPECT(v8stp_lo and v8stp_hi, true)) {     \
      vec8_store_nta(p,x);                                      \
    } else if (v8stp_lo) {                                      \
      _mm_stream_sd(&p,x);                                      \
    } else if (v8stp_hi) {                                      \
      _mm_stream_sd(&p+1, vec8_swap10(x));                      \
    }                                                           \
  })
#else
#  define vec8_store_nta_partial(p_,x_)                         \
  ({                                                            \
    CCTK_REAL8& p__=(p_);                                       \
    CCTK_REAL8& p=p__;                                          \
    CCTK_REAL8_VEC const x__=(x_);                              \
    CCTK_REAL8_VEC const x=x__;                                 \
    if (CCTK_BUILTIN_EXPECT(v8stp_lo and v8stp_hi, true)) {     \
      vec8_store_nta(p,x);                                      \
    } else if (v8stp_lo) {                                      \
      _mm_storel_pd(&p,x);                                      \
    } else if (v8stp_hi) {                                      \
      _mm_storeh_pd(&p+1,x);                                    \
    }                                                           \
  })
#endif

// Store a lower or higher partial vector (aligned and non-temporal)
#if ! VECTORISE_STREAMING_STORES
#  define vec8_store_nta_partial_lo(p,x,n) (_mm_storel_pd(&(p),x))
#  define vec8_store_nta_partial_hi(p,x,n) (_mm_storeh_pd(&(p)+1,x))
#else
#  if defined(__SSE4A__)
#    define vec8_store_nta_partial_lo(p,x,n) (_mm_stream_sd(&(p),x))
#    define vec8_store_nta_partial_hi(p,x,n)    \
  (_mm_stream_sd(&(p)+1, vec8_swap10(x)))
#  else
// TODO: use clflush once a whole cache line has been written (cache
// lines are usually larger than the CPU vector size)
#    define vec8_store_nta_partial_lo(p_,x,n)   \
  ({                                            \
    CCTK_REAL8& p__=(p_);                       \
    CCTK_REAL8& p=p__;                          \
    _mm_storel_pd(&p,x);                        \
    /* _mm_clflush(&p); */                      \
  })
#    define vec8_store_nta_partial_hi(p_,x,n)   \
  ({                                            \
    CCTK_REAL8& p__=(p_);                       \
    CCTK_REAL8& p=p__;                          \
    _mm_storeh_pd(&p+1,x);                      \
    /* _mm_clflush(&p+1); */                    \
  })
#  endif
#endif
#if 0
// This is slower; we would need a non-temporal read
#define vec8_store_nta_partial_lo(p,x,n)        \
  vec8_store_nta(p, _mm_loadh_pd(x,&(p)+1))
#define vec8_store_nta_partial_hi(p,x,n)        \
  vec8_store_nta(p, _mm_loadl_pd(x,&(p)))
#endif
#define vec8_store_nta_partial_mid(p,x,nlo,nhi) assert(0)



// Functions and operators

static const k8const_t k8sign_mask = {{ K8_IMIN, K8_IMIN, }};

// Operators

// #define k8inot(x) (_mm_xor_si128(k8all_mask,x))
// 
// #define k8iand(x,y) (_mm_and_si128(x,y))
// #define k8ior(x,y)  (_mm_or_si128(x,y))
// #define k8ixor(x,y) (_mm_xor_si128(x,y))
// 
// #define k8ineg(x) (_mm_xor_pd(k8sign_mask,x))
// 
// #define k8iadd(x,y) (_mm_add_epi64(x,y))
// #define k8isub(x,y) (_mm_sub_epi64(x,y))
// 
// #define k8not(x) (_mm_xor_pd(k8all_mask,x))
// 
// #define k8and(x,y) (_mm_and_pd(x,y))
// #define k8or(x,y)  (_mm_or_pd(x,y))
// #define k8xor(x,y) (_mm_xor_pd(x,y))

#define k8neg(x) (_mm_xor_pd(k8sign_mask.vf,x))

#define k8add(x,y) (_mm_add_pd(x,y))
#define k8sub(x,y) (_mm_sub_pd(x,y))
#define k8mul(x,y) (_mm_mul_pd(x,y))
#define k8div(x,y) (_mm_div_pd(x,y))

// Fused multiply-add, defined as [+-]x*y[+-]z
#ifdef __FMA4__
#  define k8madd(x,y,z)  (_mm_macc_pd(x,y,z))
#  define k8msub(x,y,z)  (_mm_msub_pd(x,y,z))
#  define k8nmadd(x,y,z) (_mm_nmsub_pd(x,y,z))
#  define k8nmsub(x,y,z) (_mm_nmacc_pd(x,y,z))
#else
#  define k8madd(x,y,z)  (k8add(k8mul(x,y),z))
#  define k8msub(x,y,z)  (k8sub(k8mul(x,y),z))
#  define k8nmadd(x,y,z) (k8sub(k8neg(z),k8mul(x,y)))
#  define k8nmsub(x,y,z) (k8sub(z,k8mul(x,y)))
#endif

// Cheap functions
#define k8copysign(x,y)                                         \
  (_mm_or_pd(_mm_andnot_pd(k8sign_mask.vf,x),                   \
             _mm_and_pd(k8sign_mask.vf,y)))
#define k8fabs(x)   (_mm_andnot_pd(k8sign_mask.vf,x))
#define k8fmax(x,y) (_mm_max_pd(x,y))
#define k8fmin(x,y) (_mm_min_pd(x,y))
#define k8fnabs(x)  (_mm_or_pd(k8sign_mask.vf,x))
#define k8sgn(x_)                                                       \
  ({                                                                    \
    CCTK_REAL_VEC const x__=(x_);                                       \
    CCTK_REAL_VEC const x=x__;                                          \
    CCTK_REAL_VEC const iszero = _mm_cmpeq_pd(vec8_set1(0.0), x);       \
    CCTK_REAL_VEC const sign = _mm_and_pd(k8sign_mask.vf, x);           \
    CCTK_REAL_VEC const signedone = _mm_or_pd(vec8_set1(1.0), sign);    \
    k8ifthen(iszero, vec8_set1(0.0), signedone);                        \
  })
#define k8sqrt(x)   (_mm_sqrt_pd(x))

// Expensive functions
#define K8REPL(f,x_)                            \
  ({                                            \
    CCTK_REAL8_VEC const x__=(x_);              \
    CCTK_REAL8_VEC const x=x__;                 \
    vec8_set(f(vec8_elt0(x)),                   \
             f(vec8_elt1(x)));                  \
  })
#define K8REPL2S(f,x_,a_)                       \
  ({                                            \
    CCTK_REAL8_VEC const x__=(x_);              \
    CCTK_REAL8     const a__=(a_);              \
    CCTK_REAL8_VEC const x=x__;                 \
    CCTK_REAL8     const a=a__;                 \
    vec8_set(f(vec8_elt0(x),a),                 \
             f(vec8_elt1(x),a));                \
  })
#define K8REPL2(f,x_,y_)                        \
  ({                                            \
    CCTK_REAL8_VEC const x__=(x_);              \
    CCTK_REAL8_VEC const y__=(y_);              \
    CCTK_REAL8_VEC const x=x__;                 \
    CCTK_REAL8_VEC const y=y__;                 \
    vec8_set(f(vec8_elt0(x),vec8_elt0(y)),      \
             f(vec8_elt1(x),vec8_elt1(y)));     \
  })

#define k8acos(x)    K8REPL(acos,x)
#define k8acosh(x)   K8REPL(acosh,x)
#define k8asin(x)    K8REPL(asin,x)
#define k8asinh(x)   K8REPL(asinh,x)
#define k8atan(x)    K8REPL(atan,x)
#define k8atan2(x,y) K8REPL2(atan2,x,y)
#define k8atanh(x)   K8REPL(atanh,x)
#define k8cos(x)     K8REPL(cos,x)
#define k8cosh(x)    K8REPL(cosh,x)
#define k8exp(x)     K8REPL(exp,x)
#define k8log(x)     K8REPL(log,x)
#define k8pow(x,a)   K8REPL2S(pow,x,a)
#define k8sin(x)     K8REPL(sin,x)
#define k8sinh(x)    K8REPL(sinh,x)
#define k8tan(x)     K8REPL(tan,x)
#define k8tanh(x)    K8REPL(tanh,x)

static const k8const_t k8lfalse_ = {{ +0LL, +0LL, }};
static const k8const_t k8ltrue_  = {{ -1LL, -1LL, }};
#define k8lfalse (k8lfalse_.vf)
#define k8ltrue  (k8ltrue_.vf)
#define k8lnot(x)   (_mm_xor_pd(k8ltrue,x))
#define k8land(x,y) (_mm_and_pd(x,y))
#define k8lor(x,y)  (_mm_or_pd(x,y))
#define k8lxor(x,y) (_mm_xor_pd(x,y))

#ifdef __SSE4_1__
#  define k8ifthen(x,y,z) (_mm_blendv_pd(z,y,x))
#elif 0
// This is slow (but this is what Intel/PGI produce by themselves)
#  define k8ifthen(x_,y_,z_)                    \
  ({                                            \
    CCTK_REAL8_VEC const x__=(x_);              \
    CCTK_REAL8_VEC const y__=(y_);              \
    CCTK_REAL8_VEC const z__=(z_);              \
    CCTK_REAL8_VEC const x=x__;                 \
    CCTK_REAL8_VEC const y=y__;                 \
    CCTK_REAL8_VEC const z=z__;                 \
    int const m = _mm_movemask_pd(x);           \
    CCTK_REAL8_VEC r;                           \
    switch (m) {                                \
    case 0: r = y; break;                       \
    case 1: r = _mm_move_sd(y,z); break;        \
    case 2: r = _mm_move_sd(z,y); break;        \
    case 3: r = z; break;                       \
    }                                           \
    r;                                          \
  })
#elif 0
#  ifdef __cplusplus
#    define k8signbit(x) ({ using namespace std; signbit(x); })
#  else
#    define k8signbit(x) (signbit(x))
#  endif
#  define k8ifthen(x_,y_,z_)                                            \
  ({                                                                    \
    CCTK_REAL8_VEC const x__=(x_);                                      \
    CCTK_REAL8_VEC const y__=(y_);                                      \
    CCTK_REAL8_VEC const z__=(z_);                                      \
    CCTK_REAL8_VEC const x=x__;                                         \
    CCTK_REAL8_VEC const y=y__;                                         \
    CCTK_REAL8_VEC const z=z__;                                         \
    vec8_set(k8signbit(vec8_elt0(x)) ? vec8_elt0(y) : vec8_elt0(z),     \
             k8signbit(vec8_elt1(x)) ? vec8_elt1(y) : vec8_elt1(z));    \
  })
#elif 0
// We don't need to shift -- the condition (mask) will be either all
// zeros or all ones
static const k8const_t k8ione  = {{ 0x1ULL, 0x1ULL, }};
#  define k8ifthen(x_,y_,z_)                                            \
  ({                                                                    \
    CCTK_REAL8_VEC const x__=(x_);                                      \
    CCTK_REAL8_VEC const y__=(y_);                                      \
    CCTK_REAL8_VEC const z__=(z_);                                      \
    CCTK_REAL8_VEC const x=x__;                                         \
    CCTK_REAL8_VEC const y=y__;                                         \
    CCTK_REAL8_VEC const z=z__;                                         \
    /* there is no _mm_srai_epi64(x, 63); we therefore calculate srli(x)-1 */ \
    __m128i const x_int = *(__m128i const*)&x;                          \
    __m128i const imask_int =                                           \
      _mm_sub_epi64(_mm_srli_epi64(x_int, 63), k8ione.vi);              \
    CCTK_REAL8_VEC const imask = *(CCTK_REAL8_VEC const*)&imask_int;    \
    /* (z & ~mask) | (y & mask)   where imask = ~mask */                \
    _mm_or_pd(_mm_and_pd(imask, z), _mm_andnot_pd(imask, y));           \
  })
#else
#  define k8ifthen(x_,y_,z_)                                    \
  ({                                                            \
    CCTK_REAL8_VEC const x__=(x_);                              \
    CCTK_REAL8_VEC const y__=(y_);                              \
    CCTK_REAL8_VEC const z__=(z_);                              \
    CCTK_REAL8_VEC const x=x__;                                 \
    CCTK_REAL8_VEC const y=y__;                                 \
    CCTK_REAL8_VEC const z=z__;                                 \
    /* (z & ~mask) | (y & mask)   where imask = ~mask */        \
    _mm_or_pd(_mm_and_pd(x, y), _mm_andnot_pd(x, z));           \
  })
#endif

#define k8cmpeq(x,y) (_mm_cmpeq_pd(x,y))
#define k8cmpne(x,y) (_mm_cmpneq_pd(x,y))
#define k8cmpgt(x,y) (_mm_cmpgt_pd(x,y))
#define k8cmpge(x,y) (_mm_cmpge_pd(x,y))
#define k8cmplt(x,y) (_mm_cmplt_pd(x,y))
#define k8cmple(x,y) (_mm_cmple_pd(x,y))

```

indirect/vectors-intel.hh:
```
#include <cmath>
#include <cstdlib>
using namespace std;



#if defined(__SSE__)            // SSE (Intel)

#include <xmmintrin.h>

template<>
struct vec_T<float> {
  typedef float scalar_t;
  typedef __m128 impl_t;
  impl_t v;
  
  static inline size_t size()
  {
    return sizeof(impl_t)/sizeof(scalar_t);
  }
  
  inline vec_t ()
  {
  }
  inline vec_t (scalar_t const& a)
    : v(_mm_set1_ps(a))
  {
  }
  inline vec_t (scalar_t const& a0, scalar_t const& a1,
                scalar_t const& a2, scalar_t const& a3)
    : v(_mm_set_ps(a3,a2,a1,a0)) // reverse order!
  {
  }
  
  inline vec_t (impl_t const& w)
    : v(w)
  {
  }
  inline operator impl_t ()
  {
    return v;
  }
  
private:
  static inline scalar_t elt0 (impl_t const& v)
  {
    return _mm_cvtss_f32(v);    // this is a no-op
  }
public:
  inline scalar_t operator[] (size_t const d)
  {
    switch (d) {
    case 0: return elt0(v);
    case 1: return elt0(_mm_shuffle_ps(v,v,_MM_SHUFFLE(1,0,3,2)));
    case 2: return elt0(_mm_unpackhi_ps(v,v));
    case 3: return elt0(_mm_shuffle_ps(v,v,_MM_SHUFFLE(3,2,1,0)));
    }
  }
  
  static inline vec_t load (scalar_t const& a)
  {
    return _mm_load_ps(&a);
  }
  static inline vec_t loadu (scalar_t const& a)
  {
    return _mm_loadu_ps(&a);
  }
  // Load a vector from memory that may or may not be aligned, as
  // decided by the offset and the vector size
  static inline vec_t loadu_maybe (int const off, scalar_t const& p)
  {
    if (off % size() == 0) {
      return load(p);
    } else {
      return loadu(p);
    }
  }
  static inline vec_t loadu_maybe3 (int const off0, int const off1,
                                    int const off2,
                                    scalar_t const& p)
  {
    if (off0 % size() == 0 and off1 % size() == 0 and off2 % size() == 0) {
      return load(p);
    } else {
      return loadu(p);
    }
  }
  inline void store (scalar_t& p) const
  {
    _mm_store_ps(&p,v);
  }
  inline void storeu (scalar_t& p) const
  {
    _mm_storeu_ps(&p,v);
  }
  inline void store_nta (scalar_t& p) const
  {
    _mm_stream_ps(&p,v);
  }
  inline void store_nta_partial_lo (scalar_t& p, size_t const cnt) const
  {
    switch (cnt) {
    case 4: store_nta(p); break;
    case 3: (&p)[2]=v[2];
    case 2: (&p)[1]=v[1];
    case 1: (&p)[0]=v[0];
    }
  }
  inline void store_nta_partial_hi (scalar_t& p, size_t const cnt) const
  {
    switch (cnt) {
    case 4: store_nta(p); break;
    case 3: (&p)[1]=v[1];
    case 2: (&p)[2]=v[2];
    case 1: (&p)[3]=v[3];
    }
  }
  
  inline vec_t operator+ () const
  {
    return +v;
  }
  inline vec_t operator- () const
  {
    return -v;
  }
  inline vec_t operator+ (vec_t const& x) const
  {
    return v+x.v;
  }
  inline vec_t operator- (vec_t const& x) const
  {
    return v-x.v;
  }
  inline vec_t operator* (vec_t const& x) const
  {
    return v*x.v;
  }
  inline vec_t operator/ (vec_t const& x) const
  {
    return v/x.v;
  }
  inline vec_t& operator+= (vec_t const& x)
  {
    return *this=*this+x;
  }
  inline vec_t& operator-= (vec_t const& x)
  {
    return *this=*this+x;
  }
  inline vec_t& operator*= (vec_t const& x)
  {
    return *this=*this-x;
  }
  inline vec_t& operator/= (vec_t const& x)
  {
    return *this=/this+x;
  }
};

template<typename T>
vec_t<T> exp (vec_t<T> const& x)
{
  return vec_t(exp(x.v[0]), exp(x.v[1]), exp(x.v[2]), exp(x.v[3]));
}
template<typename T>
vec_t<T> fabs (vec_t<T> const& x)
{
  return _mm_and_ps(v,_mm_set1_pi32(0x7fffffffU));
}
template<typename T>
vec_t<T> fmax (vec_t<T> const& x, vec_t<T> const& y)
{
  return _mm_max_ps(x.v, y.v);
}
template<typename T>
vec_t<T> fmin (vec_t<T> const& x, vec_t<T> const& y)
{
  return _mm_min_ps(x.v, y.v);
}
template<typename T>
vec_t<T> ifthen (bool const b, vec_t<T> const& x, vec_t<T> const& y)
{
  return b ? x : y;
}
vec_t<T> log (vec_t<T> const& x)
{
  return vec_t(log(x.v[0]), log(x.v[1]), log(x.v[2]), log(x.v[3]));
}
template<typename T>
vec_t<T> pow (vec_t<T> const& x, typename vec_t<T>::scalar_t const& a)
{
  return vec_t(pow(x.v[0],a), pow(x.v[1],a), pow(x.v[2],a), pow(x.v[3],a));
}
vec_t<T> sqrt (vec_t<T> const& x)
{
  return _mm_sqrt_ps(x.v);
}

#endif



#if defined(__SSE2__)            // SSE2 (Intel)

#include <emmintrin.h>

template<>
struct vec_T<double> {
  typedef double scalar_t;
  typedef __m128d impl_t;
  impl_t v;
  
  static inline size_t size()
  {
    return sizeof(impl_t)/sizeof(scalar_t);
  }
  
  inline vec_t ()
  {
  }
  inline vec_t (scalar_t const& a)
    : v(_mm_set1_pd(a))
  {
  }
  inline vec_t (scalar_t const& a0, scalar_t const& a1)
    : v(_mm_set_pd(a1,a0))      // reverse order!
  {
  }
  
  inline vec_t (impl_t const& w)
    : v(w)
  {
  }
  inline operator impl_t ()
  {
    return v;
  }
  
private:
  static inline scalar_t elt0 (impl_t const& v)
  {
    return _mm_cvtss_f64(v);    // this is a no-op
  }
public:
  inline scalar_t operator[] (size_t const d)
  {
    switch (d) {
    case 0: return elt0(v);
    case 1: return elt0(_mm_unpackhi_pd(v,v));
    }
  }
  
  static inline vec_t load (scalar_t const& a)
  {
    return _mm_load_pd(&a);
  }
  static inline vec_t loadu (scalar_t const& a)
  {
    return _mm_loadu_pd(&a);
  }
  // Load a vector from memory that may or may not be aligned, as
  // decided by the offset and the vector size
  static inline vec_t loadu_maybe (int const off, scalar_t const& p)
  {
    if (off % size() == 0) {
      return load(p);
    } else {
      return loadu(p);
    }
  }
  static inline vec_t loadu_maybe3 (int const off0, int const off1,
                                    int const off2,
                                    scalar_t const& p)
  {
    if (off0 % size() == 0 and off1 % size() == 0 and off2 % size() == 0) {
      return load(p);
    } else {
      return loadu(p);
    }
  }
  inline void store (scalar_t& p) const
  {
    _mm_store_pd(&p,v);
  }
  inline void storeu (scalar_t& p) const
  {
    _mm_storeu_pd(&p,v);
  }
  inline void store_nta (scalar_t& p) const
  {
    _mm_stream_pd(&p,v);
  }
  inline void store_nta_partial_lo (scalar_t& p, size_t const cnt) const
  {
    switch (cnt) {
    case 2: store_nta(p); break;
    case 1: (&p)[0]=v[0];
    }
  }
  inline void store_nta_partial_hi (scalar_t& p, size_t const cnt) const
  {
    switch (cnt) {
    case 2: store_nta(p); break;
    case 1: (&p)[1]=v[1];
    }
  }
  
  inline vec_t operator+ () const
  {
    return +v;
  }
  inline vec_t operator- () const
  {
    return -v;
  }
  inline vec_t operator+ (vec_t const& x) const
  {
    return v+x.v;
  }
  inline vec_t operator- (vec_t const& x) const
  {
    return v-x.v;
  }
  inline vec_t operator* (vec_t const& x) const
  {
    return v*x.v;
  }
  inline vec_t operator/ (vec_t const& x) const
  {
    return v/x.v;
  }
  inline vec_t& operator+= (vec_t const& x)
  {
    return *this=*this+x;
  }
  inline vec_t& operator-= (vec_t const& x)
  {
    return *this=*this+x;
  }
  inline vec_t& operator*= (vec_t const& x)
  {
    return *this=*this-x;
  }
  inline vec_t& operator/= (vec_t const& x)
  {
    return *this=/this+x;
  }
};

template<typename T>
vec_t<T> exp (vec_t<T> const& x)
{
  return vec_t(exp(x.v[0]), exp(x.v[1]));
}
template<typename T>
vec_t<T> fabs (vec_t<T> const& x)
{
  return _mm_and_pd(v,_mm_set1_epi64(0x7fffffffffffffffULL));
}
template<typename T>
vec_t<T> fmax (vec_t<T> const& x, vec_t<T> const& y)
{
  return _mm_max_pd(x.v, y.v);
}
template<typename T>
vec_t<T> fmin (vec_t<T> const& x, vec_t<T> const& y)
{
  return _mm_min_pd(x.v, y.v);
}
template<typename T>
vec_t<T> ifthen (bool const b, vec_t<T> const& x, vec_t<T> const& y)
{
  return b ? x : y;
}
vec_t<T> log (vec_t<T> const& x)
{
  return vec_t(log(x.v[0]), log(x.v[1]));
}
template<typename T>
vec_t<T> pow (vec_t<T> const& x, typename vec_t<T>::scalar_t const& a)
{
  return vec_t(pow(x.v[0],a), pow(x.v[1],a));
}
vec_t<T> sqrt (vec_t<T> const& x)
{
  return _mm_sqrt_pd(x.v);
}

#endif

```

indirect/vectors-default.hh:
```
using namespace std;



// A class template that provides a vectorised type for the underlying
// scalar type T. This implementation does "nothing", i.e. just
// provides a "vector" class with a vector size of 1, forwarding all
// operations to the scalar type.
//
// This implementation uses small integers in several places, e.g. in
// the [] operator. This is efficient only if these integers are
// compile time constants, so that the compiler can remove the
// corresponding if and switch statements.
template<typename T>
struct vec_t {
  
  // Names for the underlying scalar type, and for the vector type
  // used to implement this class. For example, with SSE2, it would be
  // scalar_t=double, and impl_t=__m128d.
  typedef T scalar_t;
  typedef T impl_t;
  
  // The payload -- the actual vector content
  impl_t v;
  
  // Vector size (number of elements)
  static inline size_t size()
  {
    return sizeof(impl_t)/sizeof(scalar_t);
  }
  
  // Constructors
  inline vec_t ()
  {
  }
  inline vec_t (scalar_t const& a)
    : v(a)
  {
  }
  
  // Convert to the implementation vector type
  inline operator impl_t ()
  {
    return v;
  }
  
  // Access individual vector elements
  inline scalar_t operator[] (size_t const d) const
  {
    return v;
  }
  
  // Load vectors from memory. For convenience when using this class,
  // these accept references to the scalar type instead of pointers to
  // the vector type. These routines are static members of the class,
  // so that they can be used as VEC::load(p); if they were
  // stand-alone functions, one would have to write load<SCALAR>(p)
  // instead.
  //
  // Aligned load
  static inline vec_t load (scalar_t const& p)
  {
    return p;
  }
  // Unaligned load
  static inline vec_t loadu (scalar_t const& p)
  {
    return p;
  }
  // Load a vector from memory that may or may not be aligned, as
  // decided by the offset and the vector size. These functions are
  // useful e.g. for loading neightbouring grid points while
  // evaluating finite differencing stencils.
  static inline vec_t loadu_maybe (int const off, scalar_t const& p)
  {
    return p;
  }
  static inline vec_t loadu_maybe3 (int const off0, int const off1,
                                    int const off2,
                                    scalar_t const& p)
  {
    return p;
  }
};

// Store vectors to memory. These routines are stand-alone functions,
// so that they can be used as vec_store(p,x); if they were class
// members, one would have to write x.store(p) instead, or possibly
// VEC::store(p,x).
//
// Aligned store
template<typename T>
inline void vec_store (typename vec_t<T>::scalar_t& p, vec_t<T> const& x)
{
  p=x.v;
}
// Unaligned store
template<typename T>
inline void vec_storeu (typename vec_t<T>::scalar_t& p, vec_t<T> const& x)
{
  p=x.v;
}
// Non-temporal store, i.e. a store that bypasses the cache
template<typename T>
inline void vec_store_nta (typename vec_t<T>::scalar_t& p, vec_t<T> const& x)
{
  p=x.v;
}
// Store the cnt lower elements of a vector, bypassing the cache if
// possible
template<typename T>
inline void vec_store_nta_partial_lo (typename vec_t<T>::scalar_t& p,
                                      vec_t<T> const& x,
                                      size_t const cnt)
{
  assert(0);
}
// Store the cnt higher elements of a vector, bypassing the cache if
// possible. This stores the vector elements into memory locations as
// if element 0 were stored at p.
template<typename T>
inline void vec_store_nta_partial_hi (typename vec_t<T>::scalar_t& p,
                                      vec_t<T> const& x,
                                      size_t const cnt)
{
  assert(0);
}

template<typename T>
inline vec_t<T> operator+ (vec_t<T> const& x)
{
  return +x.v;
}
template<typename T>
inline vec_t<T> operator- (vec_t<T> const& x)
{
  return -x.v;
}

template<typename T>
inline vec_t<T> operator+ (vec_t<T> const& x, vec_t<T> const& y)
{
  return x.v + y.v;
}
template<typename T>
inline vec_t<T> operator- (vec_t<T> const& x, vec_t<T> const& y)
{
  return x.v - y.v;
}
template<typename T>
inline vec_t<T> operator* (vec_t<T> const& x, vec_t<T> const& y)
{
  return x.v * y.v;
}
template<typename T>
inline vec_t<T> operator/ (vec_t<T> const& x, vec_t<T> const& y)
{
  return x.v / y.v;
}

template<typename T>
inline vec_t<T>& operator+= (vec_t<T>& x, vec_t<T> const& y)
{
  x.v += y.v;
  return x;
}
template<typename T>
inline vec_t<T>& operator-= (vec_t<T>& x, vec_t<T> const& y)
{
  x.v -= y.v;
  return x;
}
template<typename T>
inline vec_t<T>& operator*= (vec_t<T>& x, vec_t<T> const& y)
{
  x.v *= y.v;
  return x;
}
template<typename T>
inline vec_t<T>& operator/= (vec_t<T>& x, vec_t<T> const& y)
{
  x.v /= y.v;
  return x;
}

template<typename T>
inline vec_t<T> exp (vec_t<T> const& x)
{
  return exp(x.v);
}
template<typename T>
inline vec_t<T> fabs (vec_t<T> const& x)
{
  return fabs(x.v);
}
template<typename T>
inline vec_t<T> log (vec_t<T> const& x)
{
  return log(x.v);
}
template<typename T>
inline vec_t<T> sqrt (vec_t<T> const& x)
{
  return sqrt(x.v);
}

template<typename T>
inline vec_t<T> fmax (vec_t<T> const& x, vec_t<T> const& y)
{
  return fmax(x.v, y.v);
}
template<typename T>
inline vec_t<T> fmin (vec_t<T> const& x, vec_t<T> const& y)
{
  return fmin(x.v, y.v);
}
template<typename T>
inline vec_t<T> pow (vec_t<T> const& x, typename vec_t<T>::scalar_t const& a)
{
  return pow(x.v, a);
}
template<typename T>
inline vec_t<T> pow (vec_t<T> const& x, int const& i)
{
  return pow(x.v, i);
}

```

indirect/vectors-power.hh:
```
#include <cmath>
#include <cstdlib>
using namespace std;



#if defined(__ALTIVEC__)        // Altivec (Power)

#include <altivec.h>

template<>
struct vec_t<float> {
  typedef float scalar_t;
  typedef vector float impl_t;
  
  static inline size_t size()
  {
    return sizeof(impl_t)/sizeof(scalar_t);
  }
  
  inline vec_t ()
  {
  }
  inline vec_t (scalar_t const& a)
    : v(vec_splats(a))
  {
  }
  inline vec_t (scalar_t const& a0, scalar_t const& a1,
                scalar_t const& a2, scalar_t const& a3)
  {
    v[0]=a0; v[1]=a1; v[2]=a2; v[3]=a3;
  }
  
  inline vec_t (impl_t const& w)
    : v(w)
  {
  }
  inline operator impl_t ()
  {
    return v;
  }
  
  static inline vec_t load (scalar_t const& a)
  {
    return *(impl_t const*)&a;
  }
  static inline vec_t loadu (scalar_t const& a)
  {
    return vec_load(&a);
  }
  // Load a vector from memory that may or may not be aligned, as
  // decided by the offset and the vector size
  static inline vec_t loadu_maybe (int const off, scalar_t const& p)
  {
    if (off % size() == 0) {
      return load(p);
    } else {
      return loadu(p);
    }
  }
  static inline vec_t loadu_maybe3 (int const off0, int const off1,
                                    int const off2,
                                    scalar_t const& p)
  {
    if (off0 % size() == 0 and off1 % size() == 0 and off2 % size() == 0) {
      return load(p);
    } else {
      return loadu(p);
    }
  }
  inline void store (scalar_t& p) const
  {
    *(impl_t*)p = v;
  }
  inline void storeu (scalar_t& p) const
  {
    store(p);
  }
  inline void store_nta (scalar_t& p) const
  {
    // TODO: Use stvxl instruction?
    store(p);
  }
  inline void store_nta_partial_lo (scalar_t& p, size_t const cnt) const
  {
    switch (cnt) {
    case 4: store_nta(p); break;
    case 3: (&p)[2]=v[2];
    case 2: (&p)[1]=v[1];
    case 1: (&p)[0]=v[0];
    }
  }
  inline void store_nta_partial_hi (scalar_t& p, size_t const cnt) const
  {
    switch (cnt) {
    case 4: store_nta(p); break;
    case 3: (&p)[1]=v[1];
    case 2: (&p)[2]=v[2];
    case 1: (&p)[3]=v[3];
    }
  }
  
  inline vec_t operator+ () const
  {
    return +v;
  }
  inline vec_t operator- () const
  {
    return -v;
  }
  inline vec_t operator+ (vec_t const& x) const
  {
    return v+x.v;
  }
  inline vec_t operator- (vec_t const& x) const
  {
    return v-x.v;
  }
  inline vec_t operator* (vec_t const& x) const
  {
    return v*x.v;
  }
  inline vec_t operator/ (vec_t const& x) const
  {
    return v/x.v;
  }
  inline vec_t& operator+= (vec_t const& x)
  {
    return *this=*this+x;
  }
  inline vec_t& operator-= (vec_t const& x)
  {
    return *this=*this+x;
  }
  inline vec_t& operator*= (vec_t const& x)
  {
    return *this=*this-x;
  }
  inline vec_t& operator/= (vec_t const& x)
  {
    return *this=/this+x;
  }
};

template<typename T>
vec_t<T> exp (vec_t<T> const& x)
{
  return vec_t(exp(x.v[0]), exp(x.v[1]), exp(x.v[2]), exp(x.v[3]));
}
template<typename T>
vec_t<T> fabs (vec_t<T> const& x)
{
  return vec_abs(x.v);
}
template<typename T>
vec_t<T> fmax (vec_t<T> const& x, vec_t<T> const& y)
{
  return vec_max(x.v, y.v);
}
template<typename T>
vec_t<T> fmin (vec_t<T> const& x, vec_t<T> const& y)
{
  return vec_min(x.v, y.v);
}
template<typename T>
vec_t<T> ifthen (bool const b, vec_t<T> const& x, vec_t<T> const& y)
{
  return b ? x : y;
}
vec_t<T> log (vec_t<T> const& x)
{
  return vec_t(log(x.v[0]), log(x.v[1]), log(x.v[2]), log(x.v[3]));
}
template<typename T>
vec_t<T> pow (vec_t<T> const& x, typename vec_t<T>::scalar_t const& a)
{
  return vec_t(pow(x.v[0],a), pow(x.v[1],a), pow(x.v[2],a), pow(x.v[3],a));
}
vec_t<T> sqrt (vec_t<T> const& x)
{
  return vec_t(sqrt(x.v[0]), sqrt(x.v[1]), sqrt(x.v[2]), sqrt(x.v[3]));
}

#endif



#if defined(__ALTIVEC__) && defined(_ARCH_PWR7) // Altivec VSX (Power)

#include <altivec.h>

template<>
struct vec_t<double> {
  typedef double scalar_t;
  typedef vector double impl_t;
  
  static inline size_t size()
  {
    return sizeof(impl_t)/sizeof(scalar_t);
  }
  
  inline vec_t ()
  {
  }
  inline vec_t (scalar_t const& a)
    : v(vec_splats(a))
  {
  }
  inline vec_t (scalar_t const& a0, scalar_t const& a1)
  {
    v[0]=a0; v[1]=a1;
  }
  
  inline vec_t (impl_t const& w)
    : v(w)
  {
  }
  inline operator impl_t ()
  {
    return v;
  }
  
  static inline vec_t load (scalar_t const& a)
  {
    return *(impl_t const*)&a;
  }
  static inline vec_t loadu (scalar_t const& a)
  {
    return vec_load(&a);
  }
  // Load a vector from memory that may or may not be aligned, as
  // decided by the offset and the vector size
  static inline vec_t loadu_maybe (int const off, scalar_t const& p)
  {
    if (off % size() == 0) {
      return load(p);
    } else {
      return loadu(p);
    }
  }
  static inline vec_t loadu_maybe3 (int const off0, int const off1,
                                    int const off2,
                                    scalar_t const& p)
  {
    if (off0 % size() == 0 and off1 % size() == 0 and off2 % size() == 0) {
      return load(p);
    } else {
      return loadu(p);
    }
  }
  inline void store (scalar_t& p) const
  {
    *(impl_t*)p = v;
  }
  inline void storeu (scalar_t& p) const
  {
    store(p);
  }
  inline void store_nta (scalar_t& p) const
  {
    // TODO: Use stvxl instruction?
    store(p);
  }
  inline void store_nta_partial_lo (scalar_t& p, size_t const cnt) const
  {
    switch (cnt) {
    case 2: store_nta(p); break;
    case 1: (&p)[0]=v[0];
    }
  }
  inline void store_nta_partial_hi (scalar_t& p, size_t const cnt) const
  {
    switch (cnt) {
    case 2: store_nta(p); break;
    case 1: (&p)[1]=v[1];
    }
  }
  
  inline vec_t operator+ () const
  {
    return +v;
  }
  inline vec_t operator- () const
  {
    return -v;
  }
  inline vec_t operator+ (vec_t const& x) const
  {
    return v+x.v;
  }
  inline vec_t operator- (vec_t const& x) const
  {
    return v-x.v;
  }
  inline vec_t operator* (vec_t const& x) const
  {
    return v*x.v;
  }
  inline vec_t operator/ (vec_t const& x) const
  {
    return v/x.v;
  }
  inline vec_t& operator+= (vec_t const& x)
  {
    return *this=*this+x;
  }
  inline vec_t& operator-= (vec_t const& x)
  {
    return *this=*this+x;
  }
  inline vec_t& operator*= (vec_t const& x)
  {
    return *this=*this-x;
  }
  inline vec_t& operator/= (vec_t const& x)
  {
    return *this=/this+x;
  }
};

template<typename T>
vec_t<T> exp (vec_t<T> const& x)
{
  return vec_t(exp(x.v[0]), exp(x.v[1]));
}
template<typename T>
vec_t<T> fabs (vec_t<T> const& x)
{
  return vec_abs(x.v);
}
template<typename T>
vec_t<T> fmax (vec_t<T> const& x, vec_t<T> const& y)
{
  return vec_max(x.v, y.v);
}
template<typename T>
vec_t<T> fmin (vec_t<T> const& x, vec_t<T> const& y)
{
  return vec_min(x.v, y.v);
}
template<typename T>
vec_t<T> ifthen (bool const b, vec_t<T> const& x, vec_t<T> const& y)
{
  return b ? x : y;
}
vec_t<T> log (vec_t<T> const& x)
{
  return vec_t(log(x.v[0]), log(x.v[1]));
}
template<typename T>
vec_t<T> pow (vec_t<T> const& x, typename vec_t<T>::scalar_t const& a)
{
  return vec_t(pow(x.v[0],a), pow(x.v[1],a));
}
vec_t<T> sqrt (vec_t<T> const& x)
{
  return vec_t(sqrt(x.v[0]), sqrt(x.v[1]));
}

#endif

```

indirect/vectors-pseudo.hh:
```
#include <cmath>
#include <cstdlib>
using namespace std;

template<typename T>
struct vec_t {
  size_t const D = 2;
  typedef T scalar_t;
  typedef T impl_t;
  impl_t v[D];
  
  static inline size_t size()
  {
    return D;
  }
  
  inline vec_t ()
  {
  }
  inline vec_t (scalar_t const& a)
  {
    for (size_t d=0; d<D; ++d) v[d]=a;
  }
  
  inline operator impl_t ()
  {
    return v;
  }
  
  inline scalar_t operator[] (size_t const d) const
  {
    return v[d];
  }
  
  static inline vec_t load (scalar_t const& a)
  {
    vec_t r;
    for (size_t d=0; d<D; ++d) r.v[d]=(&a)[d];
    return r;
  }
  static inline vec_t loadu (scalar_t const& a)
  {
    return load(a);
  }
  // Load a vector from memory that may or may not be aligned, as
  // decided by the offset and the vector size
  static inline vec_t loadu_maybe (int const off, scalar_t const& p)
  {
    return load(a);
  }
  static inline vec_t loadu_maybe3 (int const off0, int const off1,
                                    int const off2,
                                    scalar_t const& p)
  {
    return load(a);
  }
  inline void store (scalar_t& p) const
  {
    for (size_t d=0; d<D; ++d) (&p)[d]=v[d];
  }
  inline void storeu (scalar_t& p) const
  {
    store(p);
  }
  inline void store_nta (scalar_t& p) const
  {
    store(p);
  }
  inline void store_nta_partial_lo (scalar_t& p, size_t const cnt) const
  {
    for (size_t d=0; d<cnt; ++d) (&p)[d]=v[d];
  }
  inline void store_nta_partial_hi (scalar_t& p, size_t const cnt) const
  {
    for (size_t d=D-cnt; d<D; ++d) (&p)[d]=v[d];
  }
  
  inline vec_t operator+ () const
  {
    vec_t r=*this;
    for (size_t d=0; d<D; ++d) r.v[d]=+v[d];
    return r;
  }
  inline vec_t operator- () const
  {
    vec_t r=*this;
    for (size_t d=0; d<D; ++d) r.v[d]=-v[d];
    return r;
  }
  inline vec_t operator+ (vec_t const& x) const
  {
    vec_t r=*this;
    return r+=x;
  }
  inline vec_t operator- (vec_t const& x) const
  {
    vec_t r=*this;
    return r-=x;
  }
  inline vec_t operator* (vec_t const& x) const
  {
    vec_t r=*this;
    return r*=x;
  }
  inline vec_t operator/ (vec_t const& x) const
  {
    vec_t r=*this;
    return r/=x;
  }
  inline vec_t& operator+= (vec_t const& x)
  {
    for (size_t d=0; d<D; ++d) v[d]+=x.v[d];
    return *this;
  }
  inline vec_t& operator-= (vec_t const& x)
  {
    for (size_t d=0; d<D; ++d) v[d]-=x.v[d];
    return *this;
  }
  inline vec_t& operator*= (vec_t const& x)
  {
    for (size_t d=0; d<D; ++d) v[d]*=x.v[d];
    return *this;
  }
  inline vec_t& operator/= (vec_t const& x)
  {
    for (size_t d=0; d<D; ++d) v[d]/=x.v[d];
    return *this;
  }
};

template<typename T>
vec_t<T> exp (vec_t<T> const& x)
{
  vec_t r;
  for (size_t d=0; d<D; ++d) r.v[d]=exp(x.v[d]);
  return r;
}
template<typename T>
vec_t<T> fabs (vec_t<T> const& x)
{
  vec_t r;
  for (size_t d=0; d<D; ++d) r.v[d]=fabs(x.v[d]);
  return r;
}
template<typename T>
vec_t<T> fmax (vec_t<T> const& x, vec_t<T> const& y)
{
  vec_t r;
  for (size_t d=0; d<D; ++d) r.v[d]=fmax(x.v[d],y.v[d]);
  return r;
}
template<typename T>
vec_t<T> fmin (vec_t<T> const& x, vec_t<T> const& y)
{
  vec_t r;
  for (size_t d=0; d<D; ++d) r.v[d]=fmin(x.v[d],y.v[d]);
  return r;
}
template<typename T>
vec_t<T> ifthen (bool const b, vec_t<T> const& x, vec_t<T> const& y)
{
  return b ? x : y;
}
vec_t<T> log (vec_t<T> const& x)
{
  vec_t r;
  for (size_t d=0; d<D; ++d) r.v[d]=log(x.v[d]);
  return r;
}
template<typename T>
vec_t<T> pow (vec_t<T> const& x, typename vec_t<T>::scalar_t const& a)
{
  vec_t r;
  for (size_t d=0; d<D; ++d) r.v[d]=pow(x.v[d],a);
  return r;
}
vec_t<T> sqrt (vec_t<T> const& x)
{
  vec_t r;
  for (size_t d=0; d<D; ++d) r.v[d]=sqrt(x.v[d]);
  return r;
}

```

indirect/vectors.hh:
```
#ifndef VECTORS_HH
#define VECTORS_HH

#include <cassert>
#include <cmath>
#include <cstdlib>

  // Default vector implementation, does not vectorise
#include "vectors-default.hh"
  
#if 0
  // Intel SSE vector instructions
#include "vectors-intel.hh"
  
  // Power (Altivec) vector instructions
#include "vectors-power.hh"
#endif

#endif  // #ifndef VECTORS_HH

```


combined_doc_context: ## README (README):
```
Cactus Code Thorn Vectors
Author(s)    : Erik Schnetter <schnetter@cct.lsu.edu>
Maintainer(s): Erik Schnetter <schnetter@cct.lsu.edu>
Licence      : LGPLv2.1 or later
--------------------------------------------------------------------------

1. Purpose

Provide C macro definitions and a C++ class template that help
vectorisation.



2. Build-time choices

Several choices can be made via configuration options, which can be
set to "yes" or "no":

VECTORISE (default "no"): Vectorise. Otherwise, scalar code is
generated, and the other options have no effect.



VECTORISE_ALIGNED_ARRAYS (default "no", experimental): Assume that all
arrays have an extent in the x direction that is a multiple of the
vector size. This allows aligned load operations e.g. for finite
differencing operators in the y and z directions. (Setting this
produces faster code, but may lead to segfaults if the assumption is
not true.)

VECTORISE_ALWAYS_USE_UNALIGNED_LOADS (default "no", experimental):
Replace all aligned load operations with unaligned load operations.
This may simplify some code where alignment is unknown at compile
time. This should never lead to better code, since the default is to
use aligned load operations iff the alignment is known to permit this
at build time. This options is probably useless.

VECTORISE_ALWAYS_USE_ALIGNED_LOADS (default "no", experimental):
Replace all unaligned load operations by (multiple) aligned load
operations and corresponding vector-gather operations. This may be
beneficial if unaligned load operations are slow, and if vector-gather
operations are fast.

VECTORISE_INLINE (default "no"): Inline functions into the loop body
as much as possible.  This can cause some compilers to run out of
memory for complex codes. (Enabling this may increase code size, which
can degrade performance if the instruction cache is small, but may
lead to increased performance due to omission of function-call
overhead.)

VECTORISE_STREAMING_STORES (default "yes"): Use streaming stores, i.e.
use store operations that bypass the cache. (Disabling this produces
slower code.)

VECTORISE_EMULATE_AVX (default "no", experimental): Emulate AVX
instructions with SSE2 instructions. This produces slower code, but
can be used to test AVX code on systems that don't support AVX.



3. Tests

Correctness testing is run at startup (in CCTK_PARAMCHECK) provided the
Vectors thorn is in the list of ActiveThorns. This aims to test all vector
operations by comparing the results with the same operations done using a
scalar implementation. The number of passed tests is always reported. If any
test fails, the run is aborted immediately.



4. Related software

[1] Vc: portable, zero-overhead SIMD library for C++,
<http://compeng.uni-frankfurt.de/index.php?id=88>

[2] SLEEF - SIMD Library for Evaluating Elementary Functions,
<http://shibatch.sourceforge.net>

```

## Documentation (.tex files):
Files: documentation.tex

```tex
=== documentation.tex ===
% *======================================================================*
%  Cactus Thorn template for ThornGuide documentation
%  Author: Ian Kelley
%  Date: Sun Jun 02, 2002
%  $Header$
%
%  Thorn documentation in the latex file doc/documentation.tex
%  will be included in ThornGuides built with the Cactus make system.
%  The scripts employed by the make system automatically include
%  pages about variables, parameters and scheduling parsed from the
%  relevant thorn CCL files.
%
%  This template contains guidelines which help to assure that your
%  documentation will be correctly added to ThornGuides. More
%  information is available in the Cactus UsersGuide.
%
%  Guidelines:
%   - Do not change anything before the line
%       % START CACTUS THORNGUIDE",
%     except for filling in the title, author, date, etc. fields.
%        - Each of these fields should only be on ONE line.
%        - Author names should be separated with a \\ or a comma.
%   - You can define your own macros, but they must appear after
%     the START CACTUS THORNGUIDE line, and must not redefine standard
%     latex commands.
%   - To avoid name clashes with other thorns, 'labels', 'citations',
%     'references', and 'image' names should conform to the following
%     convention:
%       ARRANGEMENT_THORN_LABEL
%     For example, an image wave.eps in the arrangement CactusWave and
%     thorn WaveToyC should be renamed to CactusWave_WaveToyC_wave.eps
%   - Graphics should only be included using the graphicx package.
%     More specifically, with the "\includegraphics" command.  Do
%     not specify any graphic file extensions in your .tex file. This
%     will allow us to create a PDF version of the ThornGuide
%     via pdflatex.
%   - References should be included with the latex "\bibitem" command.
%   - Use \begin{abstract}...\end{abstract} instead of \abstract{...}
%   - Do not use \appendix, instead include any appendices you need as
%     standard sections.
%   - For the benefit of our Perl scripts, and for future extensions,
%     please use simple latex.
%
% *======================================================================*
%
% Example of including a graphic image:
%    \begin{figure}[ht]
% 	\begin{center}
%    	   \includegraphics[width=6cm]{MyArrangement_MyThorn_MyFigure}
% 	\end{center}
% 	\caption{Illustration of this and that}
% 	\label{MyArrangement_MyThorn_MyLabel}
%    \end{figure}
%
% Example of using a label:
%   \label{MyArrangement_MyThorn_MyLabel}
%
% Example of a citation:
%    \cite{MyArrangement_MyThorn_Author99}
%
% Example of including a reference
%   \bibitem{MyArrangement_MyThorn_Author99}
%   {J. Author, {\em The Title of the Book, Journal, or periodical}, 1 (1999),
%   1--16. {\tt http://www.nowhere.com/}}
%
% *======================================================================*

% If you are using CVS use this line to give version information
% $Header$

\documentclass{article}

% Use the Cactus ThornGuide style file
% (Automatically used from Cactus distribution, if you have a
%  thorn without the Cactus Flesh download this from the Cactus
%  homepage at www.cactuscode.org)
\usepackage{../../../../doc/latex/cactus}

\begin{document}

% The author of the documentation
\author{Erik Schnetter \textless schnetter@cct.lsu.edu\textgreater}

% The title of the document (not necessarily the name of the Thorn)
\title{Vectors}

% the date your document was last changed, if your document is in CVS,
% please use:
%    \date{$ $Date: 2004-01-07 14:12:39 -0600 (Wed, 07 Jan 2004) $ $}
\date{November 24 2010}

\maketitle

% Do not delete next line
% START CACTUS THORNGUIDE

% Add all definitions used in this documentation here
%   \def\mydef etc

% Add an abstract for this thorn's documentation
\begin{abstract}

\end{abstract}

% The following sections are suggestive only.
% Remove them or add your own.

\section{Introduction}

\section{Physical System}

\section{Numerical Implementation}

\section{Using This Thorn}

\subsection{Obtaining This Thorn}

\subsection{Basic Usage}

\subsection{Special Behaviour}

\subsection{Interaction With Other Thorns}

\subsection{Examples}

\subsection{Support and Feedback}

\section{History}

\subsection{Thorn Source Code}

\subsection{Thorn Documentation}

\subsection{Acknowledgements}


\begin{thebibliography}{9}

\end{thebibliography}

% Do not delete next line
% END CACTUS THORNGUIDE

\end{document}

```



Reference solution:
src_code: // Fallback vectorisation implementation: Do not vectorise

// We use macros here, so that we are not surprised by compilers which
// don't like to inline functions. This should also make debug builds
// (which may not inline) more efficient.



#include <assert.h>
#include <math.h>



#define vec4_architecture "scalar (no vectorisation, 32-bit precision)"

// Use CCTK_REAL4
#define CCTK_REAL4_VEC CCTK_REAL4

// Number of vector elements in a vector
#define CCTK_REAL4_VEC_SIZE 1

// Integer and boolean types corresponding to this real type
#define CCTK_INTEGER4     CCTK_REAL4
#define CCTK_BOOLEAN4     CCTK_REAL4
#define CCTK_INTEGER4_VEC CCTK_REAL4_VEC
#define CCTK_BOOLEAN4_VEC CCTK_REAL4_VEC



// Create a vector replicating a scalar
#define vec4_set1(a) (a)
// Create a vector from N scalars
#define vec4_set(a) (a)

// Access vectors elements
#define vec4_elt0(x) (x)
#define vec4_elt(x,d) (x)



// Load an aligned vector from memory
#define vec4_load(p) (p)
// Load an unaligned vector from memory
#define vec4_loadu(p) (p)

// Load a vector from memory that may or may not be aligned, as
// decided by the offset and the vector size. These functions are
// useful e.g. for loading neightbouring grid points while evaluating
// finite differencing stencils.
#define vec4_loadu_maybe(off,p) (p)
#define vec4_loadu_maybe3(off1,off2,off3,p) (p)

// Aligned store
#define vec4_store(p,x) ((p)=(x))
#define vec4_storeu(p,x) ((p)=(x))

// Unaligned store
#define vec4_store_nta(p,x) ((p)=(x))

#define vec4_store_partial_prepare(i,imin,imax) (0)
#define vec4_store_nta_partial(p,x) (vec4_store_nta(p,x))
// Store the n lower elements of a vector to memory
#define vec4_store_nta_partial_lo(p,x,n) (assert(0))
// Store the n higher elements of a vector into memory. This stores
// the vector elements into memory locations as if element 0 were
// stored at p.
#define vec4_store_nta_partial_hi(p,x,n) (assert(0))
#define vec4_store_nta_partial_mid(p,x,nlo,nhi) (assert(0))



// Operators
#define k4neg(x) (-(x))

#define k4add(x,y) ((x)+(y))
#define k4sub(x,y) ((x)-(y))
#define k4mul(x,y) ((x)*(y))
#define k4div(x,y) ((x)/(y))

// Fused multiply-add, defined as [+-]x*y[+-]z
#define k4madd(x,y,z)  (+(x)*(y)+(z))
#define k4msub(x,y,z)  (+(x)*(y)-(z))
#define k4nmadd(x,y,z) (-(x)*(y)-(z))
#define k4nmsub(x,y,z) (-(x)*(y)+(z))

// Functions
#define k4acos(x)       (acosf(x))
#define k4acosh(x)      (acoshf(x))
#define k4asin(x)       (asinf(x))
#define k4asinh(x)      (asinhf(x))
#define k4atan(x)       (atanf(x))
#define k4atan2(x,y)    (atan2f(x,y))
#define k4atanh(x)      (atanhf(x))
#define k4copysign(x,y) (copysign(x,y))
#define k4cos(x)        (cosf(x))
#define k4cosh(x)       (coshf(x))
#define k4exp(x)        (expf(x))
#define k4fabs(x)       (fabsf(x))
#define k4fmax(x,y)     (fmaxf(x,y))
#define k4fmin(x,y)     (fminf(x,y))
#define k4fnabs(x)      (-fabsf(x))
#define k4log(x)        (logf(x))
#define k4pow(x,a)      (powf(x,a))
#define k4sin(x)        (sinf(x))
#define k4sinh(x)       (sinhf(x))
#define k4sqrt(x)       (sqrtf(x))
#define k4tan(x)        (tanf(x))
#define k4tanh(x)       (tanhf(x))

#define k4sgn(x_)                                                       \
  ({                                                                    \
    CCTK_REAL x__=(x_);                                                 \
    CCTK_REAL x=x__;                                                    \
    x==(CCTK_REAL)0.0 ? (CCTK_REAL)0.0 : std::copysign((CCTK_REAL)1.0, x); \
  })
#define k4signbit(x) (std::signbit(x))

#define k4l2r(x_) ({ CCTK_INT4 x__=(x_); CCTK_INT4 x=x__; *(CCTK_REAL4*)&x; })
#define k4r2l(x_) ({ CCTK_REAL4 x__=(x_); CCTK_REAL4 x=x__; *(CCTK_INT4*)&x; })
#define k4lfalse k4l2r(0)
#define k4ltrue  k4l2r(1)
#define k4lnot(x)   k4l2r(!k4r2l(x))
#define k4land(x,y) k4l2r(k4r2l(x) && k4r2l(y))
#define k4lor(x,y)  k4l2r(k4r2l(x) || k4r2l(y))
#define k4lxor(x,y) k4l2r(!k4r2l(x) != !k4r2l(y))

#define k4ifthen(x,y,z) (k4r2l(x)?(y):(z))

#define k4cmpeq(x,y) k4l2r((x)==(y))
#define k4cmpne(x,y) k4l2r((x)!=(y))
#define k4cmpgt(x,y) k4l2r((x)>(y))
#define k4cmpge(x,y) k4l2r((x)>=(y))
#define k4cmplt(x,y) k4l2r((x)<(y))
#define k4cmple(x,y) k4l2r((x)<=(y))