text: #pragma once
  
  #include <array>
  #include <iostream>
  
  #include "util_bit.h"
  #include "util_math.h"
  
  namespace dxvk {
  
    template <typename T>
    struct Vector4Base {
      Vector4Base()
        : x{ }, y{ }, z{ }, w{ } { }
  
      Vector4Base(T splat)
        : x(splat), y(splat), z(splat), w(splat) { }
  
      Vector4Base(T x, T y, T z, T w)
        : x(x), y(y), z(z), w(w) { }
  
      Vector4Base(const T xyzw[4])
        : x(xyzw[0]), y(xyzw[1]), z(xyzw[2]), w(xyzw[3]) { }
  
      inline       T& operator[](size_t index)       { return m_data[index]; }
      inline const T& operator[](size_t index) const { return m_data[index]; }
  
      bool operator==(const Vector4Base<T>& other) const {
        return m_data == other.m_data;
      }
  
      bool operator!=(const Vector4Base<T>& other) const {
        return !operator==(other);
      }
  
      Vector4Base operator-() const { return {-x, -y, -z, -w}; }
  
      Vector4Base operator+(const Vector4Base<T>& other) const {
        return {x + other.x, y + other.y, z + other.z, w + other.w};
      }
  
      Vector4Base operator-(const Vector4Base<T>& other) const {
        return {x - other.x, y - other.y, z - other.z, w - other.w};
      }
  
      Vector4Base operator*(T scalar) const {
        return {scalar * x, scalar * y, scalar * z, scalar * w};
      }
  
      Vector4Base operator*(const Vector4Base<T>& other) const {
        Vector4Base result;
        for (uint32_t i = 0; i < m_data.size(); i++)
          result[i] = m_data[i] * other.m_data[i];
        return result;
      }
  
      Vector4Base operator/(const Vector4Base<T>& other) const {
        Vector4Base result;
        for (uint32_t i = 0; i < m_data.size(); i++)
          result[i] = m_data[i] / other.m_data[i];
        return result;
      }
  
      Vector4Base operator/(T scalar) const {
        return {x / scalar, y / scalar, z / scalar, w / scalar};
      }
  
      Vector4Base& operator+=(const Vector4Base<T>& other) {
        x += other.x;
        y += other.y;
        z += other.z;
        w += other.w;
  
        return *this;
      }
  
      Vector4Base& operator-=(const Vector4Base<T>& other) {
        x -= other.x;
        y -= other.y;
        z -= other.z;
        w -= other.w;
  
        return *this;
      }
  
      Vector4Base& operator*=(T scalar) {
        x *= scalar;
        y *= scalar;
        z *= scalar;
        w *= scalar;
  
        return *this;
      }
  
      Vector4Base& operator/=(T scalar) {
        x /= scalar;
        y /= scalar;
        z /= scalar;
        w /= scalar;
  
        return *this;
      }
  
      inline T* data() { return m_data.data(); };
      inline const T* data() const { return m_data.data(); };
  
      union {
        std::array<T, 4> m_data;
        struct {
          T x, y, z, w;
        };
        struct {
          T r, g, b, a;
        };
      };
  
    };
  
    template <typename T>
    inline Vector4Base<T> operator*(T scalar, const Vector4Base<T>& vector) {
      return vector * scalar;
    }
  
    template <typename T>
    float dot(const Vector4Base<T>& a, const Vector4Base<T>& b) {
      return a.x * b.x + a.y * b.y + a.z * b.z + a.w * b.w;
    }
  
    template <typename T>
    T lengthSqr(const Vector4Base<T>& a) { return dot(a, a); }
  
    template <typename T>
    float length(const Vector4Base<T>& a) { return std::sqrt(float(lengthSqr(a))); }
  
    template <typename T>
    Vector4Base<T> normalize(const Vector4Base<T>& a) { return a * T(1.0f / length(a)); }
  
    template <typename T>
    std::ostream& operator<<(std::ostream& os, const Vector4Base<T>& v) {
      return os << "Vector4(" << v[0] << ", " << v[1] << ", " << v[2] << ", " << v[3] << ")";
    }
  
    using Vector4  = Vector4Base<float>;
    using Vector4i = Vector4Base<int>;
  
    static_assert(sizeof(Vector4)  == sizeof(float) * 4);
    static_assert(sizeof(Vector4i) == sizeof(int)   * 4);
  
    inline Vector4 replaceNaN(Vector4 a) {
      Vector4 result;
      __m128 value = _mm_load_ps(a.data());
      __m128 mask  = _mm_cmpeq_ps(value, value);
             value = _mm_and_ps(value, mask);
      _mm_store_ps(result.data(), value);
      return result;
    }
  
  }
  