Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Measure-Coverage.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,10 @@ try {
Get-ChildItem -Directory src/*.Test*/TestResults/* | Remove-Item -Recurse

# rebuild target project to generate source generator files
dotnet build src/QuantitiesDotNet.slnx --no-incremental --property:EmitCompilerGeneratedFiles=true
dotnet build QuantitiesDotNet.slnx --no-incremental --property:EmitCompilerGeneratedFiles=true

# test and measure coverage
dotnet test src/QuantitiesDotNet.slnx --collect:"XPlat Code Coverage" --settings src/etc/coverlet.runsettings
dotnet test QuantitiesDotNet.slnx --collect:"XPlat Code Coverage" --settings src/etc/coverlet.runsettings

# export HTML coverage report
Get-ChildItem src/*.Test*/TestResults/*/coverage.cobertura.xml `
Expand Down
2 changes: 1 addition & 1 deletion global.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"sdk": {
"version": "10.0.101",
"version": "10.0.0",
"rollForward": "latestFeature"
}
}
30 changes: 16 additions & 14 deletions src/QuantitiesDotNet.Generators/QuantityImplementBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -308,25 +308,27 @@ private void GenerateSelfOperatorsShape(SourceBuilderSlim sb)
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns></returns>
public static bool Equals({{TypeName}} x, {{TypeName}} y)
=> x._rawValue == y._rawValue;
public static bool Equals({{TypeName}} x, {{TypeName}} y) =>
x._rawValue == y._rawValue;

/// <summary> Determines which value is greater than another. </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns></returns>
public static int Compare({{TypeName}} x, {{TypeName}} y)
{
if (x._rawValue == y._rawValue) { return 0; }
return x._rawValue < y._rawValue ? -1 : 1;
}

/** <inheritdoc /> */ public static bool operator ==({{TypeName}} x, {{TypeName}} y) => Equals(x, y);
/** <inheritdoc /> */ public static bool operator !=({{TypeName}} x, {{TypeName}} y) => !Equals(x, y);
/** <inheritdoc /> */ public static bool operator < ({{TypeName}} x, {{TypeName}} y) => Compare(x, y) < 0;
/** <inheritdoc /> */ public static bool operator > ({{TypeName}} x, {{TypeName}} y) => Compare(x, y) > 0;
/** <inheritdoc /> */ public static bool operator <=({{TypeName}} x, {{TypeName}} y) => Compare(x, y) <= 0;
/** <inheritdoc /> */ public static bool operator >=({{TypeName}} x, {{TypeName}} y) => Compare(x, y) >= 0;
public static int Compare({{TypeName}} x, {{TypeName}} y) =>
(x._rawValue < y._rawValue, x._rawValue > y._rawValue) switch
{
(true, false) => -1,
(false, true) => +1,
_ => 0,
};

/** <inheritdoc /> */ public static bool operator ==({{TypeName}} x, {{TypeName}} y) => x._rawValue == y._rawValue;
/** <inheritdoc /> */ public static bool operator !=({{TypeName}} x, {{TypeName}} y) => x._rawValue != y._rawValue;
/** <inheritdoc /> */ public static bool operator < ({{TypeName}} x, {{TypeName}} y) => x._rawValue < y._rawValue;
/** <inheritdoc /> */ public static bool operator > ({{TypeName}} x, {{TypeName}} y) => x._rawValue > y._rawValue;
/** <inheritdoc /> */ public static bool operator <=({{TypeName}} x, {{TypeName}} y) => x._rawValue <= y._rawValue;
/** <inheritdoc /> */ public static bool operator >=({{TypeName}} x, {{TypeName}} y) => x._rawValue >= y._rawValue;

#endregion comparison operator implements

Expand Down
206 changes: 149 additions & 57 deletions src/QuantitiesDotNet.Test/QuantityTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,12 @@ public void Compare_NonGeneric<T>(T placeHolder)
where T : struct, IQuantity<T, double>
{
InternalHelpers.NoUse(placeHolder);
var _0 = Unsafe.BitCast<double, T>(0.0);
var p0 = Unsafe.BitCast<double, T>(+0.0);
var n0 = Unsafe.BitCast<double, T>(-0.0);
var _1 = Unsafe.BitCast<double, T>(1.0);
var nan = Unsafe.BitCast<double, T>(double.NaN);
var pinf = Unsafe.BitCast<double, T>(double.PositiveInfinity);
var ninf = Unsafe.BitCast<double, T>(double.NegativeInfinity);

Assert.Equal(_1, _1);
Assert.True (_1.Equals(_1));
Expand All @@ -75,34 +79,90 @@ public void Compare_NonGeneric<T>(T placeHolder)
Assert.False(_1 > _1);
Assert.True (_1 <= _1);
Assert.False(_1 < _1);
Assert.Equal(_1.GetHashCode(), _1.GetHashCode());

Assert.NotEqual(p0, _1);
Assert.False(p0.Equals(_1));
Assert.False(p0.Equals((object)_1));
Assert.False(T.Equals(p0, _1));
Assert.True(0 > T.Compare(p0, _1));
Assert.True(0 > p0.CompareTo(_1));
Assert.False(p0 == _1);
Assert.True (p0 != _1);
Assert.False(p0 >= _1);
Assert.False(p0 > _1);
Assert.True (p0 <= _1);
Assert.True (p0 < _1);

Assert.NotEqual(_1, p0);
Assert.False(_1.Equals(p0));
Assert.False(_1.Equals((object)p0));
Assert.False(T.Equals(_1, p0));
Assert.True(0 < T.Compare(_1, p0));
Assert.True(0 < _1.CompareTo(p0));
Assert.False(_1 == p0);
Assert.True (_1 != p0);
Assert.True (_1 >= p0);
Assert.True (_1 > p0);
Assert.False(_1 <= p0);
Assert.False(_1 < p0);

Assert.False(p0.Equals(new object()));

Assert.Equal(+0.0.GetHashCode(), p0.GetHashCode());
Assert.Equal(-0.0.GetHashCode(), n0.GetHashCode());
Assert.Equal(1.0.GetHashCode(), _1.GetHashCode());
Assert.Equal(double.NaN.GetHashCode(), nan.GetHashCode());
Assert.Equal(double.PositiveInfinity.GetHashCode(), pinf.GetHashCode());
Assert.Equal(double.NegativeInfinity.GetHashCode(), ninf.GetHashCode());

#pragma warning disable format
#pragma warning disable CA2242
static int compare(double x, double y) =>
(x < y, x > y) switch
{
(true, false) => -1,
(false, true) => +1,
_ => 0,
};

Assert.Equal(+0.0 == +0.0, p0 == p0);
Assert.Equal(+0.0 < +0.0, p0 < p0);
Assert.Equal(+0.0 > +0.0, p0 > p0);
Assert.Equal(+0.0 <= +0.0, p0 <= p0);
Assert.Equal(+0.0 >= +0.0, p0 >= p0);
Assert.Equal(compare(+0.0, +0.0), T.Compare(p0, p0));

Assert.Equal(+0.0 == -0.0, p0 == n0);
Assert.Equal(+0.0 < -0.0, p0 < n0);
Assert.Equal(+0.0 > -0.0, p0 > n0);
Assert.Equal(+0.0 <= -0.0, p0 <= n0);
Assert.Equal(+0.0 >= -0.0, p0 >= n0);
Assert.Equal(compare(+0.0, -0.0), T.Compare(p0, n0));

Assert.Equal(double.NaN == double.NaN, nan == nan);
Assert.Equal(double.NaN < double.NaN, nan < nan);
Assert.Equal(double.NaN > double.NaN, nan > nan);
Assert.Equal(double.NaN <= double.NaN, nan <= nan);
Assert.Equal(double.NaN >= double.NaN, nan >= nan);
Assert.Equal(compare(double.NaN, double.NaN), T.Compare(nan, nan));

Assert.Equal(double.PositiveInfinity == double.PositiveInfinity, pinf == pinf);
Assert.Equal(double.PositiveInfinity < double.PositiveInfinity, pinf < pinf);
Assert.Equal(double.PositiveInfinity > double.PositiveInfinity, pinf > pinf);
Assert.Equal(double.PositiveInfinity <= double.PositiveInfinity, pinf <= pinf);
Assert.Equal(double.PositiveInfinity >= double.PositiveInfinity, pinf >= pinf);
Assert.Equal(compare(double.PositiveInfinity, double.PositiveInfinity), T.Compare(pinf, pinf));

Assert.Equal(double.NegativeInfinity == double.NegativeInfinity, ninf == ninf);
Assert.Equal(double.NegativeInfinity < double.NegativeInfinity, ninf < ninf);
Assert.Equal(double.NegativeInfinity > double.NegativeInfinity, ninf > ninf);
Assert.Equal(double.NegativeInfinity <= double.NegativeInfinity, ninf <= ninf);
Assert.Equal(double.NegativeInfinity >= double.NegativeInfinity, ninf >= ninf);
Assert.Equal(compare(double.NegativeInfinity, double.NegativeInfinity), T.Compare(ninf, ninf));
#pragma warning restore CA2242
#pragma warning restore format

Assert.NotEqual(_0, _1);
Assert.False(_0.Equals(_1));
Assert.False(_0.Equals((object)_1));
Assert.False(T.Equals(_0, _1));
Assert.True(0 > T.Compare(_0, _1));
Assert.True(0 > _0.CompareTo(_1));
Assert.False(_0 == _1);
Assert.True (_0 != _1);
Assert.False(_0 >= _1);
Assert.False(_0 > _1);
Assert.True (_0 <= _1);
Assert.True (_0 < _1);

Assert.NotEqual(_1, _0);
Assert.False(_1.Equals(_0));
Assert.False(_1.Equals((object)_0));
Assert.False(T.Equals(_1, _0));
Assert.True(0 < T.Compare(_1, _0));
Assert.True(0 < _1.CompareTo(_0));
Assert.False(_1 == _0);
Assert.True (_1 != _0);
Assert.True (_1 >= _0);
Assert.True (_1 > _0);
Assert.False(_1 <= _0);
Assert.False(_1 < _0);

Assert.False(_0.Equals(new object()));
}

[Theory]
Expand All @@ -111,7 +171,8 @@ public void Compare_Generic<T>(T placeHolder)
where T : struct, IQuantity<T, decimal>
{
InternalHelpers.NoUse(placeHolder);
var _0 = Unsafe.BitCast<decimal, T>(0.0m);
var p0 = Unsafe.BitCast<decimal, T>(+0.0m);
var n0 = Unsafe.BitCast<decimal, T>(-0.0m);
var _1 = Unsafe.BitCast<decimal, T>(1.0m);

Assert.Equal(_1, _1);
Expand All @@ -126,34 +187,65 @@ public void Compare_Generic<T>(T placeHolder)
Assert.False(_1 > _1);
Assert.True (_1 <= _1);
Assert.False(_1 < _1);

Assert.NotEqual(_0, _1);
Assert.False(_0.Equals(_1));
Assert.False(_0.Equals((object)_1));
Assert.False(T.Equals(_0, _1));
Assert.True(0 > T.Compare(_0, _1));
Assert.True(0 > _0.CompareTo(_1));
Assert.False(_0 == _1);
Assert.True (_0 != _1);
Assert.False(_0 >= _1);
Assert.False(_0 > _1);
Assert.True (_0 <= _1);
Assert.True (_0 < _1);

Assert.NotEqual(_1, _0);
Assert.False(_1.Equals(_0));
Assert.False(_1.Equals((object)_0));
Assert.False(T.Equals(_1, _0));
Assert.True(0 < T.Compare(_1, _0));
Assert.True(0 < _1.CompareTo(_0));
Assert.False(_1 == _0);
Assert.True (_1 != _0);
Assert.True (_1 >= _0);
Assert.True (_1 > _0);
Assert.False(_1 <= _0);
Assert.False(_1 < _0);

Assert.False(_0.Equals(new object()));
Assert.Equal(_1.GetHashCode(), _1.GetHashCode());

Assert.NotEqual(p0, _1);
Assert.False(p0.Equals(_1));
Assert.False(p0.Equals((object)_1));
Assert.False(T.Equals(p0, _1));
Assert.True(0 > T.Compare(p0, _1));
Assert.True(0 > p0.CompareTo(_1));
Assert.False(p0 == _1);
Assert.True (p0 != _1);
Assert.False(p0 >= _1);
Assert.False(p0 > _1);
Assert.True (p0 <= _1);
Assert.True (p0 < _1);

Assert.NotEqual(_1, p0);
Assert.False(_1.Equals(p0));
Assert.False(_1.Equals((object)p0));
Assert.False(T.Equals(_1, p0));
Assert.True(0 < T.Compare(_1, p0));
Assert.True(0 < _1.CompareTo(p0));
Assert.False(_1 == p0);
Assert.True (_1 != p0);
Assert.True (_1 >= p0);
Assert.True (_1 > p0);
Assert.False(_1 <= p0);
Assert.False(_1 < p0);

Assert.False(p0.Equals(new object()));

Assert.Equal(+0.0m.GetHashCode(), p0.GetHashCode());
Assert.Equal(-0.0m.GetHashCode(), n0.GetHashCode());
Assert.Equal(1.0m.GetHashCode(), _1.GetHashCode());

#pragma warning disable format
#pragma warning disable CA2242
static int compare(double x, double y) =>
(x < y, x > y) switch
{
(true, false) => -1,
(false, true) => +1,
_ => 0,
};

Assert.Equal(+0.0 == +0.0, p0 == p0);
Assert.Equal(+0.0 < +0.0, p0 < p0);
Assert.Equal(+0.0 > +0.0, p0 > p0);
Assert.Equal(+0.0 <= +0.0, p0 <= p0);
Assert.Equal(+0.0 >= +0.0, p0 >= p0);
Assert.Equal(compare(+0.0, +0.0), T.Compare(p0, p0));

Assert.Equal(+0.0 == -0.0, p0 == n0);
Assert.Equal(+0.0 < -0.0, p0 < n0);
Assert.Equal(+0.0 > -0.0, p0 > n0);
Assert.Equal(+0.0 <= -0.0, p0 <= n0);
Assert.Equal(+0.0 >= -0.0, p0 >= n0);
Assert.Equal(compare(+0.0, -0.0), T.Compare(p0, n0));
#pragma warning restore CA2242
#pragma warning restore format
}

#pragma warning restore CS1718
Expand Down
23 changes: 23 additions & 0 deletions src/QuantitiesDotNet/IQuantity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,36 @@ public interface IQuantity<TSelf, T>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns></returns>
/// <remarks>
/// The implementation must be <c>x.RawValue == y.RawValue</c>.
/// The behaviour for edge cases conforms to the implementation of <typeparamref name="T"/>.
/// </remarks>
public static abstract bool Equals(TSelf x, TSelf y);

/// <summary> Determines which value is greater than another. </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns></returns>
/// <remarks>
/// The implementation must be <c>x.RawValue == y.RawValue ? 0 : (x.RawValue &lt; y.RawValue ? -1 : 1)</c>.
/// The behaviour for edge cases conforms to the implementation of <typeparamref name="T"/>.
/// </remarks>
public static abstract int Compare(TSelf x, TSelf y);

/// <inheritdoc cref="object.GetHashCode"/>
/// <remarks>
/// The implementation must be:
/// <code>
/// (x.RawValue &lt; y.RawValue, x.RawValue &gt; y.RawValue) switch
/// {
/// (true, false) => -1,
/// (false, true) => +1,
/// _ => 0,
/// }
/// </code>
/// The behaviour for edge cases conforms to the implementation of <typeparamref name="T"/>.
/// </remarks>
public int GetHashCode();

#endif
}