#ifndef __KOKKOSBATCHED_VECTOR_SIMD_HPP__
#define __KOKKOSBATCHED_VECTOR_SIMD_HPP__

/// \author Kyungjoo Kim (kyukim@sandia.gov)

#include <Kokkos_Complex.hpp>
#include <KokkosBatched_Vector.hpp>

#if defined(__CUDA_ARCH__) || defined(__HIP_DEVICE_COMPILE__)
#undef  __KOKKOSBATCHED_ENABLE_AVX__
#else
// compiler bug with AVX in some architectures
#define __KOKKOSBATCHED_ENABLE_AVX__
#endif

namespace KokkosBatched {

  template<typename T, int l>
  class Vector<SIMD<T>,l> {
  public:
    using type = Vector<SIMD<T>,l>;
    using value_type = T;
    using mag_type = typename Kokkos::Details::ArithTraits<T>::mag_type;

    enum : int { vector_length = l };

    typedef value_type data_type[vector_length];

    KOKKOS_INLINE_FUNCTION
    static const char* label() { return "SIMD"; }

    template<typename,int>
    friend class Vector;

  private:
    mutable data_type _data;

  public:
    KOKKOS_INLINE_FUNCTION Vector() {
      //static_assert(std::is_same<Kokkos::Impl::ActiveExecutionMemorySpace,Kokkos::HostSpace>::value,
      //              "Vector SIMD should not be instanciated in CudaSpace");
#if defined( KOKKOS_ENABLE_PRAGMA_IVDEP )
#pragma ivdep
#endif
#if defined( KOKKOS_ENABLE_PRAGMA_VECTOR )
#pragma vector always
#endif
#ifdef KOKKOSKERNELS_ENABLE_OMP_SIMD
#pragma omp simd
#endif
      for (int i=0;i<vector_length;++i)
        _data[i] = 0;
    }
    template<typename ArgValueType>
    KOKKOS_INLINE_FUNCTION Vector(const ArgValueType &val) {
#if defined( KOKKOS_ENABLE_PRAGMA_IVDEP )
#pragma ivdep
#endif
#if defined( KOKKOS_ENABLE_PRAGMA_VECTOR )
#pragma vector always
#endif
#ifdef KOKKOSKERNELS_ENABLE_OMP_SIMD
#pragma omp simd
#endif
      for (int i=0;i<vector_length;++i)
        _data[i] = val;
    }
    template<typename ArgValueType>
    KOKKOS_INLINE_FUNCTION Vector(const Vector<SIMD<ArgValueType>,vector_length> &b) {
#if defined( KOKKOS_ENABLE_PRAGMA_IVDEP )
#pragma ivdep
#endif
#if defined( KOKKOS_ENABLE_PRAGMA_VECTOR )
#pragma vector always
#endif
#ifdef KOKKOSKERNELS_ENABLE_OMP_SIMD
#pragma omp simd
#endif
      for (int i=0;i<vector_length;++i)
        _data[i] = b[i];
    }

    KOKKOS_INLINE_FUNCTION
    type& loadAligned(const value_type *p) {
#if defined( KOKKOS_ENABLE_PRAGMA_IVDEP )
#pragma ivdep
#endif
#if defined( KOKKOS_ENABLE_PRAGMA_VECTOR )
#pragma vector always
#endif
#ifdef KOKKOSKERNELS_ENABLE_OMP_SIMD
#pragma omp simd
#endif
      for (int i=0;i<vector_length;++i)
        _data[i] = p[i];
      return *this;
    }

    KOKKOS_INLINE_FUNCTION
    type& loadUnaligned(const value_type *p) {
      return loadAligned(p);
    }
      
    KOKKOS_INLINE_FUNCTION
    void storeAligned(value_type *p) const {
#if defined( KOKKOS_ENABLE_PRAGMA_IVDEP )
#pragma ivdep
#endif
#if defined( KOKKOS_ENABLE_PRAGMA_VECTOR )
#pragma vector always
#endif
#ifdef KOKKOSKERNELS_ENABLE_OMP_SIMD
#pragma omp simd
#endif
      for (int i=0;i<vector_length;++i)
        p[i] = _data[i];
    }

    KOKKOS_INLINE_FUNCTION
    void storeUnaligned(value_type *p) const {
      storeAligned(p);
    }

    KOKKOS_INLINE_FUNCTION
    value_type& operator[](const int &i) const {
      return _data[i];
    }
  };
}


#if defined(__CUDA_ARCH__) || defined(__HIP_DEVICE_COMPILE__)
namespace KokkosBatched {

  template<>
  class Vector<SIMD<float>,2> {
  public:
    using type = Vector<SIMD<float>,2>;
    using value_type = float;
    using mag_type = float;

    enum : int { vector_length = 2 };
    typedef float2 data_type;

    KOKKOS_INLINE_FUNCTION
    static const char* label() { return "GpuFloat2"; }

    template<typename,int>
    friend class Vector;

  private:
    mutable data_type _data;

  public:
    KOKKOS_INLINE_FUNCTION Vector() { _data.x = 0; _data.y = 0; }
    KOKKOS_INLINE_FUNCTION Vector(const value_type &val) { _data.x = val; _data.y = val; }
    KOKKOS_INLINE_FUNCTION Vector(const type &b) { _data.x = b._data.x; _data.y = b._data.y; }
    KOKKOS_INLINE_FUNCTION Vector(const float2 &val) { _data.x = val.x; _data.y = val.y; }

    template<typename ArgValueType>
    KOKKOS_INLINE_FUNCTION Vector(const ArgValueType &val) {
      _data.x = val; 
      _data.y = val;
    }

    template<typename ArgValueType>
    KOKKOS_INLINE_FUNCTION Vector(const Vector<SIMD<ArgValueType>,vector_length> &b) {
      _data.x = b[0];
      _data.y = b[1];
    }

    KOKKOS_INLINE_FUNCTION
    type& operator=(const float2 &val) {
      _data.x = val.x;
      _data.y = val.y;
      return *this;
    }

    KOKKOS_INLINE_FUNCTION
    float2 float2() const {
      return _data;
    }

    KOKKOS_INLINE_FUNCTION
    type& loadAligned(const value_type *p) {
      _data.x = *(p  );
      _data.y = *(p+1);
      return *this;
    }

    KOKKOS_INLINE_FUNCTION
    type& loadUnaligned(const value_type *p) {
      _data.x = *(p  );
      _data.y = *(p+1);
      return *this;
    }

    KOKKOS_INLINE_FUNCTION
    void storeAligned(value_type *p) const {
      *(p  ) = _data.x;
      *(p+1) = _data.y;
    }

    KOKKOS_INLINE_FUNCTION
    void storeUnaligned(value_type *p) const {
      *(p  ) = _data.x;
      *(p+1) = _data.y;
    }

    KOKKOS_INLINE_FUNCTION
    value_type& operator[](const int &i) const {
      return reinterpret_cast<value_type*>(&_data)[i];
    }
  };

  template<>
  class Vector<SIMD<double>,2> {
  public:
    using type = Vector<SIMD<double>,2>;
    using value_type = double;
    using mag_type = double;

    enum : int { vector_length = 2 };
    typedef double2 data_type;

    KOKKOS_INLINE_FUNCTION
    static const char* label() { return "GpuDouble2"; }

    template<typename,int>
    friend class Vector;

  private:
    mutable data_type _data;

  public:
    KOKKOS_INLINE_FUNCTION Vector() { _data.x = 0; _data.y = 0; }
    KOKKOS_INLINE_FUNCTION Vector(const value_type &val) { _data.x = val; _data.y = val; }
    KOKKOS_INLINE_FUNCTION Vector(const type &b) { _data.x = b._data.x; _data.y = b._data.y; }
    KOKKOS_INLINE_FUNCTION Vector(const double2 &val) { _data.x = val.x; _data.y = val.y; }

    template<typename ArgValueType>
    KOKKOS_INLINE_FUNCTION Vector(const ArgValueType &val) {
      _data.x = val; 
      _data.y = val;
    }

    template<typename ArgValueType>
    KOKKOS_INLINE_FUNCTION Vector(const Vector<SIMD<ArgValueType>,vector_length> &b) {
      _data.x = b[0];
      _data.y = b[1];
    }

    KOKKOS_INLINE_FUNCTION
    type& operator=(const double2 &val) {
      _data.x = val.x;
      _data.y = val.y;
      return *this;
    }

    KOKKOS_INLINE_FUNCTION
    double2 double2() const {
      return _data;
    }

    KOKKOS_INLINE_FUNCTION
    type& loadAligned(const value_type *p) {
      _data.x = *(p  );
      _data.y = *(p+1);
      return *this;
    }

    KOKKOS_INLINE_FUNCTION
    type& loadUnaligned(const value_type *p) {
      _data.x = *(p  );
      _data.y = *(p+1);
      return *this;
    }

    KOKKOS_INLINE_FUNCTION
    void storeAligned(value_type *p) const {
      *(p  ) = _data.x;
      *(p+1) = _data.y;
    }

    KOKKOS_INLINE_FUNCTION
    void storeUnaligned(value_type *p) const {
      *(p  ) = _data.x;
      *(p+1) = _data.y;
    }

    KOKKOS_INLINE_FUNCTION
    value_type& operator[](const int &i) const {
      return reinterpret_cast<value_type*>(&_data)[i];
    }
  };

  template<>
  class Vector<SIMD<float>,4> {
  public:
    using type = Vector<SIMD<float>,4>;
    using value_type = float;
    using mag_type = float;

    enum : int { vector_length = 4 };
    typedef float4 data_type;

    KOKKOS_INLINE_FUNCTION
    static const char* label() { return "GpuFloat4"; }

    template<typename,int>
    friend class Vector;

  private:
    mutable data_type _data;

  public:
    KOKKOS_INLINE_FUNCTION Vector() { _data.x = 0; _data.y = 0; _data.z = 0; _data.w = 0; }
    KOKKOS_INLINE_FUNCTION Vector(const value_type &val) { _data.x = val; _data.y = val; _data.z = val; _data.w = val; }
    KOKKOS_INLINE_FUNCTION Vector(const type &b) { _data.x = b._data.x; _data.y = b._data.y; _data.z = b._data.z; _data.w = b._data.w; }
    KOKKOS_INLINE_FUNCTION Vector(const float4 &val) { _data.x = val.x; _data.y = val.y; _data.z = val.z; _data.w = val.w; }

    template<typename ArgValueType>
    KOKKOS_INLINE_FUNCTION Vector(const ArgValueType &val) {
      _data.x = val; 
      _data.y = val;
      _data.z = val; 
      _data.w = val;
    }

    template<typename ArgValueType>
    KOKKOS_INLINE_FUNCTION Vector(const Vector<SIMD<ArgValueType>,vector_length> &b) {
      _data.x = b[0];
      _data.y = b[1];
      _data.z = b[2];
      _data.w = b[3];
    }

    KOKKOS_INLINE_FUNCTION
    type& operator=(const float4 &val) {
      _data.x = val.x;
      _data.y = val.y;
      _data.z = val.z;
      _data.w = val.w;
      return *this;
    }

    KOKKOS_INLINE_FUNCTION
    float4 float4() const {
      return _data;
    }

    KOKKOS_INLINE_FUNCTION
    type& loadAligned(const value_type *p) {
      _data.x = *(p  );
      _data.y = *(p+1);
      _data.z = *(p+2);
      _data.w = *(p+3);
      return *this;
    }

    KOKKOS_INLINE_FUNCTION
    type& loadUnaligned(const value_type *p) {
      _data.x = *(p  );
      _data.y = *(p+1);
      _data.z = *(p+2);
      _data.w = *(p+3);
      return *this;
    }

    KOKKOS_INLINE_FUNCTION
    void storeAligned(value_type *p) const {
      *(p  ) = _data.x;
      *(p+1) = _data.y;
      *(p+2) = _data.z;
      *(p+3) = _data.w;
    }

    KOKKOS_INLINE_FUNCTION
    void storeUnaligned(value_type *p) const {
      *(p  ) = _data.x;
      *(p+1) = _data.y;
      *(p+2) = _data.z;
      *(p+3) = _data.w;
    }

    KOKKOS_INLINE_FUNCTION
    value_type& operator[](const int &i) const {
      return reinterpret_cast<value_type*>(&_data)[i];
    }
  };

  template<>
  class Vector<SIMD<double>,4> {
  public:
    using type = Vector<SIMD<double>,4>;
    using value_type = double;
    using mag_type = double;

    enum : int { vector_length = 4 };
    typedef double4 data_type;

    KOKKOS_INLINE_FUNCTION
    static const char* label() { return "GpuDouble4"; }

    template<typename,int>
    friend class Vector;

  private:
    mutable data_type _data;

  public:
    KOKKOS_INLINE_FUNCTION Vector() { _data.x = 0; _data.y = 0; _data.z = 0; _data.w = 0; }
    KOKKOS_INLINE_FUNCTION Vector(const value_type &val) { _data.x = val; _data.y = val; _data.z = val; _data.w = val; }
    KOKKOS_INLINE_FUNCTION Vector(const type &b) { _data.x = b._data.x; _data.y = b._data.y; _data.z = b._data.z; _data.w = b._data.w; }
    KOKKOS_INLINE_FUNCTION Vector(const double4 &val) { _data.x = val.x; _data.y = val.y; _data.z = val.z; _data.w = val.w; }

    template<typename ArgValueType>
    KOKKOS_INLINE_FUNCTION Vector(const ArgValueType &val) {
      _data.x = val; 
      _data.y = val;
      _data.z = val; 
      _data.w = val;
    }

    template<typename ArgValueType>
    KOKKOS_INLINE_FUNCTION Vector(const Vector<SIMD<ArgValueType>,vector_length> &b) {
      _data.x = b[0];
      _data.y = b[1];
      _data.z = b[2];
      _data.w = b[3];
    }

    KOKKOS_INLINE_FUNCTION
    type& operator=(const double4 &val) {
      _data.x = val.x;
      _data.y = val.y;
      _data.z = val.z;
      _data.w = val.w;
      return *this;
    }

    KOKKOS_INLINE_FUNCTION
    double4 double4() const {
      return _data;
    }

    KOKKOS_INLINE_FUNCTION
    type& loadAligned(const value_type *p) {
      _data.x = *(p  );
      _data.y = *(p+1);
      _data.z = *(p+2);
      _data.w = *(p+3);
      return *this;
    }

    KOKKOS_INLINE_FUNCTION
    type& loadUnaligned(const value_type *p) {
      _data.x = *(p  );
      _data.y = *(p+1);
      _data.z = *(p+2);
      _data.w = *(p+3);
      return *this;
    }

    KOKKOS_INLINE_FUNCTION
    void storeAligned(value_type *p) const {
      *(p  ) = _data.x;
      *(p+1) = _data.y;
      *(p+2) = _data.z;
      *(p+3) = _data.w;
    }

    KOKKOS_INLINE_FUNCTION
    void storeUnaligned(value_type *p) const {
      *(p  ) = _data.x;
      *(p+1) = _data.y;
      *(p+2) = _data.z;
      *(p+3) = _data.w;
    }

    KOKKOS_INLINE_FUNCTION
    value_type& operator[](const int &i) const {
      return reinterpret_cast<value_type*>(&_data)[i];
    }
  };


}
#endif


#if defined(__KOKKOSBATCHED_ENABLE_AVX__)
#if defined(__AVX__) || defined(__AVX2__)
#include <immintrin.h>

namespace KokkosBatched {

  template<>
  class Vector<SIMD<double>,4> {
  public:
    using type = Vector<SIMD<double>,4>;
    using value_type = double;
    using mag_type = double;

    enum : int { vector_length = 4 };
    typedef __m256d data_type __attribute__ ((aligned(32)));

    inline
    static const char* label() { return "AVX256"; }

    template<typename,int>
    friend class Vector;

  private:
    mutable data_type _data;

  public:
    inline Vector() { _data = _mm256_setzero_pd(); }
    inline Vector(const value_type &val) { _data = _mm256_set1_pd(val); }
    inline Vector(const type &b) { _data = b._data; }
    inline Vector(const __m256d &val) { _data = val; }

    template<typename ArgValueType>
    inline Vector(const ArgValueType &val) {
      auto d = reinterpret_cast<value_type*>(&_data);
#if defined( KOKKOS_ENABLE_PRAGMA_IVDEP )
#pragma ivdep
#endif
#if defined( KOKKOS_ENABLE_PRAGMA_VECTOR )
#pragma vector always
#endif
#ifdef KOKKOSKERNELS_ENABLE_OMP_SIMD
#pragma omp simd
#endif
      for (int i=0;i<vector_length;++i)
        d[i] = val;
    }

    template<typename ArgValueType>
    inline Vector(const Vector<SIMD<ArgValueType>,vector_length> &b) {
      auto dd = reinterpret_cast<value_type*>(&_data);
      auto bb = reinterpret_cast<ArgValueType*>(&b._data);
#if defined( KOKKOS_ENABLE_PRAGMA_IVDEP )
#pragma ivdep
#endif
#if defined( KOKKOS_ENABLE_PRAGMA_VECTOR )
#pragma vector always
#endif
#ifdef KOKKOSKERNELS_ENABLE_OMP_SIMD
#pragma omp simd
#endif
      for (int i=0;i<vector_length;++i)
        dd[i] = bb[i];
    }

    inline
    type& operator=(const __m256d &val) {
      _data = val;
      return *this;
    }

    inline
    operator __m256d() const {
      return _data;
    }

    inline
    type& loadAligned(const value_type *p) {
      _data = _mm256_load_pd(p);
      return *this;
    }

    inline
    type& loadUnaligned(const value_type *p) {
      _data = _mm256_loadu_pd(p);
      return *this;
    }

    inline
    void storeAligned(value_type *p) const {
      _mm256_store_pd(p, _data);
    }

    inline
    void storeUnaligned(value_type *p) const {
      _mm256_storeu_pd(p, _data);
    }

    inline
    value_type& operator[](const int &i) const {
      return reinterpret_cast<value_type*>(&_data)[i];
    }
  };

  template<>
  class Vector<SIMD<Kokkos::complex<double> >,2> {
  public:
    using type = Vector<SIMD<Kokkos::complex<double> >,2>;
    using value_type = Kokkos::complex<double>;
    using mag_type = double;

    static const int vector_length = 2;
    typedef __m256d data_type __attribute__ ((aligned(32)));

    inline
    static const char* label() { return "AVX256"; }
      
    template<typename,int>
    friend class Vector;

  private:
    mutable data_type _data;

  public:
    inline Vector() { _data = _mm256_setzero_pd(); }
    inline Vector(const value_type &val) { _data = _mm256_broadcast_pd((const __m128d *)&val);}
    inline Vector(const mag_type &val) { const value_type a(val); _data = _mm256_broadcast_pd((__m128d const *)&a); }
    inline Vector(const type &b) { _data = b._data; }
    inline Vector(const __m256d &val) { _data = val; }
      
    //       template<typename ArgValueType>
    //       inline Vector(const ArgValueType val) {
    // #if defined( KOKKOS_ENABLE_PRAGMA_IVDEP )
    // #pragma ivdep
    // #endif
    // #if defined( KOKKOS_ENABLE_PRAGMA_VECTOR )
    // #pragma vector always
    // #endif
    //         for (int i=0;i<vector_length;++i)
    //           _data.d[i] = value_type(val);
    //       }
    template<typename ArgValueType>
    inline Vector(const Vector<SIMD<ArgValueType>,vector_length> &b) {
      auto dd = reinterpret_cast<value_type*>(&_data);
      auto bb = reinterpret_cast<ArgValueType*>(&b._data);
#if defined( KOKKOS_ENABLE_PRAGMA_IVDEP )
#pragma ivdep
#endif
#if defined( KOKKOS_ENABLE_PRAGMA_VECTOR )
#pragma vector always
#endif
#ifdef KOKKOSKERNELS_ENABLE_OMP_SIMD
#pragma omp simd
#endif
      for (int i=0;i<vector_length;++i)
        dd[i] = bb[i];
    }

    inline
    type& operator=(const __m256d &val) {
      _data = val;
      return *this;
    }

    inline
    operator __m256d() const {
      return _data;
    }

    inline
    type& loadAligned(const value_type *p) {
      _data = _mm256_load_pd((mag_type*)p);
      return *this;
    }

    inline
    type& loadUnaligned(const value_type *p) {
      _data = _mm256_loadu_pd((mag_type*)p);
      return *this;
    }

    inline
    void storeAligned(value_type *p) const {
      _mm256_store_pd((mag_type*)p, _data);
    }

    inline
    void storeUnaligned(value_type *p) const {
      _mm256_storeu_pd((mag_type*)p, _data);
    }

    inline
    value_type& operator[](const int &i) const {
      return reinterpret_cast<value_type*>(&_data)[i];
    }
  };
}
#endif /* #if defined(__AVX__) || defined(__AVX2__) */

#if defined(__AVX512F__)
#include <immintrin.h>

namespace KokkosBatched {

  template<>
  class Vector<SIMD<double>,8> {
  public:
    using type = Vector<SIMD<double>,8>;
    using value_type = double;
    using mag_type = double;
      
    enum : int { vector_length = 8 };
    typedef __m512d data_type __attribute__ ((aligned(64)));

    template<typename,int>
    friend class Vector;

  private:
    mutable data_type _data;

  public:
    inline Vector() { _data = _mm512_setzero_pd(); }
    inline Vector(const value_type &val) { _data = _mm512_set1_pd(val); }
    inline Vector(const type &b) { _data = b._data; }
    inline Vector(const __m512d &val) { _data = val; }

    template<typename ArgValueType>
    inline Vector(const ArgValueType &val) {
      auto d = reinterpret_cast<value_type*>(&_data);
#if defined( KOKKOS_ENABLE_PRAGMA_IVDEP )
#pragma ivdep
#endif
#if defined( KOKKOS_ENABLE_PRAGMA_VECTOR )
#pragma vector always
#endif
#ifdef KOKKOSKERNELS_ENABLE_OMP_SIMD
#pragma omp simd
#endif
      for (int i=0;i<vector_length;++i)
        d[i] = val;
    }
    template<typename ArgValueType>
    inline Vector(const Vector<SIMD<ArgValueType>,vector_length> &b) {
      auto dd = reinterpret_cast<value_type*>(&_data);
      auto bb = reinterpret_cast<ArgValueType*>(&b._data);
#if defined( KOKKOS_ENABLE_PRAGMA_IVDEP )
#pragma ivdep
#endif
#if defined( KOKKOS_ENABLE_PRAGMA_VECTOR )
#pragma vector always
#endif
#ifdef KOKKOSKERNELS_ENABLE_OMP_SIMD
#pragma omp simd
#endif
      for (int i=0;i<vector_length;++i)
        dd[i] = bb[i];
    }

    inline
    type& operator=(const __m512d &val) {
      _data = val;
      return *this;
    }

    inline
    operator __m512d() const {
      return _data;
    }

    inline
    type& loadAligned(const value_type *p) {
      _data = _mm512_load_pd(p);
      return *this;
    }

    inline
    type& loadUnaligned(const value_type *p) {
      _data = _mm512_loadu_pd(p);
      return *this;
    }

    inline
    void storeAligned(value_type *p) const {
      _mm512_store_pd(p, _data);
    }

    inline
    void storeUnaligned(value_type *p) const {
      _mm512_storeu_pd(p, _data);
    }

    inline
    value_type& operator[](const int &i) const {
      return reinterpret_cast<value_type*>(&_data)[i];
    }
  };

  template<>
  class Vector<SIMD<Kokkos::complex<double> >,4> {
  public:
    using type = Vector<SIMD<Kokkos::complex<double> >,4>;
    using value_type = Kokkos::complex<double>;
    using mag_type = double;

    enum : int { vector_length = 4 };
    typedef __m512d data_type __attribute__ ((aligned(64)));

    inline
    static const char* label() { return "AVX512"; }

    template<typename,int>
    friend class Vector;

  private:
    mutable data_type _data;

  public:
    inline Vector() { _data = _mm512_setzero_pd(); }
    inline Vector(const value_type &val) {
      _data = _mm512_mask_broadcast_f64x4(_mm512_set1_pd(val.imag()), 0x55, _mm256_set1_pd(val.real()));
    }
    inline Vector(const mag_type &val) {
      _data = _mm512_mask_broadcast_f64x4(_mm512_setzero_pd(), 0x55, _mm256_set1_pd(val));
    }
    inline Vector(const type &b) { _data = b._data; }
    inline Vector(const __m512d &val) { _data = val; }

    template<typename ArgValueType>
    inline Vector(const ArgValueType &val) {
      auto d = reinterpret_cast<value_type*>(&_data);
#if defined( KOKKOS_ENABLE_PRAGMA_IVDEP )
#pragma ivdep
#endif
#if defined( KOKKOS_ENABLE_PRAGMA_VECTOR )
#pragma vector always
#endif
#ifdef KOKKOSKERNELS_ENABLE_OMP_SIMD
#pragma omp simd
#endif
      for (int i=0;i<vector_length;++i)
        d[i] = val;
    }
    template<typename ArgValueType>
    inline Vector(const Vector<SIMD<ArgValueType>,vector_length> &b) {
      auto dd = reinterpret_cast<value_type*>(&_data);
      auto bb = reinterpret_cast<value_type*>(&b._data);
#if defined( KOKKOS_ENABLE_PRAGMA_IVDEP )
#pragma ivdep
#endif
#if defined( KOKKOS_ENABLE_PRAGMA_VECTOR )
#pragma vector always
#endif
#ifdef KOKKOSKERNELS_ENABLE_OMP_SIMD
#pragma omp simd
#endif
      for (int i=0;i<vector_length;++i)
        dd[i] = bb[i];
    }

    inline
    type& operator=(const __m512d &val) {
      _data = val;
      return *this;
    }

    inline
    operator __m512d() const {
      return _data;
    }

    inline
    type& loadAligned(const value_type *p) {
      _data = _mm512_load_pd((mag_type*)p);
      return *this;
    }

    inline
    type& loadUnaligned(const value_type *p) {
      _data = _mm512_loadu_pd((mag_type*)p);
      return *this;
    }

    inline
    void storeAligned(value_type *p) const {
      _mm512_store_pd((mag_type*)p, _data);
    }

    inline
    void storeUnaligned(value_type *p) const {
      _mm512_storeu_pd((mag_type*)p, _data);
    }

    inline
    value_type& operator[](const int &i) const {
      return reinterpret_cast<value_type*>(&_data)[i];
    }
  };
}

#endif /* #if defined(__AVX512F__) */
#endif /* #if defined(__KOKKOSBATCHED_ENABLE_AVX__) */

#include "KokkosBatched_Vector_SIMD_Arith.hpp"
#include "KokkosBatched_Vector_SIMD_Logical.hpp"
#include "KokkosBatched_Vector_SIMD_Relation.hpp"
#include "KokkosBatched_Vector_SIMD_Math.hpp"
#include "KokkosBatched_Vector_SIMD_Misc.hpp"
#include "KokkosBatched_Vector_SIMD_View.hpp"

#endif
