From 9ec982c19f901466ade29eeaabd77d8a5afa8bd5 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Sat, 18 May 2019 18:47:30 +0200 Subject: [PATCH] bug5043: avoid filtering out candidates for fixed mark passings in StationarySequence --- ...eCandidatesInSmallTimeRangeFilterTest.java | 14 ++++----- .../markpassingcalculation/Candidate.java | 7 +++++ .../impl/CandidateChooserImpl.java | 2 +- .../CandidateForFixedMarkPassingImpl.java | 17 +++++++++++ ...bableCandidatesInSmallTimeRangeFilter.java | 6 ++-- .../impl/StationarySequence.java | 29 ++++++++++++++----- 6 files changed, 56 insertions(+), 19 deletions(-) create mode 100644 java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/impl/CandidateForFixedMarkPassingImpl.java diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/markpassingcalculation/impl/MostProbableCandidatesInSmallTimeRangeFilterTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/markpassingcalculation/impl/MostProbableCandidatesInSmallTimeRangeFilterTest.java index 9753bb6abdd..ea52e1ccb0a 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/markpassingcalculation/impl/MostProbableCandidatesInSmallTimeRangeFilterTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/markpassingcalculation/impl/MostProbableCandidatesInSmallTimeRangeFilterTest.java @@ -19,26 +19,26 @@ public class MostProbableCandidatesInSmallTimeRangeFilterTest extends AbstractCa @Test public void testGetTimeWiseContiguousDistanceCandidates() { - final NavigableSet result = filter.getTimeWiseContiguousDistanceCandidates(competitorCandidates, c3, /* includeStartFrom */ true); + final NavigableSet result = filter.getTimeWiseContiguousCandidates(competitorCandidates, c3, /* includeStartFrom */ true); assertContainsExactly(result, c1, c2, c3, c4, c5); } @Test public void testGetTimeWiseContiguousDistanceCandidatesOnRightBorder() { - final NavigableSet result = filter.getTimeWiseContiguousDistanceCandidates(competitorCandidates, c5, /* includeStartFrom */ true); + final NavigableSet result = filter.getTimeWiseContiguousCandidates(competitorCandidates, c5, /* includeStartFrom */ true); assertContainsExactly(result, c1, c2, c3, c4, c5); } @Test public void testGetTimeWiseContiguousDistanceCandidatesOnLeftBorder() { - final NavigableSet result = filter.getTimeWiseContiguousDistanceCandidates(competitorCandidates, c6, /* includeStartFrom */ true); + final NavigableSet result = filter.getTimeWiseContiguousCandidates(competitorCandidates, c6, /* includeStartFrom */ true); assertContainsExactly(result, c6, c7, c8, c9, c10); } @Test public void testGetTimeWiseContiguousDistanceCandidatesInMiddleOfGapNotInserted() { final Candidate middleOfGap = candidate(c5.getTimePoint().plus(c5.getTimePoint().until(c6.getTimePoint()).divide(2)), "gap"); - final NavigableSet result = filter.getTimeWiseContiguousDistanceCandidates(competitorCandidates, middleOfGap, + final NavigableSet result = filter.getTimeWiseContiguousCandidates(competitorCandidates, middleOfGap, /* includeStartFrom */ true); assertContainsExactly(result, c1, c2, c3, c4, c5, middleOfGap, c6, c7, c8, c9, c10); } @@ -46,7 +46,7 @@ public class MostProbableCandidatesInSmallTimeRangeFilterTest extends AbstractCa @Test public void testGetTimeWiseContiguousDistanceCandidatesInMiddleOfGapNotInsertedDontIncludeStartFrom() { final Candidate middleOfGap = candidate(c5.getTimePoint().plus(c5.getTimePoint().until(c6.getTimePoint()).divide(2)), "gap"); - final NavigableSet result = filter.getTimeWiseContiguousDistanceCandidates(competitorCandidates, middleOfGap, + final NavigableSet result = filter.getTimeWiseContiguousCandidates(competitorCandidates, middleOfGap, /* includeStartFrom */ false); assertContainsExactly(result, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10); } @@ -55,7 +55,7 @@ public class MostProbableCandidatesInSmallTimeRangeFilterTest extends AbstractCa public void testGetTimeWiseContiguousDistanceCandidatesInMiddleOfGapInserted() { final Candidate middleOfGap = candidate(c5.getTimePoint().plus(c5.getTimePoint().until(c6.getTimePoint()).divide(2)), "gap"); competitorCandidates.add(middleOfGap); - final NavigableSet result = filter.getTimeWiseContiguousDistanceCandidates(competitorCandidates, middleOfGap, + final NavigableSet result = filter.getTimeWiseContiguousCandidates(competitorCandidates, middleOfGap, /* includeStartFrom */ true); assertContainsExactly(result, c1, c2, c3, c4, c5, middleOfGap, c6, c7, c8, c9, c10); } @@ -65,7 +65,7 @@ public class MostProbableCandidatesInSmallTimeRangeFilterTest extends AbstractCa competitorCandidates.remove(c5); competitorCandidates.remove(c6); final Candidate middleOfGap = candidate(c5.getTimePoint().plus(c5.getTimePoint().until(c6.getTimePoint()).divide(2)), "gap"); - final NavigableSet result = filter.getTimeWiseContiguousDistanceCandidates(competitorCandidates, middleOfGap, + final NavigableSet result = filter.getTimeWiseContiguousCandidates(competitorCandidates, middleOfGap, /* includeStartFrom */ true); assertContainsExactly(result, middleOfGap); } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/Candidate.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/Candidate.java index b3e60dec544..a3f12426eb4 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/Candidate.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/Candidate.java @@ -20,6 +20,13 @@ import com.sap.sse.common.Timed; public interface Candidate extends Comparable, Timed { Waypoint getWaypoint(); + + /** + * Is this a candidate for a fixed mark passing? + */ + default boolean isFixed() { + return false; + } int getOneBasedIndexOfWaypoint(); diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/impl/CandidateChooserImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/impl/CandidateChooserImpl.java index bf771b3f642..8fd60738224 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/impl/CandidateChooserImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/impl/CandidateChooserImpl.java @@ -474,7 +474,7 @@ public class CandidateChooserImpl implements CandidateChooser { public void setFixedPassing(Competitor c, Integer zeroBasedIndexOfWaypoint, TimePoint t) { LockUtil.lockForWrite(perCompetitorLocks.get(c)); try { - Candidate fixedCan = new CandidateImpl(zeroBasedIndexOfWaypoint + 1, t, 1, Util.get(race.getRace().getCourse().getWaypoints(), zeroBasedIndexOfWaypoint)); + Candidate fixedCan = new CandidateForFixedMarkPassingImpl(zeroBasedIndexOfWaypoint + 1, t, 1, Util.get(race.getRace().getCourse().getWaypoints(), zeroBasedIndexOfWaypoint)); NavigableSet fixed = fixedPassings.get(c); if (fixed != null) { // can only set the mark passing if the competitor is still part of this race if (!fixed.add(fixedCan)) { diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/impl/CandidateForFixedMarkPassingImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/impl/CandidateForFixedMarkPassingImpl.java new file mode 100644 index 00000000000..7c1f9b3d268 --- /dev/null +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/impl/CandidateForFixedMarkPassingImpl.java @@ -0,0 +1,17 @@ +package com.sap.sailing.domain.markpassingcalculation.impl; + +import com.sap.sailing.domain.base.Waypoint; +import com.sap.sse.common.TimePoint; + +public class CandidateForFixedMarkPassingImpl extends CandidateImpl { + private static final long serialVersionUID = 3657502693032556232L; + + public CandidateForFixedMarkPassingImpl(int oneBasedIndexOfWaypoint, TimePoint p, double probability, Waypoint w) { + super(oneBasedIndexOfWaypoint, p, probability, w); + } + + @Override + public boolean isFixed() { + return true; + } +} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/impl/MostProbableCandidatesInSmallTimeRangeFilter.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/impl/MostProbableCandidatesInSmallTimeRangeFilter.java index 1e37227931e..10a450605bb 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/impl/MostProbableCandidatesInSmallTimeRangeFilter.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/impl/MostProbableCandidatesInSmallTimeRangeFilter.java @@ -102,7 +102,7 @@ public class MostProbableCandidatesInSmallTimeRangeFilter { while (!newCandidatesModifiableCopy.isEmpty()) { final Candidate nextNewCandidate = newCandidatesModifiableCopy.iterator().next(); newCandidatesModifiableCopy.remove(nextNewCandidate); - final NavigableSet contiguousSequenceForNextNewCandidate = getTimeWiseContiguousDistanceCandidates(competitorCandidates, nextNewCandidate, /* includeStartFrom */ true); + final NavigableSet contiguousSequenceForNextNewCandidate = getTimeWiseContiguousCandidates(competitorCandidates, nextNewCandidate, /* includeStartFrom */ true); disjointSequencesAffectedByNewAndRemovedCandidates.add(contiguousSequenceForNextNewCandidate); // remove the candidate grouped in the sequence from the remaining new candidates to consider // because they have been "scooped up" by this sequence already and need no further consideration: @@ -122,7 +122,7 @@ public class MostProbableCandidatesInSmallTimeRangeFilter { while (!removedCandidatesModifiableCopy.isEmpty()) { final Candidate nextRemovedCandidate = removedCandidatesModifiableCopy.iterator().next(); removedCandidatesModifiableCopy.remove(nextRemovedCandidate); - final NavigableSet contiguousSequenceForNextRemovedCandidate = getTimeWiseContiguousDistanceCandidates(competitorCandidates, nextRemovedCandidate, /* includeStartFrom */ false); + final NavigableSet contiguousSequenceForNextRemovedCandidate = getTimeWiseContiguousCandidates(competitorCandidates, nextRemovedCandidate, /* includeStartFrom */ false); removeAllRemovedCandidatesInOrNearSequence(contiguousSequenceForNextRemovedCandidate, removedCandidatesModifiableCopy); // If adjacent sequences were added before and after the removed candidate, // we need to distinguish whether the gap around the removed candidate was greater @@ -233,7 +233,7 @@ public class MostProbableCandidatesInSmallTimeRangeFilter { * @param includeStartFrom * whether or not to include {@code startFrom} in the resulting set */ - public NavigableSet getTimeWiseContiguousDistanceCandidates(NavigableSet competitorCandidates, Candidate startFrom, boolean includeStartFrom) { + public NavigableSet getTimeWiseContiguousCandidates(NavigableSet competitorCandidates, Candidate startFrom, boolean includeStartFrom) { final NavigableSet result = new TreeSet<>(candidateComparator); if (includeStartFrom) { result.add(startFrom); diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/impl/StationarySequence.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/impl/StationarySequence.java index 50e87a6c0b0..f479930b149 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/impl/StationarySequence.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/impl/StationarySequence.java @@ -89,7 +89,7 @@ public class StationarySequence { // borders of this sequence: final TimeRange timeRangeNoLongerPassingFilter = getExtendedEndBorderRange(extendedBy); if (timeRangeNoLongerPassingFilter != null) { - removeFromFilterResult(timeRangeNoLongerPassingFilter, candidatesEffectivelyAdded, candidatesEffectivelyRemoved); + removeFromFilterResultUnlessFixed(timeRangeNoLongerPassingFilter, candidatesEffectivelyAdded, candidatesEffectivelyRemoved); } } return bounds != null; @@ -158,7 +158,7 @@ public class StationarySequence { // borders of this sequence: final TimeRange timeRangeNoLongerPassingFilter = getExtendedStartBorderRange(extendedBy); if (timeRangeNoLongerPassingFilter != null) { - removeFromFilterResult(timeRangeNoLongerPassingFilter, candidatesEffectivelyAdded, candidatesEffectivelyRemoved); + removeFromFilterResultUnlessFixed(timeRangeNoLongerPassingFilter, candidatesEffectivelyAdded, candidatesEffectivelyRemoved); } } return bounds != null; @@ -204,11 +204,15 @@ public class StationarySequence { return result; } - private void removeFromFilterResult(final TimeRange timeRangeNoLongerPassingFilter, Set candidatesEffectivelyAdded, + private void removeFromFilterResultUnlessFixed(final TimeRange timeRangeNoLongerPassingFilter, Set candidatesEffectivelyAdded, Set candidatesEffectivelyRemoved) { final SortedSet candidatesNoLongerPassingFilter = getCandidatesInTimeRange(timeRangeNoLongerPassingFilter); - candidatesEffectivelyAdded.removeAll(candidatesNoLongerPassingFilter); - candidatesEffectivelyRemoved.addAll(candidatesNoLongerPassingFilter); + for (final Candidate candidate : candidatesNoLongerPassingFilter) { + if (!candidate.isFixed()) { + candidatesEffectivelyAdded.removeAll(candidatesNoLongerPassingFilter); + candidatesEffectivelyRemoved.addAll(candidatesNoLongerPassingFilter); + } + } } private SortedSet getCandidatesInTimeRange(final TimeRange timeRange) { @@ -219,7 +223,16 @@ public class StationarySequence { } public Iterable getValidCandidates() { - return ()->candidates.stream().filter(c->isCloseEnoughToSequenceBorder(c)).iterator(); + return ()->candidates.stream().filter(c->isValidCandidate(c)).iterator(); + } + + /** + * A candidate that is part of this stationary sequence is considered valid if it is time-wise close + * enough to one of the sequence's borders (see {@link #CANDIDATE_FILTER_TIME_WINDOW}) or if it is + * a candidate for a {@link Candidate#isFixed() fixed mark passing}. + */ + private boolean isValidCandidate(Candidate c) { + return c.isFixed() || isCloseEnoughToSequenceBorder(c); } private boolean isCloseEnoughToSequenceBorder(Candidate candidate) { @@ -246,7 +259,7 @@ public class StationarySequence { assert !candidates.contains(candidate) && !getFirst().getTimePoint().after(candidate.getTimePoint()) && !getLast().getTimePoint().before(candidate.getTimePoint()); candidates.add(candidate); - if (isCloseEnoughToSequenceBorder(candidate)) { + if (isValidCandidate(candidate)) { candidatesEffectivelyAdded.add(candidate); candidatesEffectivelyRemoved.remove(candidate); } @@ -269,7 +282,7 @@ public class StationarySequence { void remove(Candidate candidate, Set candidatesEffectivelyAdded, Set candidatesEffectivelyRemoved, NavigableSet stationarySequenceSetToUpdate) { assert candidates.contains(candidate); - final boolean wasValidCandidate = isCloseEnoughToSequenceBorder(candidate); + final boolean wasValidCandidate = isValidCandidate(candidate); final boolean wasFirst = candidate == getFirst(); final boolean wasLast = candidate == getLast(); if (wasFirst || size() == 2) { // if size() == 2 then it will shrink to 1 and this sequence shall be removed