Merge pull request #6 from Nihlus/fix-equal-approxequal

Add new approximation algorithm and replace current usages in tests
This commit is contained in:
varon 2017-06-08 00:49:22 +02:00 committed by GitHub
commit c3210d4bd3
7 changed files with 804 additions and 311 deletions

View file

@ -3,13 +3,14 @@
* Copyright (c) 2006-2008 the OpenTK Team. * Copyright (c) 2006-2008 the OpenTK Team.
* This notice may not be removed from any source distribution. * This notice may not be removed from any source distribution.
* See license.txt for licensing detailed licensing details. * See license.txt for licensing detailed licensing details.
* *
* Contributions by Andy Gill, James Talton and Georg Wächter. * Contributions by Andy Gill, James Talton and Georg Wächter.
*/ */
#endregion #endregion
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Text; using System.Text;
namespace OpenTK namespace OpenTK
@ -354,6 +355,119 @@ namespace OpenTK
return intDiff <= (1 << maxDeltaBits); return intDiff <= (1 << maxDeltaBits);
} }
/// <summary>
/// Approximates double-precision floating point equality by an epsilon (maximum error) value.
/// This method is designed as a "fits-all" solution and attempts to handle as many cases as possible.
/// </summary>
/// <param name="a">The first float.</param>
/// <param name="b">The second float.</param>
/// <param name="epsilon">The maximum error between the two.</param>
/// <returns><value>true</value> if the values are approximately equal within the error margin; otherwise, <value>false</value>.</returns>
[SuppressMessage("ReSharper", "CompareOfFloatsByEqualityOperator")]
public static bool ApproximatelyEqualEpsilon(double a, double b, double epsilon)
{
const double doubleNormal = (1L << 52) * double.Epsilon;
double absA = Math.Abs(a);
double absB = Math.Abs(b);
double diff = Math.Abs(a - b);
if (a == b)
{
// Shortcut, handles infinities
return true;
}
if (a == 0.0f || b == 0.0f || diff < doubleNormal)
{
// a or b is zero, or both are extremely close to it.
// relative error is less meaningful here
return diff < (epsilon * doubleNormal);
}
// use relative error
return diff / Math.Min((absA + absB), double.MaxValue) < epsilon;
}
/// <summary>
/// Approximates single-precision floating point equality by an epsilon (maximum error) value.
/// This method is designed as a "fits-all" solution and attempts to handle as many cases as possible.
/// </summary>
/// <param name="a">The first float.</param>
/// <param name="b">The second float.</param>
/// <param name="epsilon">The maximum error between the two.</param>
/// <returns><value>true</value> if the values are approximately equal within the error margin; otherwise, <value>false</value>.</returns>
[SuppressMessage("ReSharper", "CompareOfFloatsByEqualityOperator")]
public static bool ApproximatelyEqualEpsilon(float a, float b, float epsilon)
{
const float floatNormal = (1 << 23) * float.Epsilon;
float absA = Math.Abs(a);
float absB = Math.Abs(b);
float diff = Math.Abs(a - b);
if (a == b)
{
// Shortcut, handles infinities
return true;
}
if (a == 0.0f || b == 0.0f || diff < floatNormal)
{
// a or b is zero, or both are extremely close to it.
// relative error is less meaningful here
return diff < (epsilon * floatNormal);
}
// use relative error
float relativeError = diff / Math.Min((absA + absB), float.MaxValue);
return relativeError < epsilon;
}
/// <summary>
/// Approximates equivalence between two single-precision floating-point numbers on a direct human scale.
/// It is important to note that this does not approximate equality - instead, it merely checks whether or not
/// two numbers could be considered equivalent to each other within a certain tolerance. The tolerance is
/// inclusive.
/// </summary>
/// <param name="a">The first value to compare.</param>
/// <param name="b">The second value to compare·</param>
/// <param name="tolerance">The tolerance within which the two values would be considered equivalent.</param>
/// <returns>Whether or not the values can be considered equivalent within the tolerance.</returns>
[SuppressMessage("ReSharper", "CompareOfFloatsByEqualityOperator")]
public static bool ApproximatelyEquivalent(float a, float b, float tolerance)
{
if (a == b)
{
// Early bailout, handles infinities
return true;
}
float diff = Math.Abs(a - b);
return diff <= tolerance;
}
/// <summary>
/// Approximates equivalence between two double-precision floating-point numbers on a direct human scale.
/// It is important to note that this does not approximate equality - instead, it merely checks whether or not
/// two numbers could be considered equivalent to each other within a certain tolerance. The tolerance is
/// inclusive.
/// </summary>
/// <param name="a">The first value to compare.</param>
/// <param name="b">The second value to compare·</param>
/// <param name="tolerance">The tolerance within which the two values would be considered equivalent.</param>
/// <returns>Whether or not the values can be considered equivalent within the tolerance.</returns>
[SuppressMessage("ReSharper", "CompareOfFloatsByEqualityOperator")]
public static bool ApproximatelyEquivalent(double a, double b, double tolerance)
{
if (a == b)
{
// Early bailout, handles infinities
return true;
}
double diff = Math.Abs(a - b);
return diff <= tolerance;
}
#endregion #endregion
#endregion #endregion

View file

@ -9,26 +9,68 @@ open OpenTK
[<AutoOpen>] [<AutoOpen>]
module private AssertHelpers = module private AssertHelpers =
[<Literal>] [<Literal>]
let private BitAccuracy = 13 let private BitAccuracy = 16
let approxEq a b = MathHelper.ApproximatelyEqual(a,b,BitAccuracy) [<Literal>]
let private EquivalenceTolerance = 0.00005f
let approxEq a b = MathHelper.ApproximatelyEquivalent(a, b, EquivalenceTolerance)
let approxEqDelta a b = MathHelper.ApproximatelyEqual(a,b,BitAccuracy)
let approxEqSingleEpsilon a b = MathHelper.ApproximatelyEqualEpsilon(a, b, 0.00001f)
let approxEqDoubleEpsilon a b = MathHelper.ApproximatelyEqualEpsilon(a, b, 0.00001)
let approxEqSingleEpsilonWithError (a, b, c : float32) = MathHelper.ApproximatelyEqualEpsilon(a, b, c)
let approxEqDoubleEpsilonWithError (a, b, c : float) = MathHelper.ApproximatelyEqualEpsilon(a, b, c)
let anyZero2 (a : Vector2) = (approxEq a.X 0.0f || approxEq a.Y 0.0f)
let anyZero3 (a : Vector3) = (approxEq a.X 0.0f || approxEq a.Y 0.0f || approxEq a.Z 0.0f)
let anyZero4 (a : Vector4) = (approxEq a.X 0.0f || approxEq a.Y 0.0f || approxEq a.Z 0.0f || approxEq a.W 0.0f)
/// We use a full type here instead of a module, as the overloading semantics are more suitable for our desired goal. /// We use a full type here instead of a module, as the overloading semantics are more suitable for our desired goal.
[<Sealed>] [<Sealed>]
type internal Assert = type internal Assert =
static member ApproximatelyEqual(a : Vector2,b : Vector2) = static member ApproximatelyEquivalent(a : Vector2,b : Vector2) =
if not <| approxEq a.X b.X && approxEq a.Y b.Y then raise <| new Xunit.Sdk.EqualException(a,b) if not <| approxEq a.X b.X && approxEq a.Y b.Y then raise <| new Xunit.Sdk.EqualException(a,b)
static member ApproximatelyEqual(a : Vector3,b : Vector3) = static member ApproximatelyEquivalent(a : Vector3,b : Vector3) =
if not <| approxEq a.X b.X && approxEq a.Y b.Y && approxEq a.Z b.Z then raise <| new Xunit.Sdk.EqualException(a,b) if not <| approxEq a.X b.X && approxEq a.Y b.Y && approxEq a.Z b.Z then raise <| new Xunit.Sdk.EqualException(a,b)
static member ApproximatelyEqual(a : Vector4,b : Vector4) = static member ApproximatelyEquivalent(a : Vector4,b : Vector4) =
if not <| approxEq a.X b.X && approxEq a.Y b.Y && approxEq a.Z b.Z && approxEq a.W b.W then if not <| approxEq a.X b.X && approxEq a.Y b.Y && approxEq a.Z b.Z && approxEq a.W b.W then
raise <| new Xunit.Sdk.EqualException(a,b) raise <| new Xunit.Sdk.EqualException(a,b)
static member ApproximatelyEqual(a : float32,b : float32) = static member ApproximatelyEquivalent(a : float32,b : float32) =
if not <| approxEq a b then raise <| new Xunit.Sdk.EqualException(a,b) if not <| approxEq a b then raise <| new Xunit.Sdk.EqualException(a,b)
static member ApproximatelyEqualEpsilon(a : float32, b : float32) =
if not <| approxEqSingleEpsilon a b then raise <| new Xunit.Sdk.EqualException(a,b)
static member ApproximatelyEqualEpsilon(a : float32, b : float32, c : float32) =
if not <| approxEqSingleEpsilonWithError(a, b, c) then raise <| new Xunit.Sdk.EqualException(a,b)
static member ApproximatelyEqualEpsilon(a : float, b : float) =
if not <| approxEqDoubleEpsilon a b then raise <| new Xunit.Sdk.EqualException(a,b)
static member ApproximatelyEqualEpsilon(a : float, b : float, c : float) =
if not <| approxEqDoubleEpsilonWithError(a, b, c) then raise <| new Xunit.Sdk.EqualException(a,b)
static member NotApproximatelyEqualEpsilon(a : float32, b : float32) =
if approxEqSingleEpsilon a b then raise <| new Xunit.Sdk.EqualException(a,b)
static member NotApproximatelyEqualEpsilon(a : float32, b : float32, c : float32) =
if approxEqSingleEpsilonWithError(a, b, c) then raise <| new Xunit.Sdk.EqualException(a,b)
static member NotApproximatelyEqualEpsilon(a : float, b : float) =
if approxEqDoubleEpsilon a b then raise <| new Xunit.Sdk.EqualException(a,b)
static member NotApproximatelyEqualEpsilon(a : float, b : float, c : float) =
if approxEqDoubleEpsilonWithError(a, b, c) then raise <| new Xunit.Sdk.EqualException(a,b)
static member ThrowsIndexExn(f:unit -> unit) = Assert.Throws<IndexOutOfRangeException>(f) |> ignore static member ThrowsIndexExn(f:unit -> unit) = Assert.Throws<IndexOutOfRangeException>(f) |> ignore

View file

@ -6,50 +6,347 @@ open FsCheck.Xunit
open System open System
open OpenTK open OpenTK
[<Properties(Arbitrary = [| typeof<OpenTKGen> |])>]
module MathHelper = module MathHelper =
/// This test ensures that approximately equal can never get it 'wrong' about the values. [<Properties(Arbitrary = [| typeof<OpenTKGen> |])>]
[<Property>] module ``ApproximatelyEqual (delta)`` =
let ``ApproximatelyEqual is never incorrect`` (a : float32,b : float32,bits : int32) = /// This test ensures that approximately equal can never get it 'wrong' about the values.
let clamped = max 0 (min bits 24) [<Property>]
let areApproxEqual = MathHelper.ApproximatelyEqual(a,b,clamped) let ``ApproximatelyEqual is never incorrect`` (a : float32,b : float32,bits : int32) =
let areExactlyEqual = a = b let clamped = max 0 (min bits 24)
let isWrong = areExactlyEqual && not areApproxEqual let areApproxEqual = MathHelper.ApproximatelyEqual(a,b,clamped)
Assert.False(isWrong) let areExactlyEqual = a = b
let isWrong = areExactlyEqual && not areApproxEqual
Assert.False(isWrong)
[<Property>] [<Property>]
let ``ApproximatelyEqual can return true if some values are not exactly equal`` (a : float32,b : float32,bits : int32) = let ``ApproximatelyEqual can return true if some values are not exactly equal`` (a : float32,b : float32,bits : int32) =
let clamped = max 0 (min bits 24) let clamped = max 0 (min bits 24)
let areApproxEqual = MathHelper.ApproximatelyEqual(a,b,clamped) let areApproxEqual = MathHelper.ApproximatelyEqual(a,b,clamped)
let areExactlyEqual = a = b let areExactlyEqual = a = b
let isWrong = areExactlyEqual && not areApproxEqual let isWrong = areExactlyEqual && not areApproxEqual
let p = new PropertyAttribute() let p = new PropertyAttribute()
Assert.False(isWrong) Assert.False(isWrong)
[<Fact>] [<Fact>]
let ``ApproximatelyEqual correctly approximates equality``() = let ``ApproximatelyEqual correctly approximates equality``() =
let a = 0.000000001f let a = 0.000000001f
let b = 0.0000000010000001f let b = 0.0000000010000001f
Assert.NotEqual(a,b) Assert.NotEqual(a,b)
[ 1..24 ] |> List.iter (fun i -> Assert.True(MathHelper.ApproximatelyEqual(a,b,i))) [ 1..24 ] |> List.iter (fun i -> Assert.True(MathHelper.ApproximatelyEqual(a,b,i)))
[<Fact>] [<Fact>]
let ``ApproximatelyEqual reports very different values as non-equal even with high bit count``() = let ``ApproximatelyEqual reports very different values as non-equal even with high bit count``() =
let a = 2.0f let a = 2.0f
let b = 1.0f let b = 1.0f
Assert.NotEqual(a,b) Assert.NotEqual(a,b)
Assert.False(MathHelper.ApproximatelyEqual(a,b,10)) Assert.False(MathHelper.ApproximatelyEqual(a,b,10))
[<Fact>] [<Fact>]
let ``ApproximatelyEqual works with single zero value``() = let ``ApproximatelyEqual works with single zero value``() =
let a = 1.0f let a = 1.0f
let b = 0.0f let b = 0.0f
Assert.NotEqual(a,b) Assert.NotEqual(a,b)
Assert.False(MathHelper.ApproximatelyEqual(a,b,0)) Assert.False(MathHelper.ApproximatelyEqual(a,b,0))
[<Fact>] [<Fact>]
let ``ApproximatelyEqual works with both zero values``() = let ``ApproximatelyEqual works with both zero values``() =
let a = 0.0f let a = 0.0f
let b = 0.0f let b = 0.0f
Assert.Equal(a,b) Assert.Equal(a,b)
Assert.True(MathHelper.ApproximatelyEqual(a,b,0)) Assert.True(MathHelper.ApproximatelyEqual(a,b,0))
[<Properties(Arbitrary = [| typeof<OpenTKGen> |])>]
module ``ApproximatelyEqual (single-precision epsilon)`` =
//
[<Fact>]
let ``ApproximatelyEqual (single precision) is correct for large positive values``() =
Assert.ApproximatelyEqualEpsilon(1000000.0f, 1000001.0f);
Assert.ApproximatelyEqualEpsilon(1000001.0f, 1000000.0f);
Assert.NotApproximatelyEqualEpsilon(10000.0f, 10001.0f);
Assert.NotApproximatelyEqualEpsilon(10001.0f, 10000.0f);
[<Fact>]
let ``ApproximatelyEqual (single precision) is correct for large negative values``() =
Assert.ApproximatelyEqualEpsilon(-1000000.0f, -1000001.0f);
Assert.ApproximatelyEqualEpsilon(-1000001.0f, -1000000.0f);
Assert.NotApproximatelyEqualEpsilon(-10000.0f, -10001.0f);
Assert.NotApproximatelyEqualEpsilon(-10001.0f, -10000.0f);
[<Fact>]
let ``ApproximatelyEqual (single precision) is correct for positive values around 1``() =
Assert.ApproximatelyEqualEpsilon(1.0000001f, 1.0000002f);
Assert.ApproximatelyEqualEpsilon(1.0000002f, 1.0000001f);
Assert.NotApproximatelyEqualEpsilon(1.0002f, 1.0001f);
Assert.NotApproximatelyEqualEpsilon(1.0001f, 1.0002f);
[<Fact>]
let ``ApproximatelyEqual (single precision) is correct for negative values around -1``() =
Assert.ApproximatelyEqualEpsilon(-1.000001f, -1.000002f);
Assert.ApproximatelyEqualEpsilon(-1.000002f, -1.000001f);
Assert.NotApproximatelyEqualEpsilon(-1.0001f, -1.0002f);
Assert.NotApproximatelyEqualEpsilon(-1.0002f, -1.0001f);
[<Fact>]
let ``ApproximatelyEqual (single precision) is correct for values between 1 and 0``() =
Assert.ApproximatelyEqualEpsilon(0.000000001000001f, 0.000000001000002f);
Assert.ApproximatelyEqualEpsilon(0.000000001000002f, 0.000000001000001f);
Assert.NotApproximatelyEqualEpsilon(0.000000000001002f, 0.000000000001001f);
Assert.NotApproximatelyEqualEpsilon(0.000000000001001f, 0.000000000001002f);
[<Fact>]
let ``ApproximatelyEqual (single precision) is correct for values between -1 and 0``() =
Assert.ApproximatelyEqualEpsilon(-0.000000001000001f, -0.000000001000002f);
Assert.ApproximatelyEqualEpsilon(-0.000000001000002f, -0.000000001000001f);
Assert.NotApproximatelyEqualEpsilon(-0.000000000001002f, -0.000000000001001f);
Assert.NotApproximatelyEqualEpsilon(-0.000000000001001f, -0.000000000001002f);
[<Fact>]
let ``ApproximatelyEqual (single precision) is correct for comparisons involving 0``() =
Assert.ApproximatelyEqualEpsilon(0.0f, 0.0f);
Assert.ApproximatelyEqualEpsilon(0.0f, -0.0f);
Assert.ApproximatelyEqualEpsilon(-0.0f, -0.0f);
Assert.NotApproximatelyEqualEpsilon(0.00000001f, 0.0f);
Assert.NotApproximatelyEqualEpsilon(0.0f, 0.00000001f);
Assert.NotApproximatelyEqualEpsilon(-0.00000001f, 0.0f);
Assert.NotApproximatelyEqualEpsilon(0.0f, -0.00000001f);
Assert.ApproximatelyEqualEpsilon(0.0f, 1e-40f, 0.01f);
Assert.ApproximatelyEqualEpsilon(1e-40f, 0.0f, 0.01f);
Assert.NotApproximatelyEqualEpsilon(1e-40f, 0.0f, 0.000001f);
Assert.NotApproximatelyEqualEpsilon(0.0f, 1e-40f, 0.000001f);
Assert.ApproximatelyEqualEpsilon(0.0f, -1e-40f, 0.1f);
Assert.ApproximatelyEqualEpsilon(-1e-40f, 0.0f, 0.1f);
Assert.NotApproximatelyEqualEpsilon(-1e-40f, 0.0f, 0.00000001f);
Assert.NotApproximatelyEqualEpsilon(0.0f, -1e-40f, 0.00000001f);
[<Fact>]
let ``ApproximatelyEqual (single precision) is correct for extreme values with overflow potential``() =
Assert.ApproximatelyEqualEpsilon(System.Single.MaxValue, System.Single.MaxValue);
Assert.NotApproximatelyEqualEpsilon(System.Single.MaxValue, -System.Single.MaxValue);
Assert.NotApproximatelyEqualEpsilon(-System.Single.MaxValue, System.Single.MaxValue);
Assert.NotApproximatelyEqualEpsilon(System.Single.MaxValue, System.Single.MaxValue / 2.0f);
Assert.NotApproximatelyEqualEpsilon(System.Single.MaxValue, -System.Single.MaxValue / 2.0f);
Assert.NotApproximatelyEqualEpsilon(-System.Single.MaxValue, System.Single.MaxValue / 2.0f);
[<Fact>]
let ``ApproximatelyEqual (single precision) is correct for values involving infinities``() =
Assert.ApproximatelyEqualEpsilon(System.Single.PositiveInfinity, System.Single.PositiveInfinity);
Assert.ApproximatelyEqualEpsilon(System.Single.NegativeInfinity, System.Single.NegativeInfinity);
Assert.NotApproximatelyEqualEpsilon(System.Single.NegativeInfinity, System.Single.PositiveInfinity);
Assert.NotApproximatelyEqualEpsilon(System.Single.PositiveInfinity, System.Single.MaxValue);
Assert.NotApproximatelyEqualEpsilon(System.Single.NegativeInfinity, -System.Single.MaxValue);
[<Fact>]
let ``ApproximatelyEqual (single precision) is correct for values involving NaN``() =
Assert.NotApproximatelyEqualEpsilon(System.Single.NaN, System.Single.NaN);
Assert.NotApproximatelyEqualEpsilon(System.Single.NaN, 0.0f);
Assert.NotApproximatelyEqualEpsilon(-0.0f, System.Single.NaN);
Assert.NotApproximatelyEqualEpsilon(System.Single.NaN, -0.0f);
Assert.NotApproximatelyEqualEpsilon(0.0f, System.Single.NaN);
Assert.NotApproximatelyEqualEpsilon(System.Single.NaN, System.Single.PositiveInfinity);
Assert.NotApproximatelyEqualEpsilon(System.Single.PositiveInfinity, System.Single.NaN);
Assert.NotApproximatelyEqualEpsilon(System.Single.NaN, System.Single.NegativeInfinity);
Assert.NotApproximatelyEqualEpsilon(System.Single.NegativeInfinity, System.Single.NaN);
Assert.NotApproximatelyEqualEpsilon(System.Single.NaN, System.Single.MaxValue);
Assert.NotApproximatelyEqualEpsilon(System.Single.MaxValue, System.Single.NaN);
Assert.NotApproximatelyEqualEpsilon(System.Single.NaN, -System.Single.MaxValue);
Assert.NotApproximatelyEqualEpsilon(-System.Single.MaxValue, System.Single.NaN);
Assert.NotApproximatelyEqualEpsilon(System.Single.NaN, System.Single.Epsilon);
Assert.NotApproximatelyEqualEpsilon(System.Single.Epsilon, System.Single.NaN);
Assert.NotApproximatelyEqualEpsilon(System.Single.NaN, -System.Single.Epsilon);
Assert.NotApproximatelyEqualEpsilon(-System.Single.Epsilon, System.Single.NaN);
[<Fact>]
let ``ApproximatelyEqual (single precision) is correct for values on opposite sides of 0``() =
Assert.NotApproximatelyEqualEpsilon(1.000000001f, -1.0f);
Assert.NotApproximatelyEqualEpsilon(-1.0f, 1.000000001f);
Assert.NotApproximatelyEqualEpsilon(-1.000000001f, 1.0f);
Assert.NotApproximatelyEqualEpsilon(1.0f, -1.000000001f);
Assert.ApproximatelyEqualEpsilon(10.0f * System.Single.Epsilon, 10.0f * -System.Single.Epsilon);
Assert.NotApproximatelyEqualEpsilon(10000.0f * System.Single.Epsilon, 10000.0f * -System.Single.Epsilon);
[<Fact>]
let ``ApproximatelyEqual (single precision) is correct for values very close to 0``() =
Assert.ApproximatelyEqualEpsilon(System.Single.Epsilon, System.Single.Epsilon);
Assert.ApproximatelyEqualEpsilon(System.Single.Epsilon, -System.Single.Epsilon);
Assert.ApproximatelyEqualEpsilon(-System.Single.Epsilon, System.Single.Epsilon);
Assert.ApproximatelyEqualEpsilon(System.Single.Epsilon, 0.0f);
Assert.ApproximatelyEqualEpsilon(0.0f, System.Single.Epsilon);
Assert.ApproximatelyEqualEpsilon(-System.Single.Epsilon, 0.0f);
Assert.ApproximatelyEqualEpsilon(0.0f, -System.Single.Epsilon);
Assert.NotApproximatelyEqualEpsilon(0.000000001f, -System.Single.Epsilon);
Assert.NotApproximatelyEqualEpsilon(0.000000001f, System.Single.Epsilon);
Assert.NotApproximatelyEqualEpsilon(System.Single.Epsilon, 0.000000001f);
Assert.NotApproximatelyEqualEpsilon(-System.Single.Epsilon, 0.000000001f);
[<Properties(Arbitrary = [| typeof<OpenTKGen> |])>]
module ``ApproximatelyEqual (double-precision epsilon)`` =
//
[<Fact>]
let ``ApproximatelyEqual (double precision) is correct for large positive values``() =
Assert.ApproximatelyEqualEpsilon(1000000.0, 1000001.0);
Assert.ApproximatelyEqualEpsilon(1000001.0, 1000000.0);
Assert.NotApproximatelyEqualEpsilon(10000.0, 10001.0);
Assert.NotApproximatelyEqualEpsilon(10001.0, 10000.0);
[<Fact>]
let ``ApproximatelyEqual (double precision) is correct for large negative values``() =
Assert.ApproximatelyEqualEpsilon(-1000000.0, -1000001.0);
Assert.ApproximatelyEqualEpsilon(-1000001.0, -1000000.0);
Assert.NotApproximatelyEqualEpsilon(-10000.0, -10001.0);
Assert.NotApproximatelyEqualEpsilon(-10001.0, -10000.0);
[<Fact>]
let ``ApproximatelyEqual (double precision) is correct for positive values around 1``() =
Assert.ApproximatelyEqualEpsilon(1.0000001, 1.0000002);
Assert.ApproximatelyEqualEpsilon(1.0000002, 1.0000001);
Assert.NotApproximatelyEqualEpsilon(1.0002, 1.0001);
Assert.NotApproximatelyEqualEpsilon(1.0001, 1.0002);
[<Fact>]
let ``ApproximatelyEqual (double precision) is correct for negative values around -1``() =
Assert.ApproximatelyEqualEpsilon(-1.000001, -1.000002);
Assert.ApproximatelyEqualEpsilon(-1.000002, -1.000001);
Assert.NotApproximatelyEqualEpsilon(-1.0001, -1.0002);
Assert.NotApproximatelyEqualEpsilon(-1.0002, -1.0001);
[<Fact>]
let ``ApproximatelyEqual (double precision) is correct for values between 1 and 0``() =
Assert.ApproximatelyEqualEpsilon(0.000000001000001, 0.000000001000002);
Assert.ApproximatelyEqualEpsilon(0.000000001000002, 0.000000001000001);
Assert.NotApproximatelyEqualEpsilon(0.000000000001002, 0.000000000001001);
Assert.NotApproximatelyEqualEpsilon(0.000000000001001, 0.000000000001002);
[<Fact>]
let ``ApproximatelyEqual (double precision) is correct for values between -1 and 0``() =
Assert.ApproximatelyEqualEpsilon(-0.000000001000001, -0.000000001000002);
Assert.ApproximatelyEqualEpsilon(-0.000000001000002, -0.000000001000001);
Assert.NotApproximatelyEqualEpsilon(-0.000000000001002, -0.000000000001001);
Assert.NotApproximatelyEqualEpsilon(-0.000000000001001, -0.000000000001002);
[<Fact>]
let ``ApproximatelyEqual (double precision) is correct for comparisons involving 0``() =
Assert.ApproximatelyEqualEpsilon(0.0, 0.0);
Assert.ApproximatelyEqualEpsilon(0.0, -0.0);
Assert.ApproximatelyEqualEpsilon(-0.0, -0.0);
Assert.NotApproximatelyEqualEpsilon(0.00000001, 0.0);
Assert.NotApproximatelyEqualEpsilon(0.0, 0.00000001);
Assert.NotApproximatelyEqualEpsilon(-0.00000001, 0.0);
Assert.NotApproximatelyEqualEpsilon(0.0, -0.00000001);
Assert.ApproximatelyEqualEpsilon(0.0, 1e-310, 0.01);
Assert.ApproximatelyEqualEpsilon(1e-310, 0.0, 0.01);
Assert.NotApproximatelyEqualEpsilon(1e-310, 0.0, 0.000001);
Assert.NotApproximatelyEqualEpsilon(0.0, 1e-310, 0.000001);
Assert.ApproximatelyEqualEpsilon(0.0, -1e-310, 0.1);
Assert.ApproximatelyEqualEpsilon(-1e-310, 0.0, 0.1);
Assert.NotApproximatelyEqualEpsilon(-1e-310, 0.0, 0.00000001);
Assert.NotApproximatelyEqualEpsilon(0.0, -1e-310, 0.00000001);
[<Fact>]
let ``ApproximatelyEqual (double precision) is correct for extreme values with overflow potential``() =
Assert.ApproximatelyEqualEpsilon(System.Double.MaxValue, System.Double.MaxValue);
Assert.NotApproximatelyEqualEpsilon(System.Double.MaxValue, -System.Double.MaxValue);
Assert.NotApproximatelyEqualEpsilon(-System.Double.MaxValue, System.Double.MaxValue);
Assert.NotApproximatelyEqualEpsilon(System.Double.MaxValue, System.Double.MaxValue / 2.0);
Assert.NotApproximatelyEqualEpsilon(System.Double.MaxValue, -System.Double.MaxValue / 2.0);
Assert.NotApproximatelyEqualEpsilon(-System.Double.MaxValue, System.Double.MaxValue / 2.0);
[<Fact>]
let ``ApproximatelyEqual (double precision) is correct for values involving infinities``() =
Assert.ApproximatelyEqualEpsilon(System.Double.PositiveInfinity, System.Double.PositiveInfinity);
Assert.ApproximatelyEqualEpsilon(System.Double.NegativeInfinity, System.Double.NegativeInfinity);
Assert.NotApproximatelyEqualEpsilon(System.Double.NegativeInfinity, System.Double.PositiveInfinity);
Assert.NotApproximatelyEqualEpsilon(System.Double.PositiveInfinity, System.Double.MaxValue);
Assert.NotApproximatelyEqualEpsilon(System.Double.NegativeInfinity, -System.Double.MaxValue);
[<Fact>]
let ``ApproximatelyEqual (double precision) is correct for values involving NaN``() =
Assert.NotApproximatelyEqualEpsilon(System.Double.NaN, System.Double.NaN);
Assert.NotApproximatelyEqualEpsilon(System.Double.NaN, 0.0);
Assert.NotApproximatelyEqualEpsilon(-0.0, System.Double.NaN);
Assert.NotApproximatelyEqualEpsilon(System.Double.NaN, -0.0);
Assert.NotApproximatelyEqualEpsilon(0.0, System.Double.NaN);
Assert.NotApproximatelyEqualEpsilon(System.Double.NaN, System.Double.PositiveInfinity);
Assert.NotApproximatelyEqualEpsilon(System.Double.PositiveInfinity, System.Double.NaN);
Assert.NotApproximatelyEqualEpsilon(System.Double.NaN, System.Double.NegativeInfinity);
Assert.NotApproximatelyEqualEpsilon(System.Double.NegativeInfinity, System.Double.NaN);
Assert.NotApproximatelyEqualEpsilon(System.Double.NaN, System.Double.MaxValue);
Assert.NotApproximatelyEqualEpsilon(System.Double.MaxValue, System.Double.NaN);
Assert.NotApproximatelyEqualEpsilon(System.Double.NaN, -System.Double.MaxValue);
Assert.NotApproximatelyEqualEpsilon(-System.Double.MaxValue, System.Double.NaN);
Assert.NotApproximatelyEqualEpsilon(System.Double.NaN, System.Double.Epsilon);
Assert.NotApproximatelyEqualEpsilon(System.Double.Epsilon, System.Double.NaN);
Assert.NotApproximatelyEqualEpsilon(System.Double.NaN, -System.Double.Epsilon);
Assert.NotApproximatelyEqualEpsilon(-System.Double.Epsilon, System.Double.NaN);
[<Fact>]
let ``ApproximatelyEqual (double precision) is correct for values on opposite sides of 0``() =
Assert.NotApproximatelyEqualEpsilon(1.000000001, -1.0);
Assert.NotApproximatelyEqualEpsilon(-1.0, 1.000000001);
Assert.NotApproximatelyEqualEpsilon(-1.000000001, 1.0);
Assert.NotApproximatelyEqualEpsilon(1.0, -1.000000001);
Assert.ApproximatelyEqualEpsilon(10.0 * System.Double.Epsilon, 10.0 * -System.Double.Epsilon);
Assert.NotApproximatelyEqualEpsilon(100000000000.0 * System.Double.Epsilon, 100000000000.0 * -System.Double.Epsilon);
[<Fact>]
let ``ApproximatelyEqual (double precision) is correct for values very close to 0``() =
Assert.ApproximatelyEqualEpsilon(System.Double.Epsilon, System.Double.Epsilon);
Assert.ApproximatelyEqualEpsilon(System.Double.Epsilon, -System.Double.Epsilon);
Assert.ApproximatelyEqualEpsilon(-System.Double.Epsilon, System.Double.Epsilon);
Assert.ApproximatelyEqualEpsilon(System.Double.Epsilon, 0.0);
Assert.ApproximatelyEqualEpsilon(0.0, System.Double.Epsilon);
Assert.ApproximatelyEqualEpsilon(-System.Double.Epsilon, 0.0);
Assert.ApproximatelyEqualEpsilon(0.0, -System.Double.Epsilon);
Assert.NotApproximatelyEqualEpsilon(0.000000001, -System.Double.Epsilon);
Assert.NotApproximatelyEqualEpsilon(0.000000001, System.Double.Epsilon);
Assert.NotApproximatelyEqualEpsilon(System.Double.Epsilon, 0.000000001);
Assert.NotApproximatelyEqualEpsilon(-System.Double.Epsilon, 0.000000001);
[<Properties(Arbitrary = [| typeof<OpenTKGen> |])>]
module ``ApproximatelyEquivalent (tolerance diff)`` =
[<Fact>]
let ``ApproximatelyEquivalent correctly approximates equivalence where the difference falls below the tolerance``() =
let a = 0.0001f
let b = 0.00019f
Assert.NotEqual(a,b)
Assert.True(MathHelper.ApproximatelyEquivalent(a, b, 0.0001f))
[<Fact>]
let ``ApproximatelyEquivalent correctly approximates equivalence where the difference is the tolerance``() =
let a = 0.0001f
let b = 0.0002f
Assert.NotEqual(a,b)
Assert.True(MathHelper.ApproximatelyEquivalent(a, b, 0.0001f))
[<Fact>]
let ``ApproximatelyEquivalent correctly approximates inequivalence where the difference exceeds the tolerance``() =
let a = 0.0001f
let b = 0.00021f
Assert.NotEqual(a,b)
Assert.False(MathHelper.ApproximatelyEquivalent(a, b, 0.0001f))
[<Fact>]
let ``ApproximatelyEquivalent reports very different values as non-equal even with a high tolerance``() =
let a = 3.0f
let b = 1.0f
Assert.NotEqual(a,b)
Assert.False(MathHelper.ApproximatelyEquivalent(a, b, 1.0f))
[<Fact>]
let ``ApproximatelyEquivalent works with single zero value``() =
let a = 1.0f
let b = 0.0f
Assert.NotEqual(a,b)
Assert.False(MathHelper.ApproximatelyEquivalent(a, b, 0.0001f))
[<Fact>]
let ``ApproximatelyEquivalent works with both zero values``() =
let a = 0.0f
let b = 0.0f
Assert.Equal(a,b)
Assert.True(MathHelper.ApproximatelyEquivalent(a, b, 0.0001f))

View file

@ -338,7 +338,7 @@ module Matrix4 =
let ``Indexed set operator throws exception for negative indices`` (b : Matrix4, x : float32) = let ``Indexed set operator throws exception for negative indices`` (b : Matrix4, x : float32) =
let mutable a = b let mutable a = b
(fun() -> a.[-1, 2] <- x) |> Assert.ThrowsIndexExn (fun() -> a.[-1, 2] <- x) |> Assert.ThrowsIndexExn
(fun() -> a.[1, -2] <- x) |> Assert.ThrowsIndexExn (fun() -> a.[1, -2] <- x) |> Assert.ThrowsIndexExn

View file

@ -145,20 +145,20 @@ module Vector2 =
[<Property>] [<Property>]
let ``Vector addition is the same as component addition`` (a : Vector2,b : Vector2) = let ``Vector addition is the same as component addition`` (a : Vector2,b : Vector2) =
let c = a + b let c = a + b
Assert.ApproximatelyEqual(a.X + b.X,c.X) Assert.ApproximatelyEquivalent(a.X + b.X,c.X)
Assert.ApproximatelyEqual(a.Y + b.Y,c.Y) Assert.ApproximatelyEquivalent(a.Y + b.Y,c.Y)
[<Property>] [<Property>]
let ``Vector addition is commutative`` (a : Vector2,b : Vector2) = let ``Vector addition is commutative`` (a : Vector2,b : Vector2) =
let c = a + b let c = a + b
let c2 = b + a let c2 = b + a
Assert.ApproximatelyEqual(c,c2) Assert.ApproximatelyEquivalent(c,c2)
[<Property>] [<Property>]
let ``Vector addition is associative`` (a : Vector2,b : Vector2,c : Vector2) = let ``Vector addition is associative`` (a : Vector2,b : Vector2,c : Vector2) =
let r1 = (a + b) + c let r1 = (a + b) + c
let r2 = a + (b + c) let r2 = a + (b + c)
Assert.ApproximatelyEqual(r1,r2) Assert.ApproximatelyEquivalent(r1,r2)
[<Property>] [<Property>]
let ``Static Vector2 addition method is the same as component addition`` (a : Vector2, b : Vector2) = let ``Static Vector2 addition method is the same as component addition`` (a : Vector2, b : Vector2) =
@ -166,7 +166,7 @@ module Vector2 =
let v1 = Vector2(a.X + b.X, a.Y + b.Y) let v1 = Vector2(a.X + b.X, a.Y + b.Y)
let sum = Vector2.Add(a, b) let sum = Vector2.Add(a, b)
Assert.ApproximatelyEqual(v1, sum) Assert.ApproximatelyEquivalent(v1, sum)
[<Property>] [<Property>]
let ``Static Vector2 addition method by reference is the same as component addition`` (a : Vector2, b : Vector2) = let ``Static Vector2 addition method by reference is the same as component addition`` (a : Vector2, b : Vector2) =
@ -174,7 +174,7 @@ module Vector2 =
let v1 = Vector2(a.X + b.X, a.Y + b.Y) let v1 = Vector2(a.X + b.X, a.Y + b.Y)
let sum = Vector2.Add(ref a, ref b) let sum = Vector2.Add(ref a, ref b)
Assert.ApproximatelyEqual(v1, sum) Assert.ApproximatelyEquivalent(v1, sum)
[<Properties(Arbitrary = [| typeof<OpenTKGen> |])>] [<Properties(Arbitrary = [| typeof<OpenTKGen> |])>]
module Multiplication = module Multiplication =
@ -210,7 +210,7 @@ module Vector2 =
let v1 = Vector2(a.X * b.X, a.Y * b.Y) let v1 = Vector2(a.X * b.X, a.Y * b.Y)
let sum = Vector2.Multiply(a, b) let sum = Vector2.Multiply(a, b)
Assert.ApproximatelyEqual(v1, sum) Assert.ApproximatelyEquivalent(v1, sum)
[<Property>] [<Property>]
let ``Static Vector2 multiplication method by reference is the same as component multiplication`` (a : Vector2, b : Vector2) = let ``Static Vector2 multiplication method by reference is the same as component multiplication`` (a : Vector2, b : Vector2) =
@ -218,7 +218,7 @@ module Vector2 =
let v1 = Vector2(a.X * b.X, a.Y * b.Y) let v1 = Vector2(a.X * b.X, a.Y * b.Y)
let sum = Vector2.Multiply(ref a, ref b) let sum = Vector2.Multiply(ref a, ref b)
Assert.ApproximatelyEqual(v1, sum) Assert.ApproximatelyEquivalent(v1, sum)
[<Property>] [<Property>]
let ``Static method Vector2-scalar multiplication is the same as component-scalar multiplication`` (a : Vector2, f : float32) = let ``Static method Vector2-scalar multiplication is the same as component-scalar multiplication`` (a : Vector2, f : float32) =
@ -242,7 +242,7 @@ module Vector2 =
let v1 = Vector2(a.X - b.X, a.Y - b.Y) let v1 = Vector2(a.X - b.X, a.Y - b.Y)
let sum = Vector2.Subtract(a, b) let sum = Vector2.Subtract(a, b)
Assert.ApproximatelyEqual(v1, sum) Assert.ApproximatelyEquivalent(v1, sum)
[<Property>] [<Property>]
let ``Static Vector2 subtraction method by reference is the same as component addition`` (a : Vector2, b : Vector2) = let ``Static Vector2 subtraction method by reference is the same as component addition`` (a : Vector2, b : Vector2) =
@ -250,49 +250,50 @@ module Vector2 =
let v1 = Vector2(a.X - b.X, a.Y - b.Y) let v1 = Vector2(a.X - b.X, a.Y - b.Y)
let sum = Vector2.Subtract(ref a, ref b) let sum = Vector2.Subtract(ref a, ref b)
Assert.ApproximatelyEqual(v1, sum) Assert.ApproximatelyEquivalent(v1, sum)
[<Properties(Arbitrary = [| typeof<OpenTKGen> |])>] [<Properties(Arbitrary = [| typeof<OpenTKGen> |])>]
module Division = module Division =
// //
[<Property>] [<Property>]
let ``Vector2-float division is the same as component-float division`` (a : Vector2, f : float32) = let ``Vector2-float division is the same as component-float division`` (a : Vector2, f : float32) =
let r = a / f if not (approxEq f 0.0f) then
let r = a / f
Assert.ApproximatelyEqual(a.X / f,r.X) Assert.ApproximatelyEquivalent(a.X / f,r.X)
Assert.ApproximatelyEqual(a.Y / f,r.Y) Assert.ApproximatelyEquivalent(a.Y / f,r.Y)
[<Property>] [<Property>]
let ``Static Vector2-Vector2 division method is the same as component division`` (a : Vector2, b : Vector2) = let ``Static Vector2-Vector2 division method is the same as component division`` (a : Vector2, b : Vector2) =
if not (anyZero2 a || anyZero2 b) then
let v1 = Vector2(a.X / b.X, a.Y / b.Y)
let sum = Vector2.Divide(a, b)
let v1 = Vector2(a.X / b.X, a.Y / b.Y) Assert.ApproximatelyEquivalent(v1, sum)
let sum = Vector2.Divide(a, b)
Assert.ApproximatelyEqual(v1, sum)
[<Property>] [<Property>]
let ``Static Vector2-Vector2 divison method by reference `` (a : Vector2, b : Vector2) = let ``Static Vector2-Vector2 divison method by reference `` (a : Vector2, b : Vector2) =
if not (anyZero2 a || anyZero2 b) then
let v1 = Vector2(a.X / b.X, a.Y / b.Y)
let sum = Vector2.Divide(ref a, ref b)
let v1 = Vector2(a.X / b.X, a.Y / b.Y) Assert.ApproximatelyEquivalent(v1, sum)
let sum = Vector2.Divide(ref a, ref b)
Assert.ApproximatelyEqual(v1, sum)
[<Property>] [<Property>]
let ``Static Vector2-scalar division method is the same as component division`` (a : Vector2, b : float32) = let ``Static Vector2-scalar division method is the same as component division`` (a : Vector2, b : float32) =
if not (approxEq b 0.0f) then
let v1 = Vector2(a.X / b, a.Y / b)
let sum = Vector2.Divide(a, b)
let v1 = Vector2(a.X / b, a.Y / b) Assert.ApproximatelyEquivalent(v1, sum)
let sum = Vector2.Divide(a, b)
Assert.ApproximatelyEqual(v1, sum)
[<Property>] [<Property>]
let ``Static Vector2-scalar divison method by reference is the same as component division`` (a : Vector2, b : float32) = let ``Static Vector2-scalar divison method by reference is the same as component division`` (a : Vector2, b : float32) =
if not (approxEq b 0.0f) then
let v1 = Vector2(a.X / b, a.Y / b)
let sum = Vector2.Divide(ref a, b)
let v1 = Vector2(a.X / b, a.Y / b) Assert.ApproximatelyEquivalent(v1, sum)
let sum = Vector2.Divide(ref a, b)
Assert.ApproximatelyEqual(v1, sum)
[<Properties(Arbitrary = [| typeof<OpenTKGen> |])>] [<Properties(Arbitrary = [| typeof<OpenTKGen> |])>]
module Negation = module Negation =
@ -406,8 +407,8 @@ module Vector2 =
if not (approxEq l 0.0f) then if not (approxEq l 0.0f) then
let norm = v.Normalized() let norm = v.Normalized()
Assert.ApproximatelyEqual(v.X / l, norm.X) Assert.ApproximatelyEquivalent(v.X / l, norm.X)
Assert.ApproximatelyEqual(v.Y / l, norm.Y) Assert.ApproximatelyEquivalent(v.Y / l, norm.Y)
[<Property>] [<Property>]
let ``Normalization of instance transforms the instance into a unit length vector with the correct components`` (a, b) = let ``Normalization of instance transforms the instance into a unit length vector with the correct components`` (a, b) =
@ -418,8 +419,8 @@ module Vector2 =
let norm = Vector2(a, b) let norm = Vector2(a, b)
norm.Normalize() norm.Normalize()
Assert.ApproximatelyEqual(v.X / l, norm.X) Assert.ApproximatelyEquivalent(v.X / l, norm.X)
Assert.ApproximatelyEqual(v.Y / l, norm.Y) Assert.ApproximatelyEquivalent(v.Y / l, norm.Y)
[<Property>] [<Property>]
let ``Fast approximate normalization of instance transforms the instance into a unit length vector with the correct components`` (a, b) = let ``Fast approximate normalization of instance transforms the instance into a unit length vector with the correct components`` (a, b) =
@ -429,21 +430,25 @@ module Vector2 =
let scale = MathHelper.InverseSqrtFast(a * a + b * b) let scale = MathHelper.InverseSqrtFast(a * a + b * b)
Assert.ApproximatelyEqual(v.X * scale, norm.X) Assert.ApproximatelyEquivalent(v.X * scale, norm.X)
Assert.ApproximatelyEqual(v.Y * scale, norm.Y) Assert.ApproximatelyEquivalent(v.Y * scale, norm.Y)
[<Property>] [<Property>]
let ``Normalization by reference is the same as division by magnitude`` (a : Vector2) = let ``Normalization by reference is the same as division by magnitude`` (a : Vector2) =
let norm = a / a.Length // Zero-length vectors can't be normalized
let vRes = Vector2.Normalize(ref a) if not (approxEq a.Length 0.0f) then
let norm = a / a.Length
let vRes = Vector2.Normalize(ref a)
Assert.ApproximatelyEqual(norm, vRes) Assert.ApproximatelyEquivalent(norm, vRes)
[<Property>] [<Property>]
let ``Normalization is the same as division by magnitude`` (a : Vector2) = let ``Normalization is the same as division by magnitude`` (a : Vector2) =
let norm = a / a.Length // Zero-length vectors can't be normalized
if not (approxEq a.Length 0.0f) then
let norm = a / a.Length
Assert.ApproximatelyEqual(norm, Vector2.Normalize(a)); Assert.ApproximatelyEquivalent(norm, Vector2.Normalize(a));
[<Property>] [<Property>]
let ``Fast approximate normalization by reference is the same as multiplication by the fast inverse square`` (a : Vector2) = let ``Fast approximate normalization by reference is the same as multiplication by the fast inverse square`` (a : Vector2) =
@ -452,7 +457,7 @@ module Vector2 =
let norm = a * scale let norm = a * scale
let vRes = Vector2.NormalizeFast(ref a) let vRes = Vector2.NormalizeFast(ref a)
Assert.ApproximatelyEqual(norm, vRes) Assert.ApproximatelyEquivalent(norm, vRes)
[<Property>] [<Property>]
let ``Fast approximate normalization is the same as multiplication by the fast inverse square`` (a : Vector2) = let ``Fast approximate normalization is the same as multiplication by the fast inverse square`` (a : Vector2) =
@ -460,66 +465,74 @@ module Vector2 =
let norm = a * scale let norm = a * scale
Assert.ApproximatelyEqual(norm, Vector2.NormalizeFast(a)); Assert.ApproximatelyEquivalent(norm, Vector2.NormalizeFast(a));
[<Properties(Arbitrary = [| typeof<OpenTKGen> |])>] [<Properties(Arbitrary = [| typeof<OpenTKGen> |])>]
module ``Magnitude min and max`` = module ``Magnitude min and max`` =
// //
[<Property>] [<Property>]
let ``MagnitudeMin selects the vector with equal or lesser magnitude given two vectors`` (v1 : Vector2, v2: Vector2) = let ``MagnitudeMin selects the vector with equal or lesser magnitude given two vectors`` (v1 : Vector2, v2: Vector2) =
let l1 = v1.LengthSquared // Results do not matter for equal vectors
let l2 = v2.LengthSquared if not (v1 = v2) then
let l1 = v1.LengthSquared
let l2 = v2.LengthSquared
let vMin = Vector2.MagnitudeMin(v1, v2) let vMin = Vector2.MagnitudeMin(v1, v2)
if vMin = v1 then if vMin = v1 then
let v1ShorterThanv2 = l1 < l2 let v1ShorterThanv2 = l1 < l2
Assert.True(v1ShorterThanv2) Assert.True(v1ShorterThanv2)
else else
let v2ShorterThanOrEqualTov1 = l2 <= l1 let v2ShorterThanOrEqualTov1 = l2 <= l1
Assert.True(v2ShorterThanOrEqualTov1) Assert.True(v2ShorterThanOrEqualTov1)
[<Property>] [<Property>]
let ``MagnitudeMax selects the vector with equal or greater magnitude given two vectors`` (v1 : Vector2, v2: Vector2) = let ``MagnitudeMax selects the vector with equal or greater magnitude given two vectors`` (v1 : Vector2, v2: Vector2) =
let l1 = v1.LengthSquared // Results do not matter for equal vectors
let l2 = v2.LengthSquared if not (v1 = v2) then
let l1 = v1.LengthSquared
let l2 = v2.LengthSquared
let vMin = Vector2.MagnitudeMax(v1, v2) let vMin = Vector2.MagnitudeMax(v1, v2)
if vMin = v1 then if vMin = v1 then
let v1LongerThanOrEqualTov2 = l1 >= l2 let v1LongerThanOrEqualTov2 = l1 >= l2
Assert.True(v1LongerThanOrEqualTov2) Assert.True(v1LongerThanOrEqualTov2)
else else
let v2LongerThanv1 = l2 > l1 let v2LongerThanv1 = l2 > l1
Assert.True(v2LongerThanv1) Assert.True(v2LongerThanv1)
[<Property>] [<Property>]
let ``MagnitudeMin by reference selects the vector with equal or lesser magnitude given two vectors`` (v1 : Vector2, v2: Vector2) = let ``MagnitudeMin by reference selects the vector with equal or lesser magnitude given two vectors`` (v1 : Vector2, v2: Vector2) =
let l1 = v1.LengthSquared // Results do not matter for equal vectors
let l2 = v2.LengthSquared if not (v1 = v2) then
let l1 = v1.LengthSquared
let l2 = v2.LengthSquared
let vMin = Vector2.MagnitudeMin(ref v1, ref v2) let vMin = Vector2.MagnitudeMin(ref v1, ref v2)
if vMin = v1 then if vMin = v1 then
let v1ShorterThanv2 = l1 < l2 let v1ShorterThanv2 = l1 < l2
Assert.True(v1ShorterThanv2) Assert.True(v1ShorterThanv2)
else else
let v2ShorterThanOrEqualTov1 = l2 <= l1 let v2ShorterThanOrEqualTov1 = l2 <= l1
Assert.True(v2ShorterThanOrEqualTov1) Assert.True(v2ShorterThanOrEqualTov1)
[<Property>] [<Property>]
let ``MagnitudeMax by reference selects the vector with equal greater magnitude given two vectors`` (v1 : Vector2, v2: Vector2) = let ``MagnitudeMax by reference selects the vector with equal greater magnitude given two vectors`` (v1 : Vector2, v2: Vector2) =
let l1 = v1.LengthSquared // Results do not matter for equal vectors
let l2 = v2.LengthSquared if not (v1 = v2) then
let l1 = v1.LengthSquared
let l2 = v2.LengthSquared
let vMin = Vector2.MagnitudeMax(ref v1, ref v2) let vMin = Vector2.MagnitudeMax(ref v1, ref v2)
if vMin = v1 then if vMin = v1 then
let v1LongerThanOrEqualTov2 = l1 >= l2 let v1LongerThanOrEqualTov2 = l1 >= l2
Assert.True(v1LongerThanOrEqualTov2) Assert.True(v1LongerThanOrEqualTov2)
else else
let v2LongerThanv1 = l2 > l1 let v2LongerThanv1 = l2 > l1
Assert.True(v2LongerThanv1) Assert.True(v2LongerThanv1)
[<Properties(Arbitrary = [| typeof<OpenTKGen> |])>] [<Properties(Arbitrary = [| typeof<OpenTKGen> |])>]
module ``Component min and max`` = module ``Component min and max`` =
@ -567,7 +580,7 @@ module Vector2 =
let transformedQuat = q * vectorQuat * inverse let transformedQuat = q * vectorQuat * inverse
let transformedVector = Vector2(transformedQuat.X, transformedQuat.Y) let transformedVector = Vector2(transformedQuat.X, transformedQuat.Y)
Assert.Equal(transformedVector, Vector2.Transform(v, q)) Assert.ApproximatelyEquivalent(transformedVector, Vector2.Transform(v, q))
[<Property>] [<Property>]
let ``Transformation by quaternion by reference is the same as multiplication by quaternion and its conjugate`` (v : Vector2, q : Quaternion) = let ``Transformation by quaternion by reference is the same as multiplication by quaternion and its conjugate`` (v : Vector2, q : Quaternion) =
@ -577,7 +590,7 @@ module Vector2 =
let transformedQuat = q * vectorQuat * inverse let transformedQuat = q * vectorQuat * inverse
let transformedVector = Vector2(transformedQuat.X, transformedQuat.Y) let transformedVector = Vector2(transformedQuat.X, transformedQuat.Y)
Assert.Equal(transformedVector, Vector2.Transform(ref v, ref q)) Assert.ApproximatelyEquivalent(transformedVector, Vector2.Transform(ref v, ref q))
[<Properties(Arbitrary = [| typeof<OpenTKGen> |])>] [<Properties(Arbitrary = [| typeof<OpenTKGen> |])>]
module Serialization = module Serialization =

View file

@ -136,9 +136,9 @@ module Vector3 =
if not (approxEq l 0.0f) then if not (approxEq l 0.0f) then
let norm = v.Normalized() let norm = v.Normalized()
Assert.ApproximatelyEqual(v.X / l, norm.X) Assert.ApproximatelyEquivalent(v.X / l, norm.X)
Assert.ApproximatelyEqual(v.Y / l, norm.Y) Assert.ApproximatelyEquivalent(v.Y / l, norm.Y)
Assert.ApproximatelyEqual(v.Z / l, norm.Z) Assert.ApproximatelyEquivalent(v.Z / l, norm.Z)
[<Property>] [<Property>]
let ``Normalization of instance transforms the instance into a unit length vector with the correct components`` (a, b, c) = let ``Normalization of instance transforms the instance into a unit length vector with the correct components`` (a, b, c) =
@ -149,9 +149,9 @@ module Vector3 =
let norm = Vector3(a, b, c) let norm = Vector3(a, b, c)
norm.Normalize() norm.Normalize()
Assert.ApproximatelyEqual(v.X / l, norm.X) Assert.ApproximatelyEquivalent(v.X / l, norm.X)
Assert.ApproximatelyEqual(v.Y / l, norm.Y) Assert.ApproximatelyEquivalent(v.Y / l, norm.Y)
Assert.ApproximatelyEqual(v.Z / l, norm.Z) Assert.ApproximatelyEquivalent(v.Z / l, norm.Z)
[<Property>] [<Property>]
let ``Fast approximate normalization of instance transforms the instance into a unit length vector with the correct components`` (a, b, c) = let ``Fast approximate normalization of instance transforms the instance into a unit length vector with the correct components`` (a, b, c) =
@ -161,22 +161,26 @@ module Vector3 =
let scale = MathHelper.InverseSqrtFast(a * a + b * b + c * c) let scale = MathHelper.InverseSqrtFast(a * a + b * b + c * c)
Assert.ApproximatelyEqual(v.X * scale, norm.X) Assert.ApproximatelyEquivalent(v.X * scale, norm.X)
Assert.ApproximatelyEqual(v.Y * scale, norm.Y) Assert.ApproximatelyEquivalent(v.Y * scale, norm.Y)
Assert.ApproximatelyEqual(v.Z * scale, norm.Z) Assert.ApproximatelyEquivalent(v.Z * scale, norm.Z)
[<Property>] [<Property>]
let ``Normalization by reference is the same as division by magnitude`` (a : Vector3) = let ``Normalization by reference is the same as division by magnitude`` (a : Vector3) =
let norm = a / a.Length // Zero-length vectors can't be normalized
let vRes = Vector3.Normalize(ref a) if not (approxEq a.Length 0.0f) then
let norm = a / a.Length
let vRes = Vector3.Normalize(ref a)
Assert.ApproximatelyEqual(norm, vRes) Assert.ApproximatelyEquivalent(norm, vRes)
[<Property>] [<Property>]
let ``Normalization is the same as division by magnitude`` (a : Vector3) = let ``Normalization is the same as division by magnitude`` (a : Vector3) =
let norm = a / a.Length // Zero-length vectors can't be normalized
if not (approxEq a.Length 0.0f) then
let norm = a / a.Length
Assert.ApproximatelyEqual(norm, Vector3.Normalize(a)); Assert.ApproximatelyEquivalent(norm, Vector3.Normalize(a));
[<Property>] [<Property>]
let ``Fast approximate normalization by reference is the same as multiplication by the fast inverse square`` (a : Vector3) = let ``Fast approximate normalization by reference is the same as multiplication by the fast inverse square`` (a : Vector3) =
@ -185,7 +189,7 @@ module Vector3 =
let norm = a * scale let norm = a * scale
let vRes = Vector3.NormalizeFast(ref a) let vRes = Vector3.NormalizeFast(ref a)
Assert.ApproximatelyEqual(norm, vRes) Assert.ApproximatelyEquivalent(norm, vRes)
[<Property>] [<Property>]
let ``Fast approximate normalization is the same as multiplication by fast inverse square`` (a : Vector3) = let ``Fast approximate normalization is the same as multiplication by fast inverse square`` (a : Vector3) =
@ -193,7 +197,7 @@ module Vector3 =
let norm = a * scale let norm = a * scale
Assert.ApproximatelyEqual(norm, Vector3.NormalizeFast(a)); Assert.ApproximatelyEquivalent(norm, Vector3.NormalizeFast(a));
[<Properties(Arbitrary = [| typeof<OpenTKGen> |])>] [<Properties(Arbitrary = [| typeof<OpenTKGen> |])>]
module Addition = module Addition =
@ -202,23 +206,23 @@ module Vector3 =
let ``Vector3 addition is the same as component addition`` (a : Vector3, b : Vector3) = let ``Vector3 addition is the same as component addition`` (a : Vector3, b : Vector3) =
let c = a + b let c = a + b
Assert.ApproximatelyEqual(a.X + b.X,c.X) Assert.ApproximatelyEquivalent(a.X + b.X,c.X)
Assert.ApproximatelyEqual(a.Y + b.Y,c.Y) Assert.ApproximatelyEquivalent(a.Y + b.Y,c.Y)
Assert.ApproximatelyEqual(a.Z + b.Z,c.Z) Assert.ApproximatelyEquivalent(a.Z + b.Z,c.Z)
[<Property>] [<Property>]
let ``Vector3 addition is commutative`` (a : Vector3, b : Vector3) = let ``Vector3 addition is commutative`` (a : Vector3, b : Vector3) =
let c = a + b let c = a + b
let c2 = b + a let c2 = b + a
Assert.ApproximatelyEqual(c, c2) Assert.ApproximatelyEquivalent(c, c2)
[<Property>] [<Property>]
let ``Vector3 addition is associative`` (a : Vector3, b : Vector3, c : Vector3) = let ``Vector3 addition is associative`` (a : Vector3, b : Vector3, c : Vector3) =
let r1 = (a + b) + c let r1 = (a + b) + c
let r2 = a + (b + c) let r2 = a + (b + c)
Assert.ApproximatelyEqual(r1, r2) Assert.ApproximatelyEquivalent(r1, r2)
[<Property>] [<Property>]
let ``Static Vector3 addition method is the same as component addition`` (a : Vector3, b : Vector3) = let ``Static Vector3 addition method is the same as component addition`` (a : Vector3, b : Vector3) =
@ -226,7 +230,7 @@ module Vector3 =
let v1 = Vector3(a.X + b.X, a.Y + b.Y, a.Z + b.Z) let v1 = Vector3(a.X + b.X, a.Y + b.Y, a.Z + b.Z)
let sum = Vector3.Add(a, b) let sum = Vector3.Add(a, b)
Assert.ApproximatelyEqual(v1, sum) Assert.ApproximatelyEquivalent(v1, sum)
[<Property>] [<Property>]
let ``Static Vector3 addition method by reference is the same as component addition`` (a : Vector3, b : Vector3) = let ``Static Vector3 addition method by reference is the same as component addition`` (a : Vector3, b : Vector3) =
@ -234,7 +238,7 @@ module Vector3 =
let v1 = Vector3(a.X + b.X, a.Y + b.Y, a.Z + b.Z) let v1 = Vector3(a.X + b.X, a.Y + b.Y, a.Z + b.Z)
let sum = Vector3.Add(ref a, ref b) let sum = Vector3.Add(ref a, ref b)
Assert.ApproximatelyEqual(v1, sum) Assert.ApproximatelyEquivalent(v1, sum)
[<Properties(Arbitrary = [| typeof<OpenTKGen> |])>] [<Properties(Arbitrary = [| typeof<OpenTKGen> |])>]
module Subtraction = module Subtraction =
@ -253,7 +257,7 @@ module Vector3 =
let v1 = Vector3(a.X - b.X, a.Y - b.Y, a.Z - b.Z) let v1 = Vector3(a.X - b.X, a.Y - b.Y, a.Z - b.Z)
let sum = Vector3.Subtract(a, b) let sum = Vector3.Subtract(a, b)
Assert.ApproximatelyEqual(v1, sum) Assert.ApproximatelyEquivalent(v1, sum)
[<Property>] [<Property>]
let ``Static Vector3 subtraction method by reference is the same as component addition`` (a : Vector3, b : Vector3) = let ``Static Vector3 subtraction method by reference is the same as component addition`` (a : Vector3, b : Vector3) =
@ -261,7 +265,7 @@ module Vector3 =
let v1 = Vector3(a.X - b.X, a.Y - b.Y, a.Z - b.Z) let v1 = Vector3(a.X - b.X, a.Y - b.Y, a.Z - b.Z)
let sum = Vector3.Subtract(ref a, ref b) let sum = Vector3.Subtract(ref a, ref b)
Assert.ApproximatelyEqual(v1, sum) Assert.ApproximatelyEquivalent(v1, sum)
[<Properties(Arbitrary = [| typeof<OpenTKGen> |])>] [<Properties(Arbitrary = [| typeof<OpenTKGen> |])>]
module Multiplication = module Multiplication =
@ -334,7 +338,7 @@ module Vector3 =
let v1 = Vector3(a.X * b.X, a.Y * b.Y, a.Z * b.Z) let v1 = Vector3(a.X * b.X, a.Y * b.Y, a.Z * b.Z)
let sum = Vector3.Multiply(a, b) let sum = Vector3.Multiply(a, b)
Assert.ApproximatelyEqual(v1, sum) Assert.ApproximatelyEquivalent(v1, sum)
[<Property>] [<Property>]
let ``Static Vector3 multiplication method by reference is the same as component multiplication`` (a : Vector3, b : Vector3) = let ``Static Vector3 multiplication method by reference is the same as component multiplication`` (a : Vector3, b : Vector3) =
@ -342,7 +346,7 @@ module Vector3 =
let v1 = Vector3(a.X * b.X, a.Y * b.Y, a.Z * b.Z) let v1 = Vector3(a.X * b.X, a.Y * b.Y, a.Z * b.Z)
let sum = Vector3.Multiply(ref a, ref b) let sum = Vector3.Multiply(ref a, ref b)
Assert.ApproximatelyEqual(v1, sum) Assert.ApproximatelyEquivalent(v1, sum)
[<Properties(Arbitrary = [| typeof<OpenTKGen> |])>] [<Properties(Arbitrary = [| typeof<OpenTKGen> |])>]
module Division = module Division =
@ -352,41 +356,41 @@ module Vector3 =
if not (approxEq f 0.0f) then // we don't support diving by zero. if not (approxEq f 0.0f) then // we don't support diving by zero.
let r = a / f let r = a / f
Assert.ApproximatelyEqual(a.X / f,r.X) Assert.ApproximatelyEquivalent(a.X / f,r.X)
Assert.ApproximatelyEqual(a.Y / f,r.Y) Assert.ApproximatelyEquivalent(a.Y / f,r.Y)
Assert.ApproximatelyEqual(a.Z / f,r.Z) Assert.ApproximatelyEquivalent(a.Z / f,r.Z)
[<Property>] [<Property>]
let ``Static Vector3-Vector3 division method is the same as component division`` (a : Vector3, b : Vector3) = let ``Static Vector3-Vector3 division method is the same as component division`` (a : Vector3, b : Vector3) =
if not (anyZero3 a || anyZero3 b) then
let v1 = Vector3(a.X / b.X, a.Y / b.Y, a.Z / b.Z)
let sum = Vector3.Divide(a, b)
let v1 = Vector3(a.X / b.X, a.Y / b.Y, a.Z / b.Z) Assert.ApproximatelyEquivalent(v1, sum)
let sum = Vector3.Divide(a, b)
Assert.ApproximatelyEqual(v1, sum)
[<Property>] [<Property>]
let ``Static Vector3-Vector3 divison method by reference is the same as component division`` (a : Vector3, b : Vector3) = let ``Static Vector3-Vector3 divison method by reference is the same as component division`` (a : Vector3, b : Vector3) =
if not (anyZero3 a || anyZero3 b) then
let v1 = Vector3(a.X / b.X, a.Y / b.Y, a.Z / b.Z)
let sum = Vector3.Divide(ref a, ref b)
let v1 = Vector3(a.X / b.X, a.Y / b.Y, a.Z / b.Z) Assert.ApproximatelyEquivalent(v1, sum)
let sum = Vector3.Divide(ref a, ref b)
Assert.ApproximatelyEqual(v1, sum)
[<Property>] [<Property>]
let ``Static Vector3-scalar division method is the same as component division`` (a : Vector3, b : float32) = let ``Static Vector3-scalar division method is the same as component division`` (a : Vector3, b : float32) =
if not (approxEq b 0.0f) then // we don't support diving by zero.
let v1 = Vector3(a.X / b, a.Y / b, a.Z / b)
let sum = Vector3.Divide(a, b)
let v1 = Vector3(a.X / b, a.Y / b, a.Z / b) Assert.ApproximatelyEquivalent(v1, sum)
let sum = Vector3.Divide(a, b)
Assert.ApproximatelyEqual(v1, sum)
[<Property>] [<Property>]
let ``Static Vector3-scalar divison method by reference is the same as component division`` (a : Vector3, b : float32) = let ``Static Vector3-scalar divison method by reference is the same as component division`` (a : Vector3, b : float32) =
if not (approxEq b 0.0f) then // we don't support diving by zero.
let v1 = Vector3(a.X / b, a.Y / b, a.Z / b)
let sum = Vector3.Divide(ref a, b)
let v1 = Vector3(a.X / b, a.Y / b, a.Z / b) Assert.ApproximatelyEquivalent(v1, sum)
let sum = Vector3.Divide(ref a, b)
Assert.ApproximatelyEqual(v1, sum)
[<Properties(Arbitrary = [| typeof<OpenTKGen> |])>] [<Properties(Arbitrary = [| typeof<OpenTKGen> |])>]
module Negation = module Negation =
@ -532,59 +536,67 @@ module Vector3 =
// //
[<Property>] [<Property>]
let ``MagnitudeMin selects the vector with equal or lesser magnitude given two vectors`` (v1 : Vector3, v2: Vector3) = let ``MagnitudeMin selects the vector with equal or lesser magnitude given two vectors`` (v1 : Vector3, v2: Vector3) =
let l1 = v1.LengthSquared // Results do not matter for equal vectors
let l2 = v2.LengthSquared if not (v1 = v2) then
let l1 = v1.LengthSquared
let l2 = v2.LengthSquared
let vMin = Vector3.MagnitudeMin(v1, v2) let vMin = Vector3.MagnitudeMin(v1, v2)
if vMin = v1 then if vMin = v1 then
let v1ShorterThanv2 = l1 < l2 let v1ShorterThanv2 = l1 < l2
Assert.True(v1ShorterThanv2) Assert.True(v1ShorterThanv2)
else else
let v2ShorterThanOrEqualTov1 = l2 <= l1 let v2ShorterThanOrEqualTov1 = l2 <= l1
Assert.True(v2ShorterThanOrEqualTov1) Assert.True(v2ShorterThanOrEqualTov1)
[<Property>] [<Property>]
let ``MagnitudeMax selects the vector with equal or greater magnitude given two vectors`` (v1 : Vector3, v2: Vector3) = let ``MagnitudeMax selects the vector with equal or greater magnitude given two vectors`` (v1 : Vector3, v2: Vector3) =
let l1 = v1.LengthSquared // Results do not matter for equal vectors
let l2 = v2.LengthSquared if not (v1 = v2) then
let l1 = v1.LengthSquared
let l2 = v2.LengthSquared
let vMin = Vector3.MagnitudeMax(v1, v2) let vMin = Vector3.MagnitudeMax(v1, v2)
if vMin = v1 then if vMin = v1 then
let v1LongerThanOrEqualTov2 = l1 >= l2 let v1LongerThanOrEqualTov2 = l1 >= l2
Assert.True(v1LongerThanOrEqualTov2) Assert.True(v1LongerThanOrEqualTov2)
else else
let v2LongerThanv1 = l2 > l1 let v2LongerThanv1 = l2 > l1
Assert.True(v2LongerThanv1) Assert.True(v2LongerThanv1)
[<Property>] [<Property>]
let ``MagnitudeMin by reference selects the vector with equal or lesser magnitude given two vectors`` (v1 : Vector3, v2: Vector3) = let ``MagnitudeMin by reference selects the vector with equal or lesser magnitude given two vectors`` (v1 : Vector3, v2: Vector3) =
let l1 = v1.LengthSquared // Results do not matter for equal vectors
let l2 = v2.LengthSquared if not (v1 = v2) then
let l1 = v1.LengthSquared
let l2 = v2.LengthSquared
let vMin = Vector3.MagnitudeMin(ref v1, ref v2) let vMin = Vector3.MagnitudeMin(ref v1, ref v2)
if vMin = v1 then if vMin = v1 then
let v1ShorterThanv2 = l1 < l2 let v1ShorterThanv2 = l1 < l2
Assert.True(v1ShorterThanv2) Assert.True(v1ShorterThanv2)
else else
let v2ShorterThanOrEqualTov1 = l2 <= l1 let v2ShorterThanOrEqualTov1 = l2 <= l1
Assert.True(v2ShorterThanOrEqualTov1) Assert.True(v2ShorterThanOrEqualTov1)
[<Property>] [<Property>]
let ``MagnitudeMax by reference selects the vector with equal or greater magnitude given two vectors`` (v1 : Vector3, v2: Vector3) = let ``MagnitudeMax by reference selects the vector with equal or greater magnitude given two vectors`` (v1 : Vector3, v2: Vector3) =
let l1 = v1.LengthSquared // Results do not matter for equal vectors
let l2 = v2.LengthSquared if not (v1 = v2) then
let l1 = v1.LengthSquared
let l2 = v2.LengthSquared
let vMin = Vector3.MagnitudeMax(ref v1, ref v2) let vMin = Vector3.MagnitudeMax(ref v1, ref v2)
if vMin = v1 then if vMin = v1 then
let v1LongerThanOrEqualTov2 = l1 >= l2 let v1LongerThanOrEqualTov2 = l1 >= l2
Assert.True(v1LongerThanOrEqualTov2) Assert.True(v1LongerThanOrEqualTov2)
else else
let v2LongerThanv1 = l2 > l1 let v2LongerThanv1 = l2 > l1
Assert.True(v2LongerThanv1) Assert.True(v2LongerThanv1)
[<Properties(Arbitrary = [| typeof<OpenTKGen> |])>] [<Properties(Arbitrary = [| typeof<OpenTKGen> |])>]
module ``Component min and max`` = module ``Component min and max`` =
@ -706,7 +718,7 @@ module Vector3 =
let transformedQuat = q * vectorQuat * inverse let transformedQuat = q * vectorQuat * inverse
let transformedVector = transformedQuat.Xyz let transformedVector = transformedQuat.Xyz
Assert.ApproximatelyEqual(transformedVector, Vector3.Transform(v, q)) Assert.ApproximatelyEquivalent(transformedVector, Vector3.Transform(v, q))
[<Property>] [<Property>]
let ``Transformation by quaternion by reference is the same as multiplication by quaternion and its conjugate`` (v : Vector3, q : Quaternion) = let ``Transformation by quaternion by reference is the same as multiplication by quaternion and its conjugate`` (v : Vector3, q : Quaternion) =
@ -716,7 +728,7 @@ module Vector3 =
let transformedQuat = q * vectorQuat * inverse let transformedQuat = q * vectorQuat * inverse
let transformedVector = transformedQuat.Xyz let transformedVector = transformedQuat.Xyz
Assert.ApproximatelyEqual(transformedVector, Vector3.Transform(ref v, ref q)) Assert.ApproximatelyEquivalent(transformedVector, Vector3.Transform(ref v, ref q))
[<Property>] [<Property>]
let ``Transformation by quaternion by multiplication using right-handed notation is the same as multiplication by quaternion and its conjugate`` (v : Vector3, q : Quaternion) = let ``Transformation by quaternion by multiplication using right-handed notation is the same as multiplication by quaternion and its conjugate`` (v : Vector3, q : Quaternion) =
@ -726,7 +738,7 @@ module Vector3 =
let transformedQuat = q * vectorQuat * inverse let transformedQuat = q * vectorQuat * inverse
let transformedVector = transformedQuat.Xyz let transformedVector = transformedQuat.Xyz
Assert.ApproximatelyEqual(transformedVector, q * v) Assert.ApproximatelyEquivalent(transformedVector, q * v)
[<Property>] [<Property>]
let ``Transformation by identity quaternion does not alter vector`` (v : Vector3) = let ``Transformation by identity quaternion does not alter vector`` (v : Vector3) =
@ -737,6 +749,6 @@ module Vector3 =
let transformedQuat = q * vectorQuat * inverse let transformedQuat = q * vectorQuat * inverse
let transformedVector = transformedQuat.Xyz let transformedVector = transformedQuat.Xyz
Assert.ApproximatelyEqual(v, transformedVector) Assert.ApproximatelyEquivalent(v, transformedVector)
Assert.ApproximatelyEqual(v, Vector3.Transform(v, q)) Assert.ApproximatelyEquivalent(v, Vector3.Transform(v, q))
Assert.ApproximatelyEqual(transformedVector, Vector3.Transform(v, q)) Assert.ApproximatelyEquivalent(transformedVector, Vector3.Transform(v, q))

View file

@ -153,25 +153,29 @@ module Vector4 =
let v = Vector4(x, y, z, w) let v = Vector4(x, y, z, w)
let l = v.Length let l = v.Length
let norm = v.Normalized() // Zero-length vectors can't be normalized
if not (approxEq l 0.0f) then
let norm = v.Normalized()
Assert.ApproximatelyEqual(v.X / l, norm.X) Assert.ApproximatelyEquivalent(v.X / l, norm.X)
Assert.ApproximatelyEqual(v.Y / l, norm.Y) Assert.ApproximatelyEquivalent(v.Y / l, norm.Y)
Assert.ApproximatelyEqual(v.Z / l, norm.Z) Assert.ApproximatelyEquivalent(v.Z / l, norm.Z)
Assert.ApproximatelyEqual(v.W / l, norm.W) Assert.ApproximatelyEquivalent(v.W / l, norm.W)
[<Property>] [<Property>]
let ``Normalization of instance transforms the instance into a unit length vector with the correct components`` (x, y, z, w) = let ``Normalization of instance transforms the instance into a unit length vector with the correct components`` (x, y, z, w) =
let v = Vector4(x, y, z, w) let v = Vector4(x, y, z, w)
let l = v.Length let l = v.Length
let norm = Vector4(x, y, z, w) // Zero-length vectors can't be normalized
norm.Normalize() if not (approxEq l 0.0f) then
let norm = Vector4(x, y, z, w)
norm.Normalize()
Assert.ApproximatelyEqual(v.X / l, norm.X) Assert.ApproximatelyEquivalent(v.X / l, norm.X)
Assert.ApproximatelyEqual(v.Y / l, norm.Y) Assert.ApproximatelyEquivalent(v.Y / l, norm.Y)
Assert.ApproximatelyEqual(v.Z / l, norm.Z) Assert.ApproximatelyEquivalent(v.Z / l, norm.Z)
Assert.ApproximatelyEqual(v.W / l, norm.W) Assert.ApproximatelyEquivalent(v.W / l, norm.W)
[<Property>] [<Property>]
let ``Fast approximate normalization of instance transforms the instance into a unit length vector with the correct components`` (x, y, z, w) = let ``Fast approximate normalization of instance transforms the instance into a unit length vector with the correct components`` (x, y, z, w) =
@ -181,23 +185,27 @@ module Vector4 =
let scale = MathHelper.InverseSqrtFast(x * x + y * y + z * z + w * w) let scale = MathHelper.InverseSqrtFast(x * x + y * y + z * z + w * w)
Assert.ApproximatelyEqual(v.X * scale, norm.X) Assert.ApproximatelyEquivalent(v.X * scale, norm.X)
Assert.ApproximatelyEqual(v.Y * scale, norm.Y) Assert.ApproximatelyEquivalent(v.Y * scale, norm.Y)
Assert.ApproximatelyEqual(v.Z * scale, norm.Z) Assert.ApproximatelyEquivalent(v.Z * scale, norm.Z)
Assert.ApproximatelyEqual(v.W * scale, norm.W) Assert.ApproximatelyEquivalent(v.W * scale, norm.W)
[<Property>] [<Property>]
let ``Normalization by reference is the same as division by magnitude`` (a : Vector4) = let ``Normalization by reference is the same as division by magnitude`` (a : Vector4) =
let norm = a / a.Length // Zero-length vectors can't be normalized
let vRes = Vector4.Normalize(ref a) if not (approxEq a.Length 0.0f) then
let norm = a / a.Length
let vRes = Vector4.Normalize(ref a)
Assert.ApproximatelyEqual(norm, vRes) Assert.ApproximatelyEquivalent(norm, vRes)
[<Property>] [<Property>]
let ``Normalization is the same as division by magnitude`` (a : Vector4) = let ``Normalization is the same as division by magnitude`` (a : Vector4) =
let norm = a / a.Length // Zero-length vectors can't be normalized
if not (approxEq a.Length 0.0f) then
let norm = a / a.Length
Assert.ApproximatelyEqual(norm, Vector4.Normalize(a)); Assert.ApproximatelyEquivalent(norm, Vector4.Normalize(a));
[<Property>] [<Property>]
let ``Fast approximate normalization by reference is the same as multiplication by the fast inverse square`` (a : Vector4) = let ``Fast approximate normalization by reference is the same as multiplication by the fast inverse square`` (a : Vector4) =
@ -206,15 +214,14 @@ module Vector4 =
let norm = a * scale let norm = a * scale
let vRes = Vector4.NormalizeFast(ref a) let vRes = Vector4.NormalizeFast(ref a)
Assert.ApproximatelyEqual(norm, vRes) Assert.ApproximatelyEquivalent(norm, vRes)
[<Property>] [<Property>]
let ``Fast approximate normalization is the same as multiplication by the fast inverse square`` (a : Vector4) = let ``Fast approximate normalization is the same as multiplication by the fast inverse square`` (a : Vector4) =
let scale = MathHelper.InverseSqrtFast(a.X * a.X + a.Y * a.Y + a.Z * a.Z + a.W * a.W) let scale = MathHelper.InverseSqrtFast(a.X * a.X + a.Y * a.Y + a.Z * a.Z + a.W * a.W)
let norm = a * scale let norm = a * scale
Assert.ApproximatelyEqual(norm, Vector4.NormalizeFast(a)); Assert.ApproximatelyEquivalent(norm, Vector4.NormalizeFast(a));
[<Properties(Arbitrary = [| typeof<OpenTKGen> |])>] [<Properties(Arbitrary = [| typeof<OpenTKGen> |])>]
module Addition = module Addition =
@ -223,24 +230,24 @@ module Vector4 =
let ``Vector4 addition is the same as component addition`` (a : Vector4, b : Vector4) = let ``Vector4 addition is the same as component addition`` (a : Vector4, b : Vector4) =
let c = a + b let c = a + b
Assert.ApproximatelyEqual(a.X + b.X,c.X) Assert.ApproximatelyEquivalent(a.X + b.X,c.X)
Assert.ApproximatelyEqual(a.Y + b.Y,c.Y) Assert.ApproximatelyEquivalent(a.Y + b.Y,c.Y)
Assert.ApproximatelyEqual(a.Z + b.Z,c.Z) Assert.ApproximatelyEquivalent(a.Z + b.Z,c.Z)
Assert.ApproximatelyEqual(a.W + b.W,c.W) Assert.ApproximatelyEquivalent(a.W + b.W,c.W)
[<Property>] [<Property>]
let ``Vector4 addition is commutative`` (a : Vector4, b : Vector4) = let ``Vector4 addition is commutative`` (a : Vector4, b : Vector4) =
let c = a + b let c = a + b
let c2 = b + a let c2 = b + a
Assert.ApproximatelyEqual(c, c2) Assert.ApproximatelyEquivalent(c, c2)
[<Property>] [<Property>]
let ``Vector4 addition is associative`` (a : Vector4, b : Vector4, c : Vector4) = let ``Vector4 addition is associative`` (a : Vector4, b : Vector4, c : Vector4) =
let r1 = (a + b) + c let r1 = (a + b) + c
let r2 = a + (b + c) let r2 = a + (b + c)
Assert.ApproximatelyEqual(r1, r2) Assert.ApproximatelyEquivalent(r1, r2)
[<Property>] [<Property>]
let ``Static Vector4 addition method is the same as component addition`` (a : Vector4, b : Vector4) = let ``Static Vector4 addition method is the same as component addition`` (a : Vector4, b : Vector4) =
@ -248,7 +255,7 @@ module Vector4 =
let v1 = Vector4(a.X + b.X, a.Y + b.Y, a.Z + b.Z, a.W + b.W) let v1 = Vector4(a.X + b.X, a.Y + b.Y, a.Z + b.Z, a.W + b.W)
let sum = Vector4.Add(a, b) let sum = Vector4.Add(a, b)
Assert.ApproximatelyEqual(v1, sum) Assert.ApproximatelyEquivalent(v1, sum)
[<Property>] [<Property>]
let ``Static Vector4 addition method by reference is the same as component addition`` (a : Vector4, b : Vector4) = let ``Static Vector4 addition method by reference is the same as component addition`` (a : Vector4, b : Vector4) =
@ -256,7 +263,7 @@ module Vector4 =
let v1 = Vector4(a.X + b.X, a.Y + b.Y, a.Z + b.Z, a.W + b.W) let v1 = Vector4(a.X + b.X, a.Y + b.Y, a.Z + b.Z, a.W + b.W)
let sum = Vector4.Add(ref a, ref b) let sum = Vector4.Add(ref a, ref b)
Assert.ApproximatelyEqual(v1, sum) Assert.ApproximatelyEquivalent(v1, sum)
[<Properties(Arbitrary = [| typeof<OpenTKGen> |])>] [<Properties(Arbitrary = [| typeof<OpenTKGen> |])>]
module Subtraction = module Subtraction =
@ -276,7 +283,7 @@ module Vector4 =
let v1 = Vector4(a.X - b.X, a.Y - b.Y, a.Z - b.Z, a.W - b.W) let v1 = Vector4(a.X - b.X, a.Y - b.Y, a.Z - b.Z, a.W - b.W)
let sum = Vector4.Subtract(a, b) let sum = Vector4.Subtract(a, b)
Assert.ApproximatelyEqual(v1, sum) Assert.ApproximatelyEquivalent(v1, sum)
[<Property>] [<Property>]
let ``Static Vector4 subtraction method by reference is the same as component addition`` (a : Vector4, b : Vector4) = let ``Static Vector4 subtraction method by reference is the same as component addition`` (a : Vector4, b : Vector4) =
@ -284,7 +291,7 @@ module Vector4 =
let v1 = Vector4(a.X - b.X, a.Y - b.Y, a.Z - b.Z, a.W - b.W) let v1 = Vector4(a.X - b.X, a.Y - b.Y, a.Z - b.Z, a.W - b.W)
let sum = Vector4.Subtract(ref a, ref b) let sum = Vector4.Subtract(ref a, ref b)
Assert.ApproximatelyEqual(v1, sum) Assert.ApproximatelyEquivalent(v1, sum)
[<Properties(Arbitrary = [| typeof<OpenTKGen> |])>] [<Properties(Arbitrary = [| typeof<OpenTKGen> |])>]
module Multiplication = module Multiplication =
@ -364,7 +371,7 @@ module Vector4 =
let v1 = Vector4(a.X * b.X, a.Y * b.Y, a.Z * b.Z, a.W * b.W) let v1 = Vector4(a.X * b.X, a.Y * b.Y, a.Z * b.Z, a.W * b.W)
let sum = Vector4.Multiply(a, b) let sum = Vector4.Multiply(a, b)
Assert.ApproximatelyEqual(v1, sum) Assert.ApproximatelyEquivalent(v1, sum)
[<Property>] [<Property>]
let ``Static Vector4 multiplication method by reference is the same as component multiplication`` (a : Vector4, b : Vector4) = let ``Static Vector4 multiplication method by reference is the same as component multiplication`` (a : Vector4, b : Vector4) =
@ -372,7 +379,7 @@ module Vector4 =
let v1 = Vector4(a.X * b.X, a.Y * b.Y, a.Z * b.Z, a.W * b.W) let v1 = Vector4(a.X * b.X, a.Y * b.Y, a.Z * b.Z, a.W * b.W)
let sum = Vector4.Multiply(ref a, ref b) let sum = Vector4.Multiply(ref a, ref b)
Assert.ApproximatelyEqual(v1, sum) Assert.ApproximatelyEquivalent(v1, sum)
[<Properties(Arbitrary = [| typeof<OpenTKGen> |])>] [<Properties(Arbitrary = [| typeof<OpenTKGen> |])>]
module Division = module Division =
@ -382,42 +389,42 @@ module Vector4 =
if not (approxEq f 0.0f) then // we don't support diving by zero. if not (approxEq f 0.0f) then // we don't support diving by zero.
let r = a / f let r = a / f
Assert.ApproximatelyEqual(a.X / f, r.X) Assert.ApproximatelyEquivalent(a.X / f, r.X)
Assert.ApproximatelyEqual(a.Y / f, r.Y) Assert.ApproximatelyEquivalent(a.Y / f, r.Y)
Assert.ApproximatelyEqual(a.Z / f, r.Z) Assert.ApproximatelyEquivalent(a.Z / f, r.Z)
Assert.ApproximatelyEqual(a.W / f, r.W) Assert.ApproximatelyEquivalent(a.W / f, r.W)
[<Property>] [<Property>]
let ``Static Vector4-Vector4 division method is the same as component division`` (a : Vector4, b : Vector4) = let ``Static Vector4-Vector4 division method is the same as component division`` (a : Vector4, b : Vector4) =
if not (anyZero4 a || anyZero4 b) then
let v1 = Vector4(a.X / b.X, a.Y / b.Y, a.Z / b.Z, a.W / b.W)
let sum = Vector4.Divide(a, b)
let v1 = Vector4(a.X / b.X, a.Y / b.Y, a.Z / b.Z, a.W / b.W) Assert.ApproximatelyEquivalent(v1, sum)
let sum = Vector4.Divide(a, b)
Assert.ApproximatelyEqual(v1, sum)
[<Property>] [<Property>]
let ``Static Vector4-Vector4 divison method by reference is the same as component division`` (a : Vector4, b : Vector4) = let ``Static Vector4-Vector4 divison method by reference is the same as component division`` (a : Vector4, b : Vector4) =
if not (anyZero4 a || anyZero4 b) then
let v1 = Vector4(a.X / b.X, a.Y / b.Y, a.Z / b.Z, a.W / b.W)
let sum = Vector4.Divide(ref a, ref b)
let v1 = Vector4(a.X / b.X, a.Y / b.Y, a.Z / b.Z, a.W / b.W) Assert.ApproximatelyEquivalent(v1, sum)
let sum = Vector4.Divide(ref a, ref b)
Assert.ApproximatelyEqual(v1, sum)
[<Property>] [<Property>]
let ``Static Vector4-scalar division method is the same as component division`` (a : Vector4, b : float32) = let ``Static Vector4-scalar division method is the same as component division`` (a : Vector4, b : float32) =
if not (approxEq b 0.0f) then // we don't support diving by zero.
let v1 = Vector4(a.X / b, a.Y / b, a.Z / b, a.W / b)
let sum = Vector4.Divide(a, b)
let v1 = Vector4(a.X / b, a.Y / b, a.Z / b, a.W / b) Assert.ApproximatelyEquivalent(v1, sum)
let sum = Vector4.Divide(a, b)
Assert.ApproximatelyEqual(v1, sum)
[<Property>] [<Property>]
let ``Static Vector4-scalar divison method by reference is the same as component division`` (a : Vector4, b : float32) = let ``Static Vector4-scalar divison method by reference is the same as component division`` (a : Vector4, b : float32) =
if not (approxEq b 0.0f) then // we don't support diving by zero.
let v1 = Vector4(a.X / b, a.Y / b, a.Z / b, a.W / b)
let sum = Vector4.Divide(ref a, b)
let v1 = Vector4(a.X / b, a.Y / b, a.Z / b, a.W / b) Assert.ApproximatelyEquivalent(v1, sum)
let sum = Vector4.Divide(ref a, b)
Assert.ApproximatelyEqual(v1, sum)
[<Properties(Arbitrary = [| typeof<OpenTKGen> |])>] [<Properties(Arbitrary = [| typeof<OpenTKGen> |])>]
module Negation = module Negation =
@ -696,59 +703,67 @@ module Vector4 =
// //
[<Property>] [<Property>]
let ``MagnitudeMin selects the vector with equal or lesser magnitude given two vectors`` (v1 : Vector4, v2: Vector4) = let ``MagnitudeMin selects the vector with equal or lesser magnitude given two vectors`` (v1 : Vector4, v2: Vector4) =
let l1 = v1.LengthSquared // Results do not matter for equal vectors
let l2 = v2.LengthSquared if not (v1 = v2) then
let l1 = v1.LengthSquared
let l2 = v2.LengthSquared
let vMin = Vector4.MagnitudeMin(v1, v2) let vMin = Vector4.MagnitudeMin(v1, v2)
if vMin = v1 then if vMin = v1 then
let v1ShorterThanv2 = l1 < l2 let v1ShorterThanv2 = l1 < l2
Assert.True(v1ShorterThanv2) Assert.True(v1ShorterThanv2)
else else
let v2ShorterThanOrEqualTov1 = l2 <= l1 let v2ShorterThanOrEqualTov1 = l2 <= l1
Assert.True(v2ShorterThanOrEqualTov1) Assert.True(v2ShorterThanOrEqualTov1)
[<Property>] [<Property>]
let ``MagnitudeMax selects the vector with equal or greater magnitude given two vectors`` (v1 : Vector4, v2: Vector4) = let ``MagnitudeMax selects the vector with equal or greater magnitude given two vectors`` (v1 : Vector4, v2: Vector4) =
let l1 = v1.LengthSquared // Results do not matter for equal vectors
let l2 = v2.LengthSquared if not (v1 = v2) then
let l1 = v1.LengthSquared
let l2 = v2.LengthSquared
let vMin = Vector4.MagnitudeMax(v1, v2) let vMin = Vector4.MagnitudeMax(v1, v2)
if vMin = v1 then if vMin = v1 then
let v1LongerThanOrEqualTov2 = l1 >= l2 let v1LongerThanOrEqualTov2 = l1 >= l2
Assert.True(v1LongerThanOrEqualTov2) Assert.True(v1LongerThanOrEqualTov2)
else else
let v2LongerThanv1 = l2 > l1 let v2LongerThanv1 = l2 > l1
Assert.True(v2LongerThanv1) Assert.True(v2LongerThanv1)
[<Property>] [<Property>]
let ``MagnitudeMin by reference selects the vector with equal or lesser magnitude given two vectors`` (v1 : Vector4, v2: Vector4) = let ``MagnitudeMin by reference selects the vector with equal or lesser magnitude given two vectors`` (v1 : Vector4, v2: Vector4) =
let l1 = v1.LengthSquared // Results do not matter for equal vectors
let l2 = v2.LengthSquared if not (v1 = v2) then
let l1 = v1.LengthSquared
let l2 = v2.LengthSquared
let vMin = Vector4.MagnitudeMin(ref v1, ref v2) let vMin = Vector4.MagnitudeMin(ref v1, ref v2)
if vMin = v1 then if vMin = v1 then
let v1ShorterThanv2 = l1 < l2 let v1ShorterThanv2 = l1 < l2
Assert.True(v1ShorterThanv2) Assert.True(v1ShorterThanv2)
else else
let v2ShorterThanOrEqualTov1 = l2 <= l1 let v2ShorterThanOrEqualTov1 = l2 <= l1
Assert.True(v2ShorterThanOrEqualTov1) Assert.True(v2ShorterThanOrEqualTov1)
[<Property>] [<Property>]
let ``MagnitudeMax by reference selects the vector with equal or greater magnitude given two vectors`` (v1 : Vector4, v2: Vector4) = let ``MagnitudeMax by reference selects the vector with equal or greater magnitude given two vectors`` (v1 : Vector4, v2: Vector4) =
let l1 = v1.LengthSquared // Results do not matter for equal vectors
let l2 = v2.LengthSquared if not (v1 = v2) then
let l1 = v1.LengthSquared
let l2 = v2.LengthSquared
let vMin = Vector4.MagnitudeMax(ref v1, ref v2) let vMin = Vector4.MagnitudeMax(ref v1, ref v2)
if vMin = v1 then if vMin = v1 then
let v1LongerThanOrEqualTov2 = l1 >= l2 let v1LongerThanOrEqualTov2 = l1 >= l2
Assert.True(v1LongerThanOrEqualTov2) Assert.True(v1LongerThanOrEqualTov2)
else else
let v2LongerThanv1 = l2 > l1 let v2LongerThanv1 = l2 > l1
Assert.True(v2LongerThanv1) Assert.True(v2LongerThanv1)
[<Properties(Arbitrary = [| typeof<OpenTKGen> |])>] [<Properties(Arbitrary = [| typeof<OpenTKGen> |])>]
module ``Component min and max`` = module ``Component min and max`` =
@ -885,7 +900,7 @@ module Vector4 =
let transformedQuat = q * vectorQuat * inverse let transformedQuat = q * vectorQuat * inverse
let transformedVector = Vector4(transformedQuat.X, transformedQuat.Y, transformedQuat.Z, transformedQuat.W) let transformedVector = Vector4(transformedQuat.X, transformedQuat.Y, transformedQuat.Z, transformedQuat.W)
Assert.Equal(transformedVector, Vector4.Transform(v, q)) Assert.ApproximatelyEquivalent(transformedVector, Vector4.Transform(v, q))
[<Property>] [<Property>]
let ``Transformation by quaternion with static method by reference is the same as multiplication by quaternion and its conjugate`` (v : Vector4, q : Quaternion) = let ``Transformation by quaternion with static method by reference is the same as multiplication by quaternion and its conjugate`` (v : Vector4, q : Quaternion) =
@ -895,7 +910,7 @@ module Vector4 =
let transformedQuat = q * vectorQuat * inverse let transformedQuat = q * vectorQuat * inverse
let transformedVector = Vector4(transformedQuat.X, transformedQuat.Y,transformedQuat.Z, transformedQuat.W) let transformedVector = Vector4(transformedQuat.X, transformedQuat.Y,transformedQuat.Z, transformedQuat.W)
Assert.Equal(transformedVector, Vector4.Transform(ref v, ref q)) Assert.ApproximatelyEquivalent(transformedVector, Vector4.Transform(ref v, ref q))
[<Property>] [<Property>]
let ``Transformation by quaternion by multiplication using right-handed notation is the same as multiplication by quaternion and its conjugate`` (v : Vector4, q : Quaternion) = let ``Transformation by quaternion by multiplication using right-handed notation is the same as multiplication by quaternion and its conjugate`` (v : Vector4, q : Quaternion) =
@ -905,4 +920,4 @@ module Vector4 =
let transformedQuat = q * vectorQuat * inverse let transformedQuat = q * vectorQuat * inverse
let transformedVector = Vector4(transformedQuat.X, transformedQuat.Y, transformedQuat.Z, transformedQuat.W) let transformedVector = Vector4(transformedQuat.X, transformedQuat.Y, transformedQuat.Z, transformedQuat.W)
Assert.Equal(transformedVector, q * v) Assert.ApproximatelyEquivalent(transformedVector, q * v)