diff --git a/src/Hl7.Fhir.Base/CompatibilitySuppressions.xml b/src/Hl7.Fhir.Base/CompatibilitySuppressions.xml
index c5bb7b18b9..864efe3716 100644
--- a/src/Hl7.Fhir.Base/CompatibilitySuppressions.xml
+++ b/src/Hl7.Fhir.Base/CompatibilitySuppressions.xml
@@ -8,6 +8,13 @@
lib/net8.0/Hl7.Fhir.Base.dlltrue
+
+ CP0001
+ T:Hl7.Fhir.ElementModel.Types.MetricConfiguration
+ lib/net8.0/Hl7.Fhir.Base.dll
+ lib/net8.0/Hl7.Fhir.Base.dll
+ true
+ CP0001T:Hl7.Fhir.Introspection.BackboneTypeAttribute
@@ -28,6 +35,13 @@
lib/netstandard2.0/Hl7.Fhir.Base.dlltrue
+
+ CP0001
+ T:Hl7.Fhir.ElementModel.Types.MetricConfiguration
+ lib/netstandard2.0/Hl7.Fhir.Base.dll
+ lib/netstandard2.0/Hl7.Fhir.Base.dll
+ true
+ CP0001T:Hl7.Fhir.Introspection.BackboneTypeAttribute
diff --git a/src/Hl7.Fhir.Base/ElementModel/Types/Quantity.cs b/src/Hl7.Fhir.Base/ElementModel/Types/Quantity.cs
index 025ce1c198..8804fe72b6 100644
--- a/src/Hl7.Fhir.Base/ElementModel/Types/Quantity.cs
+++ b/src/Hl7.Fhir.Base/ElementModel/Types/Quantity.cs
@@ -32,7 +32,12 @@ public enum QuantityUnitSystem
///
/// Unit is taken from the set of calendar units (year or month)
///
- CalendarDuration
+ CalendarDuration,
+
+ ///
+ /// Unit is not specified to be part of any coding system.
+ ///
+ Unknown
}
@@ -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)
{
@@ -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;
}
@@ -200,18 +206,16 @@ public bool Equals(Any other, QuantityComparison comparisonType) =>
///
/// For time-valued quantities, the comparison of
/// calendar durations and definite quantity durations above seconds is determined by the
- public Result TryEquals(Any other, QuantityComparison comparisonType) => other is Quantity ?
- TryCompareTo(other, comparisonType).Select(i => i == 0) : false;
+ public Result 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;
///
/// Compare two datetimes based on CQL equivalence rules
///
/// See for more details.
- 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;
@@ -236,24 +240,17 @@ public Result 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(Error.NotSupported("Comparing quantities with system other than UCUM is not supported"));
+ return Fail(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(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(Error.InvalidOperation($"Quantities with differing units '{left.Unit}' and '{right.Unit}' cannot be compared."));
}
private Result doDurationComparison(Quantity other, QuantityComparison comparisonType)
@@ -264,7 +261,7 @@ private Result doDurationComparison(Quantity other, QuantityComparison comp
if (l.Unit != r.Unit)
return Fail(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)
{
@@ -305,27 +302,6 @@ Quantity normalizeToUcum(Quantity orig)
/// to specify comparison behaviour for date comparisons.
public Result 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();
@@ -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 Add(Quantity a, Quantity b)
+ internal static Result Add(Quantity left, Quantity right)
{
- var (left, right) = alignQuantityUnits(a, b);
-
return (left.Unit == right.Unit)
? Ok(new(left.Value + right.Value, left.Unit))
- : Fail(Error.InvalidOperation($"The add operation cannot be performed on quantities with units '{left.Unit}' and '{right.Unit}'."));
+ : Fail(Error.InvalidOperation($"The add operation cannot be performed on quantities with differing units '{left.Unit}' and '{right.Unit}'."));
}
- internal static Result Substract(Quantity a, Quantity b)
+ internal static Result Substract(Quantity left, Quantity right)
{
- var (left, right) = alignQuantityUnits(a, b);
-
return (left.Unit == right.Unit)
? Ok(new(left.Value - right.Value, left.Unit))
- : Fail(Error.InvalidOperation($"The substract operation cannot be performed on quantities with units '{left.Unit}' and '{right.Unit}'."));
+ : Fail(Error.InvalidOperation($"The substract operation cannot be performed on quantities with differing units '{left.Unit}' and '{right.Unit}'."));
}
- internal static Result Multiply(Quantity a, Quantity b)
+ internal static Result Multiply(Quantity left, Quantity right)
{
- var (left, right) = alignQuantityUnits(a, b);
-
- if (!left.TryMultiply(right, out var result))
- {
- return Fail(Error.InvalidOperation($"The multiply operation cannot be performed on quantities with units '{left.Unit}' and '{right.Unit}'."));
- }
-
- return Ok(result);
+ return Ok(new(left.Value * right.Value, $"({left.Unit}).({right.Unit})"));
}
- internal static Result Divide(Quantity a, Quantity b)
+ internal static Result Divide(Quantity left, Quantity right)
{
- if (b.Value == 0) return Fail(Error.InvalidOperation("Cannot divide by zero."));
+ if (right.Value == 0) return Fail(Error.InvalidOperation("Cannot divide by zero."));
- var (left, right) = alignQuantityUnits(a, b);
-
- if (!left.TryDivide(right, out var result))
- {
- return Fail(Error.InvalidOperation($"The divide operation cannot be performed on quantities with units '{left.Unit}' and '{right.Unit}'."));
- }
-
- return Ok(result);
+ return Ok(new(left.Value / right.Value, $"({left.Unit})/({right.Unit})"));
}
public override int GetHashCode() => (Unit, Value).GetHashCode();
diff --git a/src/Hl7.Fhir.Base/ElementModel/Types/Ucum.cs b/src/Hl7.Fhir.Base/ElementModel/Types/Ucum.cs
deleted file mode 100644
index f16280ddcb..0000000000
--- a/src/Hl7.Fhir.Base/ElementModel/Types/Ucum.cs
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright (c) 2019, Firely (info@fire.ly) and contributors
- * See the file CONTRIBUTORS for details.
- *
- * This file is licensed under the BSD 3-Clause license
- * available at https://raw.githubusercontent.com/FirelyTeam/firely-net-sdk/master/LICENSE
- */
-
-#nullable enable
-
-using Fhir.Metrics;
-using M = Fhir.Metrics;
-using System;
-using System.Collections.Generic;
-using System.Diagnostics.CodeAnalysis;
-using System.Globalization;
-
-namespace Hl7.Fhir.ElementModel.Types
-{
- public static class MetricConfiguration
- {
- [CLSCompliant(false)]
- public static Lazy MetricService { get; set; } = new(() => FhirMetricService.Instance.Value);
- }
-
- internal static class Ucum
- {
- private static readonly IMetricService METRIC_SERVICE = MetricConfiguration.MetricService.Value;
-
- ///
- /// Try to canonicalize the system type quantity to Umum base quantity. So a 1,000 cm will be 10 m. Or an inch will be converted to a meter.
- ///
- /// A system type Quantity of system Ucum
- /// The converted system type Quantity when the conversion was a success.
- /// true when the conversion succeeded. Or false otherwise.
- internal static bool TryCanonicalize(this Quantity quantity, [NotNullWhen(true)] out Quantity? canonicalizedQuantity)
- {
- var qtyTuple = (
- quantity.Value.ToString(CultureInfo.InvariantCulture),
- quantity.Unit,
- quantity.System == QuantityUnitSystem.UCUM ? "http://unitsofmeasure.org" : ""
- );
-
- if (!METRIC_SERVICE.TryCanonicalize(qtyTuple, out var canonicalized))
- {
- canonicalizedQuantity = null;
- return false;
- }
-
- canonicalizedQuantity = quantityFromTuple(canonicalized!.Value);
-
- return true;
- }
-
- internal static bool TryMultiply(this Quantity quantity, Quantity multiplier, [NotNullWhen(true)] out Quantity? result)
- {
- var qty1 = (
- quantity.Value.ToString(CultureInfo.InvariantCulture),
- quantity.Unit,
- quantity.System == QuantityUnitSystem.UCUM ? "http://unitsofmeasure.org" : ""
- );
- var qty2 = (
- multiplier.Value.ToString(CultureInfo.InvariantCulture),
- multiplier.Unit,
- multiplier.System == QuantityUnitSystem.UCUM ? "http://unitsofmeasure.org" : ""
- );
-
- if (!METRIC_SERVICE.TryMultiply(qty1, qty2, out var resultTuple))
- {
- result = null;
- return false;
- }
-
- result = quantityFromTuple(resultTuple!.Value);
-
- return true;
- }
-
- internal static bool TryDivide(this Quantity quantity, Quantity divisor, [NotNullWhen(true)] out Quantity? result)
- {
- var qty1 = (
- quantity.Value.ToString(CultureInfo.InvariantCulture),
- quantity.Unit,
- quantity.System == QuantityUnitSystem.UCUM ? "http://unitsofmeasure.org" : ""
- );
- var qty2 = (
- divisor.Value.ToString(CultureInfo.InvariantCulture),
- divisor.Unit,
- divisor.System == QuantityUnitSystem.UCUM ? "http://unitsofmeasure.org" : ""
- );
-
- if (!METRIC_SERVICE.TryDivide(qty1, qty2, out var resultTuple))
- {
- result = null;
- return false;
- }
-
- result = quantityFromTuple(resultTuple!.Value);
-
- return true;
- }
-
- private static Quantity quantityFromTuple((string value, string unit, string codesystem) quantity)
- {
- return new Quantity(
- decimal.Parse(quantity.value, NumberStyles.Any, CultureInfo.InvariantCulture),
- quantity.unit == "" ? "1" : quantity.unit,
- QuantityUnitSystem.UCUM
- );
- }
- }
-}
-
-#nullable restore
diff --git a/src/Hl7.Fhir.Base/Hl7.Fhir.Base.csproj b/src/Hl7.Fhir.Base/Hl7.Fhir.Base.csproj
index 44011e9614..2a197046d4 100644
--- a/src/Hl7.Fhir.Base/Hl7.Fhir.Base.csproj
+++ b/src/Hl7.Fhir.Base/Hl7.Fhir.Base.csproj
@@ -16,7 +16,6 @@
-
diff --git a/src/Hl7.Fhir.Support.Tests/ElementModel/QuantityTests.cs b/src/Hl7.Fhir.Support.Tests/ElementModel/QuantityTests.cs
index fa8a62c93e..b76e14a5bf 100644
--- a/src/Hl7.Fhir.Support.Tests/ElementModel/QuantityTests.cs
+++ b/src/Hl7.Fhir.Support.Tests/ElementModel/QuantityTests.cs
@@ -95,12 +95,11 @@ public void DifferentUnitsNotSupported()
{
var a = new Quantity(3.14m, "kg");
var b = new Quantity(30.5m, "g");
- /*
+
Func