Repository navigation
Implement Modulo for Quantity - #394
bryantaustin13 wants to merge 1 commit into
Conversation
Modulo on Quantities always returned null. Add Quantity.modulo, which normalizes units the same way the other Quantity arithmetic does and returns null for a zero divisor, null, or units that cannot be converted. Remove ModuloQuantity and Modulo10By3Quantity from the spec-test skip list and regenerate the spec-test data. Fixes cqframework#386 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #394 +/- ##
==========================================
- Coverage 89.19% 89.15% -0.05%
==========================================
Files 60 60
Lines 5064 5079 +15
Branches 1446 1452 +6
==========================================
+ Hits 4517 4528 +11
- Misses 315 319 +4
Partials 232 232 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
Closing this in favour of #393, which fixes the same issue (#386) and was opened first. The two PRs are functionally the same: Quantity.modulo works the same way in both. Two notes from this PR that also apply to #393: Quantity mod Integer/Decimal: the number becomes a Quantity with unit '1', so 10 'g' mod 3 returns null. The Java engine keeps the left operand's unit and would return 1 'g' (cqframework/clinical_quality_language#1860). No cql-tests test covers this case. |
Fixes #386.
Problem
modon Quantity arguments always returnsnull, including same-unit cases like10.0 'g' mod 3.0 'g'.Modulo.exechas no Quantity branch, so it computesx % yon twoQuantityobjects.TruncatedDividedoesn't have this problem because it goes throughMathUtil.divide, which callsQuantity.dividedBy.The spec defines
mod(left Quantity, right Quantity) Quantity: "The mod operator computes the remainder of the division of its arguments. … For modulo operations involving quantities, the resulting quantity will have the appropriate unit. Attempting to operate on quantities with invalid or special units will result in null. … If the result of the modulo cannot be represented, or the right argument is 0, the result is null." (Modulo)Change
Quantity.modulo(src/datatypes/quantity.ts), modelled ondividedBy:normalizeUnitsWhenPossible, then takesDecimal.modulo.nullfor anullor zero divisor, for units that can't be brought to a common unit, and on overflow.'1', as in the other Quantity arithmetic.Modulo.exec(src/elm/arithmetic.ts) calls it when the left operand is a Quantity.test/datatypes/quantity-test.tscover: same unit, a negative dividend (-7 'cm' mod 3 'cm'→-1 'cm'), converted units (250 'cm' mod 1 'm'→50 'cm'), units that can't be converted, and a zero ornulldivisor.ModuloQuantityandModulo10By3Quantityare removed fromtest/spec-tests/skip-list.txt, and the spec-test data is regenerated.For mixed units,
normalizeUnitsWhenPossibleconverts to the smaller unit, so1.0 'm' mod 30.0 'cm'gives10 'cm'. The spec says only "the appropriate unit". This matches the existing add/subtract behaviour and the "most granular" rule proposed in FHIR-58137.Verification
On
master(e8096c6) and on this branch:masternpm testThe six extra passing tests are the four new unit tests and the two un-skipped spec tests.
npm run build,test:typecheck,lintandprettierare clean.Through cql-tests-runner against cqframework/cql-tests, exactly four tests go from fail to pass:
ModuloQuantity,Modulo10By3Quantity, and the mixed-unitModulo1MBy30CMQuantityandModulo1KGBy300GQuantityfrom cqframework/cql-tests#167. That runner check was done onb1a4c3ewhen filing #386, before this branch moved ontoe8096c6.🤖 Generated with Claude Code