Also implement a couple make_float4x4_* functions that are useful
for constructing input for some tests.

This reduces the number of unimplemented functions in this header
a little bit.

Signed-off-by: Martin Storsjö <mar...@martin.st>

---
For some tests, this doesn't use literal hardcoded inputs, but
composes the test inputs via other utility functions.

Some implemented functions, such as the quaternion transform()
functions, only match the WinSDK implementation for normalized
quaternions (which practically is the only reasonable usecase anyway);
the WinSDK implementation assumes normalized quaternions.
---
 .../include/windowsnumerics.impl.h            | 179 ++++++++++++++++--
 .../testcases/test_windowsnumerics.cpp        | 113 ++++++++++-
 2 files changed, 271 insertions(+), 21 deletions(-)

diff --git a/mingw-w64-headers/include/windowsnumerics.impl.h 
b/mingw-w64-headers/include/windowsnumerics.impl.h
index 7785b1eba..8409eb251 100644
--- a/mingw-w64-headers/include/windowsnumerics.impl.h
+++ b/mingw-w64-headers/include/windowsnumerics.impl.h
@@ -749,7 +749,11 @@ _WINDOWS_NUMERICS_BEGIN_NAMESPACE_ {
   inline float2 transform(const float2 &pos, const float4x4 &mat);
   inline float2 transform_normal(const float2 &norm, const float3x2 &mat);
   inline float2 transform_normal(const float2 &norm, const float4x4 &mat);
-  inline float2 transform(const float2 &val, const quaternion &rot);
+  inline float2 transform(const float2 &val, const quaternion &rot) {
+    // See comments in the float3 transform function.
+    quaternion result = rot * quaternion(float3(val.x, val.y, 0.0f), 0.0f) * 
inverse(rot);
+    return { result.x, result.y };
+  }
 
 } _WINDOWS_NUMERICS_END_NAMESPACE_
 
@@ -801,7 +805,16 @@ _WINDOWS_NUMERICS_BEGIN_NAMESPACE_ {
   // TODO: impl
   inline float3 transform(const float3 &pos, const float4x4 &mat);
   inline float3 transform_normal(const float3 &norm, const float4x4 &mat);
-  inline float3 transform(const float3 &val, const quaternion &rot);
+  inline float3 transform(const float3 &val, const quaternion &rot) {
+    // 
https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation#Using_quaternions_as_rotations
+    // If assuming rot is a unit quaternion, this could use
+    // conjugate() instead of inverse() too.
+    // This can be expressed as a matrix operation too, with
+    // 
https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation#Quaternion-derived_rotation_matrix
+    // (see make_float4x4_from_quaternion).
+    quaternion result = rot * quaternion(val, 0.0f) * inverse(rot);
+    return { result.x, result.y, result.z };
+  }
 
 } _WINDOWS_NUMERICS_END_NAMESPACE_
 
@@ -863,9 +876,19 @@ _WINDOWS_NUMERICS_BEGIN_NAMESPACE_ {
   inline float4 transform(const float4 &pos, const float4x4 &mat);
   inline float4 transform4(const float3 &pos, const float4x4 &mat);
   inline float4 transform4(const float2 &pos, const float4x4 &mat);
-  inline float4 transform(const float4 &val, const quaternion &rot);
-  inline float4 transform4(const float3 &val, const quaternion &rot);
-  inline float4 transform4(const float2 &val, const quaternion &rot);
+  inline float4 transform(const float4 &val, const quaternion &rot) {
+    // See comments in the float3 transform function.
+    quaternion result = rot * quaternion(float3(val.x, val.y, val.z), 0.0f) * 
inverse(rot);
+    return { result.x, result.y, result.z, val.w };
+  }
+  inline float4 transform4(const float3 &val, const quaternion &rot) {
+    quaternion result = rot * quaternion(val, 0.0f) * inverse(rot);
+    return { result.x, result.y, result.z, 1.0f };
+  }
+  inline float4 transform4(const float2 &val, const quaternion &rot) {
+    quaternion result = rot * quaternion(float3(val.x, val.y, 0.0f), 0.0f) * 
inverse(rot);
+    return { result.x, result.y, result.z, 1.0f };
+  }
 
 } _WINDOWS_NUMERICS_END_NAMESPACE_
 
@@ -910,11 +933,25 @@ _WINDOWS_NUMERICS_BEGIN_NAMESPACE_ {
   // TODO: impl
   inline float4x4 make_float4x4_billboard(const float3 &objpos, const float3 
&camerapos, const float3 &cameraup, const float3 &camerafwd);
   inline float4x4 make_float4x4_constrained_billboard(const float3 &objpos, 
const float3 &camerapos, const float3 &rotateaxis, const float3 &camerafwd, 
const float3 &objfwd);
-  inline float4x4 make_float4x4_translation(const float3 &pos);
+  inline float4x4 make_float4x4_translation(const float3 &pos) {
+    return {
+      1.0f,  0.0f,  0.0f,  0.0f,
+      0.0f,  1.0f,  0.0f,  0.0f,
+      0.0f,  0.0f,  1.0f,  0.0f,
+      pos.x, pos.y, pos.z, 1.0f
+    };
+  }
   inline float4x4 make_float4x4_translation(float xpos, float ypos, float 
zpos);
   inline float4x4 make_float4x4_scale(float xscale, float yscale, float 
zscale);
   inline float4x4 make_float4x4_scale(float xscale, float yscale, float 
zscale, const float3 &center);
-  inline float4x4 make_float4x4_scale(const float3 &xyzscale);
+  inline float4x4 make_float4x4_scale(const float3 &xyzscale) {
+    return {
+      xyzscale.x, 0.0f,       0.0f,       0.0f,
+      0.0f,       xyzscale.y, 0.0f,       0.0f,
+      0.0f,       0.0f,       xyzscale.z, 0.0f,
+      0.0f,       0.0f,       0.0f,       1.0f
+    };
+  }
   inline float4x4 make_float4x4_scale(const float3 &xyzscale, const float3 
&center);
   inline float4x4 make_float4x4_scale(float scale);
   inline float4x4 make_float4x4_scale(float scale, const float3 &center);
@@ -932,7 +969,25 @@ _WINDOWS_NUMERICS_BEGIN_NAMESPACE_ {
   inline float4x4 make_float4x4_orthographic_off_center(float left, float 
right, float bottom, float top, float znearplane, float zfarplane);
   inline float4x4 make_float4x4_look_at(const float3 &camerapos, const float3 
&target, const float3 &cameraup);
   inline float4x4 make_float4x4_world(const float3 &pos, const float3 &fwd, 
const float3 &up);
-  inline float4x4 make_float4x4_from_quaternion(const quaternion &quat);
+  inline float4x4 make_float4x4_from_quaternion(const quaternion &quat) {
+    // 
https://en.wikipedia.org/wiki/Quaternions_and_spatial_rotation#Quaternion-derived_rotation_matrix
+    float s = 1.0f / length_squared(quat);
+    float xx = quat.x * quat.x;
+    float yy = quat.y * quat.y;
+    float zz = quat.z * quat.z;
+    float xy = quat.x * quat.y;
+    float xz = quat.x * quat.z;
+    float xw = quat.x * quat.w;
+    float yz = quat.y * quat.z;
+    float yw = quat.y * quat.w;
+    float zw = quat.z * quat.w;
+    return {
+      1.0f - 2.0f*s*(yy + zz), 2.0f*s*(xy + zw),        2.0f*s*(xz - yw),      
  0.0f,
+      2.0f*s*(xy - zw),        1.0f - 2.0f*s*(xx + zz), 2.0f*s*(yz + xw),      
  0.0f,
+      2.0f*s*(xz + yw),        2.0f*s*(yz - xw),        1.0f - 2.0f*s*(xx + 
yy), 0.0f,
+      0.0f,                    0.0f,                    0.0f,                  
  1.0f,
+    };
+  }
   inline float4x4 make_float4x4_from_yaw_pitch_roll(float yaw, float pitch, 
float roll);
   inline float4x4 make_float4x4_shadow(const float3 &lightdir, const plane 
&plane);
   inline float4x4 make_float4x4_reflection(const plane &plane);
@@ -955,8 +1010,26 @@ _WINDOWS_NUMERICS_BEGIN_NAMESPACE_ {
     return { val.m41, val.m42, val.m43 };
   }
   inline bool invert(const float4x4 &mat, float4x4 *out);
-  inline bool decompose(const float4x4 &mat, float3 *out_scale, quaternion 
*out_rot, float3 *out_translate);
-  inline float4x4 transform(const float4x4 &val, const quaternion &rot);
+  inline bool decompose(const float4x4 &mat, float3 *out_scale, quaternion 
*out_rot, float3 *out_translate) {
+    float4x4 val = mat;
+    if (out_translate)
+      *out_translate = translation(val);
+    val.m41 = val.m42 = val.m43 = 0.0f;
+    float3 scale = float3(
+      length(float3(val.m11, val.m12, val.m13)),
+      length(float3(val.m21, val.m22, val.m23)),
+      length(float3(val.m31, val.m32, val.m33))
+    );
+    if (out_scale)
+      *out_scale = scale;
+    val = make_float4x4_scale(float3(1.0f/scale.x, 1.0f/scale.y, 
1.0f/scale.z)) * val;
+    if (out_rot)
+      *out_rot = make_quaternion_from_rotation_matrix(val);
+    return true;
+  }
+  inline float4x4 transform(const float4x4 &val, const quaternion &rot) {
+    return val * make_float4x4_from_quaternion(rot);
+  }
   inline float4x4 transpose(const float4x4 &val) {
     return  {
       val.m11, val.m21, val.m31, val.m41,
@@ -980,7 +1053,10 @@ _WINDOWS_NUMERICS_BEGIN_NAMESPACE_ {
     return { val.normal * invlen, val.d * invlen };
   }
   inline plane transform(const plane &plane, const float4x4 &mat);
-  inline plane transform(const plane &plane, const quaternion &rot);
+  inline plane transform(const plane &plane, const quaternion &rot) {
+    quaternion result = rot * quaternion(plane.normal, 0.0f) * inverse(rot);
+    return { float3(result.x, result.y, result.z), plane.d };
+  }
   inline float dot(const plane &plane, const float4 &val);
   inline float dot_coordinate(const plane &plane, const float3 &val);
   inline float dot_normal(const plane &plane, const float3 &val);
@@ -991,10 +1067,40 @@ _WINDOWS_NUMERICS_BEGIN_NAMESPACE_ {
 // Define quaternion functions
 _WINDOWS_NUMERICS_BEGIN_NAMESPACE_ {
 
-  // TODO: impl
-  inline quaternion make_quaternion_from_axis_angle(const float3 &axis, float 
angle);
-  inline quaternion make_quaternion_from_yaw_pitch_roll(float yaw, float 
pitch, float roll);
-  inline quaternion make_quaternion_from_rotation_matrix(const float4x4 &mat);
+  inline quaternion make_quaternion_from_axis_angle(const float3 &axis, float 
angle) {
+    return quaternion(axis * ::std::sin(angle * 0.5f), ::std::cos(angle * 
0.5f));
+  }
+  inline quaternion make_quaternion_from_yaw_pitch_roll(float yaw, float 
pitch, float roll) {
+    quaternion yq = make_quaternion_from_axis_angle(float3(0.0f, 1.0f, 0.0f), 
yaw);
+    quaternion pq = make_quaternion_from_axis_angle(float3(1.0f, 0.0f, 0.0f), 
pitch);
+    quaternion rq = make_quaternion_from_axis_angle(float3(0.0f, 0.0f, 1.0f), 
roll);
+    return concatenate(concatenate(rq, pq), yq);
+  }
+  inline quaternion make_quaternion_from_rotation_matrix(const float4x4 &mat) {
+    // https://en.wikipedia.org/wiki/Rotation_matrix#Quaternion
+    float t = mat.m11 + mat.m22 + mat.m33;
+    if (t > 0) {
+      float r = ::std::sqrt(1.0f + t);
+      float s = 1.0f / (2.0f * r);
+      return { (mat.m23 - mat.m32) * s, (mat.m31 - mat.m13) * s,
+               (mat.m12 - mat.m21) * s, r * 0.5f };
+    } else if (mat.m11 >= mat.m22 && mat.m11 >= mat.m33) {
+      float r = ::std::sqrt(1.0f + mat.m11 - mat.m22 - mat.m33);
+      float s = 1.0f / (2.0f * r);
+      return { r * 0.5f, (mat.m21 + mat.m12) * s,
+               (mat.m13 + mat.m31) * s, (mat.m23 - mat.m32) * s };
+    } else if (mat.m22 >= mat.m11 && mat.m22 >= mat.m33) {
+      float r = ::std::sqrt(1.0f + mat.m22 - mat.m11 - mat.m33);
+      float s = 1.0f / (2.0f * r);
+      return { (mat.m21 + mat.m12) * s, r * 0.5f,
+               (mat.m32 + mat.m23) * s, (mat.m31 - mat.m13) * s };
+    } else {
+      float r = ::std::sqrt(1.0f + mat.m33 - mat.m11 - mat.m22);
+      float s = 1.0f / (2.0f * r);
+      return { (mat.m13 + mat.m31) * s, (mat.m32 + mat.m23) * s,
+               r * 0.5f, (mat.m12 - mat.m21) * s };
+    }
+  }
   inline bool is_identity(const quaternion &val) {
     return val == quaternion::identity();
   }
@@ -1002,9 +1108,11 @@ _WINDOWS_NUMERICS_BEGIN_NAMESPACE_ {
     return ::std::sqrt(length_squared(val));
   }
   inline float length_squared(const quaternion &val) {
-    return val.x * val.x + val.y * val.y + val.z * val.z + val.w * val.w;
+    return dot(val, val);
+  }
+  inline float dot(const quaternion &val1, const quaternion &val2) {
+    return val1.x * val2.x + val1.y * val2.y + val1.z * val2.z + val1.w * 
val2.w;
   }
-  inline float dot(const quaternion &val1, const quaternion &val2);
   inline quaternion normalize(const quaternion &val) {
     return operator*(val, 1.0f / length(val));
   }
@@ -1014,9 +1122,40 @@ _WINDOWS_NUMERICS_BEGIN_NAMESPACE_ {
   inline quaternion inverse(const quaternion &val) {
     return operator*(conjugate(val), 1.0f / length_squared(val));
   }
-  inline quaternion slerp(const quaternion &val1, const quaternion &val2, 
float amount);
-  inline quaternion lerp(const quaternion &val1, const quaternion &val2, float 
amount);
-  inline quaternion concatenate(const quaternion &val1, const quaternion 
&val2);
+  inline quaternion slerp(const quaternion &val1, const quaternion &val2, 
float amount) {
+    // https://en.wikipedia.org/wiki/Slerp#Geometric_Slerp
+    float cosangle = dot(val1, val2);
+    quaternion end = val2;
+    if (cosangle < 0.0f) {
+      end = -val2;
+      cosangle = -cosangle;
+    }
+    float fact1, fact2;
+    const float epsilon = 1.0e-6f;
+    if (cosangle > 1.0f - epsilon) {
+      // Very small rotation range, or non-normalized input quaternions.
+      fact1 = (1.0f - amount);
+      fact2 = amount;
+    } else {
+      float angle = ::std::acos(cosangle);
+      float sinangle = ::std::sin(angle);
+      fact1 = ::std::sin((1.0f - amount) * angle) / sinangle;
+      fact2 = ::std::sin(amount * angle) / sinangle;
+    }
+    return val1 * fact1 + end * fact2;
+  }
+  inline quaternion lerp(const quaternion &val1, const quaternion &val2, float 
amount) {
+    quaternion end = val2;
+    if (dot(val1, val2) < 0.0f)
+      end = -val2;
+    return normalize(quaternion(
+      _impl::lerp(val1.x, end.x, amount), _impl::lerp(val1.y, end.y, amount),
+      _impl::lerp(val1.z, end.z, amount), _impl::lerp(val1.w, end.w, amount)
+    ));
+  }
+  inline quaternion concatenate(const quaternion &val1, const quaternion 
&val2) {
+    return val2 * val1;
+  }
 
 } _WINDOWS_NUMERICS_END_NAMESPACE_
 
diff --git a/mingw-w64-headers/testcases/test_windowsnumerics.cpp 
b/mingw-w64-headers/testcases/test_windowsnumerics.cpp
index e4c9ed5b0..a488e04cf 100644
--- a/mingw-w64-headers/testcases/test_windowsnumerics.cpp
+++ b/mingw-w64-headers/testcases/test_windowsnumerics.cpp
@@ -25,6 +25,8 @@
 
 using namespace Windows::Foundation::Numerics;
 
+constexpr float pi = 3.141592654f;
+
 std::ostream &operator<<(std::ostream &os, float2 const &v) {
   os << "{" << v.x << ", " << v.y << "}";
   return os;
@@ -377,6 +379,8 @@ TEST_CASE("float2-functions") {
   CHECK_THAT(lerp(v1, v2, -0.5f), eq2(0.5f, -2.0f));
   CHECK_THAT(lerp(v1, v2, 1.5f), eq2(10.5f, 22.0f));
   // TODO: transform
+  const quaternion q1 = normalize(quaternion(-1.0f, 2.0f, -3.0f, 4.0f));
+  CHECK_THAT(transform(v1, q1), eq2(3.06667f, -1.46667f));
 }
 
 // === float3 ===
@@ -474,6 +478,8 @@ TEST_CASE("float3-functions") {
   CHECK_THAT(lerp(v1, v2, -0.5f), eq3(0.5f, -2.0f, -4.5f));
   CHECK_THAT(lerp(v1, v2, 1.5f), eq3(10.5f, 22.0f, 33.5f));
   // TODO: transform
+  const quaternion q1 = normalize(quaternion(-1.0f, 2.0f, -3.0f, 4.0f));
+  CHECK_THAT(transform(v1, q1), eq3(6.73333f, -2.13333f, -0.333333f));
 }
 
 // === float4 ===
@@ -573,6 +579,12 @@ TEST_CASE("float4-functions") {
   CHECK_THAT(lerp(v1, v2, -0.5f), eq4(0.5f, -2.0f, -4.5f, 2.5f));
   CHECK_THAT(lerp(v1, v2, 1.5f), eq4(10.5f, 22.0f, 33.5f, 16.5f));
   // TODO: transform
+  const quaternion q1 = normalize(quaternion(-1.0f, 2.0f, -3.0f, 4.0f));
+  const float3 v4{ 3.0f, 4.0f, 5.0f };
+  const float2 v5{ 3.0f, 4.0f };
+  CHECK_THAT(transform(v1, q1), eq4(6.73333f, -2.13333f, -0.333333f, 6.0f));
+  CHECK_THAT(transform4(v4, q1), eq4(6.73333f, -2.13333f, -0.333333f, 1.0f));
+  CHECK_THAT(transform4(v5, q1), eq4(3.06667f, -1.46667f, -3.66667f, 1.0f));
 }
 
 // === float3x2 ===
@@ -872,6 +884,40 @@ TEST_CASE("float4x4-functions") {
     0.0f, 0.0f, 0.0f, 1.0f
   ));
   // TODO
+  CHECK_THAT(make_float4x4_translation(float3(1.0f, 2.0f, 3.0f)), eq4x4(
+    1.0f, 0.0f, 0.0f, 0.0f,
+    0.0f, 1.0f, 0.0f, 0.0f,
+    0.0f, 0.0f, 1.0f, 0.0f,
+    1.0f, 2.0f, 3.0f, 1.0f
+  ));
+  CHECK_THAT(make_float4x4_scale(float3(4.0f, 5.0f, 6.0f)), eq4x4(
+    4.0f, 0.0f, 0.0f, 0.0f,
+    0.0f, 5.0f, 0.0f, 0.0f,
+    0.0f, 0.0f, 6.0f, 0.0f,
+    0.0f, 0.0f, 0.0f, 1.0f
+  ));
+  const quaternion q1 = normalize(quaternion(-1.0f, 2.0f, -3.0f, 4.0f));
+  CHECK_THAT(make_float4x4_from_quaternion(q1), eq4x4(
+    0.133333f, -0.933333f, -0.333333f, 0.0f,
+    0.666667f,  0.333333f, -0.666667f, 0.0f,
+    0.733333f, -0.133333f,  0.666667f, 0.0f,
+    0.0f,       0.0f,       0.0f,      1.0f
+  ));
+  CHECK_THAT(transform(v1, q1), eq4x4(
+    3.66667f, -0.666667f, 0.333333f, 4.0f,
+    9.8f,     -3.6f,     -0.999999f, 8.0f,
+    15.9333f, -6.53333f, -2.33333f,  12.0f,
+    22.0667f, -9.46667f, -3.66667f,  16.0f
+  ));
+  const float3 scale = { 1.0f, 2.0f, 3.0f };
+  const float3 translate = { 4.0f, 5.0f, 6.0f };
+  const float4x4 v3 = make_float4x4_scale(scale) * 
make_float4x4_from_quaternion(q1) * make_float4x4_translation(translate);
+  float3 out_scale, out_translate;
+  quaternion out_rot;
+  CHECK(decompose(v3, &out_scale, &out_rot, &out_translate));
+  CHECK_THAT(out_scale, eq3(scale));
+  CHECK_THAT(out_rot, eqq(q1));
+  CHECK_THAT(out_translate, eq3(translate));
 }
 
 // === plane ===
@@ -896,6 +942,8 @@ TEST_CASE("plane-functions") {
   const plane v1 { 1.0f, 2.0f, 3.0f, 4.0f };
   CHECK_THAT(normalize(v1), eqp(0.267261f, 0.534522f, 0.801784f, 1.06904f));
   // TODO
+  const quaternion q1 = normalize(quaternion(-1.0f, 2.0f, -3.0f, 4.0f));
+  CHECK_THAT(transform(v1, q1), eqp(3.66667f, -0.666667f, 0.333333f, 4.0f));
 }
 
 // === quaternion ===
@@ -946,7 +994,70 @@ TEST_CASE("quaternion-functions") {
   CHECK_THAT(normalize(v1), eqq(0.182574f, 0.365148f, 0.547723f, 0.730297f));
   CHECK_THAT(conjugate(v1), eqq(-1.0f, -2.0f, -3.0f, 4.0f));
   CHECK_THAT(inverse(v1), eqq(-0.0333333f, -0.0666667f, -0.1f, 0.133333f));
-  // TODO
+  const float3 v2 { 0.0f, 1.0f, 0.0f };
+  CHECK_THAT(make_quaternion_from_axis_angle(v2, 0.0f), eqq(0.0f, 0.0f, 0.0f, 
1.0f));
+  CHECK_THAT(make_quaternion_from_axis_angle(v2, -pi/2), eqq(0.0f, -0.707107f, 
0.0f, 0.707107f));
+  CHECK_THAT(make_quaternion_from_yaw_pitch_roll(0.0f, 0.0f, 0.0f), eqq(0.0f, 
0.0f, 0.0f, 1.0f));
+  CHECK_THAT(make_quaternion_from_yaw_pitch_roll(pi/2, 0.0f, 0.0f), eqq(0.0f, 
0.707107f, 0.0f, 0.707107f));
+  CHECK_THAT(make_quaternion_from_yaw_pitch_roll(0.0f, pi/2, 0.0f), 
eqq(0.707107f, 0.0f, 0.0f, 0.707107f));
+  CHECK_THAT(make_quaternion_from_yaw_pitch_roll(0.0f, 0.0f, pi/2), eqq(0.0f, 
0.0f, 0.707107f, 0.707107f));
+  CHECK_THAT(make_quaternion_from_yaw_pitch_roll(-pi/2, 0.0f, 0.0f), eqq(0.0f, 
-0.707107f, 0.0f, 0.707107f));
+  CHECK_THAT(make_quaternion_from_yaw_pitch_roll(pi/4, pi/4, pi/4), 
eqq(0.46194f, 0.191342f, 0.191342f, 0.844623f));
+  const float4x4 m1{
+     0.707107f,  0.0f,       0.707107f, 1.0f,
+     0.0f,       1.0f,       0.0f,      2.0f,
+    -0.707107f,  0.0f,       0.707107f, 3.0f,
+     4.0f,       5.0f,       6.0f,      1.0f,
+  };
+  CHECK_THAT(make_quaternion_from_rotation_matrix(m1), eqq(0.0f, -0.382684f, 
0.0f, 0.92388f));
+  const float4x4 m2{
+     0.381126f,  0.904788f,  0.190003f, 0.0f,
+     0.523383f, -0.380566f,  0.762391f, 0.0f,
+     0.762111f, -0.191122f, -0.618594f, 0.0f,
+     0.0f,       0.0f,       0.0f,      1.0f
+  };
+  CHECK_THAT(make_quaternion_from_rotation_matrix(m2), eqq(0.771409f, 
0.462845f, 0.308563f, 0.309017f));
+  const float4x4 m3{
+    -0.618594f,  0.762391f,  0.190003f, 0.0f,
+    -0.191122f, -0.380566f,  0.904788f, 0.0f,
+     0.762111f,  0.523383f,  0.381126f, 0.0f,
+     0.0f,       0.0f,       0.0f,      1.0f
+  };
+  CHECK_THAT(make_quaternion_from_rotation_matrix(m3), eqq(0.308563f, 
0.462845f, 0.771409f, 0.309017f));
+  const float4x4 m4{
+    -0.618594f,  0.762111f, -0.191122f, 0.0f,
+     0.190003f,  0.381126f,  0.904788f, 0.0f,
+     0.762391f,  0.523383f, -0.380566f, 0.0f,
+     0.0f,       0.0f,       0.0f,      1.0f
+  };
+  CHECK_THAT(make_quaternion_from_rotation_matrix(m4), eqq(0.308563f, 
0.771409f, 0.462845f, 0.309017f));
+  const quaternion v3 { 4.0f, 3.0f, 2.0f, 1.0f };
+  CHECK(dot(v1, v3) == 20.0_a);
+  const quaternion v4 { 0.0f, 0.0f, 1.0f, 0.0f };
+  const quaternion v5 { 0.46194f, 0.191342f, 0.191342f, 0.844623f };
+  const quaternion v6 { -0.327447f, -0.218298f, -0.436596f, 0.809017f };
+  const quaternion v7 { 0.925506f, 0.154251f, 0.308502f, 0.156434f };
+  CHECK(dot(v4, v5) == 0.19134_a);
+  CHECK(dot(v6, v7) == -0.34486_a);
+  CHECK_THAT(slerp(v1, v3, 0.0), eqq(v1));
+  CHECK_THAT(slerp(v1, v3, 1.0), eqq(v3));
+  CHECK_THAT(slerp(v1, v3, 0.4), eqq(2.2f, 2.4f, 2.6f, 2.8f));
+  CHECK_THAT(slerp(v4, v5, 0.0), eqq(v4));
+  CHECK_THAT(slerp(v4, v5, 1.0), eqq(v5));
+  CHECK_THAT(slerp(v4, v5, 0.4), eqq(0.246519f, 0.102112f, 0.851841f, 
0.450743f));
+  CHECK_THAT(slerp(v6, v7, 0.0), eqq(v6));
+  CHECK_THAT(slerp(v6, v7, 1.0), eqq(-v7));
+  CHECK_THAT(slerp(v6, v7, 0.4), eqq(-0.694796f, -0.232276f, -0.464552f, 
0.497491f));
+  CHECK_THAT(lerp(v1, v3, 0.0), eqq(normalize(v1)));
+  CHECK_THAT(lerp(v1, v3, 1.0), eqq(normalize(v3)));
+  CHECK_THAT(lerp(v4, v5, 0.4), eqq(0.236225f, 0.0978475f, 0.86491f, 
0.431919f));
+  CHECK_THAT(lerp(v4, v5, 0.0), eqq(v4));
+  CHECK_THAT(lerp(v4, v5, 1.0), eqq(v5));
+  CHECK_THAT(lerp(v4, v5, 0.4), eqq(0.236225f, 0.0978475f, 0.86491f, 
0.431919f));
+  CHECK_THAT(lerp(v6, v7, 0.0), eqq(v6));
+  CHECK_THAT(lerp(v6, v7, 1.0), eqq(-v7));
+  CHECK_THAT(lerp(v6, v7, 0.4), eqq(-0.68441f, -0.232713f, -0.465426f, 
0.510691f));
+  CHECK_THAT(concatenate(v1, v3), eqq(22.0f, 4.0f, 16.0f, -12.0f));
 }
 
 
-- 
2.25.1



_______________________________________________
Mingw-w64-public mailing list
Mingw-w64-public@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/mingw-w64-public

Reply via email to