text: // Copyright © Amer Koleci and Contributors.
  // Licensed under the MIT License (MIT). See LICENSE in the repository root for more information.
  
  #pragma once
  
  #include "Math/Vector3.h"
  
  namespace Alimer
  {
      /// Four-dimensional vector.
      struct ALIMER_API Vector4
      {
      public:
          /// X coordinate.
          float x;
          /// Y coordinate.
          float y;
          /// Z coordinate.
          float z;
          /// W coordinate.
          float w;
  
          Vector4() noexcept
              : x(0.0f)
              , y(0.0f)
              , z(0.0f)
              , w(0.0f)
          {
          }
  
          constexpr explicit Vector4(float value) noexcept
              : x(value)
              , y(value)
              , z(value)
              , w(value)
          {
          }
  
          /// Construct from a 3-dimensional vector and the W coordinate.
          constexpr Vector4(const Vector3& vector, float w_) noexcept
              : x(vector.x)
              , y(vector.y)
              , z(vector.z)
              , w(w_)
          {
          }
  
          /// Construct from coordinates.
          constexpr Vector4(float x_, float y_, float z_, float w_) noexcept
              : x(x_)
              , y(y_)
              , z(z_)
              , w(w_)
          {
          }
  
          /// Construct from a float array.
          explicit Vector4(_In_reads_(4) const float* data)
              : x(data[0])
              , y(data[1])
              , z(data[2])
              , w(data[3])
          {
          }
  
          Vector4(const Vector4&) = default;
          Vector4& operator=(const Vector4&) = default;
          Vector4(Vector4&&) = default;
          Vector4& operator=(Vector4&&) = default;
  
          /// Test for equality with another vector without epsilon.
          bool operator == (const Vector4& rhs) const { return x == rhs.x && y == rhs.y && z == rhs.z && w == rhs.w; }
          /// Test for inequality with another vector without epsilon.
          bool operator != (const Vector4& rhs) const { return !(*this == rhs); }
          /// Add a vector.
          Vector4 operator + (const Vector4& rhs) const { return Vector4(x + rhs.x, y + rhs.y, z + rhs.z, w + rhs.w); }
          /// Return negation.
          Vector4 operator - () const { return Vector4(-x, -y, -z, -w); }
          /// Subtract a vector.
          Vector4 operator - (const Vector4& rhs) const { return Vector4(x - rhs.x, y - rhs.y, z - rhs.z, w - rhs.w); }
          /// Multiply with a scalar.
          Vector4 operator * (float rhs) const { return Vector4(x * rhs, y * rhs, z * rhs, w * rhs); }
          /// Multiply with a vector.
          Vector4 operator * (const Vector4& rhs) const { return Vector4(x * rhs.x, y * rhs.y, z * rhs.z, w * rhs.w); }
          /// Divide by a scalar.
          Vector4 operator / (float rhs) const { return Vector4(x / rhs, y / rhs, z / rhs, w / rhs); }
          /// Divide by a vector.
          Vector4 operator / (const Vector4& rhs) const { return Vector4(x / rhs.x, y / rhs.y, z / rhs.z, w / rhs.w); }
  
          /// Add-assign a vector.
          Vector4& operator += (const Vector4& rhs)
          {
              x += rhs.x;
              y += rhs.y;
              z += rhs.z;
              w += rhs.w;
              return *this;
          }
  
          /// Subtract-assign a vector.
          Vector4& operator -= (const Vector4& rhs)
          {
              x -= rhs.x;
              y -= rhs.y;
              z -= rhs.z;
              w -= rhs.w;
              return *this;
          }
  
          /// Multiply-assign a scalar.
          Vector4& operator *= (float rhs)
          {
              x *= rhs;
              y *= rhs;
              z *= rhs;
              w *= rhs;
              return *this;
          }
  
          /// Multiply-assign a vector.
          Vector4& operator *= (const Vector4& rhs)
          {
              x *= rhs.x;
              y *= rhs.y;
              z *= rhs.z;
              w *= rhs.w;
              return *this;
          }
  
          /// Divide-assign a scalar.
          Vector4& operator /= (float rhs)
          {
              float invRhs = 1.0f / rhs;
              x *= invRhs;
              y *= invRhs;
              z *= invRhs;
              w *= invRhs;
              return *this;
          }
  
          /// Divide-assign a vector.
          Vector4& operator /= (const Vector4& rhs)
          {
              x /= rhs.x;
              y /= rhs.y;
              z /= rhs.z;
              w /= rhs.w;
              return *this;
          }
  
          /// Calculate dot product.
          float DotProduct(const Vector4& rhs) const { return x * rhs.x + y * rhs.y + z * rhs.z + w * rhs.w; }
          /// Calculate absolute dot product.
          float AbsDotProduct(const Vector4& rhs) const { return Alimer::Abs(x * rhs.x) + Alimer::Abs(y * rhs.y) + Alimer::Abs(z * rhs.z) + Alimer::Abs(w * rhs.w); }
          /// Return absolute vector.
          Vector4 Abs() const { return Vector4(Alimer::Abs(x), Alimer::Abs(y), Alimer::Abs(z), Alimer::Abs(w)); }
          /// Linear interpolation with another vector.
          Vector4 Lerp(const Vector4& rhs, float t) const { return *this * (1.0f - t) + rhs * t; }
          /// Test for equality with another vector with epsilon.
          bool Equals(const Vector4& rhs) const { return Alimer::Equals(x, rhs.x) && Alimer::Equals(y, rhs.y) && Alimer::Equals(z, rhs.z) && Alimer::Equals(w, rhs.w); }
          /// Return whether is NaN.
          bool IsNaN() const { return Alimer::IsNaN(x) || Alimer::IsNaN(y) || Alimer::IsNaN(z) || Alimer::IsNaN(w); }
  
          /// Return float data.
          const float* Data() const { return &x; }
  
          /// Return as string.
          std::string ToString() const;
  
          /// Zero vector.
          static const Vector4 Zero;
          /// (1,1,1) vector.
          static const Vector4 One;
          static const Vector4 UnitX;
          static const Vector4 UnitY;
          static const Vector4 UnitZ;
          static const Vector4 UnitW;
      };
  
      /// Four-dimensional vector with 32 bit signed integer components.
      struct ALIMER_API Int4
      {
      public:
          /// X coordinate.
          int32_t x;
          /// Y coordinate.
          int32_t y;
          /// Z coordinate.
          int32_t z;
          /// W coordinate.
          int32_t w;
  
          Int4() = default;
  
          Int4(const Int4&) = default;
          Int4& operator=(const Int4&) = default;
  
          Int4(Int4&&) = default;
          Int4& operator=(Int4&&) = default;
  
          constexpr Int4(int32_t x_, int32_t y_, int32_t z_, int32_t w_) noexcept
              : x(x_)
              , y(y_)
              , z(z_)
              , w(w_)
          {
          }
  
          /// Construct from a float array.
          explicit Int4(_In_reads_(4) const int32_t* data)
              : x(data[0])
              , y(data[1])
              , z(data[2])
              , w(data[3])
          {
          }
      };
  
      /// Four-dimensional vector with 32 bit unsigned integer components.
      struct ALIMER_API UInt4
      {
      public:
          /// X coordinate.
          uint32_t x;
          /// Y coordinate.
          uint32_t y;
          /// Z coordinate.
          uint32_t z;
          /// W coordinate.
          uint32_t w;
  
          UInt4() = default;
  
          UInt4(const UInt4&) = default;
          UInt4& operator=(const UInt4&) = default;
  
          UInt4(UInt4&&) = default;
          UInt4& operator=(UInt4&&) = default;
  
          constexpr UInt4(uint32_t x_, uint32_t y_, uint32_t z_, uint32_t w_) noexcept
              : x(x_)
              , y(y_)
              , z(z_)
              , w(w_)
          {
          }
  
          /// Construct from a float array.
          explicit UInt4(_In_reads_(4) const uint32_t* data)
              : x(data[0])
              , y(data[1])
              , z(data[2])
              , w(data[3])
          {
          }
      };
  
      /// Multiply Vector4 with a scalar.
      inline Vector4 operator * (float lhs, const Vector4& rhs) { return rhs * lhs; }
  
      using float4 = Vector4;
      using int4 = Int4;
      using uint4 = UInt4;
  }
  