Skip to content

Commit 7ca89d1

Browse files
committed
Update start and end based on clarified definitions
1 parent 7466cea commit 7ca89d1

8 files changed

Lines changed: 633 additions & 203 deletions

File tree

examples/browser/cql4browsers.js

Lines changed: 232 additions & 153 deletions
Large diffs are not rendered by default.

src/datatypes/interval.ts

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -514,24 +514,36 @@ export class Interval {
514514

515515
start() {
516516
if (this.low == null) {
517-
if (this.lowClosed) {
518-
return minValueForInstance(this.high);
517+
const quantityInstance =
518+
this.high && this.pointType == ELM_QUANTITY_TYPE ? this.high : undefined;
519+
const minValue = minValueForType(this.pointType, quantityInstance);
520+
if (this.lowClosed || minValue == null) {
521+
return minValue;
519522
} else {
520-
return this.low;
523+
const end = ((end: any) => (end.isUncertainty ? end.high : end))(
524+
this.high == null ? maxValueForType(this.pointType) : this.end()
525+
);
526+
return new Uncertainty(minValue, end);
521527
}
522528
}
523-
return this.toClosed().low;
529+
return this.lowClosed ? this.low : successor(this.low, this.pointType);
524530
}
525531

526532
end() {
527533
if (this.high == null) {
528-
if (this.highClosed) {
529-
return maxValueForInstance(this.low);
534+
const quantityInstance =
535+
this.low && this.pointType == ELM_QUANTITY_TYPE ? this.low : undefined;
536+
const maxValue = maxValueForType(this.pointType, quantityInstance);
537+
if (this.highClosed || maxValue == null) {
538+
return maxValue;
530539
} else {
531-
return this.high;
540+
const start = ((start: any) => (start.isUncertainty ? start.low : start))(
541+
this.low == null ? minValueForType(this.pointType) : this.start()
542+
);
543+
return new Uncertainty(start, maxValue);
532544
}
533545
}
534-
return this.toClosed().high;
546+
return this.highClosed ? this.high : predecessor(this.high, this.pointType);
535547
}
536548

537549
starts(other: any, precision?: any) {

src/datatypes/quantity.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import { ELM_DECIMAL_TYPE } from '../util/elmTypes';
2-
import { decimalAdjust, isValidDecimal, overflowsOrUnderflows } from '../util/math';
2+
import {
3+
decimalAdjust,
4+
isValidDecimal,
5+
MIN_FLOAT_VALUE,
6+
MAX_FLOAT_VALUE,
7+
overflowsOrUnderflows
8+
} from '../util/math';
39
import {
410
checkUnit,
511
convertUnit,
@@ -157,6 +163,11 @@ export class Quantity {
157163
}
158164
}
159165

166+
// Require MIN/MAX here because math.js requires this file, and when we make this file require
167+
// math.js before it exports DateTime and Date, it errors due to the circular dependency...
168+
export const MIN_QUANTITY_VALUE = new Quantity(MIN_FLOAT_VALUE, '1');
169+
export const MAX_QUANTITY_VALUE = new Quantity(MAX_FLOAT_VALUE, '1');
170+
160171
export function parseQuantity(str: string) {
161172
const components = /([+|-]?\d+\.?\d*)\s*('(.+)')?/.exec(str);
162173
if (components != null && components[1] != null) {

src/util/comparison.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Uncertainty } from '../datatypes/datatypes';
1+
import { Uncertainty } from '../datatypes/uncertainty';
22

33
function areNumbers(a: any, b: any) {
44
return typeof a === 'number' && typeof b === 'number';

src/util/math.ts

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Exception } from '../datatypes/exception';
2-
import { Quantity } from '../datatypes/quantity';
2+
import { Quantity, MIN_QUANTITY_VALUE, MAX_QUANTITY_VALUE } from '../datatypes/quantity';
33
import {
44
MIN_DATETIME_VALUE as dtMinDateTimeValue,
55
MAX_DATETIME_VALUE as dtMaxDateTimeValue,
@@ -279,9 +279,14 @@ export function maxValueForInstance(val: any) {
279279
} else if (val && val.isDate) {
280280
return MAX_DATE_VALUE?.copy();
281281
} else if (val && val.isQuantity) {
282-
const val2 = val.clone();
283-
val2.value = maxValueForInstance(val2.value);
284-
return val2;
282+
// Although the spec says max Quantity has unit '1', it doesn't make sense to change the unit,
283+
// especially if this is being used in the context of an interval or uncertainty since the
284+
// left and right sides need to be comparable in those cases.
285+
const maxQuantity = MAX_QUANTITY_VALUE.clone();
286+
if (val.unit) {
287+
maxQuantity.unit = val.unit;
288+
}
289+
return maxQuantity;
285290
} else {
286291
return null;
287292
}
@@ -302,13 +307,17 @@ export function maxValueForType(type: string, quantityInstance?: Quantity) {
302307
case ELM_TIME_TYPE:
303308
return MAX_TIME_VALUE?.copy();
304309
case ELM_QUANTITY_TYPE: {
310+
// Although the spec says max Quantity has unit '1', it doesn't make sense to change the unit,
311+
// especially if this is being used in the context of an interval or uncertainty since the
312+
// left and right sides need to be comparable in those cases.
313+
const maxQuantity = MAX_QUANTITY_VALUE.clone();
305314
if (quantityInstance == null) {
306-
// can't infer a quantity unit type from nothing]
307-
return null;
315+
return maxQuantity;
316+
}
317+
if (quantityInstance.unit) {
318+
maxQuantity.unit = quantityInstance.unit;
308319
}
309-
const maxQty = quantityInstance.clone();
310-
maxQty.value = MAX_FLOAT_VALUE;
311-
return maxQty;
320+
return maxQuantity;
312321
}
313322
}
314323
return null;
@@ -330,9 +339,14 @@ export function minValueForInstance(val: any) {
330339
} else if (val && val.isDate) {
331340
return MIN_DATE_VALUE?.copy();
332341
} else if (val && val.isQuantity) {
333-
const val2 = val.clone();
334-
val2.value = minValueForInstance(val2.value);
335-
return val2;
342+
// Although the spec says max Quantity has unit '1', it doesn't make sense to change the unit,
343+
// especially if this is being used in the context of an interval or uncertainty since the
344+
// left and right sides need to be comparable in those cases.
345+
const minQuantity = MIN_QUANTITY_VALUE.clone();
346+
if (val.unit) {
347+
minQuantity.unit = val.unit;
348+
}
349+
return minQuantity;
336350
} else {
337351
return null;
338352
}
@@ -353,13 +367,17 @@ export function minValueForType(type: string, quantityInstance?: Quantity) {
353367
case ELM_TIME_TYPE:
354368
return MIN_TIME_VALUE?.copy();
355369
case ELM_QUANTITY_TYPE: {
370+
// Although the spec says max Quantity has unit '1', it doesn't make sense to change the unit,
371+
// especially if this is being used in the context of an interval or uncertainty since the
372+
// left and right sides need to be comparable in those cases.
373+
const minQuantity = MIN_QUANTITY_VALUE.clone();
356374
if (quantityInstance == null) {
357-
// can't infer a quantity unit type from nothing]
358-
return null;
375+
return minQuantity;
376+
}
377+
if (quantityInstance.unit) {
378+
minQuantity.unit = quantityInstance.unit;
359379
}
360-
const minQty = quantityInstance.clone();
361-
minQty.value = MIN_FLOAT_VALUE;
362-
return minQty;
380+
return minQuantity;
363381
}
364382
}
365383
return null;

test/datatypes/interval-data.ts

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Interval } from '../../src/datatypes/interval';
2-
import { DateTime } from '../../src/datatypes/datetime';
2+
import { DateTime, Date } from '../../src/datatypes/datetime';
3+
import { Quantity } from '../../src/datatypes/quantity';
34

45
class TestDateTime {
56
static parse(string: string) {
@@ -43,23 +44,33 @@ class TestInterval {
4344
toMinute: Interval;
4445
toSecond: Interval;
4546
toMillisecond: Interval;
47+
withNullStart: TestInterval;
48+
withNullEnd: TestInterval;
4649

47-
constructor(low: any, high: any) {
48-
const [thLow, thHigh] = Array.from([
49-
TestDateTime.fromDateTime(low),
50-
TestDateTime.fromDateTime(high)
51-
]);
50+
constructor(low: any, high: any, createNullVariants = true) {
5251
this.closed = new Interval(low, high, true, true);
5352
this.open = new Interval(low, high, false, false);
5453
this.closedOpen = new Interval(low, high, true, false);
5554
this.openClosed = new Interval(low, high, false, true);
56-
this.toYear = new Interval(thLow.toYear, thHigh.toYear);
57-
this.toMonth = new Interval(thLow.toMonth, thHigh.toMonth);
58-
this.toDay = new Interval(thLow.toDay, thHigh.toDay);
59-
this.toHour = new Interval(thLow.toHour, thHigh.toHour);
60-
this.toMinute = new Interval(thLow.toMinute, thHigh.toMinute);
61-
this.toSecond = new Interval(thLow.toSecond, thHigh.toSecond);
62-
this.toMillisecond = new Interval(thLow.toMillisecond, thHigh.toMillisecond);
55+
56+
if (low != null && high != null) {
57+
const [thLow, thHigh] = Array.from([
58+
TestDateTime.fromDateTime(low),
59+
TestDateTime.fromDateTime(high)
60+
]);
61+
this.toYear = new Interval(thLow.toYear, thHigh.toYear);
62+
this.toMonth = new Interval(thLow.toMonth, thHigh.toMonth);
63+
this.toDay = new Interval(thLow.toDay, thHigh.toDay);
64+
this.toHour = new Interval(thLow.toHour, thHigh.toHour);
65+
this.toMinute = new Interval(thLow.toMinute, thHigh.toMinute);
66+
this.toSecond = new Interval(thLow.toSecond, thHigh.toSecond);
67+
this.toMillisecond = new Interval(thLow.toMillisecond, thHigh.toMillisecond);
68+
}
69+
70+
if (createNullVariants) {
71+
this.withNullStart = new TestInterval(null, high, false);
72+
this.withNullEnd = new TestInterval(low, null, false);
73+
}
6374
}
6475
}
6576

@@ -94,6 +105,11 @@ export default () => {
94105
data['mid2012'] = TestDateTime.parse('2012-06-01T00:00:00.0');
95106
data['end2012'] = TestDateTime.parse('2012-12-31T23:59:59.999');
96107
data['aft2012'] = TestDateTime.parse('2013-06-01T00:00:00.0');
108+
data['all2012date'] = new TestInterval(Date.parse('2012-01-01'), Date.parse('2012-12-31'));
109+
data['alldaytime'] = new TestInterval(
110+
DateTime.parse('0001-01-01T00:00:00.0').getTime(),
111+
DateTime.parse('0001-01-01T23:59:59.999').getTime()
112+
);
97113
data['dIvl'] = {
98114
sameAs: {
99115
// |----------X----------|
@@ -276,5 +292,7 @@ export default () => {
276292
y: new TestInterval(0n, 100n)
277293
}
278294
};
295+
data['zeroPointFiveToNinePointFive'] = new TestInterval(0.5, 9.5);
296+
data['zeroToHundredMg'] = new TestInterval(new Quantity(0, 'mg'), new Quantity(100, 'mg'));
279297
return data;
280298
};

0 commit comments

Comments
 (0)