* Adding basic tests validating NaN.Equals(NaN) returns true for various vector types
* Updating the various vector APIs to use .NET compliant equality for the Equals method
* Fixing the Plane and Quaternion GetHashCode tests
* Removing JIT intrinsic support for the Equals instance method
* Accelerating the Equals instance method for the various vector types
SIMD_AS_HWINTRINSIC_ID(Vector2, Abs, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_AdvSimd_Abs, NI_Illegal}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_NM(Vector2, CreateBroadcast, ".ctor", 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_CreateBroadcast, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod)
SIMD_AS_HWINTRINSIC_ID(Vector2, Dot, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector64_Dot, NI_Illegal}, SimdAsHWIntrinsicFlag::None)
-SIMD_AS_HWINTRINSIC_NM(Vector2, EqualsInstance, "Equals", 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector64_op_Equality, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod)
SIMD_AS_HWINTRINSIC_ID(Vector2, get_One, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_get_One, NI_Illegal}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_ID(Vector2, get_Zero, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector64_get_Zero, NI_Illegal}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_ID(Vector2, Max, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_AdvSimd_Max, NI_Illegal}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_ID(Vector3, Abs, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_AdvSimd_Abs, NI_Illegal}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_NM(Vector3, CreateBroadcast, ".ctor", 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_CreateBroadcast, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod)
SIMD_AS_HWINTRINSIC_ID(Vector3, Dot, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector128_Dot, NI_Illegal}, SimdAsHWIntrinsicFlag::None)
-SIMD_AS_HWINTRINSIC_NM(Vector3, EqualsInstance, "Equals", 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector128_op_Equality, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod)
SIMD_AS_HWINTRINSIC_ID(Vector3, get_One, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_get_One, NI_Illegal}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_ID(Vector3, get_Zero, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector128_get_Zero, NI_Illegal}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_ID(Vector3, Max, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_AdvSimd_Max, NI_Illegal}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_ID(Vector4, Abs, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_AdvSimd_Abs, NI_Illegal}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_NM(Vector4, CreateBroadcast, ".ctor", 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_CreateBroadcast, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod)
SIMD_AS_HWINTRINSIC_ID(Vector4, Dot, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector128_Dot, NI_Illegal}, SimdAsHWIntrinsicFlag::None)
-SIMD_AS_HWINTRINSIC_NM(Vector4, EqualsInstance, "Equals", 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector128_op_Equality, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod)
SIMD_AS_HWINTRINSIC_ID(Vector4, get_One, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_get_One, NI_Illegal}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_ID(Vector4, get_Zero, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector128_get_Zero, NI_Illegal}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_ID(Vector4, Max, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_AdvSimd_Max, NI_Illegal}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_NM(VectorT128, CreateBroadcast, ".ctor", 2, {NI_VectorT128_CreateBroadcast, NI_VectorT128_CreateBroadcast, NI_VectorT128_CreateBroadcast, NI_VectorT128_CreateBroadcast, NI_VectorT128_CreateBroadcast, NI_VectorT128_CreateBroadcast, NI_VectorT128_CreateBroadcast, NI_VectorT128_CreateBroadcast, NI_VectorT128_CreateBroadcast, NI_VectorT128_CreateBroadcast}, SimdAsHWIntrinsicFlag::InstanceMethod)
SIMD_AS_HWINTRINSIC_ID(VectorT128, Dot, 2, {NI_Vector128_Dot, NI_Vector128_Dot, NI_Vector128_Dot, NI_Vector128_Dot, NI_Vector128_Dot, NI_Vector128_Dot, NI_Illegal, NI_Illegal, NI_Vector128_Dot, NI_Vector128_Dot}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_ID(VectorT128, Equals, 2, {NI_AdvSimd_CompareEqual, NI_AdvSimd_CompareEqual, NI_AdvSimd_CompareEqual, NI_AdvSimd_CompareEqual, NI_AdvSimd_CompareEqual, NI_AdvSimd_CompareEqual, NI_AdvSimd_Arm64_CompareEqual, NI_AdvSimd_Arm64_CompareEqual, NI_AdvSimd_CompareEqual, NI_AdvSimd_Arm64_CompareEqual}, SimdAsHWIntrinsicFlag::None)
-SIMD_AS_HWINTRINSIC_NM(VectorT128, EqualsInstance, "Equals", 2, {NI_Vector128_op_Equality, NI_Vector128_op_Equality, NI_Vector128_op_Equality, NI_Vector128_op_Equality, NI_Vector128_op_Equality, NI_Vector128_op_Equality, NI_Vector128_op_Equality, NI_Vector128_op_Equality, NI_Vector128_op_Equality, NI_Vector128_op_Equality}, SimdAsHWIntrinsicFlag::InstanceMethod)
SIMD_AS_HWINTRINSIC_ID(VectorT128, Floor, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_AdvSimd_Floor, NI_AdvSimd_Arm64_Floor}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_ID(VectorT128, get_AllBitsSet, 0, {NI_Vector128_get_AllBitsSet, NI_Vector128_get_AllBitsSet, NI_Vector128_get_AllBitsSet, NI_Vector128_get_AllBitsSet, NI_Vector128_get_AllBitsSet, NI_Vector128_get_AllBitsSet, NI_Vector128_get_AllBitsSet, NI_Vector128_get_AllBitsSet, NI_Vector128_get_AllBitsSet, NI_Vector128_get_AllBitsSet}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_ID(VectorT128, get_Count, 0, {NI_VectorT128_get_Count, NI_VectorT128_get_Count, NI_VectorT128_get_Count, NI_VectorT128_get_Count, NI_VectorT128_get_Count, NI_VectorT128_get_Count, NI_VectorT128_get_Count, NI_VectorT128_get_Count, NI_VectorT128_get_Count, NI_VectorT128_get_Count}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_ID(Vector2, Abs, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_Abs, NI_Illegal}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_NM(Vector2, CreateBroadcast, ".ctor", 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_CreateBroadcast, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod)
SIMD_AS_HWINTRINSIC_ID(Vector2, Dot, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector128_Dot, NI_Illegal}, SimdAsHWIntrinsicFlag::None)
-SIMD_AS_HWINTRINSIC_NM(Vector2, EqualsInstance, "Equals", 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector128_op_Equality, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod)
SIMD_AS_HWINTRINSIC_ID(Vector2, get_One, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector2_get_One, NI_Illegal}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_ID(Vector2, get_Zero, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector128_get_Zero, NI_Illegal}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_ID(Vector2, Max, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_SSE_Max, NI_Illegal}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_ID(Vector3, Abs, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_Abs, NI_Illegal}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_NM(Vector3, CreateBroadcast, ".ctor", 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_CreateBroadcast, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod)
SIMD_AS_HWINTRINSIC_ID(Vector3, Dot, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector128_Dot, NI_Illegal}, SimdAsHWIntrinsicFlag::None)
-SIMD_AS_HWINTRINSIC_NM(Vector3, EqualsInstance, "Equals", 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector128_op_Equality, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod)
SIMD_AS_HWINTRINSIC_ID(Vector3, get_One, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector3_get_One, NI_Illegal}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_ID(Vector3, get_Zero, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector128_get_Zero, NI_Illegal}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_ID(Vector3, Max, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_SSE_Max, NI_Illegal}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_ID(Vector4, Abs, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_Abs, NI_Illegal}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_NM(Vector4, CreateBroadcast, ".ctor", 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_CreateBroadcast, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod)
SIMD_AS_HWINTRINSIC_ID(Vector4, Dot, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector128_Dot, NI_Illegal}, SimdAsHWIntrinsicFlag::None)
-SIMD_AS_HWINTRINSIC_NM(Vector4, EqualsInstance, "Equals", 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector128_op_Equality, NI_Illegal}, SimdAsHWIntrinsicFlag::InstanceMethod)
SIMD_AS_HWINTRINSIC_ID(Vector4, get_One, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector4_get_One, NI_Illegal}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_ID(Vector4, get_Zero, 0, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Vector128_get_Zero, NI_Illegal}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_ID(Vector4, Max, 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_SSE_Max, NI_Illegal}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_NM(VectorT128, CreateBroadcast, ".ctor", 2, {NI_VectorT128_CreateBroadcast, NI_VectorT128_CreateBroadcast, NI_VectorT128_CreateBroadcast, NI_VectorT128_CreateBroadcast, NI_VectorT128_CreateBroadcast, NI_VectorT128_CreateBroadcast, NI_VectorT128_CreateBroadcast, NI_VectorT128_CreateBroadcast, NI_VectorT128_CreateBroadcast, NI_VectorT128_CreateBroadcast}, SimdAsHWIntrinsicFlag::InstanceMethod)
SIMD_AS_HWINTRINSIC_ID(VectorT128, Dot, 2, {NI_Illegal, NI_Illegal, NI_Vector128_Dot, NI_Vector128_Dot, NI_VectorT128_Dot, NI_VectorT128_Dot, NI_Illegal, NI_Illegal, NI_Vector128_Dot, NI_Vector128_Dot}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_ID(VectorT128, Equals, 2, {NI_SSE2_CompareEqual, NI_SSE2_CompareEqual, NI_SSE2_CompareEqual, NI_SSE2_CompareEqual, NI_SSE2_CompareEqual, NI_SSE2_CompareEqual, NI_VectorT128_Equals, NI_VectorT128_Equals, NI_SSE_CompareEqual, NI_SSE2_CompareEqual}, SimdAsHWIntrinsicFlag::None)
-SIMD_AS_HWINTRINSIC_NM(VectorT128, EqualsInstance, "Equals", 2, {NI_Vector128_op_Equality, NI_Vector128_op_Equality, NI_Vector128_op_Equality, NI_Vector128_op_Equality, NI_Vector128_op_Equality, NI_Vector128_op_Equality, NI_Vector128_op_Equality, NI_Vector128_op_Equality, NI_Vector128_op_Equality, NI_Vector128_op_Equality}, SimdAsHWIntrinsicFlag::InstanceMethod)
SIMD_AS_HWINTRINSIC_ID(VectorT128, Floor, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_SSE41_Floor, NI_SSE41_Floor}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_ID(VectorT128, get_AllBitsSet, 0, {NI_Vector128_get_AllBitsSet, NI_Vector128_get_AllBitsSet, NI_Vector128_get_AllBitsSet, NI_Vector128_get_AllBitsSet, NI_Vector128_get_AllBitsSet, NI_Vector128_get_AllBitsSet, NI_Vector128_get_AllBitsSet, NI_Vector128_get_AllBitsSet, NI_Vector128_get_AllBitsSet, NI_Vector128_get_AllBitsSet}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_ID(VectorT128, get_Count, 0, {NI_VectorT128_get_Count, NI_VectorT128_get_Count, NI_VectorT128_get_Count, NI_VectorT128_get_Count, NI_VectorT128_get_Count, NI_VectorT128_get_Count, NI_VectorT128_get_Count, NI_VectorT128_get_Count, NI_VectorT128_get_Count, NI_VectorT128_get_Count}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_NM(VectorT256, CreateBroadcast, ".ctor", 2, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_VectorT256_CreateBroadcast, NI_VectorT256_CreateBroadcast, NI_VectorT256_CreateBroadcast, NI_VectorT256_CreateBroadcast, NI_VectorT256_CreateBroadcast, NI_VectorT256_CreateBroadcast}, SimdAsHWIntrinsicFlag::InstanceMethod)
SIMD_AS_HWINTRINSIC_ID(VectorT256, Dot, 2, {NI_Illegal, NI_Illegal, NI_Vector256_Dot, NI_Vector256_Dot, NI_Vector256_Dot, NI_Vector256_Dot, NI_Illegal, NI_Illegal, NI_Vector256_Dot, NI_Vector256_Dot}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_ID(VectorT256, Equals, 2, {NI_AVX2_CompareEqual, NI_AVX2_CompareEqual, NI_AVX2_CompareEqual, NI_AVX2_CompareEqual, NI_AVX2_CompareEqual, NI_AVX2_CompareEqual, NI_AVX2_CompareEqual, NI_AVX2_CompareEqual, NI_AVX_CompareEqual, NI_AVX_CompareEqual}, SimdAsHWIntrinsicFlag::None)
-SIMD_AS_HWINTRINSIC_NM(VectorT256, EqualsInstance, "Equals", 2, {NI_Vector256_op_Equality, NI_Vector256_op_Equality, NI_Vector256_op_Equality, NI_Vector256_op_Equality, NI_Vector256_op_Equality, NI_Vector256_op_Equality, NI_Vector256_op_Equality, NI_Vector256_op_Equality, NI_Vector256_op_Equality, NI_Vector256_op_Equality}, SimdAsHWIntrinsicFlag::InstanceMethod)
SIMD_AS_HWINTRINSIC_ID(VectorT256, Floor, 1, {NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_Illegal, NI_AVX_Floor, NI_AVX_Floor}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_ID(VectorT256, get_AllBitsSet, 0, {NI_Vector256_get_AllBitsSet, NI_Vector256_get_AllBitsSet, NI_Vector256_get_AllBitsSet, NI_Vector256_get_AllBitsSet, NI_Vector256_get_AllBitsSet, NI_Vector256_get_AllBitsSet, NI_Vector256_get_AllBitsSet, NI_Vector256_get_AllBitsSet, NI_Vector256_get_AllBitsSet, NI_Vector256_get_AllBitsSet}, SimdAsHWIntrinsicFlag::None)
SIMD_AS_HWINTRINSIC_ID(VectorT256, get_Count, 0, {NI_VectorT256_get_Count, NI_VectorT256_get_Count, NI_VectorT256_get_Count, NI_VectorT256_get_Count, NI_VectorT256_get_Count, NI_VectorT256_get_Count, NI_VectorT256_get_Count, NI_VectorT256_get_Count, NI_VectorT256_get_Count, NI_VectorT256_get_Count}, SimdAsHWIntrinsicFlag::None)
Assert.False(Vector<T>.Zero.Equals(Vector<T>.One));
Assert.False(Vector<T>.Zero.Equals(new Vector<T>(Util.One<T>())));
}
+
+ [Fact]
+ public void VectorDoubleEqualsNaNTest()
+ {
+ var nan = new Vector<double>(double.NaN);
+ Assert.True(nan.Equals(nan));
+ }
+
+ [Fact]
+ public void VectorSingleEqualsNaNTest()
+ {
+ var nan = new Vector<float>(float.NaN);
+ Assert.True(nan.Equals(nan));
+ }
#endregion
#region System.Object Overloads
// A test for Matrix3x2 comparison involving NaN values
[Fact]
- public void Matrix3x2EqualsNanTest()
+ public void Matrix3x2EqualsNaNTest()
{
Matrix3x2 a = new Matrix3x2(float.NaN, 0, 0, 0, 0, 0);
Matrix3x2 b = new Matrix3x2(0, float.NaN, 0, 0, 0, 0);
Assert.False(e.IsIdentity);
Assert.False(f.IsIdentity);
- // Counterintuitive result - IEEE rules for NaN comparison are weird!
- Assert.False(a.Equals(a));
- Assert.False(b.Equals(b));
- Assert.False(c.Equals(c));
- Assert.False(d.Equals(d));
- Assert.False(e.Equals(e));
- Assert.False(f.Equals(f));
+ Assert.True(a.Equals(a));
+ Assert.True(b.Equals(b));
+ Assert.True(c.Equals(c));
+ Assert.True(d.Equals(d));
+ Assert.True(e.Equals(e));
+ Assert.True(f.Equals(f));
}
// A test to make sure these types are blittable directly into GPU buffer memory layouts
// A test for Matrix4x4 comparison involving NaN values
[Fact]
- public void Matrix4x4EqualsNanTest()
+ public void Matrix4x4EqualsNaNTest()
{
Matrix4x4 a = new Matrix4x4(float.NaN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
Matrix4x4 b = new Matrix4x4(0, float.NaN, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
Assert.False(o.IsIdentity);
Assert.False(p.IsIdentity);
- // Counterintuitive result - IEEE rules for NaN comparison are weird!
- Assert.False(a.Equals(a));
- Assert.False(b.Equals(b));
- Assert.False(c.Equals(c));
- Assert.False(d.Equals(d));
- Assert.False(e.Equals(e));
- Assert.False(f.Equals(f));
- Assert.False(g.Equals(g));
- Assert.False(h.Equals(h));
- Assert.False(i.Equals(i));
- Assert.False(j.Equals(j));
- Assert.False(k.Equals(k));
- Assert.False(l.Equals(l));
- Assert.False(m.Equals(m));
- Assert.False(n.Equals(n));
- Assert.False(o.Equals(o));
- Assert.False(p.Equals(p));
+ Assert.True(a.Equals(a));
+ Assert.True(b.Equals(b));
+ Assert.True(c.Equals(c));
+ Assert.True(d.Equals(d));
+ Assert.True(e.Equals(e));
+ Assert.True(f.Equals(f));
+ Assert.True(g.Equals(g));
+ Assert.True(h.Equals(h));
+ Assert.True(i.Equals(i));
+ Assert.True(j.Equals(j));
+ Assert.True(k.Equals(k));
+ Assert.True(l.Equals(l));
+ Assert.True(m.Equals(m));
+ Assert.True(n.Equals(n));
+ Assert.True(o.Equals(o));
+ Assert.True(p.Equals(p));
}
// A test to make sure these types are blittable directly into GPU buffer memory layouts
{
Plane target = new Plane(1.0f, 2.0f, 3.0f, 4.0f);
- int expected = target.Normal.GetHashCode() + target.D.GetHashCode();
+ int expected = HashCode.Combine(target.Normal, target.D);
int actual = target.GetHashCode();
Assert.Equal(expected, actual);
}
// A test for Plane comparison involving NaN values
[Fact]
- public void PlaneEqualsNanTest()
+ public void PlaneEqualsNaNTest()
{
Plane a = new Plane(float.NaN, 0, 0, 0);
Plane b = new Plane(0, float.NaN, 0, 0);
Assert.False(c.Equals(new Plane(0, 0, 0, 0)));
Assert.False(d.Equals(new Plane(0, 0, 0, 0)));
- // Counterintuitive result - IEEE rules for NaN comparison are weird!
- Assert.False(a.Equals(a));
- Assert.False(b.Equals(b));
- Assert.False(c.Equals(c));
- Assert.False(d.Equals(d));
+ Assert.True(a.Equals(a));
+ Assert.True(b.Equals(b));
+ Assert.True(c.Equals(c));
+ Assert.True(d.Equals(d));
}
/* Enable when size of Vector3 is correct
{
Quaternion a = new Quaternion(1.0f, 2.0f, 3.0f, 4.0f);
- int expected = unchecked(a.X.GetHashCode() + a.Y.GetHashCode() + a.Z.GetHashCode() + a.W.GetHashCode());
+ int expected = HashCode.Combine(a.X, a.Y, a.Z, a.W);
int actual = a.GetHashCode();
Assert.Equal(expected, actual);
}
// A test for Quaternion comparison involving NaN values
[Fact]
- public void QuaternionEqualsNanTest()
+ public void QuaternionEqualsNaNTest()
{
Quaternion a = new Quaternion(float.NaN, 0, 0, 0);
Quaternion b = new Quaternion(0, float.NaN, 0, 0);
Assert.False(c.IsIdentity);
Assert.False(d.IsIdentity);
- // Counterintuitive result - IEEE rules for NaN comparison are weird!
- Assert.False(a.Equals(a));
- Assert.False(b.Equals(b));
- Assert.False(c.Equals(c));
- Assert.False(d.Equals(d));
+ Assert.True(a.Equals(a));
+ Assert.True(b.Equals(b));
+ Assert.True(c.Equals(c));
+ Assert.True(d.Equals(d));
}
// A test to make sure these types are blittable directly into GPU buffer memory layouts
// A test for Vector2f comparison involving NaN values
[Fact]
- public void Vector2EqualsNanTest()
+ public void Vector2EqualsNaNTest()
{
Vector2 a = new Vector2(float.NaN, 0);
Vector2 b = new Vector2(0, float.NaN);
Assert.False(a.Equals(Vector2.Zero));
Assert.False(b.Equals(Vector2.Zero));
- // Counterintuitive result - IEEE rules for NaN comparison are weird!
- Assert.False(a.Equals(a));
- Assert.False(b.Equals(b));
+ Assert.True(a.Equals(a));
+ Assert.True(b.Equals(b));
}
// A test for Reflect (Vector2f, Vector2f)
// A test for Vector3f comparison involving NaN values
[Fact]
- public void Vector3EqualsNanTest()
+ public void Vector3EqualsNaNTest()
{
Vector3 a = new Vector3(float.NaN, 0, 0);
Vector3 b = new Vector3(0, float.NaN, 0);
Assert.False(b.Equals(Vector3.Zero));
Assert.False(c.Equals(Vector3.Zero));
- // Counterintuitive result - IEEE rules for NaN comparison are weird!
- Assert.False(a.Equals(a));
- Assert.False(b.Equals(b));
- Assert.False(c.Equals(c));
+ Assert.True(a.Equals(a));
+ Assert.True(b.Equals(b));
+ Assert.True(c.Equals(c));
}
[Fact]
// A test for Vector4f comparison involving NaN values
[Fact]
- public void Vector4EqualsNanTest()
+ public void Vector4EqualsNaNTest()
{
Vector4 a = new Vector4(float.NaN, 0, 0, 0);
Vector4 b = new Vector4(0, float.NaN, 0, 0);
Assert.False(c.Equals(Vector4.Zero));
Assert.False(d.Equals(Vector4.Zero));
- // Counterintuitive result - IEEE rules for NaN comparison are weird!
- Assert.False(a.Equals(a));
- Assert.False(b.Equals(b));
- Assert.False(c.Equals(c));
- Assert.False(d.Equals(d));
+ Assert.True(a.Equals(a));
+ Assert.True(b.Equals(b));
+ Assert.True(c.Equals(c));
+ Assert.True(d.Equals(d));
}
[Fact]
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
+using System.Runtime.Intrinsics;
namespace System.Numerics
{
/// <param name="other">The other matrix.</param>
/// <returns><see langword="true" /> if the two matrices are equal; otherwise, <see langword="false" />.</returns>
/// <remarks>Two matrices are equal if all their corresponding elements are equal.</remarks>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly bool Equals(Matrix3x2 other)
{
- return this == other;
+ // This function needs to account for floating-point equality around NaN
+ // and so must behave equivalently to the underlying float/double.Equals
+
+ if (Vector128.IsHardwareAccelerated)
+ {
+ // We'll two two overlapping comparisons. The first gets M11, M12, M21, and M22
+ // The second will get M21, M22, M31, and M32. This is more efficient overall.
+
+ return Vector128.LoadUnsafe(ref Unsafe.AsRef(in M11)).Equals(Vector128.LoadUnsafe(ref other.M11))
+ && Vector128.LoadUnsafe(ref Unsafe.AsRef(in M21)).Equals(Vector128.LoadUnsafe(ref other.M21));
+
+ }
+
+ return SoftwareFallback(in this, other);
+
+ static bool SoftwareFallback(in Matrix3x2 self, Matrix3x2 other)
+ {
+ return self.M11.Equals(other.M11) && self.M22.Equals(other.M22) // Check diagonal element first for early out.
+ && self.M12.Equals(other.M12) && self.M21.Equals(other.M21)
+ && self.M31.Equals(other.M31) && self.M32.Equals(other.M32);
+ }
}
/// <summary>Calculates the determinant for this matrix.</summary>
/// <summary>Returns a value that indicates whether this instance and another 4x4 matrix are equal.</summary>
/// <param name="other">The other matrix.</param>
/// <returns><see langword="true" /> if the two matrices are equal; otherwise, <see langword="false" />.</returns>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly bool Equals(Matrix4x4 other)
{
- return this == other;
+ // This function needs to account for floating-point equality around NaN
+ // and so must behave equivalently to the underlying float/double.Equals
+
+ if (Vector128.IsHardwareAccelerated)
+ {
+ return Vector128.LoadUnsafe(ref Unsafe.AsRef(in M11)).Equals(Vector128.LoadUnsafe(ref other.M11))
+ && Vector128.LoadUnsafe(ref Unsafe.AsRef(in M21)).Equals(Vector128.LoadUnsafe(ref other.M21))
+ && Vector128.LoadUnsafe(ref Unsafe.AsRef(in M31)).Equals(Vector128.LoadUnsafe(ref other.M31))
+ && Vector128.LoadUnsafe(ref Unsafe.AsRef(in M41)).Equals(Vector128.LoadUnsafe(ref other.M41));
+
+ }
+
+ return SoftwareFallback(in this, other);
+
+ static bool SoftwareFallback(in Matrix4x4 self, Matrix4x4 other)
+ {
+ return self.M11.Equals(other.M11) && self.M22.Equals(other.M22) && self.M33.Equals(other.M33) && self.M44.Equals(other.M44) // Check diagonal element first for early out.
+ && self.M12.Equals(other.M12) && self.M13.Equals(other.M13) && self.M14.Equals(other.M14) && self.M21.Equals(other.M21)
+ && self.M23.Equals(other.M23) && self.M24.Equals(other.M24) && self.M31.Equals(other.M31) && self.M32.Equals(other.M32)
+ && self.M34.Equals(other.M34) && self.M41.Equals(other.M41) && self.M42.Equals(other.M42) && self.M43.Equals(other.M43);
+ }
}
/// <summary>Calculates the determinant of the current 4x4 matrix.</summary>
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Runtime.CompilerServices;
+using System.Runtime.Intrinsics;
namespace System.Numerics
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly bool Equals(Plane other)
{
- if (Vector.IsHardwareAccelerated)
+ // This function needs to account for floating-point equality around NaN
+ // and so must behave equivalently to the underlying float/double.Equals
+
+ if (Vector128.IsHardwareAccelerated)
{
- return Normal.Equals(other.Normal) && D == other.D;
+ return Vector128.LoadUnsafe(ref Unsafe.AsRef(in Normal.X)).Equals(Vector128.LoadUnsafe(ref other.Normal.X));
}
- else
+
+ return SoftwareFallback(in this, other);
+
+ static bool SoftwareFallback(in Plane self, Plane other)
{
- return (Normal.X == other.Normal.X &&
- Normal.Y == other.Normal.Y &&
- Normal.Z == other.Normal.Z &&
- D == other.D);
+ return self.Normal.Equals(other.Normal)
+ && self.D.Equals(other.D);
}
}
/// <returns>The hash code.</returns>
public override readonly int GetHashCode()
{
- return Normal.GetHashCode() + D.GetHashCode();
+ return HashCode.Combine(Normal, D);
}
/// <summary>Returns the string representation of this plane object.</summary>
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
+using System.Runtime.Intrinsics;
namespace System.Numerics
{
/// <param name="other">The other quaternion.</param>
/// <returns><see langword="true" /> if the two quaternions are equal; otherwise, <see langword="false" />.</returns>
/// <remarks>Two quaternions are equal if each of their corresponding components is equal.</remarks>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly bool Equals(Quaternion other)
{
- return this == other;
+ // This function needs to account for floating-point equality around NaN
+ // and so must behave equivalently to the underlying float/double.Equals
+
+ if (Vector128.IsHardwareAccelerated)
+ {
+ return Vector128.LoadUnsafe(ref Unsafe.AsRef(in X)).Equals(Vector128.LoadUnsafe(ref other.X));
+ }
+
+ return SoftwareFallback(in this, other);
+
+ static bool SoftwareFallback(in Quaternion self, Quaternion other)
+ {
+ return self.X.Equals(other.X)
+ && self.Y.Equals(other.Y)
+ && self.Z.Equals(other.Z)
+ && self.W.Equals(other.W);
+ }
}
/// <summary>Returns the hash code for this instance.</summary>
/// <returns>The hash code.</returns>
public override readonly int GetHashCode()
{
- return unchecked(X.GetHashCode() + Y.GetHashCode() + Z.GetHashCode() + W.GetHashCode());
+ return HashCode.Combine(X, Y, Z, W);
}
/// <summary>Calculates the length of the quaternion.</summary>
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
using System.Text;
namespace System.Numerics
/// <param name="other">The other vector.</param>
/// <returns><see langword="true" /> if the two vectors are equal; otherwise, <see langword="false" />.</returns>
/// <remarks>Two vectors are equal if their <see cref="System.Numerics.Vector2.X" /> and <see cref="System.Numerics.Vector2.Y" /> elements are equal.</remarks>
- [Intrinsic]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly bool Equals(Vector2 other)
{
- return this == other;
+ // This function needs to account for floating-point equality around NaN
+ // and so must behave equivalently to the underlying float/double.Equals
+
+ if (Vector64.IsHardwareAccelerated)
+ {
+ return Vector64.LoadUnsafe(ref Unsafe.AsRef(in X)).Equals(Vector64.LoadUnsafe(ref other.X));
+ }
+ else if (Vector128.IsHardwareAccelerated)
+ {
+ return this.AsVector128().Equals(other.AsVector128());
+ }
+
+ return SoftwareFallback(in this, other);
+
+ static bool SoftwareFallback(in Vector2 self, Vector2 other)
+ {
+ return self.X.Equals(other.X)
+ && self.Y.Equals(other.Y);
+ }
}
/// <summary>Returns the hash code for this instance.</summary>
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
using System.Text;
namespace System.Numerics
/// <param name="other">The other vector.</param>
/// <returns><see langword="true" /> if the two vectors are equal; otherwise, <see langword="false" />.</returns>
/// <remarks>Two vectors are equal if their <see cref="System.Numerics.Vector3.X" />, <see cref="System.Numerics.Vector3.Y" />, and <see cref="System.Numerics.Vector3.Z" /> elements are equal.</remarks>
- [Intrinsic]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly bool Equals(Vector3 other)
{
- return this == other;
+ // This function needs to account for floating-point equality around NaN
+ // and so must behave equivalently to the underlying float/double.Equals
+
+ if (Vector128.IsHardwareAccelerated)
+ {
+ return this.AsVector128().Equals(other.AsVector128());
+ }
+
+ return SoftwareFallback(in this, other);
+
+ static bool SoftwareFallback(in Vector3 self, Vector3 other)
+ {
+ return self.X.Equals(other.X)
+ && self.Y.Equals(other.Y)
+ && self.Z.Equals(other.Z);
+ }
}
/// <summary>Returns the hash code for this instance.</summary>
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics;
using System.Text;
namespace System.Numerics
/// <param name="other">The other vector.</param>
/// <returns><see langword="true" /> if the two vectors are equal; otherwise, <see langword="false" />.</returns>
/// <remarks>Two vectors are equal if their <see cref="System.Numerics.Vector4.X" />, <see cref="System.Numerics.Vector4.Y" />, <see cref="System.Numerics.Vector4.Z" />, and <see cref="System.Numerics.Vector4.W" /> elements are equal.</remarks>
- [Intrinsic]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public readonly bool Equals(Vector4 other)
{
- return this == other;
+ // This function needs to account for floating-point equality around NaN
+ // and so must behave equivalently to the underlying float/double.Equals
+
+ if (Vector128.IsHardwareAccelerated)
+ {
+ return this.AsVector128().Equals(other.AsVector128());
+ }
+
+ return SoftwareFallback(in this, other);
+
+ static bool SoftwareFallback(in Vector4 self, Vector4 other)
+ {
+ return self.X.Equals(other.X)
+ && self.Y.Equals(other.Y)
+ && self.Z.Equals(other.Z)
+ && self.W.Equals(other.W);
+ }
}
/// <summary>Returns a value that indicates whether this instance and a specified object are equal.</summary>
/// <summary>Returns a boolean indicating whether the given vector is equal to this vector instance.</summary>
/// <param name="other">The vector to compare this instance to.</param>
/// <returns>True if the other vector is equal to this instance; False otherwise.</returns>
- [Intrinsic]
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(Vector<T> other)
- => this == other;
+ {
+ // This function needs to account for floating-point equality around NaN
+ // and so must behave equivalently to the underlying float/double.Equals
+
+ if (Vector.IsHardwareAccelerated)
+ {
+ if ((typeof(T) == typeof(double)) || (typeof(T) == typeof(float)))
+ {
+ Vector<T> result = Vector.Equals(this, other) | ~(Vector.Equals(this, this) | Vector.Equals(other, other));
+ return result.As<T, int>() == Vector<int>.AllBitsSet;
+ }
+ else
+ {
+ return this == other;
+ }
+ }
+
+ return SoftwareFallback(in this, other);
+
+ static bool SoftwareFallback(in Vector<T> self, Vector<T> other)
+ {
+ for (int index = 0; index < Count; index++)
+ {
+ if (!Scalar<T>.ObjectEquals(self.GetElementUnsafe(index), other.GetElementUnsafe(index)))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
/// <summary>Returns the hash code for this instance.</summary>
/// <returns>The hash code.</returns>
}
}
+ public static bool ObjectEquals(T left, T right)
+ {
+ if (typeof(T) == typeof(byte))
+ {
+ return ((byte)(object)left).Equals((byte)(object)right);
+ }
+ else if (typeof(T) == typeof(double))
+ {
+ return ((double)(object)left).Equals((double)(object)right);
+ }
+ else if (typeof(T) == typeof(short))
+ {
+ return ((short)(object)left).Equals((short)(object)right);
+ }
+ else if (typeof(T) == typeof(int))
+ {
+ return ((int)(object)left).Equals((int)(object)right);
+ }
+ else if (typeof(T) == typeof(long))
+ {
+ return ((long)(object)left).Equals((long)(object)right);
+ }
+ else if (typeof(T) == typeof(nint))
+ {
+ return ((nint)(object)left).Equals((nint)(object)right);
+ }
+ else if (typeof(T) == typeof(nuint))
+ {
+ return ((nuint)(object)left).Equals((nuint)(object)right);
+ }
+ else if (typeof(T) == typeof(sbyte))
+ {
+ return ((sbyte)(object)left).Equals((sbyte)(object)right);
+ }
+ else if (typeof(T) == typeof(float))
+ {
+ return ((float)(object)left).Equals((float)(object)right);
+ }
+ else if (typeof(T) == typeof(ushort))
+ {
+ return ((ushort)(object)left).Equals((ushort)(object)right);
+ }
+ else if (typeof(T) == typeof(uint))
+ {
+ return ((uint)(object)left).Equals((uint)(object)right);
+ }
+ else if (typeof(T) == typeof(ulong))
+ {
+ return ((ulong)(object)left).Equals((ulong)(object)right);
+ }
+ else
+ {
+ throw new NotSupportedException(SR.Arg_TypeNotSupported);
+ }
+ }
+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T ShiftLeft(T value, int shiftCount)
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(Vector128<T> other)
{
- ThrowHelper.ThrowForUnsupportedIntrinsicsVector128BaseType<T>();
-
- if (Sse.IsSupported && (typeof(T) == typeof(float)))
- {
- Vector128<float> result = Sse.CompareEqual(this.AsSingle(), other.AsSingle());
- return Sse.MoveMask(result) == 0b1111; // We have one bit per element
- }
+ // This function needs to account for floating-point equality around NaN
+ // and so must behave equivalently to the underlying float/double.Equals
- if (Sse2.IsSupported)
+ if (Vector128.IsHardwareAccelerated)
{
- if (typeof(T) == typeof(double))
+ if ((typeof(T) == typeof(double)) || (typeof(T) == typeof(float)))
{
- Vector128<double> result = Sse2.CompareEqual(this.AsDouble(), other.AsDouble());
- return Sse2.MoveMask(result) == 0b11; // We have one bit per element
- }
- else if (Sse41.IsSupported)
- {
- // xor + testz is slightly better for integer types
- Vector128<byte> xored = Sse2.Xor(this.AsByte(), other.AsByte());
- return Sse41.TestZ(xored, xored);
+ Vector128<T> result = Vector128.Equals(this, other) | ~(Vector128.Equals(this, this) | Vector128.Equals(other, other));
+ return result.AsInt32() == Vector128<int>.AllBitsSet;
}
else
{
- // Unlike float/double, there are no special values to consider
- // for integral types and we can just do a comparison that all
- // bytes are exactly the same.
-
- Debug.Assert((typeof(T) != typeof(float)) && (typeof(T) != typeof(double)));
- Vector128<byte> result = Sse2.CompareEqual(this.AsByte(), other.AsByte());
- return Sse2.MoveMask(result) == 0b1111_1111_1111_1111; // We have one bit per element
+ return this == other;
}
}
return SoftwareFallback(in this, other);
- static bool SoftwareFallback(in Vector128<T> vector, Vector128<T> other)
+ static bool SoftwareFallback(in Vector128<T> self, Vector128<T> other)
{
- for (int i = 0; i < Count; i++)
+ for (int index = 0; index < Count; index++)
{
- if (!((IEquatable<T>)(vector.GetElement(i))).Equals(other.GetElement(i)))
+ if (!Scalar<T>.ObjectEquals(self.GetElementUnsafe(index), other.GetElementUnsafe(index)))
{
return false;
}
}
-
return true;
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(Vector256<T> other)
{
- if (Avx.IsSupported)
+ // This function needs to account for floating-point equality around NaN
+ // and so must behave equivalently to the underlying float/double.Equals
+
+ if (Vector256.IsHardwareAccelerated)
{
- if (typeof(T) == typeof(float))
+ if ((typeof(T) == typeof(double)) || (typeof(T) == typeof(float)))
{
- Vector256<float> result = Avx.Compare(this.AsSingle(), other.AsSingle(), FloatComparisonMode.OrderedEqualNonSignaling);
- return Avx.MoveMask(result) == 0b1111_1111; // We have one bit per element
+ Vector256<T> result = Vector256.Equals(this, other) | ~(Vector256.Equals(this, this) | Vector256.Equals(other, other));
+ return result.AsInt32() == Vector256<int>.AllBitsSet;
}
-
- if (typeof(T) == typeof(double))
+ else
{
- Vector256<double> result = Avx.Compare(this.AsDouble(), other.AsDouble(), FloatComparisonMode.OrderedEqualNonSignaling);
- return Avx.MoveMask(result) == 0b1111; // We have one bit per element
+ return this == other;
}
}
- if (Avx2.IsSupported)
- {
- // Unlike float/double, there are no special values to consider
- // for integral types and we can just do a comparison that all
- // bytes are exactly the same.
-
- Debug.Assert((typeof(T) != typeof(float)) && (typeof(T) != typeof(double)));
-
- Vector256<byte> xored = Avx2.Xor(this.AsByte(), other.AsByte());
- return Avx.TestZ(xored, xored);
- }
-
return SoftwareFallback(in this, other);
- static bool SoftwareFallback(in Vector256<T> vector, Vector256<T> other)
+ static bool SoftwareFallback(in Vector256<T> self, Vector256<T> other)
{
- for (int i = 0; i < Count; i++)
+ for (int index = 0; index < Count; index++)
{
- if (!((IEquatable<T>)(vector.GetElement(i))).Equals(other.GetElement(i)))
+ if (!Scalar<T>.ObjectEquals(self.GetElementUnsafe(index), other.GetElementUnsafe(index)))
{
return false;
}
}
-
return true;
}
}
using System.Globalization;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
+using System.Runtime.Intrinsics.Arm;
using System.Text;
namespace System.Runtime.Intrinsics
/// <param name="other">The <see cref="Vector64{T}" /> to compare with the current instance.</param>
/// <returns><c>true</c> if <paramref name="other" /> is equal to the current instance; otherwise, <c>false</c>.</returns>
/// <exception cref="NotSupportedException">The type of the current instance (<typeparamref name="T" />) is not supported.</exception>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool Equals(Vector64<T> other)
{
- for (int i = 0; i < Count; i++)
+ // This function needs to account for floating-point equality around NaN
+ // and so must behave equivalently to the underlying float/double.Equals
+
+ if (Vector64.IsHardwareAccelerated)
{
- if (!((IEquatable<T>)(this.GetElement(i))).Equals(other.GetElement(i)))
+ if ((typeof(T) == typeof(double)) || (typeof(T) == typeof(float)))
{
- return false;
+ Vector64<T> result = Vector64.Equals(this, other) | ~(Vector64.Equals(this, this) | Vector64.Equals(other, other));
+ return result.AsInt32() == Vector64<int>.AllBitsSet;
+ }
+ else
+ {
+ return this == other;
}
}
- return true;
+ return SoftwareFallback(in this, other);
+
+ static bool SoftwareFallback(in Vector64<T> self, Vector64<T> other)
+ {
+ for (int index = 0; index < Count; index++)
+ {
+ if (!Scalar<T>.ObjectEquals(self.GetElementUnsafe(index), other.GetElementUnsafe(index)))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
}
/// <summary>Gets the hash code for the instance.</summary>
Assert.Equal(vector[0], values[0]);
Assert.Equal(vector[1], values[1]);
}
+
+ [Fact]
+ public void Vector128DoubleEqualsNaNTest()
+ {
+ Vector128<double> nan = Vector128.Create(double.NaN);
+ Assert.True(nan.Equals(nan));
+ }
+
+ [Fact]
+ public void Vector128SingleEqualsNaNTest()
+ {
+ Vector128<float> nan = Vector128.Create(float.NaN);
+ Assert.True(nan.Equals(nan));
+ }
}
}
Assert.Equal(vector[2], values[2]);
Assert.Equal(vector[3], values[3]);
}
+
+ [Fact]
+ public void Vector256DoubleEqualsNaNTest()
+ {
+ Vector256<double> nan = Vector256.Create(double.NaN);
+ Assert.True(nan.Equals(nan));
+ }
+
+ [Fact]
+ public void Vector256SingleEqualsNaNTest()
+ {
+ Vector256<float> nan = Vector256.Create(float.NaN);
+ Assert.True(nan.Equals(nan));
+ }
}
}
Assert.Equal(vector[0], values[0]);
}
+
+ [Fact]
+ public void Vector64DoubleEqualsNaNTest()
+ {
+ Vector64<double> nan = Vector64.Create(double.NaN);
+ Assert.True(nan.Equals(nan));
+ }
+
+ [Fact]
+ public void Vector64SingleEqualsNaNTest()
+ {
+ Vector64<float> nan = Vector64.Create(float.NaN);
+ Assert.True(nan.Equals(nan));
+ }
}
}