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>

---
v2: Added an orthogonality check in decompose(), and check that no
scale factors are zero (which would lead to divisions by zero).
Rewrote slightly make_float4x4_from_quaternion based on a
different reference.
---
 .../include/windowsnumerics.impl.h            | 189 ++++++++++++++++--
 .../testcases/test_windowsnumerics.cpp        | 115 ++++++++++-
 2 files changed, 283 insertions(+), 21 deletions(-)

diff --git a/mingw-w64-headers/include/windowsnumerics.impl.h 
b/mingw-w64-headers/include/windowsnumerics.impl.h
index 7785b1eba..76840b3a0 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,24 @@ _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/Rotation_matrix#Quaternion
+    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*yy - 2.0f*zz, 2.0f*xy + 2.0f*zw,        2.0f*xz - 2.0f*yw,   
     0.0f,
+      2.0f*xy - 2.0f*zw,        1.0f - 2.0f*xx - 2.0f*zz, 2.0f*yz + 2.0f*xw,   
     0.0f,
+      2.0f*xz + 2.0f*yw,        2.0f*yz - 2.0f*xw,        1.0f - 2.0f*xx - 
2.0f*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 +1009,37 @@ _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;
+    if (scale.x <= 0 || scale.y <= 0 || scale.z <= 0)
+      return false;
+    val = make_float4x4_scale(float3(1.0f/scale.x, 1.0f/scale.y, 
1.0f/scale.z)) * val;
+    float3 v1 = { val.m11, val.m21, val.m31 };
+    float3 v2 = { val.m12, val.m22, val.m32 };
+    float3 v3 = { val.m13, val.m23, val.m33 };
+    float d1 = dot(v1, v2);
+    float d2 = dot(v1, v3);
+    float d3 = dot(v2, v3);
+    const float epsilon = 1.0e-3f;
+    if (::std::fabsf(d1) > epsilon || ::std::fabsf(d2) > epsilon || 
::std::fabsf(d3) > epsilon)
+      return false;
+    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 +1063,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 +1077,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 +1118,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 +1132,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..b33720953 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,42 @@ 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));
+  const float4x4 v4 = make_float4x4_from_quaternion(q1) * 
make_float4x4_scale(scale) * make_float4x4_translation(translate);
+  CHECK(!decompose(v4, &out_scale, &out_rot, &out_translate));
 }
 
 // === plane ===
@@ -896,6 +944,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 +996,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