From a4603112bada302aabe2f0bc0378846affefac6f Mon Sep 17 00:00:00 2001 From: JoelCourtney Date: Fri, 16 Feb 2024 13:14:26 -0800 Subject: [PATCH] Fix and test detect edges --- .../gov/nasa/jpl/aerie/timeline/Interval.kt | 4 +-- .../aerie/timeline/ops/SerialConstantOps.kt | 2 +- .../nasa/jpl/aerie/timeline/ops/SerialOps.kt | 31 +++++------------ .../nasa/jpl/aerie/timeline/IntervalTest.kt | 1 + .../jpl/aerie/timeline/ops/SerialOpsTest.kt | 33 +++++++++++++++++++ 5 files changed, 46 insertions(+), 25 deletions(-) diff --git a/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/Interval.kt b/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/Interval.kt index ca544c6cff..b27d497765 100644 --- a/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/Interval.kt +++ b/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/Interval.kt @@ -120,8 +120,8 @@ data class Interval( fun compareEnds(other: Interval): Int { val timeComparison: Int = end.compareTo(other.end) return if (timeComparison != 0) timeComparison - else if (startInclusivity == other.startInclusivity) 0 - else if (startInclusivity == Inclusivity.Inclusive) 1 + else if (endInclusivity == other.endInclusivity) 0 + else if (endInclusivity == Inclusivity.Inclusive) 1 else -1 } diff --git a/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/SerialConstantOps.kt b/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/SerialConstantOps.kt index feb139ef8d..ba12f6906b 100644 --- a/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/SerialConstantOps.kt +++ b/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/SerialConstantOps.kt @@ -15,7 +15,7 @@ interface SerialConstantOps>: SerialOps /** [(DOC)][notEqualTo] Returns a [Windows] that is `true` when this and another profile are not equal. */ fun > notEqualTo(other: OTHER) = - map2Values(::Windows, other, BinaryOperation.combineOrNull { l, r, _ -> l == r }) + map2Values(::Windows, other, BinaryOperation.combineOrNull { l, r, _ -> l != r }) override fun changes() = detectEdges(BinaryOperation.combineOrNull { l, r, _-> l != r }) diff --git a/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/SerialOps.kt b/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/SerialOps.kt index 2e48216155..3c5ea9e7ba 100644 --- a/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/SerialOps.kt +++ b/timeline/src/main/kotlin/gov/nasa/jpl/aerie/timeline/ops/SerialOps.kt @@ -1,7 +1,9 @@ package gov.nasa.jpl.aerie.timeline.ops import gov.nasa.jpl.aerie.timeline.* +import gov.nasa.jpl.aerie.timeline.Interval.Companion.at import gov.nasa.jpl.aerie.timeline.collections.profiles.Windows +import gov.nasa.jpl.aerie.timeline.util.coalesceList import gov.nasa.jpl.aerie.timeline.util.map2Serial import gov.nasa.jpl.aerie.timeline.util.truncateList @@ -108,34 +110,19 @@ interface SerialOps>: SegmentOps { var buffer: Segment? = null val result = collect(CollectOptions(bounds, false)) .flatMap { currentSegment -> - val leftEdge: Boolean? - val rightEdge: Boolean? - val previous = buffer buffer = currentSegment val currentInterval = currentSegment.interval - val leftEdgeInterval = Interval.at(currentInterval.start) - val rightEdgeInterval = Interval.at(currentInterval.end) + val leftEdgeInterval = at(currentInterval.start) + val rightEdgeInterval = at(currentInterval.end) - rightEdge = if (currentInterval.end.isEqualTo(bounds.end) && currentInterval.endInclusivity == bounds.endInclusivity) { - if (bounds.includesEnd()) false else null - } else { - edgePredicate.invoke(currentSegment.value, null, rightEdgeInterval) - } + val rightEdge = edgePredicate(currentSegment.value, null, rightEdgeInterval) - leftEdge = if (previous != null) { - if (previous.interval.compareEndToStart(currentInterval) == 0) { - edgePredicate.invoke(previous.value, currentSegment.value, leftEdgeInterval) - } else { - edgePredicate.invoke(null, currentSegment.value, leftEdgeInterval) - } + val leftEdge = if (previous == null || previous.interval.compareEndToStart(currentInterval) == -1) { + edgePredicate(null, currentSegment.value, leftEdgeInterval) } else { - if (currentInterval.start.isEqualTo(bounds.start) && currentInterval.startInclusivity == bounds.startInclusivity) { - if (bounds.includesStart()) false else null - } else { - edgePredicate.invoke(null, currentSegment.value, leftEdgeInterval) - } + edgePredicate(previous.value, currentSegment.value, leftEdgeInterval) } listOfNotNull( @@ -147,7 +134,7 @@ interface SerialOps>: SegmentOps { Segment(rightEdgeInterval, rightEdge).transpose() ) } - truncateList(result, opts) + truncateList(coalesceList(result, Segment::valueEquals), opts) } /** diff --git a/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/IntervalTest.kt b/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/IntervalTest.kt index 38637d8139..9dff1ef246 100644 --- a/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/IntervalTest.kt +++ b/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/IntervalTest.kt @@ -165,6 +165,7 @@ class IntervalTest { assertEquals(1, between(seconds(0), seconds(1)).compareEnds(at(seconds(0)))) assertEquals(-1, between(seconds(0), seconds(1), Exclusive).compareEnds(at(seconds(1)))) assertEquals(0, between(seconds(0), seconds(1)).compareEnds(at(seconds(1)))) + assertEquals(-1, betweenClosedOpen(seconds(0), seconds(1)).compareEnds(at(seconds(1)))) } @Test diff --git a/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/SerialOpsTest.kt b/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/SerialOpsTest.kt index 272f545f01..f84350bd71 100644 --- a/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/SerialOpsTest.kt +++ b/timeline/src/test/kotlin/gov/nasa/jpl/aerie/timeline/ops/SerialOpsTest.kt @@ -1,6 +1,7 @@ package gov.nasa.jpl.aerie.timeline.ops import gov.nasa.jpl.aerie.merlin.protocol.types.Duration +import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.milliseconds import gov.nasa.jpl.aerie.merlin.protocol.types.Duration.seconds import gov.nasa.jpl.aerie.timeline.* import gov.nasa.jpl.aerie.timeline.Interval.Companion.at @@ -17,4 +18,36 @@ class SerialOpsTest { // set, assignGaps, and map2Values are not tested here because they are trivial delegations to map2Serial. // see Map2SerialTest.kt + + @Test + fun detectEdges() { + val result = Discrete( + Segment(betweenClosedOpen(seconds(0), seconds(1)), "hello"), + Segment(between(seconds(1), seconds(2)), "oooo"), + Segment(between(seconds(2), seconds(3), Interval.Inclusivity.Exclusive), "aaaa"), + Segment(between(seconds(5), seconds(6)), "ao") + ).detectEdges(BinaryOperation.cases( + { l, _ -> l.endsWith('o') }, + { r, _ -> r.startsWith('o') }, + { l, r, _ -> l.endsWith(r.first()) } + )) + + assertIterableEquals( + listOf( + Segment(betweenClosedOpen(seconds(0), seconds(1)), false), + Segment(at(seconds(1)), true), + Segment(between(seconds(1), seconds(3), Interval.Inclusivity.Exclusive, Interval.Inclusivity.Inclusive), false), + Segment(betweenClosedOpen(seconds(5), seconds(6)), false), + Segment(at(seconds(6)), true) + ), + result.collect() + ) + + // collecting on smaller bounds that start in the middle of "oooo" to make sure it does not get truncated + // and count the bounds start as the segment start. + assertIterableEquals( + listOf(Segment(between(milliseconds(1500), seconds(3)), false)), + result.collect(between(milliseconds(1500), seconds(4))) + ) + } }