Skip to content

Commit

Permalink
Merge pull request #2834 from FirelyTeam/remove-old-ucum-library
Browse files Browse the repository at this point in the history
Remove dependency on old ucum library
  • Loading branch information
mmsmits authored Oct 23, 2024
2 parents f530fdd + 6df0782 commit 690f0a0
Show file tree
Hide file tree
Showing 9 changed files with 62 additions and 351 deletions.
14 changes: 14 additions & 0 deletions src/Hl7.Fhir.Base/CompatibilitySuppressions.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@
<Right>lib/net8.0/Hl7.Fhir.Base.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Hl7.Fhir.ElementModel.Types.MetricConfiguration</Target>
<Left>lib/net8.0/Hl7.Fhir.Base.dll</Left>
<Right>lib/net8.0/Hl7.Fhir.Base.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Hl7.Fhir.Introspection.BackboneTypeAttribute</Target>
Expand All @@ -28,6 +35,13 @@
<Right>lib/netstandard2.0/Hl7.Fhir.Base.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Hl7.Fhir.ElementModel.Types.MetricConfiguration</Target>
<Left>lib/netstandard2.0/Hl7.Fhir.Base.dll</Left>
<Right>lib/netstandard2.0/Hl7.Fhir.Base.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0001</DiagnosticId>
<Target>T:Hl7.Fhir.Introspection.BackboneTypeAttribute</Target>
Expand Down
104 changes: 31 additions & 73 deletions src/Hl7.Fhir.Base/ElementModel/Types/Quantity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,12 @@ public enum QuantityUnitSystem
/// <summary>
/// Unit is taken from the set of calendar units (year or month)
/// </summary>
CalendarDuration
CalendarDuration,

/// <summary>
/// Unit is not specified to be part of any coding system.
/// </summary>
Unknown
}


Expand Down Expand Up @@ -73,16 +78,16 @@ public static Quantity ForCalendarDuration(decimal value, string calendarUnit)
}

private static readonly string QUANTITY_BASE_REGEX =
@"(?'value'(\+|-)?\d+(\.\d+)?)\s*(('(?'unit'[^\']+)')|(?'time'[a-zA-Z]+))";
@"(?'value'(\+|-)?\d+(\.\d+)?)\s*(('(?'unit'[^\']+)')|(?'time'[a-zA-Z]+))";

public static readonly Regex QUANTITYREGEX =
new(QUANTITY_BASE_REGEX, RegexOptions.ExplicitCapture | RegexOptions.Compiled);
new(QUANTITY_BASE_REGEX, RegexOptions.ExplicitCapture | RegexOptions.Compiled);

internal static readonly Regex QUANTITYREGEX_FOR_PARSE =
new($"^{QUANTITY_BASE_REGEX}?$", RegexOptions.ExplicitCapture | RegexOptions.Compiled);

public static Quantity Parse(string representation) =>
TryParse(representation, out var result) ? result : throw new FormatException($"String '{representation}' was not recognized as a valid quantity.");
TryParse(representation, out var result) ? result : throw new FormatException($"String '{representation}' was not recognized as a valid quantity.");

public static bool TryParse(string representation, [NotNullWhen(true)] out Quantity? quantity)
{
Expand All @@ -105,7 +110,8 @@ public static bool TryParse(string representation, [NotNullWhen(true)] out Quant
{
if (TryParseTimeUnit(result.Groups["time"].Value, out var tv, out var isCalendarUnit))
{
quantity = isCalendarUnit ? ForCalendarDuration(value, tv)
quantity = isCalendarUnit
? ForCalendarDuration(value, tv)
: new Quantity(value, tv);
return true;
}
Expand Down Expand Up @@ -200,18 +206,16 @@ public bool Equals(Any other, QuantityComparison comparisonType) =>
/// </summary>
/// <remarks>For time-valued quantities, the comparison of
/// calendar durations and definite quantity durations above seconds is determined by the <paramref name="comparisonType"/></remarks>
public Result<bool> TryEquals(Any other, QuantityComparison comparisonType) => other is Quantity ?
TryCompareTo(other, comparisonType).Select(i => i == 0) : false;
public Result<bool> TryEquals(Any other, QuantityComparison comparisonType) => other is Quantity ? TryCompareTo(other, comparisonType).Select(i => i == 0) : false;

public static bool operator ==(Quantity a, Quantity b) => Equals(a, b);
public static bool operator !=(Quantity a, Quantity b) => !Equals(a, b);
public static bool operator ==(Quantity a, Quantity b) => a.CompareTo(b) == 0;
public static bool operator !=(Quantity a, Quantity b) => a.CompareTo(b) != 0;

/// <summary>
/// Compare two datetimes based on CQL equivalence rules
/// </summary>
/// <remarks>See <see cref="TryCompareTo(Any)"/> for more details.</remarks>
public int CompareTo(object? obj) => obj is Quantity q ?
TryCompareTo(q, CQL_EQUIVALENCE_COMPARISON).ValueOrThrow() : throw NotSameTypeComparison(this, obj);
public int CompareTo(object? obj) => obj is Quantity q ? TryCompareTo(q, CQL_EQUIVALENCE_COMPARISON).ValueOrThrow() : throw NotSameTypeComparison(this, obj);

public static bool operator <(Quantity a, Quantity b) => a.CompareTo(b) < 0;
public static bool operator <=(Quantity a, Quantity b) => a.CompareTo(b) <= 0;
Expand All @@ -236,24 +240,17 @@ public Result<int> TryCompareTo(Any? other, QuantityComparison comparisonType)

if (IsDuration && otherQ.IsDuration) return doDurationComparison(otherQ, comparisonType);

if (System != QuantityUnitSystem.UCUM || otherQ.System != QuantityUnitSystem.UCUM)
if (System != otherQ.System)
{
return Fail<int>(Error.NotSupported("Comparing quantities with system other than UCUM is not supported"));
return Fail<int>(Error.NotSupported("Comparing a UCUM quantity with a non-UCUM quantity is not supported."));
}

Quantity? left = this;
Quantity? right = otherQ;

if (Unit != otherQ.Unit)
{
// align units with each other
if (!this.TryCanonicalize(out left)) left = this;
if (!otherQ.TryCanonicalize(out right)) right = otherQ;
}
Quantity left = this;
Quantity right = otherQ;

return (left.Unit == right.Unit)
? decimal.Compare(Math.Round(left.Value, 8), Math.Round(right.Value, 8)) // aligns with Decimal
: Fail<int>(Error.InvalidOperation($"UCUM quanties with unit '{left.Unit}' and '{right.Unit}' cannot be compared."));
? decimal.Compare(Math.Round(left.Value, 8), Math.Round(right.Value, 8)) // aligns with Decimal
: Fail<int>(Error.InvalidOperation($"Quantities with differing units '{left.Unit}' and '{right.Unit}' cannot be compared."));
}

private Result<int> doDurationComparison(Quantity other, QuantityComparison comparisonType)
Expand All @@ -264,7 +261,7 @@ private Result<int> doDurationComparison(Quantity other, QuantityComparison comp
if (l.Unit != r.Unit)
return Fail<int>(Error.InvalidOperation($"Durations of {l.Unit} and {r.Unit} cannot be compared,"));
else
return decimal.Compare(Math.Round(l.Value, 8), Math.Round(r.Value, 8)); // aligns with Decimal
return decimal.Compare(Math.Round(l.Value, 8), Math.Round(r.Value, 8)); // aligns with Decimal

Quantity normalizeToUcum(Quantity orig)
{
Expand Down Expand Up @@ -305,27 +302,6 @@ Quantity normalizeToUcum(Quantity orig)
/// to specify comparison behaviour for date comparisons.</remarks>
public Result<int> TryCompareTo(Any other) => TryCompareTo(other, CQL_EQUIVALENCE_COMPARISON);


private static (Quantity, Quantity) alignQuantityUnits(Quantity a, Quantity b)
{
if (a.System != QuantityUnitSystem.UCUM || b.System != QuantityUnitSystem.UCUM)
{
Error.NotSupported("Arithmetic operations on quantities using systems other than UCUM are not supported.");
}

Quantity? left = a;
Quantity? right = b;

if (a.Unit != b.Unit)
{
// align units with each other
if (!a.TryCanonicalize(out left)) left = a;
if (!b.TryCanonicalize(out right)) right = b;
}

return (left, right);
}

public static Quantity? operator +(Quantity a, Quantity b) =>
Add(a, b).ValueOrDefault();

Expand All @@ -338,48 +314,30 @@ private static (Quantity, Quantity) alignQuantityUnits(Quantity a, Quantity b)
public static Quantity? operator /(Quantity a, Quantity b) =>
Divide(a, b).ValueOrDefault();

internal static Result<Quantity> Add(Quantity a, Quantity b)
internal static Result<Quantity> Add(Quantity left, Quantity right)
{
var (left, right) = alignQuantityUnits(a, b);

return (left.Unit == right.Unit)
? Ok<Quantity>(new(left.Value + right.Value, left.Unit))
: Fail<Quantity>(Error.InvalidOperation($"The add operation cannot be performed on quantities with units '{left.Unit}' and '{right.Unit}'."));
: Fail<Quantity>(Error.InvalidOperation($"The add operation cannot be performed on quantities with differing units '{left.Unit}' and '{right.Unit}'."));
}

internal static Result<Quantity> Substract(Quantity a, Quantity b)
internal static Result<Quantity> Substract(Quantity left, Quantity right)
{
var (left, right) = alignQuantityUnits(a, b);

return (left.Unit == right.Unit)
? Ok<Quantity>(new(left.Value - right.Value, left.Unit))
: Fail<Quantity>(Error.InvalidOperation($"The substract operation cannot be performed on quantities with units '{left.Unit}' and '{right.Unit}'."));
: Fail<Quantity>(Error.InvalidOperation($"The substract operation cannot be performed on quantities with differing units '{left.Unit}' and '{right.Unit}'."));
}

internal static Result<Quantity> Multiply(Quantity a, Quantity b)
internal static Result<Quantity> Multiply(Quantity left, Quantity right)
{
var (left, right) = alignQuantityUnits(a, b);

if (!left.TryMultiply(right, out var result))
{
return Fail<Quantity>(Error.InvalidOperation($"The multiply operation cannot be performed on quantities with units '{left.Unit}' and '{right.Unit}'."));
}

return Ok(result);
return Ok<Quantity>(new(left.Value * right.Value, $"({left.Unit}).({right.Unit})"));
}

internal static Result<Quantity> Divide(Quantity a, Quantity b)
internal static Result<Quantity> Divide(Quantity left, Quantity right)
{
if (b.Value == 0) return Fail<Quantity>(Error.InvalidOperation("Cannot divide by zero."));
if (right.Value == 0) return Fail<Quantity>(Error.InvalidOperation("Cannot divide by zero."));

var (left, right) = alignQuantityUnits(a, b);

if (!left.TryDivide(right, out var result))
{
return Fail<Quantity>(Error.InvalidOperation($"The divide operation cannot be performed on quantities with units '{left.Unit}' and '{right.Unit}'."));
}

return Ok(result);
return Ok<Quantity>(new(left.Value / right.Value, $"({left.Unit})/({right.Unit})"));
}

public override int GetHashCode() => (Unit, Value).GetHashCode();
Expand Down
114 changes: 0 additions & 114 deletions src/Hl7.Fhir.Base/ElementModel/Types/Ucum.cs

This file was deleted.

1 change: 0 additions & 1 deletion src/Hl7.Fhir.Base/Hl7.Fhir.Base.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Fhir.Metrics" Version="1.3.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="System.Reflection.Emit.Lightweight" Version="4.7.0" />
<PackageReference Include="System.ComponentModel.Annotations" Version="5.0.0" />
Expand Down
Loading

0 comments on commit 690f0a0

Please sign in to comment.