Skip to content
This repository has been archived by the owner on Dec 21, 2022. It is now read-only.

Fix expand Interval for int, decimal and date #499

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 3 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
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ public static List<Interval> getExpandedInterval(Interval interval, Quantity per

List<Interval> expansion = new ArrayList<>();
Object start = interval.getStart();
Object end = addPer(start, per);

if ((start instanceof Integer || start instanceof BigDecimal)
&& !per.getUnit().equals("1"))
Expand All @@ -84,11 +83,21 @@ public static List<Interval> getExpandedInterval(Interval interval, Quantity per
return expansion;
}

while (LessOrEqualEvaluator.lessOrEqual(PredecessorEvaluator.predecessor(end), interval.getEnd()))
{
expansion.add(new Interval(start, true, end, false));
start = end;
end = addPer(start, per);
if (start instanceof Integer) {
Object end = addPer(start, per);
Object predecessorOfEnd = PredecessorEvaluator.predecessor(end);

while (LessOrEqualEvaluator.lessOrEqual(predecessorOfEnd, interval.getEnd())) {
expansion.add(new Interval(start, true, predecessorOfEnd, true));
start = end;
end = addPer(start, per);
predecessorOfEnd = PredecessorEvaluator.predecessor(end);
}
} else if(start instanceof BigDecimal) {
JPercival marked this conversation as resolved.
Show resolved Hide resolved
while (LessOrEqualEvaluator.lessOrEqual(start, interval.getEnd())) {
expansion.add(new Interval(start, true, start, true));
start = addPer(start, per);
}
}

return expansion;
Expand Down Expand Up @@ -116,20 +125,22 @@ public static List<Interval> getExpandedInterval(Interval interval, Quantity per
Interval unit = null;
Object start = interval.getStart();
Object end = AddEvaluator.add(start, per);
Object predecessorOfEnd = PredecessorEvaluator.predecessor(end);
for (int j = 0; j < (Integer) i; ++j)
{
unit = new Interval(start, true, end, false);
unit = new Interval(start, true, predecessorOfEnd, true);
expansion.add(unit);
start = end;
end = AddEvaluator.add(start, per);
predecessorOfEnd = PredecessorEvaluator.predecessor(end);
}

if (unit != null)
{
i = DurationBetweenEvaluator.duration(unit.getEnd(), interval.getEnd(), Precision.fromString(precision));
if (i instanceof Integer && (Integer) i == 1)
{
expansion.add(new Interval(start, true, end, false));
expansion.add(new Interval(start, true, PredecessorEvaluator.predecessor(end), true));
}
}
else
Expand Down Expand Up @@ -163,34 +174,20 @@ public static List<Interval> expand(Iterable<Interval> list, Quantity per)
return intervals;
}

boolean isTemporal =
intervals.get(0).getStart() instanceof BaseTemporal
|| intervals.get(0).getEnd() instanceof BaseTemporal;

if(per == null) {
per = determinePer(intervals.get(0), isTemporal);
}


// collapses overlapping intervals
intervals = CollapseEvaluator.collapse(intervals, new Quantity().withValue(BigDecimal.ZERO).withUnit(per == null ? "1" : per.getUnit()));

boolean isTemporal =
intervals.get(0).getStart() instanceof BaseTemporal
|| intervals.get(0).getEnd() instanceof BaseTemporal;

intervals.sort(new CqlList().valueSort);

if (per == null)
{
if (isTemporal)
{
per = new Quantity()
.withValue(new BigDecimal("1.0"))
.withUnit(
BaseTemporal.getLowestPrecision(
(BaseTemporal) intervals.get(0).getStart(),
(BaseTemporal) intervals.get(0).getEnd()
)
);
}
else
{
per = new Quantity().withValue(new BigDecimal("1.0")).withDefaultUnit();
}
}

String precision = per.getUnit().equals("1") ? null : per.getUnit();

// prevent duplicates
Expand All @@ -216,6 +213,47 @@ public static List<Interval> expand(Iterable<Interval> list, Quantity per)
return set.isEmpty() ? new ArrayList<>() : new ArrayList<>(set);
}

/*
The number with the fewest decimal places determines the per for decimal.
[1, 45] -> 1 // scale 0
[1.0, 2.0] -> .1 //scale 1
[1.000001, 2] -> 1 //scale 0
[1.0, 2.01] -> .1 // scale 1
[1, 2.010101010] -> 1 //scale 0
[2.01010101, 1] -> 1 //scale 0
[1.00, 2.00] -> .01 //scale 2
[1.00, 2.0005] -> .01 //scale 2
*/
private static Quantity determinePer(Interval interval, boolean isTemporal) {
Quantity per = null;

if (isTemporal) {
per = new Quantity()
.withValue(new BigDecimal("1.0"))
.withUnit(
BaseTemporal.getLowestPrecision(
(BaseTemporal) interval.getStart(),
(BaseTemporal) interval.getEnd()
)
);
} else {
per = new Quantity().withDefaultUnit();
int scale;
if ((interval.getStart() instanceof BigDecimal)) {
scale = ((BigDecimal) interval.getStart()).scale();
if (((BigDecimal) interval.getEnd()).scale() < scale) {
scale = ((BigDecimal) interval.getEnd()).scale();
}
BigDecimal d = BigDecimal.valueOf(Math.pow(10.0, BigDecimal.valueOf(scale).doubleValue()));
per.withValue(BigDecimal.ONE.divide(d));
} else {
per = new Quantity().withValue(new BigDecimal("1.0"));
}

}
return per;
}

@Override
@SuppressWarnings("unchecked")
protected Object internalEvaluate(Context context)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import java.util.List;

import org.opencds.cqf.cql.engine.elm.execution.EquivalentEvaluator;
import org.opencds.cqf.cql.engine.runtime.Date;
import org.opencds.cqf.cql.engine.runtime.DateTime;
import org.opencds.cqf.cql.engine.runtime.Interval;
import org.opencds.cqf.cql.engine.runtime.Quantity;
Expand Down Expand Up @@ -172,6 +173,72 @@ public void TestBefore() {
assertThat(result, is(false));
}

/**
* {@link org.opencds.cqf.cql.engine.elm.execution.ExpandEvaluator#evaluate(Context)}
*/
@Test
public void testExpand() {
Context context = new Context(library);
Object result;

result = context.resolveExpressionRef("TestIntegerIntervalExpand").getExpression().evaluate(context);
Assert.assertTrue(((Interval)((List<?>) result).get(0)).equal(new Interval(4, true, 4, true)));
Assert.assertTrue(((Interval)((List<?>) result).get(1)).equal(new Interval(5, true, 5, true)));
Assert.assertTrue(((Interval)((List<?>) result).get(2)).equal(new Interval(6, true, 6, true)));

result = context.resolveExpressionRef("TestDecimalIntervalExpand").getExpression().evaluate(context);
Assert.assertTrue(((Interval)((List<?>) result).get(0)).equal(new Interval(new BigDecimal("4.0"), true, new BigDecimal("4.0"), true)));
Assert.assertTrue(((Interval)((List<?>) result).get(1)).equal(new Interval(new BigDecimal("5.0"), true, new BigDecimal("5.0"), true)));

result = context.resolveExpressionRef("TestDateIntervalExpandClosedPerDay").getExpression().evaluate(context);
Interval interval = ((Interval)((List<?>)result).get(0));
Assert.assertTrue(EquivalentEvaluator.equivalent(interval.getStart(), new Date(2018, 1, 1)));
Assert.assertTrue(interval.getLowClosed() && interval.getHighClosed());
Assert.assertTrue(EquivalentEvaluator.equivalent(interval.getEnd(), new Date(2018, 1, 1)));
interval = ((Interval)((List<?>)result).get(1));
Assert.assertTrue(EquivalentEvaluator.equivalent(interval.getStart(), new Date(2018, 1, 2)));
Assert.assertTrue(interval.getLowClosed() && interval.getHighClosed());
Assert.assertTrue(EquivalentEvaluator.equivalent(interval.getEnd(), new Date(2018, 1, 2)));


result = context.resolveExpressionRef("TestDateIntervalExpandClosedPerWeekEmpty").getExpression().evaluate(context);
Assert.assertTrue(((List)result).isEmpty());

result = context.resolveExpressionRef("TestDateIntervalExpandClosedPerWeek").getExpression().evaluate(context);
interval = ((Interval)((List<?>)result).get(0));
Assert.assertTrue(EquivalentEvaluator.equivalent(interval.getStart(), new Date(2018, 1, 1)));
Assert.assertTrue(interval.getLowClosed() && interval.getHighClosed());
Assert.assertTrue(EquivalentEvaluator.equivalent(interval.getEnd(), new Date(2018, 1, 7)));

result = context.resolveExpressionRef("TestDecimal2IntervalExpand").getExpression().evaluate(context);
Assert.assertTrue(((Interval)((List<?>) result).get(0)).equal(new Interval(new BigDecimal("1.0"), true, new BigDecimal("1.0"), true)));
Assert.assertTrue(((Interval)((List<?>) result).get(1)).equal(new Interval(new BigDecimal("1.1"), true, new BigDecimal("1.1"), true)));


result = context.resolveExpressionRef("TestDecimal3IntervalExpand").getExpression().evaluate(context);
Assert.assertTrue(((Interval)((List<?>) result).get(0)).equal(new Interval(new BigDecimal("1.000001"), true, new BigDecimal("1.000001"), true)));

result = context.resolveExpressionRef("TestDecimal4IntervalExpand").getExpression().evaluate(context);
Assert.assertTrue(((Interval)((List<?>) result).get(0)).equal(new Interval(new BigDecimal("1.0"), true, new BigDecimal("1.0"), true)));
Assert.assertTrue(((Interval)((List<?>) result).get(1)).equal(new Interval(new BigDecimal("1.1"), true, new BigDecimal("1.1"), true)));

result = context.resolveExpressionRef("TestDecimal5IntervalExpand").getExpression().evaluate(context);
Assert.assertTrue(((Interval)((List<?>) result).get(0)).equal(new Interval(new BigDecimal("1"), true, new BigDecimal("1"), true)));
Assert.assertTrue(((Interval)((List<?>) result).get(1)).equal(new Interval(new BigDecimal("2"), true, new BigDecimal("2"), true)));

result = context.resolveExpressionRef("TestDecimal6IntervalExpand").getExpression().evaluate(context);
Assert.assertTrue(((Interval)((List<?>) result).get(0)).equal(new Interval(new BigDecimal("1.00"), true, new BigDecimal("1.00"), true)));
Assert.assertTrue(((Interval)((List<?>) result).get(1)).equal(new Interval(new BigDecimal("1.01"), true, new BigDecimal("1.01"), true)));

result = context.resolveExpressionRef("TestDecimal7IntervalExpand").getExpression().evaluate(context);
Assert.assertTrue(((Interval)((List<?>) result).get(0)).equal(new Interval(new BigDecimal("1.00"), true, new BigDecimal("1.00"), true)));
Assert.assertTrue(((Interval)((List<?>) result).get(1)).equal(new Interval(new BigDecimal("1.01"), true, new BigDecimal("1.01"), true)));

result = context.resolveExpressionRef("TestDecimal8IntervalExpand").getExpression().evaluate(context);
Assert.assertTrue(((Interval)((List<?>) result).get(0)).equal(new Interval(new BigDecimal("1.00"), true, new BigDecimal("1.00"), true)));
Assert.assertTrue(((Interval)((List<?>) result).get(1)).equal(new Interval(new BigDecimal("1.30"), true, new BigDecimal("1.30"), true)));
}

/**
* {@link org.opencds.cqf.cql.engine.elm.execution.CollapseEvaluator#evaluate(Context)}
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,20 @@ define DateTimeBeforeFalse: Interval[DateTime(2012, 1, 1), DateTime(2012, 1, 15)
define TimeBeforeTrue: Interval[@T15:59:59.999, @T20:59:59.999] before @T22:59:59.999
define TimeBeforeFalse: Interval[@T15:59:59.999, @T20:59:59.999] before @T10:59:59.999

//Expand
define TestIntegerIntervalExpand: expand {Interval[4,6]}
define TestDecimalIntervalExpand: expand {Interval[4.0,6]}
define TestDateIntervalExpandClosedPerDay: expand { Interval[@2018-01-01, @2018-01-03] } per day
define TestDateIntervalExpandClosedPerWeekEmpty: expand { Interval[@2018-01-01, @2018-01-03] } per week
define TestDateIntervalExpandClosedPerWeek: expand { Interval[@2018-01-01, @2018-01-10] } per week
define TestDecimal2IntervalExpand: expand {Interval[1.0, 2.0]}
define TestDecimal3IntervalExpand: expand {Interval[1.000001, 2]}
JPercival marked this conversation as resolved.
Show resolved Hide resolved
define TestDecimal4IntervalExpand: expand {Interval[1.0, 2.01]}
define TestDecimal5IntervalExpand: expand {Interval[1, 2.0101201]}
define TestDecimal6IntervalExpand: expand {Interval[1.00, 2.00]}
define TestDecimal7IntervalExpand: expand {Interval[1.00, 2.0005]}
define TestDecimal8IntervalExpand: expand {Interval[1.00, 2.0005]} per 0.3

//Collapse
define TestCollapseNull: collapse null as List<Interval<Integer>>
define IntegerIntervalCollapse: collapse { Interval[1,5], Interval[3,7], Interval[12,19], Interval[7,10] }
Expand Down