diff --git a/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractConcatNode.java b/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractConcatNode.java index 31946fe0b50..a2c2028ee43 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractConcatNode.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractConcatNode.java @@ -66,6 +66,7 @@ protected boolean canProduceTuples() { @Override public final void insertLeft(LeftTuple_ tuple) { var outTuple = getOutTupleFromLeft(tuple); + outTuple.setIndictmentSource(tuple.getIndictmentSource()); tuple.setStore(leftSourceTupleCloneStoreIndex, outTuple); propagationQueue.insert(outTuple); } @@ -109,6 +110,7 @@ public final void retractLeft(LeftTuple_ tuple) { @Override public final void insertRight(RightTuple_ tuple) { var outTuple = getOutTupleFromRight(tuple); + outTuple.setIndictmentSource(tuple.getIndictmentSource()); tuple.setStore(rightSourceTupleCloneStoreIndex, outTuple); propagationQueue.insert(outTuple); } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractFlattenNode.java b/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractFlattenNode.java index b20fe59e1c2..16fdff5bc58 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractFlattenNode.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractFlattenNode.java @@ -73,6 +73,7 @@ private void addTuple(InTuple_ originalTuple, FlattenedItem_ item, var reuse = bag.reuseOrAdvance(); if (reuse == null) { var created = createTuple(originalTuple, bag.value); + created.setIndictmentSource(originalTuple.getIndictmentSource()); bag.append(created); propagationQueue.insert(created); } else { diff --git a/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractGroupNode.java b/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractGroupNode.java index 87c6ecc0e5e..0c588892276 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractGroupNode.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractGroupNode.java @@ -10,6 +10,7 @@ import ai.timefold.solver.core.impl.bavet.common.tuple.Tuple; import ai.timefold.solver.core.impl.bavet.common.tuple.TupleLifecycle; import ai.timefold.solver.core.impl.bavet.common.tuple.TupleState; +import ai.timefold.solver.core.impl.bavet.common.tuple.indictment.IndictmentSource; public abstract class AbstractGroupNode extends AbstractSingleInputNode { @@ -110,6 +111,7 @@ private void createTuple(InTuple_ tuple, GroupKey_ userSuppliedKey) { } tuple.setStore(groupStoreIndex, group); var outTuple = group.getTuple(); + outTuple.setIndictmentSource(IndictmentSource.aggregating(tuple, outTuple)); switch (outTuple.getState()) { case CREATING, UPDATING -> { // Already in the correct state. @@ -190,6 +192,8 @@ public final void update(InTuple_ tuple) { if (sameKey) { updateGroup(tuple, oldGroup); } else { + var oldOutTuple = oldGroup.getTuple(); + oldOutTuple.setIndictmentSource(IndictmentSource.removeFromAggregate(tuple, oldOutTuple)); if (hasCollector) { groupRetract(tuple); } @@ -274,6 +278,8 @@ public final void retract(InTuple_ tuple) { // No fail fast if null because we don't track which tuples made it through the filter predicate(s) return; } + var oldOutTuple = group.getTuple(); + oldOutTuple.setIndictmentSource(IndictmentSource.removeFromAggregate(tuple, oldOutTuple)); if (hasCollector) { groupRetract(tuple); } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractIfExistsNode.java b/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractIfExistsNode.java index cb57dd83d0b..9c54c27001d 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractIfExistsNode.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractIfExistsNode.java @@ -5,6 +5,7 @@ import ai.timefold.solver.core.impl.bavet.common.tuple.TupleLifecycle; import ai.timefold.solver.core.impl.bavet.common.tuple.TupleState; import ai.timefold.solver.core.impl.bavet.common.tuple.UniTuple; +import ai.timefold.solver.core.impl.bavet.common.tuple.indictment.IndictmentSource; import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; @@ -115,6 +116,20 @@ protected void incrementCounterRight(ExistsCounter counter) { counter.countRight++; } + protected void incrementCounterRightUpdatingIndictment(ExistsCounter counter, UniTuple rightTuple) { + IndictmentSource.addSupport(getId(), counter.getTuple(), rightTuple); + if (counter.countRight == 0) { + if (shouldExist) { + doInsertCounter(counter); + } else { + doRetractCounter(counter); + } + } else if (shouldExist) { + doUpdateCounter(counter); + } + counter.countRight++; + } + protected void decrementCounterRight(ExistsCounter counter) { counter.countRight--; if (counter.countRight == 0) { @@ -126,6 +141,20 @@ protected void decrementCounterRight(ExistsCounter counter) { } // Else do not even propagate an update } + protected void decrementCounterRightUpdatingIndictment(ExistsCounter counter, UniTuple rightTuple) { + IndictmentSource.removeSupport(getId(), counter.getTuple(), rightTuple); + counter.countRight--; + if (counter.countRight == 0) { + if (shouldExist) { + doRetractCounter(counter); + } else { + doInsertCounter(counter); + } + } else if (shouldExist) { + doUpdateCounter(counter); + } + } + // Clears the left tracker list rooted at leftTuple's inputStoreIndexLeftTrackerList slot, // cross-removing each tracker from its right tuple's hidden list. No-op when !isFiltering. // Walk safety: removeFromRight only touches right-side links, so leftNext is stable across the call. @@ -164,11 +193,20 @@ private void removeRight(FilteringTracker tracker) { // Walk safety: removeFromLeft only touches left-side links, so rightNext is stable across the call. protected void clearRightTrackerList(UniTuple rightTuple) { FilteringTracker tracker = rightTuple.removeStore(inputStoreIndexRightTrackerList); - while (tracker != null) { - var next = tracker.rightNext; - decrementCounterRight(tracker.counter); - removeLeft(tracker); - tracker = next; + if (rightTuple.getIndictmentSource() == IndictmentSource.DISABLED) { + while (tracker != null) { + var next = tracker.rightNext; + decrementCounterRight(tracker.counter); + removeLeft(tracker); + tracker = next; + } + } else { + while (tracker != null) { + var next = tracker.rightNext; + decrementCounterRightUpdatingIndictment(tracker.counter, rightTuple); + removeLeft(tracker); + tracker = next; + } } } @@ -196,13 +234,17 @@ protected void updateCounterLeft(ExistsCounter counter, UniTuple(counter, rightTuple); linkLeft(tracker); linkRight(tracker); + } else { + IndictmentSource.removeSupport(getId(), counter.getTuple(), rightTuple); } } @@ -247,13 +289,17 @@ protected void updateCounterRight(ExistsCounter counter, UniTuple(counter, rightTuple); linkLeft(tracker); linkRight(tracker); + } else { + IndictmentSource.removeSupport(getId(), counter.getTuple(), rightTuple); } } @@ -266,6 +312,21 @@ private void doInsertCounter(ExistsCounter counter) { } } + private void doUpdateCounter(ExistsCounter counter) { + switch (counter.state) { + case CREATING, UPDATING -> { + // Do nothing + } + case OK -> { + propagationQueue.update(counter); + } + case DYING, DEAD, ABORTING -> { + throw new IllegalStateException("Impossible state: The counter (%s) has an impossible retract state (%s)." + .formatted(counter, counter.state)); + } + } + } + private void doRetractCounter(ExistsCounter counter) { switch (counter.state) { case CREATING -> // Kill it before it propagates. @@ -323,4 +384,4 @@ protected static final class FilteringTracker { } -} +} \ No newline at end of file diff --git a/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractIndexedIfExistsNode.java b/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractIndexedIfExistsNode.java index 7e27a733fbf..70d21539be6 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractIndexedIfExistsNode.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractIndexedIfExistsNode.java @@ -14,6 +14,7 @@ import ai.timefold.solver.core.impl.bavet.common.tuple.Tuple; import ai.timefold.solver.core.impl.bavet.common.tuple.TupleLifecycle; import ai.timefold.solver.core.impl.bavet.common.tuple.UniTuple; +import ai.timefold.solver.core.impl.bavet.common.tuple.indictment.IndictmentSource; import ai.timefold.solver.core.impl.util.ListEntry; import org.jspecify.annotations.Nullable; @@ -99,6 +100,12 @@ private void updateCounterRight(LeftTuple_ leftTuple, Object compositeKey, Exist leftTuple.setStore(inputStoreIndexLeftCounterEntry, counterEntry); if (!isFiltering) { counter.countRight = rightSize(leftTuple, compositeKey); + if (leftTuple.getIndictmentSource() != IndictmentSource.DISABLED) { + leftTuple.getIndictmentSource().getSupportForNodeId(getId()).clear(); + forEachRightFromLeft(leftTuple, compositeKey, rightTuple -> { + IndictmentSource.addSupport(getId(), leftTuple, rightTuple); + }); + } } else { // Trackers link themselves into the left tuple's inputStoreIndexLeftTrackerList slot. // No list object is needed; the slot starts null and the first tracker becomes the head. @@ -203,7 +210,12 @@ public final void insertRight(UniTuple rightTuple) { private void updateCounterLeft(UniTuple rightTuple, Object compositeKey) { if (!isFiltering) { - forEachLeftCounter(rightTuple, compositeKey, this::incrementCounterRight); + if (rightTuple.getIndictmentSource() == IndictmentSource.DISABLED) { + forEachLeftCounter(rightTuple, compositeKey, this::incrementCounterRight); + } else { + forEachLeftCounter(rightTuple, compositeKey, + counter -> incrementCounterRightUpdatingIndictment(counter, rightTuple)); + } } else { // Trackers link themselves into the right tuple's inputStoreIndexRightTrackerList slot. // No list object is needed; the slot starts null and the first tracker becomes the head. @@ -265,7 +277,12 @@ public final void retractRight(UniTuple rightTuple) { bucket.removeRight(compositeKey, entry); fusedEqualIndex.removeBucketIfEmpty(compositeKey, bucket); if (!isFiltering) { - bucket.forEachLeft(compositeKey, this::decrementCounterRight); + if (rightTuple.getIndictmentSource() == IndictmentSource.DISABLED) { + bucket.forEachLeft(compositeKey, this::decrementCounterRight); + } else { + bucket.forEachLeft(compositeKey, + leftTuple -> decrementCounterRightUpdatingIndictment(leftTuple, rightTuple)); + } } else { clearRightTrackerList(rightTuple); } @@ -342,4 +359,4 @@ private void forEachLeftCounter(UniTuple rightTuple, Object compositeKey } } -} +} \ No newline at end of file diff --git a/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractJoinNode.java b/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractJoinNode.java index 6edbe0b583f..40bdd719a37 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractJoinNode.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractJoinNode.java @@ -10,6 +10,7 @@ import ai.timefold.solver.core.impl.bavet.common.tuple.TupleList; import ai.timefold.solver.core.impl.bavet.common.tuple.TupleState; import ai.timefold.solver.core.impl.bavet.common.tuple.UniTuple; +import ai.timefold.solver.core.impl.bavet.common.tuple.indictment.IndictmentSource; import org.jspecify.annotations.Nullable; @@ -110,6 +111,7 @@ private void insertOutTupleIfActiveFiltered(LeftTuple_ leftTuple, UniTuple rightTuple) { var outTuple = createOutTuple(leftTuple, rightTuple); + outTuple.setIndictmentSource(IndictmentSource.joining(leftTuple, rightTuple)); TupleList outTupleListLeft = leftTuple.getStore(inputStoreIndexLeftOutTupleList); outTupleListLeft.add(outTuple); outTuple.setStore(outputStoreIndexLeftOutTupleList, outTupleListLeft); diff --git a/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractMapNode.java b/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractMapNode.java index 6fbc4ed2a12..394354f5af6 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractMapNode.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractMapNode.java @@ -30,6 +30,7 @@ public final void insert(InTuple_ tuple) { + ") was already added in the tupleStore."); } var outTuple = map(tuple); + outTuple.setIndictmentSource(tuple.getIndictmentSource()); tuple.setStore(inputStoreIndex, outTuple); propagationQueue.insert(outTuple); } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractNodeBuildHelper.java b/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractNodeBuildHelper.java index 4543fc0d978..188ebc9a3ff 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractNodeBuildHelper.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractNodeBuildHelper.java @@ -24,6 +24,7 @@ public abstract class AbstractNodeBuildHelper { private final Set activeStreamSet; private final Map nodeCreatorMap; + private final Map streamToCreatedNodeMap; private final Map> tupleLifecycleMap; private final Map storeIndexMap; @@ -34,6 +35,7 @@ protected AbstractNodeBuildHelper(Set activeStreamSet) { this.activeStreamSet = activeStreamSet; int activeStreamSetSize = activeStreamSet.size(); this.nodeCreatorMap = HashMap.newHashMap(Math.max(16, activeStreamSetSize)); + this.streamToCreatedNodeMap = HashMap.newHashMap(Math.max(16, activeStreamSetSize)); this.tupleLifecycleMap = HashMap.newHashMap(Math.max(16, activeStreamSetSize)); this.storeIndexMap = HashMap.newHashMap(Math.max(16, activeStreamSetSize / 2)); this.reversedNodeList = new ArrayList<>(activeStreamSetSize); @@ -51,6 +53,7 @@ public void addNode(AbstractNode node, Stream_ creator, @Nullable Stream_ parent reversedNodeList.add(node); node.addLocationSet(creator.getLocationSet()); nodeCreatorMap.put(node, creator); + streamToCreatedNodeMap.put(creator, node); if (!(node instanceof AbstractRootNode)) { if (parent == null) { throw new IllegalStateException("Impossible state: The node (%s) has no parent (%s).".formatted(node, parent)); @@ -63,6 +66,7 @@ public void addNode(AbstractNode node, Stream_ creator, Stream_ leftParent, Stre reversedNodeList.add(node); node.addLocationSet(creator.getLocationSet()); nodeCreatorMap.put(node, creator); + streamToCreatedNodeMap.put(creator, node); putInsertUpdateRetract(leftParent, TupleLifecycle.ofLeft((LeftTupleLifecycle) node)); putInsertUpdateRetract(rightParent, TupleLifecycle.ofRight((RightTupleLifecycle) node)); } @@ -143,6 +147,24 @@ public Stream_ getNodeCreatingStream(AbstractNode node) { return nodeCreatorMap.get(node); } + public List getParentNodeList(Stream_ stream) { + var out = new ArrayList(); + while (stream != null) { + var node = streamToCreatedNodeMap.get(stream); + if (node != null) { + out.add(node); + } + if (stream instanceof BavetStreamBinaryOperation binaryOperation) { + out.addAll(getParentNodeList((Stream_) binaryOperation.getLeftParent())); + out.addAll(getParentNodeList((Stream_) binaryOperation.getRightParent())); + return out; + } else { + stream = stream.getParent(); + } + } + return out; + } + public AbstractNode findParentNode(Stream_ childNodeCreator) { if (childNodeCreator == null) { // We've recursed to the bottom without finding a parent node. throw new IllegalStateException( diff --git a/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractUnindexedIfExistsNode.java b/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractUnindexedIfExistsNode.java index b0a2224f260..012931f68c8 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractUnindexedIfExistsNode.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/AbstractUnindexedIfExistsNode.java @@ -6,6 +6,7 @@ import ai.timefold.solver.core.impl.bavet.common.tuple.Tuple; import ai.timefold.solver.core.impl.bavet.common.tuple.TupleLifecycle; import ai.timefold.solver.core.impl.bavet.common.tuple.UniTuple; +import ai.timefold.solver.core.impl.bavet.common.tuple.indictment.IndictmentSource; import ai.timefold.solver.core.impl.util.ElementAwareLinkedList; /** @@ -103,7 +104,11 @@ public final void insertRight(UniTuple rightTuple) { } rightTuple.setStore(inputStoreIndexRightEntry, rightTupleList.add(rightTuple)); if (!isFiltering) { - counterList.forEach(this::incrementCounterRight); + if (rightTuple.getIndictmentSource() == IndictmentSource.DISABLED) { + counterList.forEach(this::incrementCounterRight); + } else { + counterList.forEach(counter -> incrementCounterRightUpdatingIndictment(counter, rightTuple)); + } } else { // Trackers link themselves into the right tuple's inputStoreIndexRightTrackerList slot. // No list object is needed; the slot starts null and the first tracker becomes the head. @@ -138,10 +143,14 @@ public final void retractRight(UniTuple rightTuple) { } rightEntry.remove(); if (!isFiltering) { - counterList.forEach(this::decrementCounterRight); + if (rightTuple.getIndictmentSource() == IndictmentSource.DISABLED) { + counterList.forEach(this::decrementCounterRight); + } else { + counterList.forEach(counter -> decrementCounterRightUpdatingIndictment(counter, rightTuple)); + } } else { clearRightTrackerList(rightTuple); } } -} +} \ No newline at end of file diff --git a/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/ExistsCounter.java b/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/ExistsCounter.java index 0b69b862fb4..db646736653 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/ExistsCounter.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/ExistsCounter.java @@ -31,7 +31,7 @@ public void setState(TupleState state) { @Override public String toString() { - return "Counter(" + leftTuple + ")"; + return "Counter(%s)".formatted(leftTuple); } } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/TupleRecorder.java b/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/TupleRecorder.java index c2fab90d528..2ae88ccdb76 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/TupleRecorder.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/TupleRecorder.java @@ -1,10 +1,13 @@ package ai.timefold.solver.core.impl.bavet.common; +import java.util.ArrayList; import java.util.IdentityHashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.function.UnaryOperator; import ai.timefold.solver.core.impl.bavet.common.tuple.Tuple; +import ai.timefold.solver.core.impl.bavet.common.tuple.indictment.IndictmentSource; import org.jspecify.annotations.NullMarked; @@ -13,6 +16,18 @@ public record TupleRecorder(List recordedTupleList UnaryOperator mapper, IdentityHashMap inputTupleToOutputTuple) { public void recordTuple(Tuple_ tuple) { - recordedTupleList.add(inputTupleToOutputTuple.computeIfAbsent(tuple, mapper)); + var outTuple = inputTupleToOutputTuple.computeIfAbsent(tuple, mapper); + if (tuple.getIndictmentSource() != IndictmentSource.DISABLED) { + if (outTuple.getIndictmentSource() == IndictmentSource.DISABLED) { + outTuple.setIndictmentSource( + new IndictmentSource.AggregateIndictmentSource(new ArrayList<>(), new LinkedHashMap<>())); + } + // Precompute uses an independent node network, so we need to aggregate its supports to not interfere with the + // outer node network support + var aggregateIndictmentSource = (IndictmentSource.AggregateIndictmentSource) outTuple.getIndictmentSource(); + tuple.getIndictmentSource() + .visitAllSources(source -> aggregateIndictmentSource.sourceList().add(IndictmentSource.of(source))); + } + recordedTupleList.add(outTuple); } } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/tuple/Tuple.java b/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/tuple/Tuple.java index c4b03d67f33..31421c67e85 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/tuple/Tuple.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/tuple/Tuple.java @@ -3,6 +3,7 @@ import java.util.function.Function; import ai.timefold.solver.core.api.score.stream.uni.UniConstraintStream; +import ai.timefold.solver.core.impl.bavet.common.tuple.indictment.IndictmentSource; import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; @@ -23,6 +24,10 @@ @NullMarked public sealed interface Tuple permits BiTuple, QuadTuple, TriTuple, UniTuple { + IndictmentSource getIndictmentSource(); + + void setIndictmentSource(IndictmentSource indictmentSource); + TupleState getState(); void setState(TupleState state); diff --git a/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/tuple/UniversalTuple.java b/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/tuple/UniversalTuple.java index 5efa7381d24..63cadf46844 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/tuple/UniversalTuple.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/tuple/UniversalTuple.java @@ -1,5 +1,7 @@ package ai.timefold.solver.core.impl.bavet.common.tuple; +import ai.timefold.solver.core.impl.bavet.common.tuple.indictment.IndictmentSource; + import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; @@ -20,6 +22,7 @@ final class UniversalTuple private @Nullable B b; private @Nullable C c; private @Nullable D d; + private IndictmentSource indictmentSource = IndictmentSource.DISABLED; private TupleState state = TupleState.DEAD; // It's the node's job to mark a new tuple as CREATING. UniversalTuple(int storeSize, int cardinality) { @@ -27,6 +30,14 @@ final class UniversalTuple this.store = storeSize > 0 ? new Object[storeSize] : EMPTY_STORE; } + int cardinality() { + return cardinality; + } + + int storeSize() { + return store.length; + } + @Override public void setA(@Nullable A a) { this.a = a; @@ -95,6 +106,16 @@ public void setStore(int index, @Nullable Object value) { return value; } + @Override + public IndictmentSource getIndictmentSource() { + return indictmentSource; + } + + @Override + public void setIndictmentSource(IndictmentSource indictmentSource) { + this.indictmentSource = indictmentSource; + } + @Override public String toString() { return switch (cardinality) { diff --git a/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/tuple/indictment/IndictmentSource.java b/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/tuple/indictment/IndictmentSource.java new file mode 100644 index 00000000000..69f565f2421 --- /dev/null +++ b/core/src/main/java/ai/timefold/solver/core/impl/bavet/common/tuple/indictment/IndictmentSource.java @@ -0,0 +1,205 @@ +package ai.timefold.solver.core.impl.bavet.common.tuple.indictment; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Consumer; + +import ai.timefold.solver.core.impl.bavet.common.tuple.Tuple; + +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +@NullMarked +public sealed interface IndictmentSource { + IndictmentSource DISABLED = new DisabledIndictmentSource(); + + void visitSources(Set visited, long @Nullable [] involvedNodeIds, Consumer sourceConsumer); + + Map> support(); + + default void visitAllSources(Consumer sourceConsumer) { + visitSources(new HashSet<>(), null, sourceConsumer); + } + + default void visitSources(long[] involvedNodeIds, Consumer sourceConsumer) { + visitSources(new HashSet<>(), involvedNodeIds, sourceConsumer); + } + + default Set getSupportForNodeId(long nodeId) { + return support().computeIfAbsent(nodeId, ignored -> new LinkedHashSet<>()); + } + + static boolean checkIfAlreadyVisitedAndVisitSupport(IndictmentSource self, Set visited, + long @Nullable [] involvedNodeIds, Consumer sourceConsumer) { + if (!visited.add(self)) { + return true; + } + + if (involvedNodeIds == null) { + for (var indictmentSourceSet : self.support().values()) { + for (var indictmentSource : indictmentSourceSet) { + indictmentSource.visitSources(visited, null, sourceConsumer); + } + } + } else { + for (var nodeId : involvedNodeIds) { + for (var indictmentSource : self.support().getOrDefault(nodeId, Collections.emptySet())) { + indictmentSource.visitSources(visited, involvedNodeIds, sourceConsumer); + } + } + } + return false; + } + + static IndictmentSource of(Object source) { + return new RootIndictmentSource(source, new LinkedHashMap<>()); + } + + static IndictmentSource joining(Tuple left, Tuple right) { + if (left.getIndictmentSource() == DISABLED) { + return DISABLED; + } + return new JoinedIndictmentSource(left.getIndictmentSource(), right.getIndictmentSource(), new LinkedHashMap<>()); + } + + static IndictmentSource aggregating(Tuple elementTuple, Tuple groupTuple) { + if (elementTuple.getIndictmentSource() == DISABLED) { + return DISABLED; + } + if (groupTuple.getIndictmentSource() instanceof AggregateIndictmentSource aggregateIndictmentSource) { + aggregateIndictmentSource.sourceList.add(elementTuple.getIndictmentSource()); + return aggregateIndictmentSource; + } else { + var collection = new ArrayList(); + collection.add(elementTuple.getIndictmentSource()); + return new AggregateIndictmentSource(collection, new LinkedHashMap<>()); + } + } + + static IndictmentSource removeFromAggregate(Tuple elementTuple, Tuple groupTuple) { + if (elementTuple.getIndictmentSource() == DISABLED) { + return DISABLED; + } + if (groupTuple.getIndictmentSource() instanceof AggregateIndictmentSource aggregateIndictmentSource) { + aggregateIndictmentSource.sourceList.remove(elementTuple.getIndictmentSource()); + return aggregateIndictmentSource; + } else { + var collection = new ArrayList(); + collection.add(elementTuple.getIndictmentSource()); + return new AggregateIndictmentSource(collection, new LinkedHashMap<>()); + } + } + + static void addSupport(long nodeId, Tuple carry, Tuple support) { + if (carry.getIndictmentSource() == DISABLED) { + return; + } + carry.getIndictmentSource().getSupportForNodeId(nodeId).add(support.getIndictmentSource()); + } + + static void clearSupport(long nodeId, Tuple carry) { + if (carry.getIndictmentSource() == DISABLED) { + return; + } + carry.getIndictmentSource().getSupportForNodeId(nodeId).clear(); + } + + static void removeSupport(long nodeId, Tuple carry, Tuple support) { + if (carry.getIndictmentSource() == DISABLED) { + return; + } + carry.getIndictmentSource().getSupportForNodeId(nodeId).remove(support.getIndictmentSource()); + } + + record DisabledIndictmentSource() implements IndictmentSource { + @Override + public void visitSources(Set visited, long @Nullable [] involvedNodeIds, + Consumer sourceConsumer) { + throw new UnsupportedOperationException("Impossible state: indictments are disabled."); + } + + @Override + public Map> support() { + throw new UnsupportedOperationException("Impossible state: indictments are disabled."); + } + } + + record RootIndictmentSource(Object source, Map> support) implements IndictmentSource { + @Override + public void visitSources(Set visited, long @Nullable [] involvedNodeIds, + Consumer sourceConsumer) { + if (checkIfAlreadyVisitedAndVisitSupport(this, visited, involvedNodeIds, sourceConsumer)) { + return; + } + sourceConsumer.accept(source); + } + + @Override + public boolean equals(Object o) { + return this == o; + } + + @Override + public int hashCode() { + return System.identityHashCode(this); + } + } + + record JoinedIndictmentSource(IndictmentSource left, IndictmentSource right, + Map> support) implements IndictmentSource { + @Override + public void visitSources(Set visited, long @Nullable [] involvedNodeIds, + Consumer sourceConsumer) { + if (checkIfAlreadyVisitedAndVisitSupport(this, visited, involvedNodeIds, sourceConsumer)) { + return; + } + left.visitSources(visited, involvedNodeIds, sourceConsumer); + right.visitSources(visited, involvedNodeIds, sourceConsumer); + } + + @Override + public boolean equals(Object o) { + return this == o; + } + + @Override + public int hashCode() { + return System.identityHashCode(this); + } + } + + record AggregateIndictmentSource(List sourceList, + Map> support) implements IndictmentSource { + @Override + public void visitSources(Set visited, long @Nullable [] involvedNodeIds, + Consumer sourceConsumer) { + if (checkIfAlreadyVisitedAndVisitSupport(this, visited, involvedNodeIds, sourceConsumer)) { + return; + } + for (var source : sourceList) { + source.visitSources(visited, involvedNodeIds, sourceConsumer); + } + } + + @Override + public Set getSupportForNodeId(long nodeId) { + return support.computeIfAbsent(nodeId, ignored -> new LinkedHashSet<>()); + } + + @Override + public boolean equals(Object o) { + return this == o; + } + + @Override + public int hashCode() { + return System.identityHashCode(this); + } + } +} diff --git a/core/src/main/java/ai/timefold/solver/core/impl/bavet/uni/AbstractForEachUniNode.java b/core/src/main/java/ai/timefold/solver/core/impl/bavet/uni/AbstractForEachUniNode.java index a2843c24ea9..cfb61d0607f 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/bavet/uni/AbstractForEachUniNode.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/bavet/uni/AbstractForEachUniNode.java @@ -11,6 +11,7 @@ import ai.timefold.solver.core.impl.bavet.common.tuple.TupleLifecycle; import ai.timefold.solver.core.impl.bavet.common.tuple.TupleState; import ai.timefold.solver.core.impl.bavet.common.tuple.UniTuple; +import ai.timefold.solver.core.impl.bavet.common.tuple.indictment.IndictmentSource; import org.jspecify.annotations.NullMarked; import org.jspecify.annotations.Nullable; @@ -31,12 +32,14 @@ public abstract sealed class AbstractForEachUniNode private final Class forEachClass; private final int outputStoreSize; private final StaticPropagationQueue> propagationQueue; + private final boolean indictmentsEnabled; protected final Map> tupleMap = HashMap.newHashMap(1000); protected AbstractForEachUniNode(Class forEachClass, TupleLifecycle> nextNodesTupleLifecycle, - int outputStoreSize) { + boolean indictmentsEnabled, int outputStoreSize) { this.forEachClass = forEachClass; this.outputStoreSize = outputStoreSize; + this.indictmentsEnabled = indictmentsEnabled; this.propagationQueue = new StaticPropagationQueue<>(nextNodesTupleLifecycle); } @@ -58,6 +61,9 @@ public Class[] getSourceClasses() { @Override public void insert(@Nullable A a) { var tuple = UniTuple.of(a, outputStoreSize); + if (indictmentsEnabled) { + tuple.setIndictmentSource(IndictmentSource.of(a)); + } var old = tupleMap.put(a, tuple); if (old != null) { throw new IllegalStateException(""" diff --git a/core/src/main/java/ai/timefold/solver/core/impl/bavet/uni/ForEachFilteredUniNode.java b/core/src/main/java/ai/timefold/solver/core/impl/bavet/uni/ForEachFilteredUniNode.java index 015042f2d97..18533d8acfc 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/bavet/uni/ForEachFilteredUniNode.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/bavet/uni/ForEachFilteredUniNode.java @@ -24,8 +24,8 @@ public final class ForEachFilteredUniNode private int tupleCountWithoutFiltering = 0; public ForEachFilteredUniNode(Class forEachClass, Predicate filter, - TupleLifecycle> nextNodesTupleLifecycle, int outputStoreSize) { - super(forEachClass, nextNodesTupleLifecycle, outputStoreSize); + TupleLifecycle> nextNodesTupleLifecycle, boolean indictmentsEnabled, int outputStoreSize) { + super(forEachClass, nextNodesTupleLifecycle, indictmentsEnabled, outputStoreSize); this.nextNodesTupleLifecycle = Objects.requireNonNull(nextNodesTupleLifecycle); this.filter = Objects.requireNonNull(filter); } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/bavet/uni/ForEachUnfilteredUniNode.java b/core/src/main/java/ai/timefold/solver/core/impl/bavet/uni/ForEachUnfilteredUniNode.java index fb40cb58be0..46198e99ca8 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/bavet/uni/ForEachUnfilteredUniNode.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/bavet/uni/ForEachUnfilteredUniNode.java @@ -15,8 +15,8 @@ public final class ForEachUnfilteredUniNode private boolean isActive; public ForEachUnfilteredUniNode(Class forEachClass, TupleLifecycle> nextNodesTupleLifecycle, - int outputStoreSize) { - super(forEachClass, nextNodesTupleLifecycle, outputStoreSize); + boolean indictmentsEnabled, int outputStoreSize) { + super(forEachClass, nextNodesTupleLifecycle, indictmentsEnabled, outputStoreSize); this.nextNodesTupleLifecycle = nextNodesTupleLifecycle; } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/neighborhood/stream/enumerating/uni/AbstractForEachEnumeratingStream.java b/core/src/main/java/ai/timefold/solver/core/impl/neighborhood/stream/enumerating/uni/AbstractForEachEnumeratingStream.java index ebb8cf59945..cf301f81438 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/neighborhood/stream/enumerating/uni/AbstractForEachEnumeratingStream.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/neighborhood/stream/enumerating/uni/AbstractForEachEnumeratingStream.java @@ -41,7 +41,7 @@ public final void collectActiveEnumeratingStreams(Set buildHelper) { TupleLifecycle> tupleLifecycle = buildHelper.getAggregatedTupleLifecycle(childStreamList); var outputStoreSize = buildHelper.extractTupleStoreSize(this); - var node = new ForEachUnfilteredUniNode<>(forEachClass, tupleLifecycle, outputStoreSize); + var node = new ForEachUnfilteredUniNode<>(forEachClass, tupleLifecycle, false, outputStoreSize); if (shouldIncludeNull && node.supports(LifecycleOperation.INSERT)) { node.insert(null); } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/constraint/ConstraintMatch.java b/core/src/main/java/ai/timefold/solver/core/impl/score/constraint/ConstraintMatch.java index 25a9746b816..f977608580e 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/constraint/ConstraintMatch.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/constraint/ConstraintMatch.java @@ -2,6 +2,8 @@ import static java.util.Objects.requireNonNull; +import java.util.List; + import ai.timefold.solver.core.api.score.Score; import ai.timefold.solver.core.api.score.stream.ConstraintJustification; import ai.timefold.solver.core.api.score.stream.ConstraintRef; @@ -28,6 +30,7 @@ public final class ConstraintMatch> implements Comp private final ConstraintRef constraintRef; private final @Nullable ConstraintJustification justification; + private final @Nullable List indictedObjects; private final Score_ score; /** @@ -35,9 +38,11 @@ public final class ConstraintMatch> implements Comp * @param justification only null if justifications are disabled * @param score penalty or reward associated with the constraint match */ - public ConstraintMatch(ConstraintRef constraintRef, @Nullable ConstraintJustification justification, Score_ score) { + public ConstraintMatch(ConstraintRef constraintRef, @Nullable ConstraintJustification justification, + @Nullable List indictedObjects, Score_ score) { this.constraintRef = requireNonNull(constraintRef); this.justification = justification; + this.indictedObjects = indictedObjects; this.score = requireNonNull(score); } @@ -62,6 +67,10 @@ public ConstraintRef getConstraintRef() { return (Justification_) justification; } + public @Nullable List getIndictedObjects() { + return indictedObjects; + } + public Score_ getScore() { return score; } @@ -89,7 +98,8 @@ public int compareTo(ConstraintMatch other) { @Override public String toString() { - return "%s/%s=%s".formatted(getConstraintRef().id(), justification, score); + return "%s/%s=%s (indicting %s)".formatted(getConstraintRef().id(), justification, score, + indictedObjects); } } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/constraint/ConstraintMatchPolicy.java b/core/src/main/java/ai/timefold/solver/core/impl/score/constraint/ConstraintMatchPolicy.java index f239ad113da..bd963663f6d 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/constraint/ConstraintMatchPolicy.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/constraint/ConstraintMatchPolicy.java @@ -13,33 +13,37 @@ @NullMarked public enum ConstraintMatchPolicy { - DISABLED(false, false), - ENABLED_WITHOUT_JUSTIFICATIONS(true, false), - ENABLED(true, true); + DISABLED(false, false, false), + ENABLED_WITHOUT_JUSTIFICATIONS_AND_INDICTMENTS(true, false, false), + ENABLED_WITHOUT_JUSTIFICATIONS(true, false, true), + ENABLED_WITHOUT_INDICTMENTS(true, true, false), + ENABLED(true, true, true); /** * To achieve the most performance out of the underlying solver, * the policy should match whatever policy was used for score analysis. * For example, if the fetch policy specifies that only match counts are necessary and not matches themselves * ({@link ScoreAnalysisFetchPolicy#FETCH_MATCH_COUNT}), - * we can configure the solver to not produce justifications ({@link #ENABLED_WITHOUT_JUSTIFICATIONS}). + * we can configure the solver to not produce justifications ({@link #ENABLED_WITHOUT_JUSTIFICATIONS_AND_INDICTMENTS}). * * @param scoreAnalysisFetchPolicy * @return Match policy best suited for the given fetch policy. */ public static ConstraintMatchPolicy match(ScoreAnalysisFetchPolicy scoreAnalysisFetchPolicy) { return switch (scoreAnalysisFetchPolicy) { - case FETCH_MATCH_COUNT, FETCH_SHALLOW -> ENABLED_WITHOUT_JUSTIFICATIONS; + case FETCH_MATCH_COUNT, FETCH_SHALLOW -> ENABLED_WITHOUT_JUSTIFICATIONS_AND_INDICTMENTS; case FETCH_ALL -> ENABLED; }; } private final boolean enabled; private final boolean justificationEnabled; + private final boolean indictmentsEnabled; - ConstraintMatchPolicy(boolean enabled, boolean justificationEnabled) { + ConstraintMatchPolicy(boolean enabled, boolean justificationEnabled, boolean indictmentsEnabled) { this.enabled = enabled; this.justificationEnabled = justificationEnabled; + this.indictmentsEnabled = indictmentsEnabled; } public boolean isEnabled() { @@ -50,4 +54,7 @@ public boolean isJustificationEnabled() { return justificationEnabled; } + public boolean isIndictmentsEnabled() { + return indictmentsEnabled; + } } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/constraint/ConstraintMatchTotal.java b/core/src/main/java/ai/timefold/solver/core/impl/score/constraint/ConstraintMatchTotal.java index 6902f3014eb..4ed2b3e0014 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/constraint/ConstraintMatchTotal.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/constraint/ConstraintMatchTotal.java @@ -68,18 +68,32 @@ public Score_ getScore() { * @return never null */ public ConstraintMatch addConstraintMatch(List justifications, Score_ score) { - return addConstraintMatch(DefaultConstraintJustification.of(score, justifications), score); + return addConstraintMatch(DefaultConstraintJustification.of(score, justifications), justifications, score); } /** * Creates a {@link ConstraintMatch} and adds it to the collection returned by {@link #getConstraintMatchSet()}. - * It will be justified with the provided {@link ConstraintJustification}. + * It will be justified with the provided {@link ConstraintJustification} and will indict the given objects. + * + * @param score never null + * @return never null + */ + public ConstraintMatch addConstraintMatch(ConstraintJustification justification, List indictedObjects, + Score_ score) { + var constraintMatch = new ConstraintMatch<>(constraintRef, justification, indictedObjects, score); + addConstraintMatch(constraintMatch); + return constraintMatch; + } + + /** + * Creates a {@link ConstraintMatch} and adds it to the collection returned by {@link #getConstraintMatchSet()}. + * It will be justified with the provided {@link ConstraintJustification} and will indict no objects. * * @param score never null * @return never null */ public ConstraintMatch addConstraintMatch(ConstraintJustification justification, Score_ score) { - var constraintMatch = new ConstraintMatch(constraintRef, justification, score); + var constraintMatch = new ConstraintMatch<>(constraintRef, justification, null, score); addConstraintMatch(constraintMatch); return constraintMatch; } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/director/InnerScoreDirector.java b/core/src/main/java/ai/timefold/solver/core/impl/score/director/InnerScoreDirector.java index 756ffedcc49..26fd18fbd0f 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/director/InnerScoreDirector.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/director/InnerScoreDirector.java @@ -82,7 +82,7 @@ public interface InnerScoreDirector> /** * @return {@link ConstraintMatchPolicy#ENABLED} if {@link #getConstraintMatchTotalMap()} can be called. - * {@link ConstraintMatchPolicy#ENABLED_WITHOUT_JUSTIFICATIONS} if only the former can be called. + * {@link ConstraintMatchPolicy#ENABLED_WITHOUT_JUSTIFICATIONS_AND_INDICTMENTS} if only the former can be called. * {@link ConstraintMatchPolicy#DISABLED} if neither can be called. */ ConstraintMatchPolicy getConstraintMatchPolicy(); diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/BavetConstraintSessionFactory.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/BavetConstraintSessionFactory.java index 7629a033f5e..d30aa1805ef 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/BavetConstraintSessionFactory.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/BavetConstraintSessionFactory.java @@ -117,13 +117,17 @@ public BavetConstraintSession buildSession(Solution_ workingSolution, } return new BavetConstraintSession<>(scoreInliner, buildNodeNetwork(workingSolution, consistencyTracker, constraintStreamSet, scoreInliner, constraintProfiler, - scoreDirectorDerived)); + constraintMatchPolicy.isIndictmentsEnabled(), scoreDirectorDerived)); } private ConstraintStreamsBavetNodeNetwork buildNodeNetwork(Solution_ workingSolution, ConsistencyTracker consistencyTracker, Set> constraintStreamSet, - AbstractScoreInliner scoreInliner, InnerConstraintProfiler profiler, boolean scoreDirectorDerived) { - var buildHelper = new ConstraintNodeBuildHelper<>(consistencyTracker, constraintStreamSet, scoreInliner, profiler); + AbstractScoreInliner scoreInliner, InnerConstraintProfiler profiler, + boolean indictmentsEnabled, + boolean scoreDirectorDerived) { + var buildHelper = + new ConstraintNodeBuildHelper<>(consistencyTracker, constraintStreamSet, scoreInliner, indictmentsEnabled, + profiler); var declaredClassToNodeMap = new LinkedHashMap, List>>(); var nodeList = buildHelper.buildNodeList(constraintStreamSet, buildHelper, BavetAbstractConstraintStream::buildNode, node -> { diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/bi/BavetPrecomputeBiConstraintStream.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/bi/BavetPrecomputeBiConstraintStream.java index fa5de8e8069..af65f4a3e48 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/bi/BavetPrecomputeBiConstraintStream.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/bi/BavetPrecomputeBiConstraintStream.java @@ -39,7 +39,8 @@ public void setAftBridge(BavetAftBridgeBiConstraintStream aftSt @Override public > void buildNode(ConstraintNodeBuildHelper buildHelper) { Supplier>> precomputeBuildHelperSupplier = - () -> new BavetPrecomputeBuildHelper<>(recordingPrecomputedConstraintStream, entityClassSet); + () -> new BavetPrecomputeBuildHelper<>(recordingPrecomputedConstraintStream, + buildHelper.isIndictmentEnabled(), entityClassSet); var outputStoreSize = buildHelper.extractTupleStoreSize(aftStream); buildHelper.addNode(new PrecomputeBiNode<>(precomputeBuildHelperSupplier, diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/bi/BavetScoringBiConstraintStream.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/bi/BavetScoringBiConstraintStream.java index 3bf4653eb17..4360cfb9428 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/bi/BavetScoringBiConstraintStream.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/bi/BavetScoringBiConstraintStream.java @@ -53,8 +53,9 @@ public > void buildNode(ConstraintNodeBuildHelper buildScoreImpacter(ConstraintMatchPolicy constraintMatchPolicy) { return switch (constraintMatchPolicy) { case DISABLED -> scoreImpact::impactNaked; - case ENABLED -> scoreImpact::impactFull; - case ENABLED_WITHOUT_JUSTIFICATIONS -> scoreImpact::impactWithoutJustification; + case ENABLED, ENABLED_WITHOUT_INDICTMENTS -> scoreImpact::impactFull; + case ENABLED_WITHOUT_JUSTIFICATIONS, ENABLED_WITHOUT_JUSTIFICATIONS_AND_INDICTMENTS -> + scoreImpact::impactWithoutJustification; }; } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/bi/BiBigDecimalImpactHandler.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/bi/BiBigDecimalImpactHandler.java index a24737b796e..0a86db1d218 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/bi/BiBigDecimalImpactHandler.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/bi/BiBigDecimalImpactHandler.java @@ -31,7 +31,7 @@ public ScoreImpact impactFull(WeightedScoreImpacter impacter, BiTuple impactFull(WeightedScoreImpacter impacter, BiTuple { public BavetPrecomputeBuildHelper( BavetAbstractConstraintStream recordingPrecomputeConstraintStream, + boolean indictmentsEnabled, Set> entityClassSet) { if (recordingPrecomputeConstraintStream.getRetrievalSemantics() != RetrievalSemantics.PRECOMPUTE) { throw new IllegalStateException( @@ -63,7 +64,7 @@ public BavetPrecomputeBuildHelper( var buildHelper = new ConstraintNodeBuildHelper<>(new ConsistencyTracker<>(), streamSet, AbstractScoreInliner.buildScoreInliner(new SimpleScoreDefinition(), Collections.emptyMap(), ConstraintMatchPolicy.DISABLED), - null); + indictmentsEnabled, null); var declaredClassToNodeMap = new LinkedHashMap, List>>(); var nodeList = buildHelper.buildNodeList(streamSet, buildHelper, diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/common/ConstraintNodeBuildHelper.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/common/ConstraintNodeBuildHelper.java index 0179f24fd5d..9c677bf4f1b 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/common/ConstraintNodeBuildHelper.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/common/ConstraintNodeBuildHelper.java @@ -41,6 +41,7 @@ public final class ConstraintNodeBuildHelper scoreInliner; private final ConsistencyTracker consistencyTracker; private final @Nullable InnerConstraintProfiler constraintProfiler; + private final boolean indictmentsEnabled; private final Map, Map>> entityDescriptorToForEachCriteriaToPredicateMap; private final Map, List>> streamToProfileIdSets; private final Map, Scorer> streamToScorers = new HashMap<>(); @@ -49,11 +50,13 @@ public final class ConstraintNodeBuildHelper consistencyTracker, Set> activeStreamSet, AbstractScoreInliner scoreInliner, + boolean indictmentsEnabled, @Nullable InnerConstraintProfiler profiler) { super(activeStreamSet); this.consistencyTracker = consistencyTracker; this.scoreInliner = scoreInliner; this.constraintProfiler = profiler; + this.indictmentsEnabled = indictmentsEnabled; this.entityDescriptorToForEachCriteriaToPredicateMap = new HashMap<>(); this.streamToProfileIdSets = HashMap.newHashMap(Math.max(16, activeStreamSet.size() / 2)); } @@ -132,6 +135,14 @@ public Scorer getScorer(BavetScoringConstraintStream stream) { public ConstraintStreamsBavetNodeNetwork buildNodeNetwork(List nodeList, Map, List>> declaredClassToNodeMap, Map, Scorer> constraintToScorerMap, boolean scoreDirectorDerived) { + for (var scorerEntry : constraintToScorerMap.entrySet()) { + var scorerParentNodes = getParentNodeList( + (BavetAbstractConstraintStream) scorerEntry.getKey().getScoringConstraintStream()); + var scorerNodeIds = scorerParentNodes.stream() + .mapToLong(AbstractNode::getId) + .toArray(); + scorerEntry.getValue().setNodeIds(scorerNodeIds); + } return ConstraintStreamsBavetNodeNetwork.of(nodeList, declaredClassToNodeMap, (Map) constraintToScorerMap, node -> { if (constraintProfiler == null) { return node.getPropagator(); @@ -153,4 +164,7 @@ public ConstraintStreamsBavetNodeNetwork buildPrecomputeNodeNetwork(List> void buildNode(ConstraintNodeBuildHelper buildHelper) { Supplier>> precomputeBuildHelperSupplier = - () -> new BavetPrecomputeBuildHelper<>(recordingPrecomputedConstraintStream, entityClassSet); + () -> new BavetPrecomputeBuildHelper<>(recordingPrecomputedConstraintStream, + buildHelper.isIndictmentEnabled(), entityClassSet); var outputStoreSize = buildHelper.extractTupleStoreSize(aftStream); buildHelper.addNode(new PrecomputeQuadNode<>(precomputeBuildHelperSupplier, diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/quad/BavetScoringQuadConstraintStream.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/quad/BavetScoringQuadConstraintStream.java index ebaecf8b6da..8f81dd5acab 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/quad/BavetScoringQuadConstraintStream.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/quad/BavetScoringQuadConstraintStream.java @@ -54,8 +54,9 @@ public > void buildNode(ConstraintNodeBuildHelper buildScoreImpacter(ConstraintMatchPolicy constraintMatchPolicy) { return switch (constraintMatchPolicy) { case DISABLED -> scoreImpact::impactNaked; - case ENABLED -> scoreImpact::impactFull; - case ENABLED_WITHOUT_JUSTIFICATIONS -> scoreImpact::impactWithoutJustification; + case ENABLED, ENABLED_WITHOUT_INDICTMENTS -> scoreImpact::impactFull; + case ENABLED_WITHOUT_JUSTIFICATIONS, ENABLED_WITHOUT_JUSTIFICATIONS_AND_INDICTMENTS -> + scoreImpact::impactWithoutJustification; }; } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/quad/QuadBigDecimalImpactHandler.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/quad/QuadBigDecimalImpactHandler.java index acbe999c17c..2e48e38937a 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/quad/QuadBigDecimalImpactHandler.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/quad/QuadBigDecimalImpactHandler.java @@ -35,8 +35,7 @@ public ScoreImpact impactFull(WeightedScoreImpacter impacter, QuadTuple var d = tuple.getD(); var constraint = impacter.getContext().getConstraint(); return impacter.impactScore(matchWeigher.apply(a, b, c, d), - ConstraintMatchSupplier.of(constraint.getJustificationMapping(), a, b, - c, d)); + ConstraintMatchSupplier.of(constraint.getJustificationMapping(), tuple)); } } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/quad/QuadLongImpactHandler.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/quad/QuadLongImpactHandler.java index c58f81da4c0..b3866e237ff 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/quad/QuadLongImpactHandler.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/quad/QuadLongImpactHandler.java @@ -33,8 +33,7 @@ public ScoreImpact impactFull(WeightedScoreImpacter impacter, QuadTuple var d = tuple.getD(); var constraint = impacter.getContext().getConstraint(); return impacter.impactScore(matchWeigher.applyAsLong(a, b, c, d), - ConstraintMatchSupplier.of(constraint.getJustificationMapping(), a, b, - c, d)); + ConstraintMatchSupplier.of(constraint.getJustificationMapping(), tuple)); } } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/tri/BavetPrecomputeTriConstraintStream.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/tri/BavetPrecomputeTriConstraintStream.java index 151f648749f..315f588a46f 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/tri/BavetPrecomputeTriConstraintStream.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/tri/BavetPrecomputeTriConstraintStream.java @@ -38,7 +38,8 @@ public void setAftBridge(BavetAftBridgeTriConstraintStream a @Override public > void buildNode(ConstraintNodeBuildHelper buildHelper) { Supplier>> precomputeBuildHelperSupplier = - () -> new BavetPrecomputeBuildHelper<>(recordingPrecomputedConstraintStream, entityClassSet); + () -> new BavetPrecomputeBuildHelper<>(recordingPrecomputedConstraintStream, + buildHelper.isIndictmentEnabled(), entityClassSet); var outputStoreSize = buildHelper.extractTupleStoreSize(aftStream); buildHelper.addNode(new PrecomputeTriNode<>(precomputeBuildHelperSupplier, diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/tri/BavetScoringTriConstraintStream.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/tri/BavetScoringTriConstraintStream.java index 41e55d2a562..6412a07540f 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/tri/BavetScoringTriConstraintStream.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/tri/BavetScoringTriConstraintStream.java @@ -54,8 +54,9 @@ public > void buildNode(ConstraintNodeBuildHelper buildScoreImpacter(ConstraintMatchPolicy constraintMatchPolicy) { return switch (constraintMatchPolicy) { case DISABLED -> scoreImpact::impactNaked; - case ENABLED -> scoreImpact::impactFull; - case ENABLED_WITHOUT_JUSTIFICATIONS -> scoreImpact::impactWithoutJustification; + case ENABLED, ENABLED_WITHOUT_INDICTMENTS -> scoreImpact::impactFull; + case ENABLED_WITHOUT_JUSTIFICATIONS, ENABLED_WITHOUT_JUSTIFICATIONS_AND_INDICTMENTS -> + scoreImpact::impactWithoutJustification; }; } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/tri/TriBigDecimalImpactHandler.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/tri/TriBigDecimalImpactHandler.java index b2f3f62902d..9cb71402639 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/tri/TriBigDecimalImpactHandler.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/tri/TriBigDecimalImpactHandler.java @@ -27,8 +27,7 @@ public ScoreImpact impactFull(WeightedScoreImpacter impacter, TriTuple< var c = tuple.getC(); var constraint = impacter.getContext().getConstraint(); return impacter.impactScore(matchWeigher.apply(a, b, c), - ConstraintMatchSupplier.of(constraint.getJustificationMapping(), a, b, - c)); + ConstraintMatchSupplier.of(constraint.getJustificationMapping(), tuple)); } @Override diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/tri/TriLongImpactHandler.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/tri/TriLongImpactHandler.java index 5f8b21f8ffb..6fd63bf641e 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/tri/TriLongImpactHandler.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/tri/TriLongImpactHandler.java @@ -31,8 +31,7 @@ public ScoreImpact impactFull(WeightedScoreImpacter impacter, TriTuple< var c = tuple.getC(); var constraint = impacter.getContext().getConstraint(); return impacter.impactScore(matchWeigher.applyAsLong(a, b, c), - ConstraintMatchSupplier.of(constraint.getJustificationMapping(), a, b, - c)); + ConstraintMatchSupplier.of(constraint.getJustificationMapping(), tuple)); } } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/uni/BavetForEachUniConstraintStream.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/uni/BavetForEachUniConstraintStream.java index f0458c90a6b..f0505a1eeb6 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/uni/BavetForEachUniConstraintStream.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/uni/BavetForEachUniConstraintStream.java @@ -54,9 +54,10 @@ public > void buildNode(ConstraintNodeBuildHelper> getAggregatedTupleLifecycle(childStreamList); int outputStoreSize = buildHelper.extractTupleStoreSize(this); var filter = filterFunction != null ? filterFunction.apply(buildHelper) : null; - var node = filter == null ? new ForEachUnfilteredUniNode<>(forEachClass, tupleLifecycle, outputStoreSize) + var node = filter == null ? new ForEachUnfilteredUniNode<>(forEachClass, tupleLifecycle, + buildHelper.isIndictmentEnabled(), outputStoreSize) : new ForEachFilteredUniNode<>(forEachClass, filter, tupleLifecycle, - outputStoreSize); + buildHelper.isIndictmentEnabled(), outputStoreSize); buildHelper.addNode(node, this, null); } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/uni/BavetPrecomputeUniConstraintStream.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/uni/BavetPrecomputeUniConstraintStream.java index 0b26746e9f4..3c1720a171b 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/uni/BavetPrecomputeUniConstraintStream.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/uni/BavetPrecomputeUniConstraintStream.java @@ -38,7 +38,8 @@ public void setAftBridge(BavetAftBridgeUniConstraintStream aftStre @Override public > void buildNode(ConstraintNodeBuildHelper buildHelper) { Supplier>> precomputeBuildHelperSupplier = - () -> new BavetPrecomputeBuildHelper<>(recordingPrecomputedConstraintStream, entityClassSet); + () -> new BavetPrecomputeBuildHelper<>(recordingPrecomputedConstraintStream, + buildHelper.isIndictmentEnabled(), entityClassSet); var outputStoreSize = buildHelper.extractTupleStoreSize(aftStream); buildHelper.addNode(new PrecomputeUniNode<>(precomputeBuildHelperSupplier, diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/uni/BavetScoringUniConstraintStream.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/uni/BavetScoringUniConstraintStream.java index 2ae32839cb5..091dd1611a4 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/uni/BavetScoringUniConstraintStream.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/uni/BavetScoringUniConstraintStream.java @@ -53,8 +53,9 @@ public > void buildNode(ConstraintNodeBuildHelper buildScoreImpacter(ConstraintMatchPolicy constraintMatchPolicy) { return switch (constraintMatchPolicy) { case DISABLED -> scoreImpact::impactNaked; - case ENABLED -> scoreImpact::impactFull; - case ENABLED_WITHOUT_JUSTIFICATIONS -> scoreImpact::impactWithoutJustification; + case ENABLED, ENABLED_WITHOUT_INDICTMENTS -> scoreImpact::impactFull; + case ENABLED_WITHOUT_JUSTIFICATIONS, ENABLED_WITHOUT_JUSTIFICATIONS_AND_INDICTMENTS -> + scoreImpact::impactWithoutJustification; }; } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/uni/UniBigDecimalImpactHandler.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/uni/UniBigDecimalImpactHandler.java index 0da5ce44888..9bd7b27c833 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/uni/UniBigDecimalImpactHandler.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/uni/UniBigDecimalImpactHandler.java @@ -28,7 +28,7 @@ public ScoreImpact impactFull(WeightedScoreImpacter impacter, UniTuple< var a = tuple.getA(); var constraint = impacter.getContext().getConstraint(); return impacter.impactScore(matchWeigher.apply(a), - ConstraintMatchSupplier.of(constraint.getJustificationMapping(), a)); + ConstraintMatchSupplier.of(constraint.getJustificationMapping(), tuple)); } } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/uni/UniLongImpactHandler.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/uni/UniLongImpactHandler.java index 27d8c2a6312..5484b5e4bda 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/uni/UniLongImpactHandler.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/bavet/uni/UniLongImpactHandler.java @@ -22,7 +22,7 @@ public ScoreImpact impactFull(WeightedScoreImpacter impacter, UniTuple< var a = tuple.getA(); var constraint = impacter.getContext().getConstraint(); return impacter.impactScore(matchWeigher.applyAsLong(a), - ConstraintMatchSupplier.of(constraint.getJustificationMapping(), a)); + ConstraintMatchSupplier.of(constraint.getJustificationMapping(), tuple)); } @Override diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/common/AbstractConstraint.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/common/AbstractConstraint.java index 2da51dbbf06..093c962a70f 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/common/AbstractConstraint.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/common/AbstractConstraint.java @@ -25,6 +25,7 @@ public abstract class AbstractConstraint> Score_ extractConstraintWeight(Solution_ solution) { return adjustConstraintWeight((Score_) determineConstraintWeight(solution)); diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/common/inliner/AbstractScoreInliner.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/common/inliner/AbstractScoreInliner.java index ed7863f6930..70f5b66b6f7 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/common/inliner/AbstractScoreInliner.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/common/inliner/AbstractScoreInliner.java @@ -104,7 +104,8 @@ private void validateConstraintWeight(Constraint constraint, Score_ constraintWe public abstract WeightedScoreImpacter buildWeightedScoreImpacter(AbstractConstraint constraint); protected final ScoreImpact addConstraintMatch(Constraint constraint, - ConstraintMatchSupplier constraintMatchSupplier, ScoreImpact scoreImpact) { + ConstraintMatchSupplier constraintMatchSupplier, + ScoreImpact scoreImpact) { var constraintMatchList = getConstraintMatchList(constraint); /* * Creating a constraint match is a heavy operation which may yet be undone. diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/common/inliner/ConstraintMatchSupplier.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/common/inliner/ConstraintMatchSupplier.java index 09c4e7115d1..623e37f127d 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/common/inliner/ConstraintMatchSupplier.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/common/inliner/ConstraintMatchSupplier.java @@ -1,6 +1,10 @@ package ai.timefold.solver.core.impl.score.stream.common.inliner; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; import java.util.Objects; import java.util.function.BiFunction; import java.util.stream.Collectors; @@ -11,9 +15,17 @@ import ai.timefold.solver.core.api.score.Score; import ai.timefold.solver.core.api.score.stream.Constraint; import ai.timefold.solver.core.api.score.stream.ConstraintJustification; +import ai.timefold.solver.core.impl.bavet.common.tuple.BiTuple; +import ai.timefold.solver.core.impl.bavet.common.tuple.QuadTuple; +import ai.timefold.solver.core.impl.bavet.common.tuple.TriTuple; +import ai.timefold.solver.core.impl.bavet.common.tuple.Tuple; +import ai.timefold.solver.core.impl.bavet.common.tuple.UniTuple; +import ai.timefold.solver.core.impl.bavet.common.tuple.indictment.IndictmentSource; import ai.timefold.solver.core.impl.score.constraint.ConstraintMatch; +import ai.timefold.solver.core.impl.score.stream.common.AbstractConstraint; import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; /** * Allows creating {@link ConstraintMatch} instances lazily if and only if they are required by the end user. @@ -31,6 +43,17 @@ public interface ConstraintMatchSupplier> extends BiFunction> { + static @Nullable List collectIndictments(Constraint constraint, Tuple tuple) { + if (tuple.getIndictmentSource() == IndictmentSource.DISABLED) { + return null; + } + var out = new LinkedHashSet<>(); + var abstractConstraint = (AbstractConstraint) constraint; + var involvedNodeIds = Objects.requireNonNull(abstractConstraint.getInvolvedNodeIds()); + tuple.getIndictmentSource().visitSources(involvedNodeIds, out::add); + return new ArrayList<>(out); + } + /** * * @return the constraint match returned by the supplier will have its justification set to null. @@ -39,18 +62,19 @@ public interface ConstraintMatchSupplier> */ static > ConstraintMatchSupplier empty() { return (constraint, impact) -> new ConstraintMatch<>(constraint.getConstraintRef(), null, - impact); + Collections.emptyList(), impact); } static > ConstraintMatchSupplier of( BiFunction, ConstraintJustification> justificationMapping, - A a) { + UniTuple tuple) { return (constraint, impact) -> { try { - var justification = justificationMapping.apply(a, impact); - return new ConstraintMatch<>(constraint.getConstraintRef(), justification, impact); + var justification = justificationMapping.apply(tuple.getA(), impact); + return new ConstraintMatch<>(constraint.getConstraintRef(), justification, + collectIndictments(constraint, tuple), impact); } catch (Exception e) { - throw createJustificationException(constraint, e, a); + throw createJustificationException(constraint, e, tuple.getA()); } }; } @@ -68,39 +92,42 @@ private static String factsToString(Object... facts) { static > ConstraintMatchSupplier of( TriFunction justificationMapping, - A a, B b) { + BiTuple tuple) { return (constraint, impact) -> { try { - var justification = justificationMapping.apply(a, b, impact); - return new ConstraintMatch<>(constraint.getConstraintRef(), justification, impact); + var justification = justificationMapping.apply(tuple.getA(), tuple.getB(), impact); + return new ConstraintMatch<>(constraint.getConstraintRef(), justification, + collectIndictments(constraint, tuple), impact); } catch (Exception e) { - throw createJustificationException(constraint, e, a, b); + throw createJustificationException(constraint, e, tuple.getA(), tuple.getB()); } }; } static > ConstraintMatchSupplier of( QuadFunction justificationMapping, - A a, B b, C c) { + TriTuple tuple) { return (constraint, impact) -> { try { - var justification = justificationMapping.apply(a, b, c, impact); - return new ConstraintMatch<>(constraint.getConstraintRef(), justification, impact); + var justification = justificationMapping.apply(tuple.getA(), tuple.getB(), tuple.getC(), impact); + return new ConstraintMatch<>(constraint.getConstraintRef(), justification, + collectIndictments(constraint, tuple), impact); } catch (Exception e) { - throw createJustificationException(constraint, e, a, b, c); + throw createJustificationException(constraint, e, tuple.getA(), tuple.getB(), tuple.getC()); } }; } static > ConstraintMatchSupplier of( PentaFunction justificationMapping, - A a, B b, C c, D d) { + QuadTuple tuple) { return (constraint, impact) -> { try { - var justification = justificationMapping.apply(a, b, c, d, impact); - return new ConstraintMatch<>(constraint.getConstraintRef(), justification, impact); + var justification = justificationMapping.apply(tuple.getA(), tuple.getB(), tuple.getC(), tuple.getD(), impact); + return new ConstraintMatch<>(constraint.getConstraintRef(), justification, + collectIndictments(constraint, tuple), impact); } catch (Exception e) { - throw createJustificationException(constraint, e, a, b, c, d); + throw createJustificationException(constraint, e, tuple.getA(), tuple.getB(), tuple.getC(), tuple.getD()); } }; } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/common/inliner/ScoreContext.java b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/common/inliner/ScoreContext.java index ebdd5f0b040..f38bdb980fa 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/score/stream/common/inliner/ScoreContext.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/score/stream/common/inliner/ScoreContext.java @@ -29,6 +29,10 @@ public final Score_ getConstraintWeight() { return constraintWeight; } + public void setInvolvedNodeIds(long[] involvedNodeIds) { + constraint.setInvolvedNodeIds(involvedNodeIds); + } + protected final ScoreImpact possiblyAddConstraintMatch(ScoreImpact scoreImpact, @Nullable ConstraintMatchSupplier constraintMatchSupplier) { if (!inliner.constraintMatchPolicy.isEnabled()) { diff --git a/core/src/test/java/ai/timefold/solver/core/impl/bavet/uni/ForEachFilteredUniNodeActivityTest.java b/core/src/test/java/ai/timefold/solver/core/impl/bavet/uni/ForEachFilteredUniNodeActivityTest.java index 59b936e7a4d..8826d97609a 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/bavet/uni/ForEachFilteredUniNodeActivityTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/bavet/uni/ForEachFilteredUniNodeActivityTest.java @@ -26,7 +26,7 @@ class ForEachFilteredUniNodeActivityTest { private final Set passing = new HashSet<>(); private ForEachFilteredUniNode node(TupleLifecycle> downstream) { - return new ForEachFilteredUniNode<>(String.class, passing::contains, downstream, 1); + return new ForEachFilteredUniNode<>(String.class, passing::contains, downstream, false, 1); } @SuppressWarnings("unchecked") diff --git a/core/src/test/java/ai/timefold/solver/core/impl/bavet/uni/ForEachUniNodeActivityTest.java b/core/src/test/java/ai/timefold/solver/core/impl/bavet/uni/ForEachUniNodeActivityTest.java index 5017b89c41b..40b09c9ade7 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/bavet/uni/ForEachUniNodeActivityTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/bavet/uni/ForEachUniNodeActivityTest.java @@ -25,7 +25,7 @@ private static TupleLifecycle> mockDownstream(boolean active) { @Test void unfilteredInactiveWhenNoFacts() { var downstream = mockDownstream(true); - var node = new ForEachUnfilteredUniNode<>(String.class, downstream, 1); + var node = new ForEachUnfilteredUniNode<>(String.class, downstream, false, 1); node.afterAllFactsInserted(true); assertThat(node.isActive()).isFalse(); } @@ -33,7 +33,7 @@ void unfilteredInactiveWhenNoFacts() { @Test void unfilteredActiveWhenFactsExist() { var downstream = mockDownstream(true); - var node = new ForEachUnfilteredUniNode<>(String.class, downstream, 1); + var node = new ForEachUnfilteredUniNode<>(String.class, downstream, false, 1); node.insert("a"); node.afterAllFactsInserted(true); assertThat(node.isActive()).isTrue(); @@ -42,7 +42,7 @@ void unfilteredActiveWhenFactsExist() { @Test void unfilteredInactiveWhenDownstreamInactive() { var downstream = mockDownstream(false); - var node = new ForEachUnfilteredUniNode<>(String.class, downstream, 1); + var node = new ForEachUnfilteredUniNode<>(String.class, downstream, false, 1); node.insert("a"); node.afterAllFactsInserted(true); assertThat(node.isActive()).isFalse(); @@ -51,7 +51,7 @@ void unfilteredInactiveWhenDownstreamInactive() { @Test void filteredInactiveWhenNoFacts() { var downstream = mockDownstream(true); - var node = new ForEachFilteredUniNode<>(String.class, s -> true, downstream, 1); + var node = new ForEachFilteredUniNode<>(String.class, s -> true, downstream, false, 1); node.afterAllFactsInserted(true); assertThat(node.isActive()).isFalse(); } @@ -59,7 +59,7 @@ void filteredInactiveWhenNoFacts() { @Test void filteredActiveWhenFactInsertedEvenIfFilteredOut() { var downstream = mockDownstream(true); - var node = new ForEachFilteredUniNode<>(String.class, s -> false, downstream, 1); + var node = new ForEachFilteredUniNode<>(String.class, s -> false, downstream, false, 1); node.insert("a"); node.afterAllFactsInserted(true); assertThat(node.isActive()).isTrue(); @@ -68,7 +68,7 @@ void filteredActiveWhenFactInsertedEvenIfFilteredOut() { @Test void filteredInactiveWhenDownstreamInactive() { var downstream = mockDownstream(false); - var node = new ForEachFilteredUniNode<>(String.class, s -> true, downstream, 1); + var node = new ForEachFilteredUniNode<>(String.class, s -> true, downstream, false, 1); node.insert("a"); node.afterAllFactsInserted(true); assertThat(node.isActive()).isFalse(); diff --git a/core/src/test/java/ai/timefold/solver/core/impl/score/constraint/ConstraintMatchTest.java b/core/src/test/java/ai/timefold/solver/core/impl/score/constraint/ConstraintMatchTest.java index 5102f4cd01b..f7e31759389 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/score/constraint/ConstraintMatchTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/score/constraint/ConstraintMatchTest.java @@ -3,6 +3,8 @@ import static ai.timefold.solver.core.api.score.SimpleScore.ONE; import static ai.timefold.solver.core.api.score.SimpleScore.ZERO; +import java.util.List; + import ai.timefold.solver.core.api.score.Score; import ai.timefold.solver.core.api.score.SimpleScore; import ai.timefold.solver.core.api.score.stream.ConstraintRef; @@ -26,6 +28,7 @@ private > ConstraintMatch buildConstraintMa Object... facts) { return new ConstraintMatch<>(ConstraintRef.of(constraintName), DefaultConstraintJustification.of(score, facts), + List.of(facts), score); } diff --git a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/bavet/BavetRegressionTest.java b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/bavet/BavetRegressionTest.java index 9ef46b39528..623ca650111 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/bavet/BavetRegressionTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/bavet/BavetRegressionTest.java @@ -52,8 +52,8 @@ void joinWithNullKeyFromRight() { scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, entity1), - assertMatch(entity2, entity2)); + assertMatch(entity1, entity1).withIndictedObjects(entity1), + assertMatch(entity2, entity2).withIndictedObjects(entity2)); // Switch entity1 and entity2 values; now entity2 has null and entity1 does not. scoreDirector.beforeVariableChanged(entity1, "value"); @@ -63,8 +63,8 @@ void joinWithNullKeyFromRight() { entity2.setValue(null); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(entity1, entity1), - assertMatch(entity2, entity2)); + assertMatch(entity1, entity1).withIndictedObjects(entity1), + assertMatch(entity2, entity2).withIndictedObjects(entity2)); // Put both to null. scoreDirector.beforeVariableChanged(entity1, "value"); @@ -74,10 +74,10 @@ void joinWithNullKeyFromRight() { entity2.setValue(null); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(entity1, entity1), - assertMatch(entity1, entity2), - assertMatch(entity2, entity1), - assertMatch(entity2, entity2)); + assertMatch(entity1, entity1).withIndictedObjects(entity1), + assertMatch(entity1, entity2).withIndictedObjects(entity1, entity2), + assertMatch(entity2, entity1).withIndictedObjects(entity1, entity2), + assertMatch(entity2, entity2).withIndictedObjects(entity2)); } @TestTemplate @@ -112,7 +112,7 @@ public void filteringJoinNullConflictRight() { scoreDirector.afterListVariableElementAssigned(entity, "valueList", value1); assertScore(scoreDirector, - assertMatch(value1, value1)); + assertMatch(value1, value1).withIndictedObjects(value1)); // Unassign+assign and check result. var variableDescriptor = scoreDirector.getSolutionDescriptor() @@ -126,7 +126,7 @@ public void filteringJoinNullConflictRight() { scoreDirector.afterListVariableElementAssigned(variableDescriptor, value2); assertScore(scoreDirector, - assertMatch(value2, value2)); + assertMatch(value2, value2).withIndictedObjects(value2)); // Reassign and check result. scoreDirector.beforeListVariableElementAssigned(variableDescriptor, value1); @@ -138,7 +138,7 @@ public void filteringJoinNullConflictRight() { scoreDirector.afterListVariableElementAssigned(variableDescriptor, value1); assertScore(scoreDirector, - assertMatch(value1, value1)); + assertMatch(value1, value1).withIndictedObjects(value1)); } } @@ -173,7 +173,7 @@ public void filteringJoinNullConflictRightUnindexed() { scoreDirector.afterListVariableElementAssigned(entity, "valueList", value1); assertScore(scoreDirector, - assertMatch(value1, value1)); + assertMatch(value1, value1).withIndictedObjects(value1)); // Unassign+assign and check result. var variableDescriptor = scoreDirector.getSolutionDescriptor() @@ -187,7 +187,7 @@ public void filteringJoinNullConflictRightUnindexed() { scoreDirector.afterListVariableElementAssigned(variableDescriptor, value2); assertScore(scoreDirector, - assertMatch(value2, value2)); + assertMatch(value2, value2).withIndictedObjects(value2)); // Reassign and check result. scoreDirector.beforeListVariableElementAssigned(variableDescriptor, value1); @@ -199,7 +199,7 @@ public void filteringJoinNullConflictRightUnindexed() { scoreDirector.afterListVariableElementAssigned(variableDescriptor, value1); assertScore(scoreDirector, - assertMatch(value1, value1)); + assertMatch(value1, value1).withIndictedObjects(value1)); } } @@ -236,7 +236,7 @@ public void filteringJoinNullConflictRightViaIfExists() { scoreDirector.afterListVariableElementAssigned(entity, "valueList", value1); assertScore(scoreDirector, - assertMatch(value1, value1)); + assertMatch(value1, value1).withIndictedObjects(value1)); // Unassign+assign and check result. var variableDescriptor = scoreDirector.getSolutionDescriptor() @@ -250,7 +250,7 @@ public void filteringJoinNullConflictRightViaIfExists() { scoreDirector.afterListVariableElementAssigned(variableDescriptor, value2); assertScore(scoreDirector, - assertMatch(value2, value2)); + assertMatch(value2, value2).withIndictedObjects(value2)); // Reassign and check result. scoreDirector.beforeListVariableElementAssigned(variableDescriptor, value1); @@ -262,7 +262,7 @@ public void filteringJoinNullConflictRightViaIfExists() { scoreDirector.afterListVariableElementAssigned(variableDescriptor, value1); assertScore(scoreDirector, - assertMatch(value1, value1)); + assertMatch(value1, value1).withIndictedObjects(value1)); } } @@ -301,10 +301,10 @@ public void filteringJoinNullConflictRightUnassignOne() { scoreDirector.afterListVariableElementAssigned(entity, "valueList", value1); assertScore(scoreDirector, - assertMatch(value1, value1), - assertMatch(value1, value2), - assertMatch(value2, value1), - assertMatch(value2, value2)); + assertMatch(value1, value1).withIndictedObjects(value1), + assertMatch(value1, value2).withIndictedObjects(value1, value2), + assertMatch(value2, value1).withIndictedObjects(value1, value2), + assertMatch(value2, value2).withIndictedObjects(value2)); // Unassign and check result. var variableDescriptor = scoreDirector.getSolutionDescriptor() @@ -316,7 +316,7 @@ public void filteringJoinNullConflictRightUnassignOne() { scoreDirector.afterListVariableElementUnassigned(variableDescriptor, value1); assertScore(scoreDirector, - assertMatch(value2, value2)); + assertMatch(value2, value2).withIndictedObjects(value2)); // Reassign and check result. scoreDirector.beforeListVariableElementAssigned(variableDescriptor, value1); @@ -326,10 +326,10 @@ public void filteringJoinNullConflictRightUnassignOne() { scoreDirector.afterListVariableElementAssigned(variableDescriptor, value1); assertScore(scoreDirector, - assertMatch(value1, value1), - assertMatch(value1, value2), - assertMatch(value2, value1), - assertMatch(value2, value2)); + assertMatch(value1, value1).withIndictedObjects(value1), + assertMatch(value1, value2).withIndictedObjects(value1, value2), + assertMatch(value2, value1).withIndictedObjects(value1, value2), + assertMatch(value2, value2).withIndictedObjects(value2)); } } @@ -491,7 +491,7 @@ void filteringJoinNullConflict() { scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity2, entity2)); // Only entity1 is left, because forEach/join ignore nulls. + assertMatch(entity2, entity2).withIndictedObjects(entity2)); // Only entity1 is left, because forEach/join ignore nulls. // Switch entity1 and entity2 values; now entity2 has null and entity1 does not. scoreDirector.beforeVariableChanged(entity1, "value"); @@ -501,7 +501,7 @@ void filteringJoinNullConflict() { entity2.setValue(null); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(entity1, entity1)); + assertMatch(entity1, entity1).withIndictedObjects(entity1)); // Switch entity1 and entity2 values again to test the same from the other side. scoreDirector.beforeVariableChanged(entity1, "value"); @@ -511,7 +511,7 @@ void filteringJoinNullConflict() { entity2.setValue(value); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(entity2, entity2)); + assertMatch(entity2, entity2).withIndictedObjects(entity2)); } /** @@ -653,7 +653,7 @@ void filteringJoinNullConflictDifferentNodes() { scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity2, entity2)); + assertMatch(entity2, entity2).withIndictedObjects(entity2)); // Switch entity1 and entity2 values; now entity2 has null and entity1 does not. scoreDirector.beforeVariableChanged(entity1, "value"); @@ -663,7 +663,7 @@ void filteringJoinNullConflictDifferentNodes() { entity2.setValue(null); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(entity1, entity1)); + assertMatch(entity1, entity1).withIndictedObjects(entity1)); // Switch entity1 and entity2 values again to test the same from the other side. scoreDirector.beforeVariableChanged(entity1, "value"); @@ -673,7 +673,7 @@ void filteringJoinNullConflictDifferentNodes() { entity2.setValue(value); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(entity2, entity2)); + assertMatch(entity2, entity2).withIndictedObjects(entity2)); } /** diff --git a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/AbstractAdvancedGroupByConstraintStreamTest.java b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/AbstractAdvancedGroupByConstraintStreamTest.java index 834c2a78217..d7efab5062a 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/AbstractAdvancedGroupByConstraintStreamTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/AbstractAdvancedGroupByConstraintStreamTest.java @@ -54,7 +54,7 @@ void collectedDowngradedAndFiltered() { // From scratch scoreDirector.setWorkingSolution(solution); - assertScore(scoreDirector, assertMatch(new Pair<>("M", 1L))); + assertScore(scoreDirector, assertMatch(new Pair<>("M", 1L)).withIndictedObjects(entity)); // Incremental scoreDirector.beforeEntityRemoved(entity); @@ -85,7 +85,7 @@ void collectedAndFiltered() { // From scratch scoreDirector.setWorkingSolution(solution); - assertScore(scoreDirector, assertMatchWithScore(-10, 10L)); + assertScore(scoreDirector, assertMatchWithScore(-10, 10L).withIndictedObjects(solution.getEntityList().toArray())); // Incremental Stream.of(entity1, entity2).forEach(entity -> { @@ -109,14 +109,14 @@ void collectedFilteredRecollected() { // From scratch scoreDirector.setWorkingSolution(solution); - assertScore(scoreDirector, assertMatchWithScore(-2, 2L)); + assertScore(scoreDirector, assertMatchWithScore(-2, 2L).withIndictedObjects(solution.getEntityList().toArray())); // Incremental TestdataLavishEntity entity = solution.getFirstEntity(); scoreDirector.beforeEntityRemoved(entity); solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); - assertScore(scoreDirector, assertMatchWithScore(-1, 1L)); + assertScore(scoreDirector, assertMatchWithScore(-1, 1L).withIndictedObjects(solution.getEntityList().toArray())); } @TestTemplate @@ -136,14 +136,15 @@ void uniGroupByRecollected() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-2, asSet(entity1.getEntityGroup(), entity2.getEntityGroup()))); + assertMatchWithScore(-2, asSet(entity1.getEntityGroup(), entity2.getEntityGroup())).withIndictedObjects(entity1, + entity2)); // Incremental scoreDirector.beforeEntityRemoved(entity1); solution.getEntityList().remove(entity1); scoreDirector.afterEntityRemoved(entity1); assertScore(scoreDirector, - assertMatchWithScore(-1, asSet(entity2.getEntityGroup()))); + assertMatchWithScore(-1, asSet(entity2.getEntityGroup())).withIndictedObjects(entity2)); } @TestTemplate @@ -163,7 +164,8 @@ void biGroupByRecollectedToList() { scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, assertMatchWithScore(-1, - Arrays.asList(solution.getFirstEntityGroup(), solution.getEntityGroupList().get(1)))); + Arrays.asList(solution.getFirstEntityGroup(), solution.getEntityGroupList().get(1))) + .withIndictedObjects(solution.getEntityList().toArray())); // Incremental TestdataLavishEntity entity = solution.getFirstEntity(); @@ -172,7 +174,8 @@ void biGroupByRecollectedToList() { scoreDirector.afterEntityRemoved(entity); assertScore(scoreDirector, assertMatchWithScore(-1, - Arrays.asList(solution.getFirstEntityGroup(), solution.getEntityGroupList().get(1)))); + Arrays.asList(solution.getFirstEntityGroup(), solution.getEntityGroupList().get(1))) + .withIndictedObjects(solution.getEntityList().toArray())); } @TestTemplate @@ -192,7 +195,8 @@ void biGroupByRecollectedToMap() { scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, assertMatchWithScore(-1, - asMap(solution.getFirstEntityGroup(), 3L, solution.getEntityGroupList().get(1), 1L))); + asMap(solution.getFirstEntityGroup(), 3L, solution.getEntityGroupList().get(1), 1L)) + .withIndictedObjects(solution.getEntityList().toArray())); // Incremental TestdataLavishEntity entity = solution.getFirstEntity(); @@ -201,7 +205,8 @@ void biGroupByRecollectedToMap() { scoreDirector.afterEntityRemoved(entity); assertScore(scoreDirector, assertMatchWithScore(-1, - asMap(solution.getFirstEntityGroup(), 1L, solution.getEntityGroupList().get(1), 1L))); + asMap(solution.getFirstEntityGroup(), 1L, solution.getEntityGroupList().get(1), 1L)) + .withIndictedObjects(solution.getEntityList().toArray())); } @TestTemplate @@ -224,16 +229,21 @@ void triGroupByRecollected() { scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, assertMatchWithScore(-1, - asMap(solution.getFirstEntityGroup(), 3L, solution.getEntityGroupList().get(1), 3L))); + asMap(solution.getFirstEntityGroup(), 3L, solution.getEntityGroupList().get(1), 3L)) + .withIndictedObjects(solution.getEntityList().toArray())); // Incremental TestdataLavishEntity entity = solution.getFirstEntity(); + TestdataLavishEntity group1Entity1 = solution.getEntityList().get(1); + TestdataLavishEntity group1Entity3 = solution.getEntityList().get(3); + TestdataLavishEntity group1Entity5 = solution.getEntityList().get(5); scoreDirector.beforeEntityRemoved(entity); solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); assertScore(scoreDirector, assertMatchWithScore(-1, - asMap(solution.getEntityGroupList().get(1), 3L))); + asMap(solution.getEntityGroupList().get(1), 3L)) + .withIndictedObjects(group1Entity1, group1Entity3, group1Entity5)); } @TestTemplate @@ -260,16 +270,22 @@ void quadGroupByRecollected() { scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, assertMatchWithScore(-1, - asMap(solution.getFirstEntityGroup(), 12L, solution.getEntityGroupList().get(1), 12L))); + asMap(solution.getFirstEntityGroup(), 12L, solution.getEntityGroupList().get(1), 12L)) + .withIndictedObjects(solution.getEntityList().toArray())); // Incremental TestdataLavishEntity entity = solution.getFirstEntity(); + TestdataLavishEntity group1Entity1 = solution.getEntityList().get(1); + TestdataLavishEntity group1Entity3 = solution.getEntityList().get(3); + TestdataLavishEntity group1Entity5 = solution.getEntityList().get(5); + TestdataLavishEntity group1Entity7 = solution.getEntityList().get(7); scoreDirector.beforeEntityRemoved(entity); solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); assertScore(scoreDirector, assertMatchWithScore(-1, - asMap(solution.getEntityGroupList().get(1), 12L))); + asMap(solution.getEntityGroupList().get(1), 12L)) + .withIndictedObjects(group1Entity1, group1Entity3, group1Entity5, group1Entity7)); } @TestTemplate @@ -286,18 +302,21 @@ void biGroupByRegrouped() { TestdataLavishEntity entity = solution.getFirstEntity(); TestdataLavishEntity entity2 = solution.getEntityList().get(1); + TestdataLavishEntity entityInGroup0_2 = solution.getEntityList().get(2); + TestdataLavishEntity entityInGroup1_3 = solution.getEntityList().get(3); // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, entity.getEntityGroup(), 1L), - assertMatchWithScore(-1, entity2.getEntityGroup(), 1L)); + assertMatchWithScore(-1, entity.getEntityGroup(), 1L).withIndictedObjects(entity, entityInGroup0_2), + assertMatchWithScore(-1, entity2.getEntityGroup(), 1L).withIndictedObjects(entity2, entityInGroup1_3)); // Incremental scoreDirector.beforeEntityRemoved(entity); solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); - assertScore(scoreDirector, assertMatchWithScore(-1, entity2.getEntityGroup(), 1L)); + assertScore(scoreDirector, + assertMatchWithScore(-1, entity2.getEntityGroup(), 1L).withIndictedObjects(entity2, entityInGroup1_3)); } @TestTemplate @@ -318,18 +337,25 @@ void triGroupByRegrouped() { TestdataLavishEntity entity = solution.getFirstEntity(); TestdataLavishEntity entity2 = solution.getEntityList().get(1); + TestdataLavishEntity entityInGroup0_2 = solution.getEntityList().get(2); + TestdataLavishEntity entityInGroup0_4 = solution.getEntityList().get(4); + TestdataLavishEntity entityInGroup1_3 = solution.getEntityList().get(3); + TestdataLavishEntity entityInGroup1_5 = solution.getEntityList().get(5); // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, entity.getEntityGroup(), 1L), - assertMatchWithScore(-1, entity2.getEntityGroup(), 1L)); + assertMatchWithScore(-1, entity.getEntityGroup(), 1L).withIndictedObjects(entity, entityInGroup0_2, + entityInGroup0_4), + assertMatchWithScore(-1, entity2.getEntityGroup(), 1L).withIndictedObjects(entity2, entityInGroup1_3, + entityInGroup1_5)); // Incremental scoreDirector.beforeEntityRemoved(entity); solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); - assertScore(scoreDirector, assertMatchWithScore(-1, entity2.getEntityGroup(), 1L)); + assertScore(scoreDirector, assertMatchWithScore(-1, entity2.getEntityGroup(), 1L).withIndictedObjects(entity2, + entityInGroup1_3, entityInGroup1_5)); } @TestTemplate @@ -354,18 +380,27 @@ void quadGroupByRegrouped() { TestdataLavishEntity entity = solution.getFirstEntity(); TestdataLavishEntity entity2 = solution.getEntityList().get(1); + TestdataLavishEntity entityInGroup0_2 = solution.getEntityList().get(2); + TestdataLavishEntity entityInGroup0_4 = solution.getEntityList().get(4); + TestdataLavishEntity entityInGroup0_6 = solution.getEntityList().get(6); + TestdataLavishEntity entityInGroup1_3 = solution.getEntityList().get(3); + TestdataLavishEntity entityInGroup1_5 = solution.getEntityList().get(5); + TestdataLavishEntity entityInGroup1_7 = solution.getEntityList().get(7); // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, entity.getEntityGroup(), 1L), - assertMatchWithScore(-1, entity2.getEntityGroup(), 1L)); + assertMatchWithScore(-1, entity.getEntityGroup(), 1L).withIndictedObjects(entity, entityInGroup0_2, + entityInGroup0_4, entityInGroup0_6), + assertMatchWithScore(-1, entity2.getEntityGroup(), 1L).withIndictedObjects(entity2, entityInGroup1_3, + entityInGroup1_5, entityInGroup1_7)); // Incremental scoreDirector.beforeEntityRemoved(entity); solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); - assertScore(scoreDirector, assertMatchWithScore(-1, entity2.getEntityGroup(), 1L)); + assertScore(scoreDirector, assertMatchWithScore(-1, entity2.getEntityGroup(), 1L).withIndictedObjects(entity2, + entityInGroup1_3, entityInGroup1_5, entityInGroup1_7)); } @TestTemplate @@ -383,18 +418,23 @@ void biGroupByRegroupedDouble() { TestdataLavishEntity entity = solution.getFirstEntity(); TestdataLavishEntity entity2 = solution.getEntityList().get(1); + TestdataLavishEntity entityInGroup0_2 = solution.getEntityList().get(2); + TestdataLavishEntity entityInGroup1_3 = solution.getEntityList().get(3); // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, new Object[] { entity.getEntityGroup().toString(), 1L }), - assertMatchWithScore(-1, new Object[] { entity2.getEntityGroup().toString(), 1L })); + assertMatchWithScore(-1, new Object[] { entity.getEntityGroup().toString(), 1L }).withIndictedObjects(entity, + entityInGroup0_2), + assertMatchWithScore(-1, new Object[] { entity2.getEntityGroup().toString(), 1L }).withIndictedObjects(entity2, + entityInGroup1_3)); // Incremental scoreDirector.beforeEntityRemoved(entity); solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); - assertScore(scoreDirector, assertMatchWithScore(-1, new Object[] { entity2.getEntityGroup().toString(), 1L })); + assertScore(scoreDirector, assertMatchWithScore(-1, new Object[] { entity2.getEntityGroup().toString(), 1L }) + .withIndictedObjects(entity2, entityInGroup1_3)); } @TestTemplate @@ -417,18 +457,25 @@ void triGroupByRegroupedDouble() { TestdataLavishEntity entity = solution.getFirstEntity(); TestdataLavishEntity entity2 = solution.getEntityList().get(1); + TestdataLavishEntity entityInGroup0_2 = solution.getEntityList().get(2); + TestdataLavishEntity entityInGroup0_4 = solution.getEntityList().get(4); + TestdataLavishEntity entityInGroup1_3 = solution.getEntityList().get(3); + TestdataLavishEntity entityInGroup1_5 = solution.getEntityList().get(5); // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, new Object[] { entity.getEntityGroup().toString(), 1L }), - assertMatchWithScore(-1, new Object[] { entity2.getEntityGroup().toString(), 1L })); + assertMatchWithScore(-1, new Object[] { entity.getEntityGroup().toString(), 1L }).withIndictedObjects(entity, + entityInGroup0_2, entityInGroup0_4), + assertMatchWithScore(-1, new Object[] { entity2.getEntityGroup().toString(), 1L }).withIndictedObjects(entity2, + entityInGroup1_3, entityInGroup1_5)); // Incremental scoreDirector.beforeEntityRemoved(entity); solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); - assertScore(scoreDirector, assertMatchWithScore(-1, new Object[] { entity2.getEntityGroup().toString(), 1L })); + assertScore(scoreDirector, assertMatchWithScore(-1, new Object[] { entity2.getEntityGroup().toString(), 1L }) + .withIndictedObjects(entity2, entityInGroup1_3, entityInGroup1_5)); } @TestTemplate @@ -454,18 +501,27 @@ void quadGroupByRegroupedDouble() { TestdataLavishEntity entity = solution.getFirstEntity(); TestdataLavishEntity entity2 = solution.getEntityList().get(1); + TestdataLavishEntity entityInGroup0_2 = solution.getEntityList().get(2); + TestdataLavishEntity entityInGroup0_4 = solution.getEntityList().get(4); + TestdataLavishEntity entityInGroup0_6 = solution.getEntityList().get(6); + TestdataLavishEntity entityInGroup1_3 = solution.getEntityList().get(3); + TestdataLavishEntity entityInGroup1_5 = solution.getEntityList().get(5); + TestdataLavishEntity entityInGroup1_7 = solution.getEntityList().get(7); // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, new Object[] { entity.getEntityGroup().toString(), 1L }), - assertMatchWithScore(-1, new Object[] { entity2.getEntityGroup().toString(), 1L })); + assertMatchWithScore(-1, new Object[] { entity.getEntityGroup().toString(), 1L }).withIndictedObjects(entity, + entityInGroup0_2, entityInGroup0_4, entityInGroup0_6), + assertMatchWithScore(-1, new Object[] { entity2.getEntityGroup().toString(), 1L }).withIndictedObjects(entity2, + entityInGroup1_3, entityInGroup1_5, entityInGroup1_7)); // Incremental scoreDirector.beforeEntityRemoved(entity); solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); - assertScore(scoreDirector, assertMatchWithScore(-1, new Object[] { entity2.getEntityGroup().toString(), 1L })); + assertScore(scoreDirector, assertMatchWithScore(-1, new Object[] { entity2.getEntityGroup().toString(), 1L }) + .withIndictedObjects(entity2, entityInGroup1_3, entityInGroup1_5, entityInGroup1_7)); } @TestTemplate @@ -491,15 +547,20 @@ void existsAfterGroupBy() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-2, solution.getFirstEntityGroup(), 2L), - assertMatchWithScore(-2, entityGroup1, 2L)); + assertMatchWithScore(-2, solution.getFirstEntityGroup(), 2L) + .withIndictedObjects(solution.getFirstEntityGroup(), solution.getFirstEntity(), + entity3), + assertMatchWithScore(-2, entityGroup1, 2L) + .withIndictedObjects(entityGroup1, entity1, entity2)); // Incremental scoreDirector.beforeProblemFactRemoved(entityGroup1); solution.getEntityGroupList().remove(entityGroup1); scoreDirector.afterProblemFactRemoved(entityGroup1); assertScore(scoreDirector, - assertMatchWithScore(-2, solution.getFirstEntityGroup(), 2L)); + assertMatchWithScore(-2, solution.getFirstEntityGroup(), 2L) + .withIndictedObjects(solution.getFirstEntityGroup(), solution.getFirstEntity(), + entity3)); } @TestTemplate @@ -526,15 +587,17 @@ void groupByAfterExists() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-2, solution.getFirstEntityGroup(), 2L), - assertMatchWithScore(-2, entityGroup1, 2L)); + assertMatchWithScore(-2, solution.getFirstEntityGroup(), 2L).withIndictedObjects(solution.getFirstEntityGroup(), + solution.getFirstEntity(), entity3), + assertMatchWithScore(-2, entityGroup1, 2L).withIndictedObjects(entityGroup1, entity1, entity2)); // Incremental scoreDirector.beforeProblemFactRemoved(entityGroup1); solution.getEntityGroupList().remove(entityGroup1); scoreDirector.afterProblemFactRemoved(entityGroup1); assertScore(scoreDirector, - assertMatchWithScore(-2, solution.getFirstEntityGroup(), 2L)); + assertMatchWithScore(-2, solution.getFirstEntityGroup(), 2L).withIndictedObjects(solution.getFirstEntityGroup(), + solution.getFirstEntity(), entity3)); } @TestTemplate @@ -561,15 +624,20 @@ void groupByAfterExistsBi() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-3, solution.getFirstEntityGroup(), 3L), - assertMatchWithScore(-3, entityGroup1, 3L)); + assertMatchWithScore(-3, solution.getFirstEntityGroup(), 3L).withIndictedObjects( + solution.getFirstEntityGroup(), solution.getFirstEntity(), + entity1, entity2, entity3), + assertMatchWithScore(-3, entityGroup1, 3L).withIndictedObjects( + entityGroup1, entity1, entity2, entity3)); // Incremental scoreDirector.beforeProblemFactRemoved(entityGroup1); solution.getEntityGroupList().remove(entityGroup1); scoreDirector.afterProblemFactRemoved(entityGroup1); assertScore(scoreDirector, - assertMatchWithScore(-3, solution.getFirstEntityGroup(), 3L)); + assertMatchWithScore(-3, solution.getFirstEntityGroup(), 3L).withIndictedObjects( + solution.getFirstEntityGroup(), solution.getFirstEntity(), + entity1, entity2, entity3)); } @TestTemplate @@ -639,10 +707,10 @@ void reusedStreamsInJoin() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, entity1, entity1), - assertMatchWithScore(-1, entity1, entity3), - assertMatchWithScore(-1, entity3, entity1), - assertMatchWithScore(-1, entity3, entity3)); + assertMatchWithScore(-1, entity1, entity1).withIndictedObjects(entity1), + assertMatchWithScore(-1, entity1, entity3).withIndictedObjects(entity1, entity3), + assertMatchWithScore(-1, entity3, entity1).withIndictedObjects(entity3, entity1), + assertMatchWithScore(-1, entity3, entity3).withIndictedObjects(entity3)); } } diff --git a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/AbstractConstraintStreamTest.java b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/AbstractConstraintStreamTest.java index 530b4205c8d..6487bc17450 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/AbstractConstraintStreamTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/AbstractConstraintStreamTest.java @@ -69,6 +69,7 @@ protected void assertScore(InnerScoreDirector assertableMatch.score) .sum(); if (implSupport.constraintMatchPolicy().isJustificationEnabled()) { + var indictmentsEnabled = implSupport.constraintMatchPolicy().isIndictmentsEnabled(); for (var assertableMatch : assertableMatches) { var constraintMatchTotals = scoreDirector.getConstraintMatchTotalMap(); @@ -78,7 +79,8 @@ protected void assertScore(InnerScoreDirector assertableMatch.isEqualTo(obj, indictmentsEnabled))) { fail("The assertableMatch (" + assertableMatch + ") is lacking," + " it's not in the constraintMatchSet (" + constraintMatchTotal.getConstraintMatchSet() + ")."); @@ -89,7 +91,7 @@ protected void assertScore(InnerScoreDirector assertableMatch.constraintRef.equals(constraintMatch.getConstraintRef())) - .noneMatch(assertableMatch -> assertableMatch.isEqualTo(constraintMatch))) { + .noneMatch(assertableMatch -> assertableMatch.isEqualTo(constraintMatch, indictmentsEnabled))) { fail("The constraintMatch (" + constraintMatch + ") is in excess," + " it's not in the assertableMatches (" + Arrays.toString(assertableMatches) + ")."); } @@ -127,14 +129,21 @@ protected static class AssertableMatch { private final int score; private final ConstraintRef constraintRef; private final List justificationList; + private List indictmentList; public AssertableMatch(int score, ConstraintRef constraintRef, Object... justifications) { this.justificationList = Arrays.asList(justifications); this.constraintRef = constraintRef; this.score = score; + this.indictmentList = justificationList; } - public boolean isEqualTo(ConstraintMatch constraintMatch) { + public AssertableMatch withIndictedObjects(Object... indictedObjects) { + this.indictmentList = Arrays.asList(indictedObjects); + return this; + } + + public boolean isEqualTo(ConstraintMatch constraintMatch, boolean indictmentsEnabled) { if (score != ((SimpleScore) constraintMatch.getScore()).score()) { return false; } @@ -148,19 +157,32 @@ public boolean isEqualTo(ConstraintMatch constraintMatch) { return false; } // Can't simply compare the lists, since the elements may be in different orders. The order is not relevant. - return justificationList.containsAll(actualJustificationList); + if (!justificationList.containsAll(actualJustificationList)) { + return false; + } } else { // Support for custom justification mapping. if (justificationList.size() != 1) { Assertions.fail("Expected number of justifications (" + justificationList.size() + ") does not match actual (1; " + justification + ")."); } - return justification == justificationList.getFirst(); + if (justification != justificationList.getFirst()) { + return false; + } + } + if (!indictmentsEnabled) { + return true; + } + var indictedObjects = constraintMatch.getIndictedObjects(); + if (indictedObjects.size() != indictmentList.size()) { + return false; } + return indictmentList.containsAll(indictedObjects); } @Override public String toString() { - return constraintRef + " " + justificationList + "=" + score; + return "%s %s=%d (indicting %s)".formatted(constraintRef, justificationList, score, + indictmentList); } } diff --git a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/ConstraintStreamTestExtension.java b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/ConstraintStreamTestExtension.java index c16f5df7d54..a2eeaca2027 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/ConstraintStreamTestExtension.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/ConstraintStreamTestExtension.java @@ -33,7 +33,10 @@ public boolean supportsTestTemplate(ExtensionContext context) { @Override public Stream provideTestTemplateInvocationContexts(ExtensionContext context) { return Stream - .of(ConstraintMatchPolicy.ENABLED, ConstraintMatchPolicy.ENABLED_WITHOUT_JUSTIFICATIONS, + .of(ConstraintMatchPolicy.ENABLED, + ConstraintMatchPolicy.ENABLED_WITHOUT_INDICTMENTS, + ConstraintMatchPolicy.ENABLED_WITHOUT_JUSTIFICATIONS, + ConstraintMatchPolicy.ENABLED_WITHOUT_JUSTIFICATIONS_AND_INDICTMENTS, ConstraintMatchPolicy.DISABLED) .map(ConstraintStreamTestExtension::invocationContext); } diff --git a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/bi/AbstractBiConstraintStreamPrecomputeTest.java b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/bi/AbstractBiConstraintStreamPrecomputeTest.java index 032a4493f05..48d9ae75ea3 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/bi/AbstractBiConstraintStreamPrecomputeTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/bi/AbstractBiConstraintStreamPrecomputeTest.java @@ -1,5 +1,7 @@ package ai.timefold.solver.core.impl.score.stream.common.bi; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.function.Function; @@ -11,7 +13,6 @@ import ai.timefold.solver.core.impl.score.stream.common.AbstractConstraintStreamTest; import ai.timefold.solver.core.impl.score.stream.common.ConstraintStreamImplSupport; import ai.timefold.solver.core.impl.score.stream.common.ConstraintStreamPrecomputeTest; -import ai.timefold.solver.core.impl.util.Pair; import ai.timefold.solver.core.testdomain.score.lavish.TestdataLavishEntity; import ai.timefold.solver.core.testdomain.score.lavish.TestdataLavishEntityGroup; import ai.timefold.solver.core.testdomain.score.lavish.TestdataLavishSolution; @@ -354,9 +355,27 @@ public void filter_1_changed_forEachUnfilteredUniquePair() { assertMatch(entity2, entity3)); } + record Expected(A a, B b, Object... indicted) { + Expected addIndicted(Object indictedObject) { + for (var object : indicted) { + if (object == indictedObject) { + return this; + } + } + var newIndictments = Arrays.copyOf(indicted, indicted.length + 1); + newIndictments[indicted.length] = indictedObject; + return new Expected<>(a, b, newIndictments); + } + } + + Expected expect(A a, B b, Object... indicted) { + return new Expected<>(a, b, indicted); + } + private void assertPrecompute(TestdataLavishSolution solution, - List> expectedValues, + List> expectedValues, Function> entityStreamSupplier) { + expectedValues = new ArrayList<>(expectedValues); var scoreDirector = buildScoreDirector(factory -> factory.precompute(entityStreamSupplier) .ifExists(TestdataLavishEntity.class) @@ -371,11 +390,16 @@ private void assertPrecompute(TestdataLavishSolution solution, scoreDirector.beforeVariableChanged(entity, "value"); entity.setValue(solution.getFirstValue()); scoreDirector.afterVariableChanged(entity, "value"); + var listIterator = expectedValues.listIterator(); + while (listIterator.hasNext()) { + var expected = listIterator.next(); + listIterator.set(expected.addIndicted(entity)); + } } assertScore(scoreDirector, expectedValues.stream() - .map(pair -> new Object[] { pair.key(), pair.value() }) - .map(AbstractConstraintStreamTest::assertMatch) + .map(expected -> assertMatch(expected.a, expected.b) + .withIndictedObjects(expected.indicted)) .toArray(AssertableMatch[]::new)); } @@ -384,15 +408,19 @@ private void assertPrecompute(TestdataLavishSolution solution, public void ifExists() { var solution = TestdataLavishSolution.generateEmptySolution(); var entityWithoutGroup = new TestdataLavishEntity(); + entityWithoutGroup.setCode("A"); var entityWithGroup = new TestdataLavishEntity(); + entityWithGroup.setCode("B"); var entityGroup = new TestdataLavishEntityGroup(); + entityGroup.setCode("C"); entityWithGroup.setEntityGroup(entityGroup); solution.getEntityList().addAll(List.of(entityWithoutGroup, entityWithGroup)); solution.getEntityGroupList().add(entityGroup); var value = new TestdataLavishValue(); + value.setCode("D"); solution.getValueList().add(value); - assertPrecompute(solution, List.of(new Pair<>(entityWithGroup, value)), + assertPrecompute(solution, List.of(expect(entityWithGroup, value, entityWithGroup, value, entityGroup)), pf -> pf.forEachUnfiltered(TestdataLavishEntity.class) .join(TestdataLavishValue.class) .ifExists(TestdataLavishEntityGroup.class, Joiners.equal( @@ -413,7 +441,7 @@ public void ifNotExists() { var value = new TestdataLavishValue(); solution.getValueList().add(value); - assertPrecompute(solution, List.of(new Pair<>(entityWithoutGroup, value)), + assertPrecompute(solution, List.of(expect(entityWithoutGroup, value, entityWithoutGroup, value)), pf -> pf.forEachUnfiltered(TestdataLavishEntity.class) .join(TestdataLavishValue.class) .ifNotExists(TestdataLavishEntityGroup.class, Joiners.equal( @@ -434,7 +462,7 @@ public void groupBy() { var value = new TestdataLavishValue(); solution.getValueList().add(value); - assertPrecompute(solution, List.of(new Pair<>(entityGroup, 1L)), + assertPrecompute(solution, List.of(expect(entityGroup, 1L)), pf -> pf.forEachUnfiltered(TestdataLavishEntity.class) .filter(entity -> entity.getEntityGroup() != null) .groupBy(TestdataLavishEntity::getEntityGroup, ConstraintCollectors.count())); @@ -453,8 +481,8 @@ public void flatten() { var value = new TestdataLavishValue(); solution.getValueList().add(value); - assertPrecompute(solution, List.of(new Pair<>(entityWithoutGroup, entityWithoutGroup), - new Pair<>(entityWithGroup, entityWithoutGroup)), + assertPrecompute(solution, List.of(expect(entityWithoutGroup, entityWithoutGroup), + expect(entityWithGroup, entityWithoutGroup)), pf -> pf.forEachUnfiltered(TestdataLavishEntity.class) .flatten(List::of)); } @@ -475,8 +503,8 @@ record ValueHolder(int value) { var value = new TestdataLavishValue(); solution.getValueList().add(value); - assertPrecompute(solution, List.of(new Pair<>(entity1, new ValueHolder(entity1.getIntegerProperty())), - new Pair<>(entity2, new ValueHolder(entity2.getIntegerProperty()))), + assertPrecompute(solution, List.of(expect(entity1, new ValueHolder(entity1.getIntegerProperty())), + expect(entity2, new ValueHolder(entity2.getIntegerProperty()))), pf -> pf.forEachUnfiltered(TestdataLavishEntity.class) .flatten(entity -> List.of(new ValueHolder(entity.getIntegerProperty())))); } @@ -494,8 +522,8 @@ public void flattenLast() { var value = new TestdataLavishValue(); solution.getValueList().add(value); - assertPrecompute(solution, List.of(new Pair<>(entityWithoutGroup, value), - new Pair<>(entityWithGroup, value)), + assertPrecompute(solution, List.of(expect(entityWithoutGroup, value, entityWithoutGroup, value), + expect(entityWithGroup, value, entityWithGroup, value)), pf -> pf.forEachUnfiltered(TestdataLavishEntity.class) .groupBy(ConstraintCollectors.toList()) .flattenLast(entityList -> entityList) @@ -518,8 +546,8 @@ record ValueHolder(int value) { var value = new TestdataLavishValue(); solution.getValueList().add(value); - assertPrecompute(solution, List.of(new Pair<>(new ValueHolder(1), value), - new Pair<>(new ValueHolder(2), value)), + assertPrecompute(solution, List.of(expect(new ValueHolder(1), value, entity1, value), + expect(new ValueHolder(2), value, entity2, value)), pf -> pf.forEachUnfiltered(TestdataLavishEntity.class) .groupBy(ConstraintCollectors.toList()) .flattenLast(entityList -> entityList @@ -544,8 +572,8 @@ public void map() { var value = new TestdataLavishValue(); solution.getValueList().add(value); - assertPrecompute(solution, List.of(new Pair<>(entityGroup, value), - new Pair<>(entityGroup, value)), + assertPrecompute(solution, List.of(expect(entityGroup, value, entityWithGroup1, value), + expect(entityGroup, value, entityWithGroup2, value)), pf -> pf.forEachUnfiltered(TestdataLavishEntity.class) .join(TestdataLavishValue.class) .filter((entity, joinedValue) -> entity.getEntityGroup() != null) @@ -558,15 +586,21 @@ public void map() { public void concat() { var solution = TestdataLavishSolution.generateEmptySolution(); var entityWithoutGroup = new TestdataLavishEntity(); + entityWithoutGroup.setCode("A"); var entityWithGroup = new TestdataLavishEntity(); + entityWithGroup.setCode("B"); var entityGroup = new TestdataLavishEntityGroup(); + entityGroup.setCode("C"); entityWithGroup.setEntityGroup(entityGroup); solution.getEntityList().addAll(List.of(entityWithoutGroup, entityWithGroup)); solution.getEntityGroupList().add(entityGroup); var value = new TestdataLavishValue(); + value.setCode("D"); solution.getValueList().add(value); - assertPrecompute(solution, List.of(new Pair<>(entityWithoutGroup, value), new Pair<>(entityWithGroup, value)), + assertPrecompute(solution, List.of( + expect(entityWithoutGroup, value, entityWithoutGroup, value), + expect(entityWithGroup, value, entityWithGroup, value)), pf -> pf.forEachUnfiltered(TestdataLavishEntity.class) .join(TestdataLavishValue.class) .filter((entity, joinedValue) -> entity.getEntityGroup() == null) @@ -590,7 +624,7 @@ public void distinct() { var value = new TestdataLavishValue(); solution.getValueList().add(value); - assertPrecompute(solution, List.of(new Pair<>(entityGroup, value)), + assertPrecompute(solution, List.of(expect(entityGroup, value, entityWithGroup1, entityWithGroup2, value)), pf -> pf.forEachUnfiltered(TestdataLavishEntity.class) .join(TestdataLavishValue.class) .filter((entity, joinedValue) -> entity.getEntityGroup() != null) @@ -615,9 +649,9 @@ public void complement() { solution.getValueList().add(value); assertPrecompute(solution, List.of( - new Pair<>(entityWithGroup1, value), - new Pair<>(entityWithGroup2, value), - new Pair<>(entityWithoutGroup, null)), + expect(entityWithGroup1, value, entityWithGroup1, value), + expect(entityWithGroup2, value, entityWithGroup2, value), + expect(entityWithoutGroup, null, entityWithoutGroup)), pf -> pf.forEachUnfiltered(TestdataLavishEntity.class) .join(TestdataLavishValue.class) .filter((entity, joinedValue) -> entity.getEntityGroup() != null) diff --git a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/bi/AbstractBiConstraintStreamTest.java b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/bi/AbstractBiConstraintStreamTest.java index 0fd2d1f078f..1642ba043f6 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/bi/AbstractBiConstraintStreamTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/bi/AbstractBiConstraintStreamTest.java @@ -166,10 +166,10 @@ public void join_filterOnAssignedValue_unassignOne() { scoreDirector.afterListVariableElementAssigned(entity, "valueList", value1); assertScore(scoreDirector, - assertMatch(entity, value1, value1), - assertMatch(entity, value1, value2), - assertMatch(entity, value2, value1), - assertMatch(entity, value2, value2)); + assertMatch(entity, value1, value1).withIndictedObjects(entity, value1), + assertMatch(entity, value1, value2).withIndictedObjects(entity, value1, value2), + assertMatch(entity, value2, value1).withIndictedObjects(entity, value1, value2), + assertMatch(entity, value2, value2).withIndictedObjects(entity, value2)); // Unassign and check result. var variableDescriptor = scoreDirector.getSolutionDescriptor() @@ -181,7 +181,7 @@ public void join_filterOnAssignedValue_unassignOne() { scoreDirector.afterListVariableElementUnassigned(variableDescriptor, value1); assertScore(scoreDirector, - assertMatch(entity, value2, value2)); + assertMatch(entity, value2, value2).withIndictedObjects(entity, value2)); // Reassign and check result. scoreDirector.beforeListVariableElementAssigned(variableDescriptor, value1); @@ -191,10 +191,10 @@ public void join_filterOnAssignedValue_unassignOne() { scoreDirector.afterListVariableElementAssigned(variableDescriptor, value1); assertScore(scoreDirector, - assertMatch(entity, value1, value1), - assertMatch(entity, value1, value2), - assertMatch(entity, value2, value1), - assertMatch(entity, value2, value2)); + assertMatch(entity, value1, value1).withIndictedObjects(entity, value1), + assertMatch(entity, value1, value2).withIndictedObjects(entity, value1, value2), + assertMatch(entity, value2, value1).withIndictedObjects(entity, value1, value2), + assertMatch(entity, value2, value2).withIndictedObjects(entity, value2)); } } @@ -231,7 +231,7 @@ public void join_filterOnAssignedValue_unassignOneReassignOther() { scoreDirector.afterListVariableElementAssigned(entity, "valueList", value1); assertScore(scoreDirector, - assertMatch(entity, value1, value1)); + assertMatch(entity, value1, value1).withIndictedObjects(entity, value1)); // Unassign+assign and check result. var variableDescriptor = scoreDirector.getSolutionDescriptor() @@ -245,7 +245,7 @@ public void join_filterOnAssignedValue_unassignOneReassignOther() { scoreDirector.afterListVariableElementAssigned(variableDescriptor, value2); assertScore(scoreDirector, - assertMatch(entity, value2, value2)); + assertMatch(entity, value2, value2).withIndictedObjects(entity, value2)); // Reassign and check result. scoreDirector.beforeListVariableElementAssigned(variableDescriptor, value1); @@ -257,7 +257,7 @@ public void join_filterOnAssignedValue_unassignOneReassignOther() { scoreDirector.afterListVariableElementAssigned(variableDescriptor, value1); assertScore(scoreDirector, - assertMatch(entity, value1, value1)); + assertMatch(entity, value1, value1).withIndictedObjects(entity, value1)); } } @@ -710,24 +710,24 @@ public void joinAfterGroupBy() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(1L, 1L, extra1), - assertMatch(1L, 1L, extra2)); + assertMatch(1L, 1L, extra1).withIndictedObjects(entity1, entity2, extra1), + assertMatch(1L, 1L, extra2).withIndictedObjects(entity1, entity2, extra2)); // Incremental scoreDirector.beforeVariableChanged(entity2, "value"); entity2.setValue(value2); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(2L, 2L, extra1), - assertMatch(2L, 2L, extra2)); + assertMatch(2L, 2L, extra1).withIndictedObjects(entity1, entity2, extra1), + assertMatch(2L, 2L, extra2).withIndictedObjects(entity1, entity2, extra2)); // Incremental scoreDirector.beforeEntityRemoved(entity2); solution.getEntityList().remove(entity2); scoreDirector.afterEntityRemoved(entity2); assertScore(scoreDirector, - assertMatch(1L, 1L, extra1), - assertMatch(1L, 1L, extra2)); + assertMatch(1L, 1L, extra1).withIndictedObjects(entity1, extra1), + assertMatch(1L, 1L, extra2).withIndictedObjects(entity1, extra2)); } @Override @@ -776,8 +776,8 @@ public void ifExists_filterOnAssignedValue_unassignOne() { scoreDirector.afterListVariableElementAssigned(entity, "valueList", value1); assertScore(scoreDirector, - assertMatch(entity, value1), - assertMatch(entity, value2)); + assertMatch(entity, value1).withIndictedObjects(entity, value1, value2), + assertMatch(entity, value2).withIndictedObjects(entity, value1, value2)); // Unassign and check result. var variableDescriptor = scoreDirector.getSolutionDescriptor() @@ -789,7 +789,7 @@ public void ifExists_filterOnAssignedValue_unassignOne() { scoreDirector.afterListVariableElementUnassigned(variableDescriptor, value1); assertScore(scoreDirector, - assertMatch(entity, value2)); + assertMatch(entity, value2).withIndictedObjects(entity, value2)); // Reassign and check result. scoreDirector.beforeListVariableElementAssigned(variableDescriptor, value1); @@ -799,8 +799,8 @@ public void ifExists_filterOnAssignedValue_unassignOne() { scoreDirector.afterListVariableElementAssigned(variableDescriptor, value1); assertScore(scoreDirector, - assertMatch(entity, value1), - assertMatch(entity, value2)); + assertMatch(entity, value1).withIndictedObjects(entity, value1, value2), + assertMatch(entity, value2).withIndictedObjects(entity, value1, value2)); } } @@ -917,7 +917,8 @@ public void ifExists_0Join1Filter() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(solution.getFirstEntity(), entity2)); + assertMatch(solution.getFirstEntity(), entity2) + .withIndictedObjects(solution.getFirstEntity(), entity2, solution.getFirstEntityGroup())); // Incremental var toRemove = solution.getFirstEntityGroup(); @@ -949,17 +950,22 @@ public void ifExists_1Join0Filter() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(solution.getFirstEntity(), entity1), - assertMatch(solution.getFirstEntity(), entity2), - assertMatch(entity1, entity2)); + assertMatch(solution.getFirstEntity(), entity1) + .withIndictedObjects(solution.getFirstEntityGroup(), solution.getFirstEntity(), entity1), + assertMatch(solution.getFirstEntity(), entity2) + .withIndictedObjects(solution.getFirstEntityGroup(), solution.getFirstEntity(), entity2), + assertMatch(entity1, entity2) + .withIndictedObjects(entityGroup, entity1, entity2)); // Incremental scoreDirector.beforeProblemFactRemoved(entityGroup); solution.getEntityGroupList().remove(entityGroup); scoreDirector.afterProblemFactRemoved(entityGroup); assertScore(scoreDirector, - assertMatch(solution.getFirstEntity(), entity1), - assertMatch(solution.getFirstEntity(), entity2)); + assertMatch(solution.getFirstEntity(), entity1) + .withIndictedObjects(solution.getFirstEntityGroup(), solution.getFirstEntity(), entity1), + assertMatch(solution.getFirstEntity(), entity2) + .withIndictedObjects(solution.getFirstEntityGroup(), solution.getFirstEntity(), entity2)); } @Override @@ -986,7 +992,8 @@ public void ifExists_1Join1Filter() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, entity2)); + assertMatch(entity1, entity2) + .withIndictedObjects(entity1, entity2, entityGroup)); // Incremental scoreDirector.beforeProblemFactRemoved(entityGroup); @@ -1221,21 +1228,21 @@ public void ifExistsAfterGroupBy() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(1L, 1L)); + assertMatch(1L, 1L).withIndictedObjects(entity1, entity2)); // Incremental scoreDirector.beforeVariableChanged(entity2, "value"); entity2.setValue(value2); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(2L, 2L)); + assertMatch(2L, 2L).withIndictedObjects(entity1, entity2)); // Incremental scoreDirector.beforeEntityRemoved(entity2); solution.getEntityList().remove(entity2); scoreDirector.afterEntityRemoved(entity2); assertScore(scoreDirector, - assertMatch(1L, 1L)); + assertMatch(1L, 1L).withIndictedObjects(entity1)); } @Override @@ -1258,11 +1265,16 @@ public void groupBy_1Mapping0Collector() { .penalize(SimpleScore.ONE) .asConstraint(TEST_CONSTRAINT_ID)); - // From scratch + // From scratch: groupBy accumulates indicted objects from all tuples in the group scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, solution.getFirstEntityGroup()), - assertMatchWithScore(-1, entityGroup1)); + assertMatchWithScore(-1, solution.getFirstEntityGroup()) + .withIndictedObjects(solution.getFirstEntity(), solution.getEntityList().get(1), + solution.getEntityList().get(2), + solution.getEntityList().get(3), solution.getEntityList().get(4), + solution.getEntityList().get(5), + solution.getEntityList().get(6), entity3), + assertMatchWithScore(-1, entityGroup1).withIndictedObjects(entity1, entity2)); // Incremental Stream.of(entity1, entity2).forEach(entity -> { @@ -1270,7 +1282,11 @@ public void groupBy_1Mapping0Collector() { solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); }); - assertScore(scoreDirector, assertMatchWithScore(-1, solution.getFirstEntityGroup())); + assertScore(scoreDirector, assertMatchWithScore(-1, solution.getFirstEntityGroup()) + .withIndictedObjects(solution.getFirstEntity(), solution.getEntityList().get(1), + solution.getEntityList().get(2), + solution.getEntityList().get(3), solution.getEntityList().get(4), solution.getEntityList().get(5), + solution.getEntityList().get(6), entity3)); } @Override @@ -1285,11 +1301,20 @@ public void groupBy_1Mapping1Collector() { .penalize(SimpleScore.ONE) .asConstraint(TEST_CONSTRAINT_ID)); - // From scratch + // From scratch: groupBy accumulates indicted objects from all tuples in the group scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, solution.getFirstEntity().toString(), 6L), - assertMatchWithScore(-1, solution.getEntityList().get(1).toString(), 5L)); + assertMatchWithScore(-1, solution.getFirstEntity().toString(), 6L) + .withIndictedObjects(solution.getFirstEntity(), solution.getEntityList().get(1), + solution.getEntityList().get(2), + solution.getEntityList().get(3), solution.getEntityList().get(4), + solution.getEntityList().get(5), + solution.getEntityList().get(6)), + assertMatchWithScore(-1, solution.getEntityList().get(1).toString(), 5L) + .withIndictedObjects(solution.getEntityList().get(1), solution.getEntityList().get(2), + solution.getEntityList().get(3), solution.getEntityList().get(4), + solution.getEntityList().get(5), + solution.getEntityList().get(6))); // Incremental; we have a new first entity, and less entities in total. var entity = solution.getFirstEntity(); @@ -1297,7 +1322,10 @@ public void groupBy_1Mapping1Collector() { solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); assertScore(scoreDirector, - assertMatchWithScore(-1, solution.getFirstEntity().toString(), 5L)); + assertMatchWithScore(-1, solution.getFirstEntity().toString(), 5L) + .withIndictedObjects(solution.getFirstEntity(), solution.getEntityList().get(1), + solution.getEntityList().get(2), solution.getEntityList().get(3), + solution.getEntityList().get(4), solution.getEntityList().get(5))); } @Override @@ -1316,11 +1344,13 @@ public void groupBy_1Mapping2Collector() { var entity1 = solution.getFirstEntity(); var entity2 = solution.getEntityList().get(1); - // From scratch + // From scratch: groupBy accumulates indicted objects from all tuples in the group scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, entity1.toString(), 2L, singleton(entity1)), - assertMatchWithScore(-1, entity2.toString(), 1L, singleton(entity2))); + assertMatchWithScore(-1, entity1.toString(), 2L, singleton(entity1)) + .withIndictedObjects(entity1, entity2, solution.getEntityList().get(2)), + assertMatchWithScore(-1, entity2.toString(), 1L, singleton(entity2)) + .withIndictedObjects(entity2, solution.getEntityList().get(2))); // Incremental var entity = solution.getFirstEntity(); @@ -1328,7 +1358,8 @@ public void groupBy_1Mapping2Collector() { solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); assertScore(scoreDirector, - assertMatchWithScore(-1, entity2.toString(), 1L, singleton(entity2))); + assertMatchWithScore(-1, entity2.toString(), 1L, singleton(entity2)) + .withIndictedObjects(entity2, solution.getEntityList().get(1))); } @Override @@ -1350,13 +1381,15 @@ public void groupBy_1Mapping3Collector() { var entity2 = solution.getEntityList().get(1); entity2.setLongProperty(Long.MIN_VALUE); - // From scratch + // From scratch: groupBy accumulates indicted objects from all tuples in the group scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, assertMatchWithScore(-1, entity1.toString(), Long.MAX_VALUE, Long.MAX_VALUE, - singleton(entity1)), + singleton(entity1)) + .withIndictedObjects(entity1, entity2, solution.getEntityList().get(2)), assertMatchWithScore(-1, entity2.toString(), Long.MIN_VALUE, Long.MIN_VALUE, - singleton(entity2))); + singleton(entity2)) + .withIndictedObjects(entity2, solution.getEntityList().get(2))); // Incremental var entity = solution.getFirstEntity(); @@ -1365,7 +1398,8 @@ public void groupBy_1Mapping3Collector() { scoreDirector.afterEntityRemoved(entity); assertScore(scoreDirector, assertMatchWithScore(-1, entity2.toString(), Long.MIN_VALUE, Long.MIN_VALUE, - singleton(entity2))); + singleton(entity2)) + .withIndictedObjects(entity2, solution.getEntityList().get(1))); } @Override @@ -1379,16 +1413,19 @@ public void groupBy_0Mapping1Collector() { .penalize(SimpleScore.ONE, count -> count) .asConstraint(TEST_CONSTRAINT_ID)); + var entity1 = solution.getFirstEntity(); + var entity2 = solution.getEntityList().get(1); + var entity3 = solution.getEntityList().get(2); + // From scratch scoreDirector.setWorkingSolution(solution); - assertScore(scoreDirector, assertMatchWithScore(-3, 3L)); + assertScore(scoreDirector, assertMatchWithScore(-3, 3L).withIndictedObjects(entity1, entity2, entity3)); // Incremental - var entity = solution.getFirstEntity(); - scoreDirector.beforeEntityRemoved(entity); - solution.getEntityList().remove(entity); - scoreDirector.afterEntityRemoved(entity); - assertScore(scoreDirector, assertMatchWithScore(-1, 1L)); + scoreDirector.beforeEntityRemoved(entity1); + solution.getEntityList().remove(entity1); + scoreDirector.afterEntityRemoved(entity1); + assertScore(scoreDirector, assertMatchWithScore(-1, 1L).withIndictedObjects(entity2, entity3)); } @Override @@ -1403,16 +1440,18 @@ public void groupBy_0Mapping2Collector() { .asConstraint(TEST_CONSTRAINT_ID)); var entity1 = solution.getFirstEntity(); + var entity2 = solution.getEntityList().get(1); + var entity3 = solution.getEntityList().get(2); // From scratch scoreDirector.setWorkingSolution(solution); - assertScore(scoreDirector, assertMatchWithScore(-1, 3L, 2L)); + assertScore(scoreDirector, assertMatchWithScore(-1, 3L, 2L).withIndictedObjects(entity1, entity2, entity3)); // Incremental scoreDirector.beforeEntityRemoved(entity1); solution.getEntityList().remove(entity1); scoreDirector.afterEntityRemoved(entity1); - assertScore(scoreDirector, assertMatchWithScore(-1, 1L, 1L)); + assertScore(scoreDirector, assertMatchWithScore(-1, 1L, 1L).withIndictedObjects(entity2, entity3)); } @Override @@ -1437,14 +1476,14 @@ public void groupBy_0Mapping3Collector() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, 3L, 0L, 1L)); + assertMatchWithScore(-1, 3L, 0L, 1L).withIndictedObjects(entity1, entity2, entity3)); // Incremental scoreDirector.beforeEntityRemoved(entity1); solution.getEntityList().remove(entity1); scoreDirector.afterEntityRemoved(entity1); assertScore(scoreDirector, - assertMatchWithScore(-1, 1L, 1L, 1L)); + assertMatchWithScore(-1, 1L, 1L, 1L).withIndictedObjects(entity2, entity3)); } @Override @@ -1470,14 +1509,14 @@ public void groupBy_0Mapping4Collector() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, 3L, 0L, 1L, asSet(entity1, entity2))); + assertMatchWithScore(-1, 3L, 0L, 1L, asSet(entity1, entity2)).withIndictedObjects(entity1, entity2, entity3)); // Incremental scoreDirector.beforeEntityRemoved(entity1); solution.getEntityList().remove(entity1); scoreDirector.afterEntityRemoved(entity1); assertScore(scoreDirector, - assertMatchWithScore(-1, 1L, 1L, 1L, asSet(entity2))); + assertMatchWithScore(-1, 1L, 1L, 1L, asSet(entity2)).withIndictedObjects(entity2, entity3)); } @Override @@ -1493,21 +1532,23 @@ public void groupBy_2Mapping0Collector() { var group1 = solution.getEntityGroupList().get(0); var group2 = solution.getEntityGroupList().get(1); var group3 = solution.getEntityGroupList().get(2); + var entity1 = solution.getEntityList().get(0); + var entity2 = solution.getEntityList().get(1); + var entity3 = solution.getEntityList().get(2); // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, group1, group2), - assertMatchWithScore(-1, group1, group3), - assertMatchWithScore(-1, group2, group3)); + assertMatchWithScore(-1, group1, group2).withIndictedObjects(entity1, entity2), + assertMatchWithScore(-1, group1, group3).withIndictedObjects(entity1, entity3), + assertMatchWithScore(-1, group2, group3).withIndictedObjects(entity2, entity3)); // Incremental - var entity = solution.getFirstEntity(); - scoreDirector.beforeEntityRemoved(entity); - solution.getEntityList().remove(entity); - scoreDirector.afterEntityRemoved(entity); + scoreDirector.beforeEntityRemoved(entity1); + solution.getEntityList().remove(entity1); + scoreDirector.afterEntityRemoved(entity1); assertScore(scoreDirector, - assertMatchWithScore(-1, group2, group3)); + assertMatchWithScore(-1, group2, group3).withIndictedObjects(entity2, entity3)); } @Override @@ -1523,24 +1564,27 @@ public void groupBy_2Mapping1Collector() { var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); + var entity1 = solution.getEntityList().get(0); + var entity2 = solution.getEntityList().get(1); + var entity3 = solution.getEntityList().get(2); + var entity4 = solution.getEntityList().get(3); // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, group1, group1, 1L), - assertMatchWithScore(-3, group1, group2, 3L), - assertMatchWithScore(-1, group2, group1, 1L), - assertMatchWithScore(-1, group2, group2, 1L)); + assertMatchWithScore(-1, group1, group1, 1L).withIndictedObjects(entity1, entity3), + assertMatchWithScore(-3, group1, group2, 3L).withIndictedObjects(entity1, entity2, entity4, entity3), + assertMatchWithScore(-1, group2, group1, 1L).withIndictedObjects(entity2, entity3), + assertMatchWithScore(-1, group2, group2, 1L).withIndictedObjects(entity2, entity4)); - // Incremental - var entity = solution.getFirstEntity(); - scoreDirector.beforeEntityRemoved(entity); - solution.getEntityList().remove(entity); - scoreDirector.afterEntityRemoved(entity); + // Incremental: groupBy indicted objects may be stale after incremental changes (acceptable per rules) + scoreDirector.beforeEntityRemoved(entity1); + solution.getEntityList().remove(entity1); + scoreDirector.afterEntityRemoved(entity1); assertScore(scoreDirector, - assertMatchWithScore(-1, group2, group2, 1L), - assertMatchWithScore(-1, group1, group2, 1L), - assertMatchWithScore(-1, group2, group1, 1L)); + assertMatchWithScore(-1, group2, group1, 1L).withIndictedObjects(entity2, entity3), + assertMatchWithScore(-1, group2, group2, 1L).withIndictedObjects(entity2, entity4), + assertMatchWithScore(-1, group1, group2, 1L).withIndictedObjects(entity4, entity3)); } @Override @@ -1557,24 +1601,27 @@ public void groupBy_2Mapping2Collector() { var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); + var entity1 = solution.getEntityList().get(0); + var entity2 = solution.getEntityList().get(1); + var entity3 = solution.getEntityList().get(2); + var entity4 = solution.getEntityList().get(3); // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-2, group1, group1, 1L, 1L), - assertMatchWithScore(-6, group1, group2, 3L, 3L), - assertMatchWithScore(-2, group2, group1, 1L, 1L), - assertMatchWithScore(-2, group2, group2, 1L, 1L)); + assertMatchWithScore(-2, group1, group1, 1L, 1L).withIndictedObjects(entity1, entity3), + assertMatchWithScore(-6, group1, group2, 3L, 3L).withIndictedObjects(entity1, entity2, entity4, entity3), + assertMatchWithScore(-2, group2, group1, 1L, 1L).withIndictedObjects(entity2, entity3), + assertMatchWithScore(-2, group2, group2, 1L, 1L).withIndictedObjects(entity2, entity4)); - // Incremental - var entity = solution.getFirstEntity(); - scoreDirector.beforeEntityRemoved(entity); - solution.getEntityList().remove(entity); - scoreDirector.afterEntityRemoved(entity); + // Incremental: groupBy indicted objects may be stale after incremental changes (acceptable per rules) + scoreDirector.beforeEntityRemoved(entity1); + solution.getEntityList().remove(entity1); + scoreDirector.afterEntityRemoved(entity1); assertScore(scoreDirector, - assertMatchWithScore(-2, group2, group2, 1L, 1L), - assertMatchWithScore(-2, group1, group2, 1L, 1L), - assertMatchWithScore(-2, group2, group1, 1L, 1L)); + assertMatchWithScore(-2, group2, group1, 1L, 1L).withIndictedObjects(entity2, entity3), + assertMatchWithScore(-2, group2, group2, 1L, 1L).withIndictedObjects(entity2, entity4), + assertMatchWithScore(-2, group1, group2, 1L, 1L).withIndictedObjects(entity4, entity3)); } @Override @@ -1592,21 +1639,23 @@ public void groupBy_3Mapping0Collector() { var group3 = solution.getEntityGroupList().get(2); var value1 = solution.getValueList().get(0); var value2 = solution.getValueList().get(1); + var entity1 = solution.getEntityList().get(0); + var entity2 = solution.getEntityList().get(1); + var entity3 = solution.getEntityList().get(2); // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, group1, group2, value1), - assertMatchWithScore(-1, group1, group3, value1), - assertMatchWithScore(-1, group2, group3, value2)); + assertMatchWithScore(-1, group1, group2, value1).withIndictedObjects(entity1, entity2), + assertMatchWithScore(-1, group1, group3, value1).withIndictedObjects(entity1, entity3), + assertMatchWithScore(-1, group2, group3, value2).withIndictedObjects(entity2, entity3)); // Incremental - var entity = solution.getFirstEntity(); - scoreDirector.beforeEntityRemoved(entity); - solution.getEntityList().remove(entity); - scoreDirector.afterEntityRemoved(entity); + scoreDirector.beforeEntityRemoved(entity1); + solution.getEntityList().remove(entity1); + scoreDirector.afterEntityRemoved(entity1); assertScore(scoreDirector, - assertMatchWithScore(-1, group2, group3, value2)); + assertMatchWithScore(-1, group2, group3, value2).withIndictedObjects(entity2, entity3)); } @Override @@ -1625,21 +1674,23 @@ public void groupBy_3Mapping1Collector() { var group3 = solution.getEntityGroupList().get(2); var value1 = solution.getValueList().get(0); var value2 = solution.getValueList().get(1); + var entity1 = solution.getEntityList().get(0); + var entity2 = solution.getEntityList().get(1); + var entity3 = solution.getEntityList().get(2); // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, group1, group2, value1, 1L), - assertMatchWithScore(-1, group1, group3, value1, 1L), - assertMatchWithScore(-1, group2, group3, value2, 1L)); + assertMatchWithScore(-1, group1, group2, value1, 1L).withIndictedObjects(entity1, entity2), + assertMatchWithScore(-1, group1, group3, value1, 1L).withIndictedObjects(entity1, entity3), + assertMatchWithScore(-1, group2, group3, value2, 1L).withIndictedObjects(entity2, entity3)); // Incremental - var entity = solution.getFirstEntity(); - scoreDirector.beforeEntityRemoved(entity); - solution.getEntityList().remove(entity); - scoreDirector.afterEntityRemoved(entity); + scoreDirector.beforeEntityRemoved(entity1); + solution.getEntityList().remove(entity1); + scoreDirector.afterEntityRemoved(entity1); assertScore(scoreDirector, - assertMatchWithScore(-1, group2, group3, value2, 1L)); + assertMatchWithScore(-1, group2, group3, value2, 1L).withIndictedObjects(entity2, entity3)); } @Override @@ -1658,21 +1709,23 @@ public void groupBy_4Mapping0Collector() { var group3 = solution.getEntityGroupList().get(2); var value1 = solution.getValueList().get(0); var value2 = solution.getValueList().get(1); + var entity1 = solution.getEntityList().get(0); + var entity2 = solution.getEntityList().get(1); + var entity3 = solution.getEntityList().get(2); // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, group1, group2, value1, value2), - assertMatchWithScore(-1, group1, group3, value1, value1), - assertMatchWithScore(-1, group2, group3, value2, value1)); + assertMatchWithScore(-1, group1, group2, value1, value2).withIndictedObjects(entity1, entity2), + assertMatchWithScore(-1, group1, group3, value1, value1).withIndictedObjects(entity1, entity3), + assertMatchWithScore(-1, group2, group3, value2, value1).withIndictedObjects(entity2, entity3)); // Incremental - var entity = solution.getFirstEntity(); - scoreDirector.beforeEntityRemoved(entity); - solution.getEntityList().remove(entity); - scoreDirector.afterEntityRemoved(entity); + scoreDirector.beforeEntityRemoved(entity1); + solution.getEntityList().remove(entity1); + scoreDirector.afterEntityRemoved(entity1); assertScore(scoreDirector, - assertMatchWithScore(-1, group2, group3, value2, value1)); + assertMatchWithScore(-1, group2, group3, value2, value1).withIndictedObjects(entity2, entity3)); } @Override @@ -1709,22 +1762,23 @@ public void mapToUniWithDuplicates() { var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); + var entity1 = solution.getFirstEntity(); + var entity2 = solution.getEntityList().get(1); + var entity3 = solution.getEntityList().get(2); // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(asSet(group1, group2)), - assertMatch(asSet(group1, group2)), - assertMatch(asSet(group1))); - - var entity = solution.getFirstEntity(); + assertMatch(asSet(group1, group2)).withIndictedObjects(entity1, entity2), + assertMatch(asSet(group1, group2)).withIndictedObjects(entity2, entity3), + assertMatch(asSet(group1)).withIndictedObjects(entity1, entity3)); // Incremental - scoreDirector.beforeEntityRemoved(entity); - solution.getEntityList().remove(entity); - scoreDirector.afterEntityRemoved(entity); + scoreDirector.beforeEntityRemoved(entity1); + solution.getEntityList().remove(entity1); + scoreDirector.afterEntityRemoved(entity1); assertScore(scoreDirector, - assertMatch(asSet(group1, group2))); + assertMatch(asSet(group1, group2)).withIndictedObjects(entity2, entity3)); } @Override @@ -1740,22 +1794,23 @@ public void mapToUniWithoutDuplicates() { var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); var group3 = solution.getEntityGroupList().get(2); + var entity1 = solution.getFirstEntity(); + var entity2 = solution.getEntityList().get(1); + var entity3 = solution.getEntityList().get(2); // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(asSet(group1, group2)), - assertMatch(asSet(group1, group3)), - assertMatch(asSet(group2, group3))); - - var entity = solution.getFirstEntity(); + assertMatch(asSet(group1, group2)).withIndictedObjects(entity1, entity2), + assertMatch(asSet(group1, group3)).withIndictedObjects(entity1, entity3), + assertMatch(asSet(group2, group3)).withIndictedObjects(entity2, entity3)); // Incremental - scoreDirector.beforeEntityRemoved(entity); - solution.getEntityList().remove(entity); - scoreDirector.afterEntityRemoved(entity); + scoreDirector.beforeEntityRemoved(entity1); + solution.getEntityList().remove(entity1); + scoreDirector.afterEntityRemoved(entity1); assertScore(scoreDirector, - assertMatch(asSet(group2, group3))); + assertMatch(asSet(group2, group3)).withIndictedObjects(entity2, entity3)); } @Override @@ -1771,21 +1826,22 @@ public void mapToUniAndDistinctWithDuplicates() { var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); + var entity1 = solution.getFirstEntity(); + var entity2 = solution.getEntityList().get(1); + var entity3 = solution.getEntityList().get(2); - // From scratch + // From scratch: distinct accumulates indicted objects from all tuples with same key scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(asSet(group1, group2)), - assertMatch(asSet(group1))); - - var entity = solution.getFirstEntity(); + assertMatch(asSet(group1, group2)).withIndictedObjects(entity1, entity2, entity3), + assertMatch(asSet(group1)).withIndictedObjects(entity1, entity3)); // Incremental - scoreDirector.beforeEntityRemoved(entity); - solution.getEntityList().remove(entity); - scoreDirector.afterEntityRemoved(entity); + scoreDirector.beforeEntityRemoved(entity1); + solution.getEntityList().remove(entity1); + scoreDirector.afterEntityRemoved(entity1); assertScore(scoreDirector, - assertMatch(asSet(group1, group2))); + assertMatch(asSet(group1, group2)).withIndictedObjects(entity2, entity3)); } @Override @@ -1802,22 +1858,23 @@ public void mapToUniAndDistinctWithoutDuplicates() { var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); var group3 = solution.getEntityGroupList().get(2); + var entity1 = solution.getFirstEntity(); + var entity2 = solution.getEntityList().get(1); + var entity3 = solution.getEntityList().get(2); // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(asSet(group1, group2)), - assertMatch(asSet(group1, group3)), - assertMatch(asSet(group2, group3))); - - var entity = solution.getFirstEntity(); + assertMatch(asSet(group1, group2)).withIndictedObjects(entity1, entity2), + assertMatch(asSet(group1, group3)).withIndictedObjects(entity1, entity3), + assertMatch(asSet(group2, group3)).withIndictedObjects(entity2, entity3)); // Incremental - scoreDirector.beforeEntityRemoved(entity); - solution.getEntityList().remove(entity); - scoreDirector.afterEntityRemoved(entity); + scoreDirector.beforeEntityRemoved(entity1); + solution.getEntityList().remove(entity1); + scoreDirector.afterEntityRemoved(entity1); assertScore(scoreDirector, - assertMatch(asSet(group2, group3))); + assertMatch(asSet(group2, group3)).withIndictedObjects(entity2, entity3)); } @Override @@ -1837,18 +1894,21 @@ public void mapToBi() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(group1, group2), - assertMatch(group2, group1), - assertMatch(group1, group1)); + assertMatch(group1, group2).withIndictedObjects(solution.getFirstEntity(), solution.getEntityList().get(1)), + assertMatch(group2, group1).withIndictedObjects(solution.getEntityList().get(1), + solution.getEntityList().get(2)), + assertMatch(group1, group1).withIndictedObjects(solution.getFirstEntity(), solution.getEntityList().get(2))); - var entity = solution.getFirstEntity(); + var entity1 = solution.getFirstEntity(); + var entity2 = solution.getEntityList().get(1); + var entity3 = solution.getEntityList().get(2); // Incremental - scoreDirector.beforeEntityRemoved(entity); - solution.getEntityList().remove(entity); - scoreDirector.afterEntityRemoved(entity); + scoreDirector.beforeEntityRemoved(entity1); + solution.getEntityList().remove(entity1); + scoreDirector.afterEntityRemoved(entity1); assertScore(scoreDirector, - assertMatch(group2, group1)); + assertMatch(group2, group1).withIndictedObjects(entity2, entity3)); } @Override @@ -1872,18 +1932,23 @@ public void mapToTri() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(group1, group2, sum01), - assertMatch(group2, group1, sum12), - assertMatch(group1, group1, sum02)); + assertMatch(group1, group2, sum01).withIndictedObjects(solution.getFirstEntity(), + solution.getEntityList().get(1)), + assertMatch(group2, group1, sum12).withIndictedObjects(solution.getEntityList().get(1), + solution.getEntityList().get(2)), + assertMatch(group1, group1, sum02).withIndictedObjects(solution.getFirstEntity(), + solution.getEntityList().get(2))); - var entity = solution.getFirstEntity(); + var entity1 = solution.getFirstEntity(); + var entity2 = solution.getEntityList().get(1); + var entity3 = solution.getEntityList().get(2); // Incremental - scoreDirector.beforeEntityRemoved(entity); - solution.getEntityList().remove(entity); - scoreDirector.afterEntityRemoved(entity); + scoreDirector.beforeEntityRemoved(entity1); + solution.getEntityList().remove(entity1); + scoreDirector.afterEntityRemoved(entity1); assertScore(scoreDirector, - assertMatch(group2, group1, sum12)); + assertMatch(group2, group1, sum12).withIndictedObjects(entity2, entity3)); } @Override @@ -1907,18 +1972,23 @@ public void mapToQuad() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(group1, group2, value1, value2), - assertMatch(group2, group1, value2, value1), - assertMatch(group1, group1, value1, value1)); + assertMatch(group1, group2, value1, value2).withIndictedObjects(solution.getFirstEntity(), + solution.getEntityList().get(1)), + assertMatch(group2, group1, value2, value1).withIndictedObjects(solution.getEntityList().get(1), + solution.getEntityList().get(2)), + assertMatch(group1, group1, value1, value1).withIndictedObjects(solution.getFirstEntity(), + solution.getEntityList().get(2))); - var entity = solution.getFirstEntity(); + var entity1 = solution.getFirstEntity(); + var entity2 = solution.getEntityList().get(1); + var entity3 = solution.getEntityList().get(2); // Incremental - scoreDirector.beforeEntityRemoved(entity); - solution.getEntityList().remove(entity); - scoreDirector.afterEntityRemoved(entity); + scoreDirector.beforeEntityRemoved(entity1); + solution.getEntityList().remove(entity1); + scoreDirector.afterEntityRemoved(entity1); assertScore(scoreDirector, - assertMatch(group2, group1, value2, value1)); + assertMatch(group2, group1, value2, value1).withIndictedObjects(entity2, entity3)); } @Override @@ -1938,9 +2008,12 @@ public void expandToTri() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(solution.getFirstEntity(), solution.getEntityList().get(1), sum01), - assertMatch(solution.getEntityList().get(1), solution.getEntityList().get(2), sum12), - assertMatch(solution.getFirstEntity(), solution.getEntityList().get(2), sum02)); + assertMatch(solution.getFirstEntity(), solution.getEntityList().get(1), sum01) + .withIndictedObjects(solution.getFirstEntity(), solution.getEntityList().get(1)), + assertMatch(solution.getEntityList().get(1), solution.getEntityList().get(2), sum12) + .withIndictedObjects(solution.getEntityList().get(1), solution.getEntityList().get(2)), + assertMatch(solution.getFirstEntity(), solution.getEntityList().get(2), sum02) + .withIndictedObjects(solution.getFirstEntity(), solution.getEntityList().get(2))); var entity = solution.getFirstEntity(); @@ -1949,7 +2022,8 @@ public void expandToTri() { solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); assertScore(scoreDirector, - assertMatch(solution.getFirstEntity(), solution.getEntityList().get(1), sum12)); + assertMatch(solution.getFirstEntity(), solution.getEntityList().get(1), sum12) + .withIndictedObjects(solution.getFirstEntity(), solution.getEntityList().get(1))); } @Override @@ -1975,9 +2049,12 @@ public void expandToQuad() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(solution.getFirstEntity(), solution.getEntityList().get(1), sum01, concat01), - assertMatch(solution.getEntityList().get(1), solution.getEntityList().get(2), sum12, concat12), - assertMatch(solution.getFirstEntity(), solution.getEntityList().get(2), sum02, concat02)); + assertMatch(solution.getFirstEntity(), solution.getEntityList().get(1), sum01, concat01) + .withIndictedObjects(solution.getFirstEntity(), solution.getEntityList().get(1)), + assertMatch(solution.getEntityList().get(1), solution.getEntityList().get(2), sum12, concat12) + .withIndictedObjects(solution.getEntityList().get(1), solution.getEntityList().get(2)), + assertMatch(solution.getFirstEntity(), solution.getEntityList().get(2), sum02, concat02) + .withIndictedObjects(solution.getFirstEntity(), solution.getEntityList().get(2))); var entity = solution.getFirstEntity(); @@ -1986,7 +2063,8 @@ public void expandToQuad() { solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); assertScore(scoreDirector, - assertMatch(solution.getFirstEntity(), solution.getEntityList().get(1), sum12, concat12)); + assertMatch(solution.getFirstEntity(), solution.getEntityList().get(1), sum12, concat12) + .withIndictedObjects(solution.getFirstEntity(), solution.getEntityList().get(1))); } @Override @@ -2007,9 +2085,9 @@ public void flatten() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, entity2, group1), - assertMatch(entity1, entity2, group1), - assertMatch(entity1, entity2, group2)); + assertMatch(entity1, entity2, group1).withIndictedObjects(entity1, entity2), + assertMatch(entity1, entity2, group1).withIndictedObjects(entity1, entity2), + assertMatch(entity1, entity2, group2).withIndictedObjects(entity1, entity2)); // Incremental scoreDirector.beforeEntityRemoved(entity1); @@ -2033,27 +2111,28 @@ public void flattenLastWithDuplicates() { .penalize(SimpleScore.ONE) .asConstraint(TEST_CONSTRAINT_ID)); - // From scratch + // From scratch: flattenLast produces multiple tuples per pair, indicted objects are from original pair scoreDirector.setWorkingSolution(solution); + var entity3 = solution.getEntityList().get(2); assertScore(scoreDirector, - assertMatch(entity1, group1), - assertMatch(entity1, group1), - assertMatch(entity1, group2), - assertMatch(entity2, group2), - assertMatch(entity2, group1), - assertMatch(entity2, group2), - assertMatch(entity1, group1), - assertMatch(entity1, group1), - assertMatch(entity1, group2)); + assertMatch(entity1, group1).withIndictedObjects(entity1, entity2), + assertMatch(entity1, group1).withIndictedObjects(entity1, entity2), + assertMatch(entity1, group2).withIndictedObjects(entity1, entity2), + assertMatch(entity1, group1).withIndictedObjects(entity1, entity3), + assertMatch(entity1, group1).withIndictedObjects(entity1, entity3), + assertMatch(entity1, group2).withIndictedObjects(entity1, entity3), + assertMatch(entity2, group1).withIndictedObjects(entity2, entity3), + assertMatch(entity2, group1).withIndictedObjects(entity2, entity3), + assertMatch(entity2, group2).withIndictedObjects(entity2, entity3)); // Incremental scoreDirector.beforeEntityRemoved(entity1); solution.getEntityList().remove(entity1); scoreDirector.afterEntityRemoved(entity1); assertScore(scoreDirector, - assertMatch(entity2, group2), - assertMatch(entity2, group1), - assertMatch(entity2, group2)); + assertMatch(entity2, group1).withIndictedObjects(entity2, entity3), + assertMatch(entity2, group1).withIndictedObjects(entity2, entity3), + assertMatch(entity2, group2).withIndictedObjects(entity2, entity3)); } @Override @@ -2071,19 +2150,20 @@ public void flattenLastWithoutDuplicates() { .penalize(SimpleScore.ONE) .asConstraint(TEST_CONSTRAINT_ID)); - // From scratch + // From scratch: flattenLast produces one tuple per pair, indicted objects are from original pair scoreDirector.setWorkingSolution(solution); + var entity3 = solution.getEntityList().get(2); assertScore(scoreDirector, - assertMatch(entity2, group1), - assertMatch(entity1, group2), - assertMatch(entity1, group1)); + assertMatch(entity1, group2).withIndictedObjects(entity1, entity2), + assertMatch(entity1, group1).withIndictedObjects(entity1, entity3), + assertMatch(entity2, group1).withIndictedObjects(entity2, entity3)); // Incremental scoreDirector.beforeEntityRemoved(entity1); solution.getEntityList().remove(entity1); scoreDirector.afterEntityRemoved(entity1); assertScore(scoreDirector, - assertMatch(entity2, group1)); + assertMatch(entity2, group1).withIndictedObjects(entity2, entity3)); } @Override @@ -2102,21 +2182,22 @@ public void flattenLastAndDistinctWithDuplicates() { .penalize(SimpleScore.ONE) .asConstraint(TEST_CONSTRAINT_ID)); - // From scratch + // From scratch: flattenLast + distinct produces unique tuples, indicted from original pairs scoreDirector.setWorkingSolution(solution); + var entity3 = solution.getEntityList().get(2); assertScore(scoreDirector, - assertMatch(entity1, group1), - assertMatch(entity1, group2), - assertMatch(entity2, group2), - assertMatch(entity2, group1)); + assertMatch(entity1, group1).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity1, group2).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity2, group1).withIndictedObjects(entity2, entity3), + assertMatch(entity2, group2).withIndictedObjects(entity2, entity3)); // Incremental scoreDirector.beforeEntityRemoved(entity1); solution.getEntityList().remove(entity1); scoreDirector.afterEntityRemoved(entity1); assertScore(scoreDirector, - assertMatch(entity2, group1), - assertMatch(entity2, group2)); + assertMatch(entity2, group1).withIndictedObjects(entity2, entity3), + assertMatch(entity2, group2).withIndictedObjects(entity2, entity3)); } @Override @@ -2135,19 +2216,20 @@ public void flattenLastAndDistinctWithoutDuplicates() { .penalize(SimpleScore.ONE) .asConstraint(TEST_CONSTRAINT_ID)); - // From scratch + // From scratch: flattenLast + distinct produces unique tuples, indicted from original pairs scoreDirector.setWorkingSolution(solution); + var entity3 = solution.getEntityList().get(2); assertScore(scoreDirector, - assertMatch(entity2, group1), - assertMatch(entity1, group2), - assertMatch(entity1, group1)); + assertMatch(entity1, group2).withIndictedObjects(entity1, entity2), + assertMatch(entity1, group1).withIndictedObjects(entity1, entity3), + assertMatch(entity2, group1).withIndictedObjects(entity2, entity3)); // Incremental scoreDirector.beforeEntityRemoved(entity1); solution.getEntityList().remove(entity1); scoreDirector.afterEntityRemoved(entity1); assertScore(scoreDirector, - assertMatch(entity2, group1)); + assertMatch(entity2, group1).withIndictedObjects(entity2, entity3)); } @Override @@ -2180,8 +2262,8 @@ public void concatUniWithoutValueDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, entity2), - assertMatch(entity2, null)); + assertMatch(entity1, entity2).withIndictedObjects(entity1, entity2), + assertMatch(entity2, null).withIndictedObjects(entity2)); // Incremental scoreDirector.beforeVariableChanged(entity3, "value"); @@ -2192,8 +2274,8 @@ public void concatUniWithoutValueDuplicates() { entity2.setValue(value3); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(entity1, entity3), - assertMatch(entity3, null)); + assertMatch(entity1, entity3).withIndictedObjects(entity1, entity3), + assertMatch(entity3, null).withIndictedObjects(entity3)); } @Override @@ -2227,8 +2309,8 @@ public void concatAndDistinctUniWithoutValueDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, entity2), - assertMatch(entity2, null)); + assertMatch(entity1, entity2).withIndictedObjects(entity1, entity2), + assertMatch(entity2, null).withIndictedObjects(entity2)); // Incremental scoreDirector.beforeVariableChanged(entity3, "value"); @@ -2239,8 +2321,8 @@ public void concatAndDistinctUniWithoutValueDuplicates() { entity2.setValue(value3); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(entity1, entity3), - assertMatch(entity3, null)); + assertMatch(entity1, entity3).withIndictedObjects(entity1, entity3), + assertMatch(entity3, null).withIndictedObjects(entity3)); } @Override @@ -2275,8 +2357,8 @@ public void concatBiWithoutValueDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, entity2), - assertMatch(entity2, entity3)); + assertMatch(entity1, entity2).withIndictedObjects(entity1, entity2), + assertMatch(entity2, entity3).withIndictedObjects(entity2, entity3)); // Incremental scoreDirector.beforeVariableChanged(entity3, "value"); @@ -2287,8 +2369,8 @@ public void concatBiWithoutValueDuplicates() { entity2.setValue(value3); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(entity1, entity3), - assertMatch(entity3, entity2)); + assertMatch(entity1, entity3).withIndictedObjects(entity1, entity3), + assertMatch(entity3, entity2).withIndictedObjects(entity3, entity2)); } @Override @@ -2323,8 +2405,8 @@ public void concatBiWithValueDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, entity2), - assertMatch(entity1, entity2)); + assertMatch(entity1, entity2).withIndictedObjects(entity1, entity2), + assertMatch(entity1, entity2).withIndictedObjects(entity1, entity2)); // Incremental scoreDirector.beforeVariableChanged(entity3, "value"); @@ -2335,8 +2417,8 @@ public void concatBiWithValueDuplicates() { entity2.setValue(value3); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(entity1, entity3), - assertMatch(entity1, entity3)); + assertMatch(entity1, entity3).withIndictedObjects(entity1, entity3), + assertMatch(entity1, entity3).withIndictedObjects(entity1, entity3)); } @Override @@ -2372,8 +2454,8 @@ public void concatAndDistinctBiWithoutValueDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, entity2), - assertMatch(entity2, entity3)); + assertMatch(entity1, entity2).withIndictedObjects(entity1, entity2), + assertMatch(entity2, entity3).withIndictedObjects(entity2, entity3)); // Incremental scoreDirector.beforeVariableChanged(entity3, "value"); @@ -2384,8 +2466,8 @@ public void concatAndDistinctBiWithoutValueDuplicates() { entity2.setValue(value3); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(entity1, entity3), - assertMatch(entity3, entity2)); + assertMatch(entity1, entity3).withIndictedObjects(entity1, entity3), + assertMatch(entity3, entity2).withIndictedObjects(entity3, entity2)); } @Override @@ -2421,7 +2503,7 @@ public void concatAndDistinctBiWithValueDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, entity2)); + assertMatch(entity1, entity2).withIndictedObjects(entity1, entity2)); // Incremental scoreDirector.beforeVariableChanged(entity3, "value"); @@ -2432,7 +2514,7 @@ public void concatAndDistinctBiWithValueDuplicates() { entity2.setValue(value3); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(entity1, entity3)); + assertMatch(entity1, entity3).withIndictedObjects(entity1, entity3)); } @Override @@ -2469,8 +2551,8 @@ public void concatTriWithoutValueDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, entity2, null), - assertMatch(entity2, entity3, entity1)); + assertMatch(entity1, entity2, null).withIndictedObjects(entity1, entity2), + assertMatch(entity2, entity3, entity1).withIndictedObjects(entity2, entity3, entity1)); // Incremental scoreDirector.beforeVariableChanged(entity3, "value"); @@ -2481,8 +2563,8 @@ public void concatTriWithoutValueDuplicates() { entity2.setValue(value3); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(entity1, entity3, null), - assertMatch(entity3, entity2, entity1)); + assertMatch(entity1, entity3, null).withIndictedObjects(entity1, entity3), + assertMatch(entity3, entity2, entity1).withIndictedObjects(entity3, entity2, entity1)); } @Override @@ -2520,8 +2602,8 @@ public void concatAndDistinctTriWithoutValueDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, entity2, null), - assertMatch(entity2, entity3, entity1)); + assertMatch(entity1, entity2, null).withIndictedObjects(entity1, entity2), + assertMatch(entity2, entity3, entity1).withIndictedObjects(entity2, entity3, entity1)); // Incremental scoreDirector.beforeVariableChanged(entity3, "value"); @@ -2532,8 +2614,8 @@ public void concatAndDistinctTriWithoutValueDuplicates() { entity2.setValue(value3); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(entity1, entity3, null), - assertMatch(entity3, entity2, entity1)); + assertMatch(entity1, entity3, null).withIndictedObjects(entity1, entity3), + assertMatch(entity3, entity2, entity1).withIndictedObjects(entity3, entity2, entity1)); } @Override @@ -2572,8 +2654,8 @@ public void concatQuadWithoutValueDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, entity2, null, null), - assertMatch(entity2, entity3, entity1, entity2)); + assertMatch(entity1, entity2, null, null).withIndictedObjects(entity1, entity2), + assertMatch(entity2, entity3, entity1, entity2).withIndictedObjects(entity2, entity3, entity1)); // Incremental scoreDirector.beforeVariableChanged(entity3, "value"); @@ -2584,8 +2666,8 @@ public void concatQuadWithoutValueDuplicates() { entity2.setValue(value3); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(entity1, entity3, null, null), - assertMatch(entity3, entity2, entity1, entity3)); + assertMatch(entity1, entity3, null, null).withIndictedObjects(entity1, entity3), + assertMatch(entity3, entity2, entity1, entity3).withIndictedObjects(entity3, entity2, entity1)); } @Override @@ -2625,8 +2707,8 @@ public void concatAndDistinctQuadWithoutValueDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, entity2, null, null), - assertMatch(entity2, entity3, entity1, entity2)); + assertMatch(entity1, entity2, null, null).withIndictedObjects(entity1, entity2), + assertMatch(entity2, entity3, entity1, entity2).withIndictedObjects(entity2, entity3, entity1)); // Incremental scoreDirector.beforeVariableChanged(entity3, "value"); @@ -2637,8 +2719,8 @@ public void concatAndDistinctQuadWithoutValueDuplicates() { entity2.setValue(value3); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(entity1, entity3, null, null), - assertMatch(entity3, entity2, entity1, entity3)); + assertMatch(entity1, entity3, null, null).withIndictedObjects(entity1, entity3), + assertMatch(entity3, entity2, entity1, entity3).withIndictedObjects(entity3, entity2, entity1)); } @Override @@ -2677,15 +2759,15 @@ public void concatAfterGroupBy() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, value1, value2, 1L), - assertMatchWithScore(-1, value2, value3, 1L)); + assertMatchWithScore(-1, value1, value2, 1L).withIndictedObjects(entity1, entity2), + assertMatchWithScore(-1, value2, value3, 1L).withIndictedObjects(entity2, entity3)); // Incremental scoreDirector.beforeVariableChanged(entity3, "value"); entity3.setValue(value2); scoreDirector.afterVariableChanged(entity3, "value"); assertScore(scoreDirector, - assertMatchWithScore(-2, value1, value2, 2L)); + assertMatchWithScore(-2, value1, value2, 2L).withIndictedObjects(entity1, entity2, entity3)); // Incremental for which the first change matches a join that doesn't survive the second change scoreDirector.beforeVariableChanged(entity1, "value"); @@ -2695,8 +2777,8 @@ public void concatAfterGroupBy() { entity3.setValue(value1); scoreDirector.afterVariableChanged(entity3, "value"); assertScore(scoreDirector, - assertMatchWithScore(-1, value1, value2, 1L), - assertMatchWithScore(-1, value2, value3, 1L)); + assertMatchWithScore(-1, value1, value2, 1L).withIndictedObjects(entity3, entity2), + assertMatchWithScore(-1, value2, value3, 1L).withIndictedObjects(entity2, entity1)); } @Override @@ -2729,18 +2811,18 @@ public void complement() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, 0), - assertMatch(entity2, Integer.MAX_VALUE), - assertMatch(entity3, Integer.MAX_VALUE)); + assertMatch(entity1, 0).withIndictedObjects(entity1), + assertMatch(entity2, Integer.MAX_VALUE).withIndictedObjects(entity2), + assertMatch(entity3, Integer.MAX_VALUE).withIndictedObjects(entity3)); // Incremental; all entities are still present, but the indexes are different. scoreDirector.beforeVariableChanged(entity2, "value"); entity2.setValue(value1); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(entity1, 0), - assertMatch(entity2, 0), - assertMatch(entity3, Integer.MAX_VALUE)); + assertMatch(entity1, 0).withIndictedObjects(entity1), + assertMatch(entity2, 0).withIndictedObjects(entity2), + assertMatch(entity3, Integer.MAX_VALUE).withIndictedObjects(entity3)); } @Override @@ -3222,15 +3304,15 @@ public void joinerEqualsAndSameness() { scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, // Each entity's BigDecimal property is joined to itself. - assertMatch(entity1, entity1), - assertMatch(entity2, entity2), - assertMatch(entity3, entity3), + assertMatch(entity1, entity1).withIndictedObjects(entity1), + assertMatch(entity2, entity2).withIndictedObjects(entity2), + assertMatch(entity3, entity3).withIndictedObjects(entity3), // Each entity's BigDecimal property is joined to each other entity's. - assertMatch(entity1, entity2), - assertMatch(entity1, entity3), - assertMatch(entity2, entity1), - assertMatch(entity2, entity3), - assertMatch(entity3, entity1), - assertMatch(entity3, entity2)); + assertMatch(entity1, entity2).withIndictedObjects(entity1, entity2), + assertMatch(entity1, entity3).withIndictedObjects(entity1, entity3), + assertMatch(entity2, entity1).withIndictedObjects(entity2, entity1), + assertMatch(entity2, entity3).withIndictedObjects(entity2, entity3), + assertMatch(entity3, entity1).withIndictedObjects(entity3, entity1), + assertMatch(entity3, entity2).withIndictedObjects(entity3, entity2)); } } diff --git a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/quad/AbstractQuadConstraintStreamPrecomputeTest.java b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/quad/AbstractQuadConstraintStreamPrecomputeTest.java index 06d6b9df156..d12b05d80fe 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/quad/AbstractQuadConstraintStreamPrecomputeTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/quad/AbstractQuadConstraintStreamPrecomputeTest.java @@ -1,5 +1,7 @@ package ai.timefold.solver.core.impl.score.stream.common.quad; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.function.Function; @@ -186,9 +188,27 @@ public void filter_3_changed() { (entity, value, entityGroup, valueGroup) -> new Quadruple<>(value, entityGroup, valueGroup, entity)); } + record ExpectedQuad(A a, B b, C c, D d, Object... indicted) { + ExpectedQuad withIndictedObject(Object indictedObject) { + for (var obj : indicted) { + if (obj == indictedObject) { + return this; + } + } + var newIndicted = Arrays.copyOf(indicted, indicted.length + 1); + newIndicted[indicted.length] = indictedObject; + return new ExpectedQuad<>(a, b, c, d, newIndicted); + } + } + + ExpectedQuad expected(A a, B b, C c, D d, Object... indicted) { + return new ExpectedQuad<>(a, b, c, d, indicted); + } + private void assertPrecompute(TestdataLavishSolution solution, - List> expectedValues, + List> expectedValues, Function> entityStreamSupplier) { + expectedValues = new ArrayList<>(expectedValues); var scoreDirector = buildScoreDirector(factory -> factory.precompute(entityStreamSupplier) .ifExists(TestdataLavishEntity.class) @@ -203,11 +223,16 @@ private void assertPrecompute(TestdataLavishSolution solution, scoreDirector.beforeVariableChanged(entity, "value"); entity.setValue(solution.getFirstValue()); scoreDirector.afterVariableChanged(entity, "value"); + var listIterator = expectedValues.listIterator(); + while (listIterator.hasNext()) { + var expected = listIterator.next(); + listIterator.set(expected.withIndictedObject(entity)); + } } assertScore(scoreDirector, expectedValues.stream() - .map(quad -> new Object[] { quad.a(), quad.b(), quad.c(), quad.d() }) - .map(AbstractConstraintStreamTest::assertMatch) + .map(expected -> assertMatch(expected.a, expected.b, expected.c, expected.d) + .withIndictedObjects(expected.indicted)) .toArray(AssertableMatch[]::new)); } @@ -224,7 +249,7 @@ public void ifExists() { var value = new TestdataLavishValue(); solution.getValueList().add(value); - assertPrecompute(solution, List.of(new Quadruple<>(entityWithGroup, value, value, value)), + assertPrecompute(solution, List.of(expected(entityWithGroup, value, value, value, entityWithGroup, value, entityGroup)), pf -> pf.forEachUnfiltered(TestdataLavishEntity.class) .join(TestdataLavishValue.class) .join(TestdataLavishValue.class) @@ -247,7 +272,7 @@ public void ifNotExists() { var value = new TestdataLavishValue(); solution.getValueList().add(value); - assertPrecompute(solution, List.of(new Quadruple<>(entityWithoutGroup, value, value, value)), + assertPrecompute(solution, List.of(expected(entityWithoutGroup, value, value, value, entityWithoutGroup, value)), pf -> pf.forEachUnfiltered(TestdataLavishEntity.class) .join(TestdataLavishValue.class) .join(TestdataLavishValue.class) @@ -270,7 +295,7 @@ public void groupBy() { var value = new TestdataLavishValue(); solution.getValueList().add(value); - assertPrecompute(solution, List.of(new Quadruple<>(entityGroup, 1L, 1L, 1L)), + assertPrecompute(solution, List.of(expected(entityGroup, 1L, 1L, 1L)), pf -> pf.forEachUnfiltered(TestdataLavishEntity.class) .filter(entity -> entity.getEntityGroup() != null) .groupBy(TestdataLavishEntity::getEntityGroup, @@ -292,8 +317,9 @@ public void flattenLast() { var value = new TestdataLavishValue(); solution.getValueList().add(value); - assertPrecompute(solution, List.of(new Quadruple<>(entityWithoutGroup, value, value, value), - new Quadruple<>(entityWithGroup, value, value, value)), + assertPrecompute(solution, List.of( + expected(entityWithoutGroup, value, value, value, entityWithoutGroup, value), + expected(entityWithGroup, value, value, value, entityWithGroup, value)), pf -> pf.forEachUnfiltered(TestdataLavishEntity.class) .groupBy(ConstraintCollectors.toList()) .flattenLast(entityList -> entityList) @@ -318,8 +344,9 @@ record ValueHolder(int value) { var value = new TestdataLavishValue(); solution.getValueList().add(value); - assertPrecompute(solution, List.of(new Quadruple<>(new ValueHolder(1), value, value, value), - new Quadruple<>(new ValueHolder(2), value, value, value)), + assertPrecompute(solution, List.of( + expected(new ValueHolder(1), value, value, value, entity1, value), + expected(new ValueHolder(2), value, value, value, entity2, value)), pf -> pf.forEachUnfiltered(TestdataLavishEntity.class) .groupBy(ConstraintCollectors.toList()) .flattenLast(entityList -> entityList @@ -346,8 +373,8 @@ public void map() { var value = new TestdataLavishValue(); solution.getValueList().add(value); - assertPrecompute(solution, List.of(new Quadruple<>(entityGroup, value, value, value), - new Quadruple<>(entityGroup, value, value, value)), + assertPrecompute(solution, List.of(expected(entityGroup, value, value, value, entityWithGroup1, value), + expected(entityGroup, value, value, value, entityWithGroup2, value)), pf -> pf.forEachUnfiltered(TestdataLavishEntity.class) .join(TestdataLavishValue.class) .join(TestdataLavishValue.class) @@ -373,8 +400,8 @@ public void concat() { solution.getValueList().add(value); assertPrecompute(solution, - List.of(new Quadruple<>(entityWithoutGroup, value, value, value), - new Quadruple<>(entityWithGroup, value, value, value)), + List.of(expected(entityWithoutGroup, value, value, value, entityWithoutGroup, value), + expected(entityWithGroup, value, value, value, entityWithGroup, value)), pf -> pf.forEachUnfiltered(TestdataLavishEntity.class) .join(TestdataLavishValue.class) .join(TestdataLavishValue.class) @@ -403,7 +430,8 @@ public void distinct() { var value = new TestdataLavishValue(); solution.getValueList().add(value); - assertPrecompute(solution, List.of(new Quadruple<>(entityGroup, value, value, value)), + assertPrecompute(solution, + List.of(expected(entityGroup, value, value, value, entityWithGroup1, entityWithGroup2, value)), pf -> pf.forEachUnfiltered(TestdataLavishEntity.class) .join(TestdataLavishValue.class) .join(TestdataLavishValue.class) @@ -432,9 +460,9 @@ public void complement() { solution.getValueList().add(value); assertPrecompute(solution, List.of( - new Quadruple<>(entityWithGroup1, value, value, value), - new Quadruple<>(entityWithGroup2, value, value, value), - new Quadruple<>(entityWithoutGroup, null, null, null)), + expected(entityWithGroup1, value, value, value, entityWithGroup1, value), + expected(entityWithGroup2, value, value, value, entityWithGroup2, value), + expected(entityWithoutGroup, null, null, null, entityWithoutGroup)), pf -> pf.forEachUnfiltered(TestdataLavishEntity.class) .join(TestdataLavishValue.class) .join(TestdataLavishValue.class) diff --git a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/quad/AbstractQuadConstraintStreamTest.java b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/quad/AbstractQuadConstraintStreamTest.java index 7673d675b98..48b5bee5d68 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/quad/AbstractQuadConstraintStreamTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/quad/AbstractQuadConstraintStreamTest.java @@ -129,7 +129,7 @@ public void filter_consecutive() { // From scratch scoreDirector.setWorkingSolution(solution); - assertScore(scoreDirector, assertMatch(entity4, entity5, entity4, entity4)); + assertScore(scoreDirector, assertMatch(entity4, entity5, entity4, entity4).withIndictedObjects(entity4, entity5)); // Remove entity scoreDirector.beforeEntityRemoved(entity4); @@ -175,7 +175,8 @@ public void ifExists_0Joiner0Filter() { scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, assertMatch(solution.getFirstEntity(), solution.getFirstEntityGroup(), solution.getFirstValue(), - solution.getFirstEntity())); + solution.getFirstEntity()).withIndictedObjects(solution.getFirstEntity(), + solution.getFirstEntityGroup(), solution.getFirstValue())); // Incremental scoreDirector.beforeProblemFactRemoved(valueGroup); @@ -209,9 +210,15 @@ public void ifExists_0Join1Filter() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity2, entity1, entityGroup, solution.getFirstValue()), - assertMatch(entity2, solution.getFirstEntity(), solution.getFirstEntityGroup(), solution.getFirstValue()), - assertMatch(entity1, solution.getFirstEntity(), solution.getFirstEntityGroup(), solution.getFirstValue())); + assertMatch(entity2, entity1, entityGroup, solution.getFirstValue()) + .withIndictedObjects(entity2, entity1, entityGroup, solution.getFirstValue(), + solution.getFirstValue().getValueGroup()), + assertMatch(entity2, solution.getFirstEntity(), solution.getFirstEntityGroup(), solution.getFirstValue()) + .withIndictedObjects(entity2, solution.getFirstEntity(), solution.getFirstEntityGroup(), + solution.getFirstValue(), solution.getFirstValue().getValueGroup()), + assertMatch(entity1, solution.getFirstEntity(), solution.getFirstEntityGroup(), solution.getFirstValue()) + .withIndictedObjects(entity1, solution.getFirstEntity(), solution.getFirstEntityGroup(), + solution.getFirstValue(), solution.getFirstValue().getValueGroup())); // Incremental var toRemove = solution.getFirstValueGroup(); @@ -343,7 +350,8 @@ public void ifNotExists_0Joiner0Filter() { scoreDirector.afterProblemFactRemoved(valueGroup); assertScore(scoreDirector, assertMatch(solution.getFirstEntity(), solution.getFirstEntityGroup(), solution.getFirstValue(), - solution.getFirstEntity())); + solution.getFirstEntity()).withIndictedObjects(solution.getFirstEntity(), + solution.getFirstEntityGroup(), solution.getFirstValue())); } @Override @@ -489,21 +497,21 @@ public void ifExistsAfterGroupBy() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(1L, 1L, 1L, 1L)); + assertMatch(1L, 1L, 1L, 1L).withIndictedObjects(entity1, entity2)); // Incremental scoreDirector.beforeVariableChanged(entity2, "value"); entity2.setValue(value2); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(2L, 2L, 2L, 2L)); + assertMatch(2L, 2L, 2L, 2L).withIndictedObjects(entity1, entity2)); // Incremental scoreDirector.beforeEntityRemoved(entity2); solution.getEntityList().remove(entity2); scoreDirector.afterEntityRemoved(entity2); assertScore(scoreDirector, - assertMatch(1L, 1L, 1L, 1L)); + assertMatch(1L, 1L, 1L, 1L).withIndictedObjects(entity1)); } @Override @@ -526,10 +534,17 @@ public void groupBy_0Mapping1Collector() { .penalize(SimpleScore.ONE, count -> count) .asConstraint(TEST_CONSTRAINT_ID)); + var entity1 = solution.getFirstEntity(); + var entity2 = solution.getEntityList().get(1); + var entity3 = solution.getEntityList().get(2); + // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-5, 5L)); // E1 G1 V1 E1, E1 G1 V1 E3, E2 G2 V2 E2, E3 G1 V1 E1, E3 G1 V1 E3 + assertMatchWithScore(-5, 5L).withIndictedObjects( // E1 G1 V1 E1, E1 G1 V1 E3, E2 G2 V2 E2, E3 G1 V1 E1, E3 G1 V1 E3 + entity1, solution.getFirstEntityGroup(), solution.getFirstValue(), + entity2, solution.getEntityGroupList().get(1), solution.getValueList().get(1), + entity3)); // Incremental var entity = solution.getFirstEntity(); @@ -537,7 +552,9 @@ public void groupBy_0Mapping1Collector() { solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); assertScore(scoreDirector, - assertMatchWithScore(-2, 2L)); // E2 G2 V2 E2, E3 G1 V1 E3 + assertMatchWithScore(-2, 2L).withIndictedObjects( // E2 G2 V2 E2, E3 G1 V1 E3 + entity2, solution.getEntityGroupList().get(1), solution.getValueList().get(1), + entity3, solution.getFirstEntityGroup(), solution.getFirstValue())); } @Override @@ -554,16 +571,18 @@ public void groupBy_0Mapping2Collector() { .asConstraint(TEST_CONSTRAINT_ID)); var entity1 = solution.getFirstEntity(); + var entity2 = solution.getEntityList().get(1); + var entity3 = solution.getEntityList().get(2); // From scratch scoreDirector.setWorkingSolution(solution); - assertScore(scoreDirector, assertMatchWithScore(-1, 3L, 2L)); + assertScore(scoreDirector, assertMatchWithScore(-1, 3L, 2L).withIndictedObjects(entity1, entity2, entity3)); // Incremental scoreDirector.beforeEntityRemoved(entity1); solution.getEntityList().remove(entity1); scoreDirector.afterEntityRemoved(entity1); - assertScore(scoreDirector, assertMatchWithScore(-1, 1L, 1L)); + assertScore(scoreDirector, assertMatchWithScore(-1, 1L, 1L).withIndictedObjects(entity2, entity3)); } @Override @@ -592,14 +611,14 @@ public void groupBy_0Mapping3Collector() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, 3L, 0L, 1L)); + assertMatchWithScore(-1, 3L, 0L, 1L).withIndictedObjects(entity1, entity2, entity3)); // Incremental scoreDirector.beforeEntityRemoved(entity1); solution.getEntityList().remove(entity1); scoreDirector.afterEntityRemoved(entity1); assertScore(scoreDirector, - assertMatchWithScore(-1, 1L, 1L, 1L)); + assertMatchWithScore(-1, 1L, 1L, 1L).withIndictedObjects(entity2, entity3)); } @Override @@ -629,14 +648,14 @@ public void groupBy_0Mapping4Collector() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, 3L, 0L, 1L, asSet(entity1, entity2))); + assertMatchWithScore(-1, 3L, 0L, 1L, asSet(entity1, entity2)).withIndictedObjects(entity1, entity2, entity3)); // Incremental scoreDirector.beforeEntityRemoved(entity1); solution.getEntityList().remove(entity1); scoreDirector.afterEntityRemoved(entity1); assertScore(scoreDirector, - assertMatchWithScore(-1, 1L, 1L, 1L, asSet(entity2))); + assertMatchWithScore(-1, 1L, 1L, 1L, asSet(entity2)).withIndictedObjects(entity2, entity3)); } @Override @@ -659,8 +678,10 @@ public void groupBy_1Mapping0Collector() { scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, value2), - assertMatchWithScore(-1, value1)); + assertMatchWithScore(-1, value2).withIndictedObjects(solution.getEntityList().get(1), + solution.getEntityGroupList().get(1), solution.getValueList().get(1)), + assertMatchWithScore(-1, value1).withIndictedObjects(solution.getFirstEntity(), solution.getFirstEntityGroup(), + solution.getFirstValue(), solution.getEntityList().get(2))); } @Override @@ -678,14 +699,19 @@ public void groupBy_1Mapping1Collector() { .penalize(SimpleScore.ONE, (group, count) -> count) .asConstraint(TEST_CONSTRAINT_ID)); + var entity1 = solution.getFirstEntity(); + var entity2 = solution.getEntityList().get(1); + var entity3 = solution.getEntityList().get(2); var value1 = solution.getFirstValue(); var value2 = solution.getValueList().get(1); // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, value2, 1L), - assertMatchWithScore(-4, value1, 4L)); + assertMatchWithScore(-1, value2, 1L).withIndictedObjects(entity2, + solution.getEntityGroupList().get(1), solution.getValueList().get(1)), + assertMatchWithScore(-4, value1, 4L).withIndictedObjects(entity1, solution.getFirstEntityGroup(), value1, + entity3)); // Incremental var entity = solution.getFirstEntity(); @@ -693,8 +719,9 @@ public void groupBy_1Mapping1Collector() { solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); assertScore(scoreDirector, - assertMatchWithScore(-1, value2, 1L), - assertMatchWithScore(-1, value1, 1L)); + assertMatchWithScore(-1, value2, 1L).withIndictedObjects(entity2, + solution.getEntityGroupList().get(1), solution.getValueList().get(1)), + assertMatchWithScore(-1, value1, 1L).withIndictedObjects(entity3, solution.getFirstEntityGroup(), value1)); } @Override @@ -714,12 +741,14 @@ public void groupBy_1Mapping2Collector() { var entity1 = solution.getFirstEntity(); var entity2 = solution.getEntityList().get(1); + var entity3 = solution.getEntityList().get(2); // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, entity1.toString(), 2L, singleton(entity1)), - assertMatchWithScore(-1, entity2.toString(), 1L, singleton(entity2))); + assertMatchWithScore(-1, entity1.toString(), 2L, singleton(entity1)).withIndictedObjects(entity1, entity2, + entity3), + assertMatchWithScore(-1, entity2.toString(), 1L, singleton(entity2)).withIndictedObjects(entity2, entity3)); // Incremental var entity = solution.getFirstEntity(); @@ -727,7 +756,7 @@ public void groupBy_1Mapping2Collector() { solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); assertScore(scoreDirector, - assertMatchWithScore(-1, entity2.toString(), 1L, singleton(entity2))); + assertMatchWithScore(-1, entity2.toString(), 1L, singleton(entity2)).withIndictedObjects(entity2, entity3)); } @Override @@ -752,14 +781,15 @@ public void groupBy_1Mapping3Collector() { entity1.setLongProperty(Long.MAX_VALUE); var entity2 = solution.getEntityList().get(1); entity2.setLongProperty(Long.MIN_VALUE); + var entity3 = solution.getEntityList().get(2); // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, assertMatchWithScore(-1, entity1.toString(), Long.MAX_VALUE, Long.MAX_VALUE, - singleton(entity1)), + singleton(entity1)).withIndictedObjects(entity1, entity2, entity3), assertMatchWithScore(-1, entity2.toString(), Long.MIN_VALUE, Long.MIN_VALUE, - singleton(entity2))); + singleton(entity2)).withIndictedObjects(entity2, entity3)); // Incremental var entity = solution.getFirstEntity(); @@ -768,7 +798,7 @@ public void groupBy_1Mapping3Collector() { scoreDirector.afterEntityRemoved(entity); assertScore(scoreDirector, assertMatchWithScore(-1, entity2.toString(), Long.MIN_VALUE, Long.MIN_VALUE, - singleton(entity2))); + singleton(entity2)).withIndictedObjects(entity2, entity3)); } @Override @@ -785,6 +815,9 @@ public void groupBy_2Mapping0Collector() { .penalize(SimpleScore.ONE) .asConstraint(TEST_CONSTRAINT_ID)); + var entity1 = solution.getFirstEntity(); + var entity2 = solution.getEntityList().get(1); + var entity3 = solution.getEntityList().get(2); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); var value1 = solution.getFirstValue(); @@ -793,8 +826,9 @@ public void groupBy_2Mapping0Collector() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, group2, value2), - assertMatchWithScore(-1, group1, value1)); + assertMatchWithScore(-1, group2, value2).withIndictedObjects(entity2, + solution.getEntityGroupList().get(1), solution.getValueList().get(1)), + assertMatchWithScore(-1, group1, value1).withIndictedObjects(entity1, group1, value1, entity3)); // Incremental var entity = solution.getFirstEntity(); @@ -802,8 +836,9 @@ public void groupBy_2Mapping0Collector() { solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); assertScore(scoreDirector, - assertMatchWithScore(-1, group2, value2), - assertMatchWithScore(-1, group1, value1)); + assertMatchWithScore(-1, group2, value2).withIndictedObjects(entity2, + solution.getEntityGroupList().get(1), solution.getValueList().get(1)), + assertMatchWithScore(-1, group1, value1).withIndictedObjects(entity3, group1, value1)); } @Override @@ -822,6 +857,9 @@ public void groupBy_2Mapping1Collector() { .penalize(SimpleScore.ONE, (group, value, count) -> count) .asConstraint(TEST_CONSTRAINT_ID)); + var entity1 = solution.getFirstEntity(); + var entity2 = solution.getEntityList().get(1); + var entity3 = solution.getEntityList().get(2); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); var value1 = solution.getFirstValue(); @@ -830,8 +868,9 @@ public void groupBy_2Mapping1Collector() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, group2, value2, 1L), - assertMatchWithScore(-4, group1, value1, 4L)); + assertMatchWithScore(-1, group2, value2, 1L).withIndictedObjects(entity2, + solution.getEntityGroupList().get(1), solution.getValueList().get(1)), + assertMatchWithScore(-4, group1, value1, 4L).withIndictedObjects(entity1, group1, value1, entity3)); // Incremental var entity = solution.getFirstEntity(); @@ -839,8 +878,9 @@ public void groupBy_2Mapping1Collector() { solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); assertScore(scoreDirector, - assertMatchWithScore(-1, group2, value2, 1L), - assertMatchWithScore(-1, group1, value1, 1L)); + assertMatchWithScore(-1, group2, value2, 1L).withIndictedObjects(entity2, + solution.getEntityGroupList().get(1), solution.getValueList().get(1)), + assertMatchWithScore(-1, group1, value1, 1L).withIndictedObjects(entity3, group1, value1)); } @Override @@ -859,6 +899,9 @@ public void groupBy_2Mapping2Collector() { .penalize(SimpleScore.ONE, (group, value, count, sameCount) -> count + sameCount) .asConstraint(TEST_CONSTRAINT_ID)); + var entity1 = solution.getFirstEntity(); + var entity2 = solution.getEntityList().get(1); + var entity3 = solution.getEntityList().get(2); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); var value1 = solution.getFirstValue(); @@ -867,8 +910,8 @@ public void groupBy_2Mapping2Collector() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-2, group2, value2, 1L, 1L), - assertMatchWithScore(-8, group1, value1, 4L, 4L)); + assertMatchWithScore(-2, group2, value2, 1L, 1L).withIndictedObjects(entity2, group2, value2), + assertMatchWithScore(-8, group1, value1, 4L, 4L).withIndictedObjects(entity1, group1, value1, entity3)); // Incremental var entity = solution.getFirstEntity(); @@ -876,8 +919,8 @@ public void groupBy_2Mapping2Collector() { solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); assertScore(scoreDirector, - assertMatchWithScore(-2, group2, value2, 1L, 1L), - assertMatchWithScore(-2, group1, value1, 1L, 1L)); + assertMatchWithScore(-2, group2, value2, 1L, 1L).withIndictedObjects(entity2, group2, value2), + assertMatchWithScore(-2, group1, value1, 1L, 1L).withIndictedObjects(entity3, group1, value1)); } @Override @@ -897,6 +940,9 @@ public void groupBy_3Mapping0Collector() { .penalize(SimpleScore.ONE) .asConstraint(TEST_CONSTRAINT_ID)); + var entity1 = solution.getFirstEntity(); + var entity2 = solution.getEntityList().get(1); + var entity3 = solution.getEntityList().get(2); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); var value1 = solution.getFirstValue(); @@ -905,8 +951,9 @@ public void groupBy_3Mapping0Collector() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, group2, group2, value2), - assertMatchWithScore(-1, group1, group1, value1)); + assertMatchWithScore(-1, group2, group2, value2).withIndictedObjects(entity2, + solution.getEntityGroupList().get(1), solution.getValueList().get(1)), + assertMatchWithScore(-1, group1, group1, value1).withIndictedObjects(entity1, group1, value1, entity3)); // Incremental var entity = solution.getFirstEntity(); @@ -914,8 +961,9 @@ public void groupBy_3Mapping0Collector() { solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); assertScore(scoreDirector, - assertMatchWithScore(-1, group2, value2, value2), - assertMatchWithScore(-1, group1, value1, value1)); + assertMatchWithScore(-1, group2, value2, value2).withIndictedObjects(entity2, + solution.getEntityGroupList().get(1), solution.getValueList().get(1)), + assertMatchWithScore(-1, group1, value1, value1).withIndictedObjects(entity3, group1, value1)); } @Override @@ -935,14 +983,19 @@ public void groupBy_3Mapping1Collector() { .penalize(SimpleScore.ONE) .asConstraint(TEST_CONSTRAINT_ID)); + var entity1 = solution.getFirstEntity(); + var entity2 = solution.getEntityList().get(1); + var entity3 = solution.getEntityList().get(2); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, group2, group2, group2, 1L), - assertMatchWithScore(-1, group1, group1, group1, 4L)); + assertMatchWithScore(-1, group2, group2, group2, 1L).withIndictedObjects(entity2, + solution.getEntityGroupList().get(1), solution.getValueList().get(1)), + assertMatchWithScore(-1, group1, group1, group1, 4L).withIndictedObjects(entity1, group1, + solution.getFirstValue(), entity3)); // Incremental var entity = solution.getFirstEntity(); @@ -950,8 +1003,10 @@ public void groupBy_3Mapping1Collector() { solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); assertScore(scoreDirector, - assertMatchWithScore(-1, group2, group2, group2, 1L), - assertMatchWithScore(-1, group1, group1, group1, 1L)); + assertMatchWithScore(-1, group2, group2, group2, 1L).withIndictedObjects(entity2, + solution.getEntityGroupList().get(1), solution.getValueList().get(1)), + assertMatchWithScore(-1, group1, group1, group1, 1L).withIndictedObjects(entity3, group1, + solution.getFirstValue())); } @Override @@ -972,6 +1027,9 @@ public void groupBy_4Mapping0Collector() { .penalize(SimpleScore.ONE) .asConstraint(TEST_CONSTRAINT_ID)); + var entity1 = solution.getFirstEntity(); + var entity2 = solution.getEntityList().get(1); + var entity3 = solution.getEntityList().get(2); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); var value1 = solution.getFirstValue(); @@ -980,8 +1038,9 @@ public void groupBy_4Mapping0Collector() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, group2, group2, group2, value2), - assertMatchWithScore(-1, group1, group1, group1, value1)); + assertMatchWithScore(-1, group2, group2, group2, value2).withIndictedObjects(entity2, + solution.getEntityGroupList().get(1), solution.getValueList().get(1)), + assertMatchWithScore(-1, group1, group1, group1, value1).withIndictedObjects(entity1, group1, value1, entity3)); // Incremental var entity = solution.getFirstEntity(); @@ -989,8 +1048,9 @@ public void groupBy_4Mapping0Collector() { solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); assertScore(scoreDirector, - assertMatchWithScore(-1, group2, group2, group2, value2), - assertMatchWithScore(-1, group1, group1, group1, value1)); + assertMatchWithScore(-1, group2, group2, group2, value2).withIndictedObjects(entity2, + solution.getEntityGroupList().get(1), solution.getValueList().get(1)), + assertMatchWithScore(-1, group1, group1, group1, value1).withIndictedObjects(entity3, group1, value1)); } @Override @@ -1017,7 +1077,7 @@ public void distinct() { // On a distinct stream, this is a no-op. scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, assertMatch(entity1, entity2, group1, group2), - assertMatch(entity1, entity3, group1, group1), + assertMatch(entity1, entity3, group1, group1).withIndictedObjects(entity1, entity3, group1), assertMatch(entity2, entity3, group2, group1)); } @@ -1036,22 +1096,24 @@ public void mapToUniWithDuplicates() { .asConstraint(TEST_CONSTRAINT_ID)); var entity = solution.getFirstEntity(); + var entity2 = solution.getEntityList().get(1); + var entity3 = solution.getEntityList().get(2); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(asSet(group1, group2)), - assertMatch(asSet(group1, group2)), - assertMatch(asSet(group1))); + assertMatch(asSet(group1, group2)).withIndictedObjects(entity, entity2, group1, group2), + assertMatch(asSet(group1, group2)).withIndictedObjects(entity2, entity3, group2, group1), + assertMatch(asSet(group1)).withIndictedObjects(entity, entity3, group1)); // Incremental scoreDirector.beforeEntityRemoved(entity); solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); assertScore(scoreDirector, - assertMatch(asSet(group1, group2))); + assertMatch(asSet(group1, group2)).withIndictedObjects(entity2, entity3, group2, group1)); } @Override @@ -1074,7 +1136,8 @@ public void mapToUniWithoutDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(asSet(group1, group2))); + assertMatch(asSet(group1, group2)).withIndictedObjects(solution.getFirstEntity(), + solution.getEntityList().get(1), group1, group2)); var entity = solution.getFirstEntity(); @@ -1101,21 +1164,23 @@ public void mapToUniAndDistinctWithDuplicates() { .asConstraint(TEST_CONSTRAINT_ID)); var entity = solution.getFirstEntity(); + var entity2 = solution.getEntityList().get(1); + var entity3 = solution.getEntityList().get(2); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(asSet(group1, group2)), - assertMatch(asSet(group1))); + assertMatch(asSet(group1, group2)).withIndictedObjects(entity, entity2, group1, group2, entity3), + assertMatch(asSet(group1)).withIndictedObjects(entity, entity3, group1)); // Incremental scoreDirector.beforeEntityRemoved(entity); solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); assertScore(scoreDirector, - assertMatch(asSet(group1, group2))); + assertMatch(asSet(group1, group2)).withIndictedObjects(entity2, entity3, group2, group1)); } @Override @@ -1139,7 +1204,8 @@ public void mapToUniAndDistinctWithoutDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(asSet(group1, group2))); + assertMatch(asSet(group1, group2)).withIndictedObjects(solution.getFirstEntity(), + solution.getEntityList().get(1), group1, group2)); var entity = solution.getFirstEntity(); @@ -1168,7 +1234,9 @@ public void mapToBi() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(solution.getFirstEntity(), solution.getEntityList().get(1))); + assertMatch(solution.getFirstEntity(), solution.getEntityList().get(1)).withIndictedObjects( + solution.getFirstEntity(), solution.getEntityList().get(1), solution.getFirstEntityGroup(), + solution.getEntityGroupList().get(1))); var entity = solution.getFirstEntity(); @@ -1199,7 +1267,9 @@ public void mapToTri() { scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, assertMatch(solution.getFirstEntity(), solution.getEntityList().get(1), - solution.getFirstEntity().getLongProperty() + solution.getEntityList().get(1).getLongProperty())); + solution.getFirstEntity().getLongProperty() + solution.getEntityList().get(1).getLongProperty()) + .withIndictedObjects(solution.getFirstEntity(), solution.getEntityList().get(1), + solution.getFirstEntityGroup(), solution.getEntityGroupList().get(1))); var entity = solution.getFirstEntity(); @@ -1232,7 +1302,9 @@ public void mapToQuad() { assertScore(scoreDirector, assertMatchWithScore(-1, solution.getFirstEntity().getCode(), solution.getEntityList().get(1).getCode(), solution.getFirstEntityGroup().getCode(), - solution.getEntityGroupList().get(1).getCode())); + solution.getEntityGroupList().get(1).getCode()) + .withIndictedObjects(solution.getFirstEntity(), solution.getEntityList().get(1), + solution.getFirstEntityGroup(), solution.getEntityGroupList().get(1))); var entity = solution.getFirstEntity(); @@ -1264,15 +1336,15 @@ public void flattenLastWithDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, entity2, entity3, group1), - assertMatch(entity1, entity2, entity3, group1), - assertMatch(entity1, entity2, entity3, group2), - assertMatch(entity1, entity3, entity2, group1), - assertMatch(entity1, entity3, entity2, group1), - assertMatch(entity1, entity3, entity2, group2), - assertMatch(entity2, entity3, entity1, group1), - assertMatch(entity2, entity3, entity1, group1), - assertMatch(entity2, entity3, entity1, group2)); + assertMatch(entity1, entity2, entity3, group1).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity1, entity2, entity3, group1).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity1, entity2, entity3, group2).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity1, entity3, entity2, group1).withIndictedObjects(entity1, entity3, entity2), + assertMatch(entity1, entity3, entity2, group1).withIndictedObjects(entity1, entity3, entity2), + assertMatch(entity1, entity3, entity2, group2).withIndictedObjects(entity1, entity3, entity2), + assertMatch(entity2, entity3, entity1, group1).withIndictedObjects(entity2, entity3, entity1), + assertMatch(entity2, entity3, entity1, group1).withIndictedObjects(entity2, entity3, entity1), + assertMatch(entity2, entity3, entity1, group2).withIndictedObjects(entity2, entity3, entity1)); // Incremental scoreDirector.beforeEntityRemoved(entity1); @@ -1302,12 +1374,12 @@ public void flattenLastWithoutDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, entity2, entity3, group1), - assertMatch(entity1, entity2, entity3, group2), - assertMatch(entity1, entity3, entity2, group1), - assertMatch(entity1, entity3, entity2, group2), - assertMatch(entity2, entity3, entity1, group1), - assertMatch(entity2, entity3, entity1, group2)); + assertMatch(entity1, entity2, entity3, group1).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity1, entity2, entity3, group2).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity1, entity3, entity2, group1).withIndictedObjects(entity1, entity3, entity2), + assertMatch(entity1, entity3, entity2, group2).withIndictedObjects(entity1, entity3, entity2), + assertMatch(entity2, entity3, entity1, group1).withIndictedObjects(entity2, entity3, entity1), + assertMatch(entity2, entity3, entity1, group2).withIndictedObjects(entity2, entity3, entity1)); // Incremental scoreDirector.beforeEntityRemoved(entity1); @@ -1338,12 +1410,12 @@ public void flattenLastAndDistinctWithDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, entity2, entity3, group1), - assertMatch(entity1, entity2, entity3, group2), - assertMatch(entity1, entity3, entity2, group1), - assertMatch(entity1, entity3, entity2, group2), - assertMatch(entity2, entity3, entity1, group1), - assertMatch(entity2, entity3, entity1, group2)); + assertMatch(entity1, entity2, entity3, group1).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity1, entity2, entity3, group2).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity1, entity3, entity2, group1).withIndictedObjects(entity1, entity3, entity2), + assertMatch(entity1, entity3, entity2, group2).withIndictedObjects(entity1, entity3, entity2), + assertMatch(entity2, entity3, entity1, group1).withIndictedObjects(entity2, entity3, entity1), + assertMatch(entity2, entity3, entity1, group2).withIndictedObjects(entity2, entity3, entity1)); // Incremental scoreDirector.beforeEntityRemoved(entity1); @@ -1374,12 +1446,12 @@ public void flattenLastAndDistinctWithoutDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, entity2, entity3, group1), - assertMatch(entity1, entity2, entity3, group2), - assertMatch(entity1, entity3, entity2, group1), - assertMatch(entity1, entity3, entity2, group2), - assertMatch(entity2, entity3, entity1, group1), - assertMatch(entity2, entity3, entity1, group2)); + assertMatch(entity1, entity2, entity3, group1).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity1, entity2, entity3, group2).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity1, entity3, entity2, group1).withIndictedObjects(entity1, entity3, entity2), + assertMatch(entity1, entity3, entity2, group2).withIndictedObjects(entity1, entity3, entity2), + assertMatch(entity2, entity3, entity1, group1).withIndictedObjects(entity2, entity3, entity1), + assertMatch(entity2, entity3, entity1, group2).withIndictedObjects(entity2, entity3, entity1)); // Incremental scoreDirector.beforeEntityRemoved(entity1); @@ -1422,8 +1494,8 @@ public void concatUniWithoutValueDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, entity2, entity3, entity1), - assertMatch(entity2, null, null, null)); + assertMatch(entity1, entity2, entity3, entity1).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity2, null, null, null).withIndictedObjects(entity2)); // Incremental scoreDirector.beforeVariableChanged(entity3, "value"); @@ -1434,8 +1506,8 @@ public void concatUniWithoutValueDuplicates() { entity2.setValue(value3); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(entity1, entity3, entity2, entity1), - assertMatch(entity3, null, null, null)); + assertMatch(entity1, entity3, entity2, entity1).withIndictedObjects(entity1, entity3, entity2), + assertMatch(entity3, null, null, null).withIndictedObjects(entity3)); } @Override @@ -1473,8 +1545,8 @@ public void concatAndDistinctUniWithoutValueDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, entity2, entity3, entity1), - assertMatch(entity2, null, null, null)); + assertMatch(entity1, entity2, entity3, entity1).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity2, null, null, null).withIndictedObjects(entity2)); // Incremental scoreDirector.beforeVariableChanged(entity3, "value"); @@ -1485,8 +1557,8 @@ public void concatAndDistinctUniWithoutValueDuplicates() { entity2.setValue(value3); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(entity1, entity3, entity2, entity1), - assertMatch(entity3, null, null, null)); + assertMatch(entity1, entity3, entity2, entity1).withIndictedObjects(entity1, entity3, entity2), + assertMatch(entity3, null, null, null).withIndictedObjects(entity3)); } @Override @@ -1525,8 +1597,8 @@ public void concatBiWithoutValueDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, entity2, entity3, entity1), - assertMatch(entity2, entity3, null, null)); + assertMatch(entity1, entity2, entity3, entity1).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity2, entity3, null, null).withIndictedObjects(entity2, entity3)); // Incremental scoreDirector.beforeVariableChanged(entity3, "value"); @@ -1537,8 +1609,8 @@ public void concatBiWithoutValueDuplicates() { entity2.setValue(value3); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(entity1, entity3, entity2, entity1), - assertMatch(entity3, entity2, null, null)); + assertMatch(entity1, entity3, entity2, entity1).withIndictedObjects(entity1, entity3, entity2), + assertMatch(entity3, entity2, null, null).withIndictedObjects(entity3, entity2)); } @Override @@ -1578,8 +1650,8 @@ public void concatAndDistinctBiWithoutValueDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, entity2, entity3, entity1), - assertMatch(entity2, entity3, null, null)); + assertMatch(entity1, entity2, entity3, entity1).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity2, entity3, null, null).withIndictedObjects(entity2, entity3)); // Incremental scoreDirector.beforeVariableChanged(entity3, "value"); @@ -1590,8 +1662,8 @@ public void concatAndDistinctBiWithoutValueDuplicates() { entity2.setValue(value3); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(entity1, entity3, entity2, entity1), - assertMatch(entity3, entity2, null, null)); + assertMatch(entity1, entity3, entity2, entity1).withIndictedObjects(entity1, entity3, entity2), + assertMatch(entity3, entity2, null, null).withIndictedObjects(entity3, entity2)); } @Override @@ -1632,8 +1704,8 @@ public void concatTriWithoutValueDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, entity2, entity3, entity1), - assertMatch(entity2, entity3, entity1, null)); + assertMatch(entity1, entity2, entity3, entity1).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity2, entity3, entity1, null).withIndictedObjects(entity2, entity3, entity1)); // Incremental scoreDirector.beforeVariableChanged(entity3, "value"); @@ -1644,8 +1716,8 @@ public void concatTriWithoutValueDuplicates() { entity2.setValue(value3); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(entity1, entity3, entity2, entity1), - assertMatch(entity3, entity2, entity1, null)); + assertMatch(entity1, entity3, entity2, entity1).withIndictedObjects(entity1, entity3, entity2), + assertMatch(entity3, entity2, entity1, null).withIndictedObjects(entity3, entity2, entity1)); } @Override @@ -1687,8 +1759,8 @@ public void concatAndDistinctTriWithoutValueDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, entity2, entity3, entity1), - assertMatch(entity2, entity3, entity1, null)); + assertMatch(entity1, entity2, entity3, entity1).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity2, entity3, entity1, null).withIndictedObjects(entity2, entity3, entity1)); // Incremental scoreDirector.beforeVariableChanged(entity3, "value"); @@ -1699,8 +1771,8 @@ public void concatAndDistinctTriWithoutValueDuplicates() { entity2.setValue(value3); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(entity1, entity3, entity2, entity1), - assertMatch(entity3, entity2, entity1, null)); + assertMatch(entity1, entity3, entity2, entity1).withIndictedObjects(entity1, entity3, entity2), + assertMatch(entity3, entity2, entity1, null).withIndictedObjects(entity3, entity2, entity1)); } @Override @@ -1743,8 +1815,8 @@ public void concatQuadWithoutValueDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, entity2, entity3, entity1), - assertMatch(entity2, entity3, entity1, entity2)); + assertMatch(entity1, entity2, entity3, entity1).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity2, entity3, entity1, entity2).withIndictedObjects(entity2, entity3, entity1)); // Incremental scoreDirector.beforeVariableChanged(entity3, "value"); @@ -1755,8 +1827,8 @@ public void concatQuadWithoutValueDuplicates() { entity2.setValue(value3); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(entity1, entity3, entity2, entity1), - assertMatch(entity3, entity2, entity1, entity3)); + assertMatch(entity1, entity3, entity2, entity1).withIndictedObjects(entity1, entity3, entity2), + assertMatch(entity3, entity2, entity1, entity3).withIndictedObjects(entity3, entity2, entity1)); } @Override @@ -1799,8 +1871,8 @@ public void concatQuadWithValueDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, entity2, entity3, entity1), - assertMatch(entity1, entity2, entity3, entity1)); + assertMatch(entity1, entity2, entity3, entity1).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity1, entity2, entity3, entity1).withIndictedObjects(entity1, entity2, entity3)); // Incremental scoreDirector.beforeVariableChanged(entity3, "value"); @@ -1811,8 +1883,8 @@ public void concatQuadWithValueDuplicates() { entity2.setValue(value3); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(entity1, entity3, entity2, entity1), - assertMatch(entity1, entity3, entity2, entity1)); + assertMatch(entity1, entity3, entity2, entity1).withIndictedObjects(entity1, entity3, entity2), + assertMatch(entity1, entity3, entity2, entity1).withIndictedObjects(entity1, entity3, entity2)); } @Override @@ -1856,8 +1928,8 @@ public void concatAndDistinctQuadWithoutValueDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, entity2, entity3, entity1), - assertMatch(entity2, entity3, entity1, entity2)); + assertMatch(entity1, entity2, entity3, entity1).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity2, entity3, entity1, entity2).withIndictedObjects(entity2, entity3, entity1)); // Incremental scoreDirector.beforeVariableChanged(entity3, "value"); @@ -1868,8 +1940,8 @@ public void concatAndDistinctQuadWithoutValueDuplicates() { entity2.setValue(value3); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(entity1, entity3, entity2, entity1), - assertMatch(entity3, entity2, entity1, entity3)); + assertMatch(entity1, entity3, entity2, entity1).withIndictedObjects(entity1, entity3, entity2), + assertMatch(entity3, entity2, entity1, entity3).withIndictedObjects(entity3, entity2, entity1)); } @Override @@ -1913,7 +1985,7 @@ public void concatAndDistinctQuadWithValueDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, entity2, entity3, entity1)); + assertMatch(entity1, entity2, entity3, entity1).withIndictedObjects(entity1, entity2, entity3)); // Incremental scoreDirector.beforeVariableChanged(entity3, "value"); @@ -1924,7 +1996,7 @@ public void concatAndDistinctQuadWithValueDuplicates() { entity2.setValue(value3); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(entity1, entity3, entity2, entity1)); + assertMatch(entity1, entity3, entity2, entity1).withIndictedObjects(entity1, entity3, entity2)); } @Override @@ -1971,8 +2043,10 @@ public void concatAfterGroupBy() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, value1, value2, value3.getCode() + value1.getCode(), 1L), - assertMatchWithScore(-1, value3, value2, value1.getCode() + value3.getCode(), 1L)); + assertMatchWithScore(-1, value1, value2, value3.getCode() + value1.getCode(), 1L).withIndictedObjects(entity1, + entity2, entity3), + assertMatchWithScore(-1, value3, value2, value1.getCode() + value3.getCode(), 1L).withIndictedObjects(entity3, + entity2, entity1)); // Incremental scoreDirector.beforeVariableChanged(entity3, "value"); @@ -1988,8 +2062,10 @@ public void concatAfterGroupBy() { entity3.setValue(value1); scoreDirector.afterVariableChanged(entity3, "value"); assertScore(scoreDirector, - assertMatchWithScore(-1, value1, value2, value3.getCode() + value1.getCode(), 1L), - assertMatchWithScore(-1, value3, value2, value1.getCode() + value3.getCode(), 1L)); + assertMatchWithScore(-1, value1, value2, value3.getCode() + value1.getCode(), 1L).withIndictedObjects(entity1, + entity2, entity3), + assertMatchWithScore(-1, value3, value2, value1.getCode() + value3.getCode(), 1L).withIndictedObjects(entity3, + entity2, entity1)); } @Override @@ -2024,18 +2100,18 @@ public void complement() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, 0, 1, 2), - assertMatch(entity2, Integer.MAX_VALUE, -1, 0), - assertMatch(entity3, Integer.MAX_VALUE, -1, 0)); + assertMatch(entity1, 0, 1, 2).withIndictedObjects(entity1), + assertMatch(entity2, Integer.MAX_VALUE, -1, 0).withIndictedObjects(entity2), + assertMatch(entity3, Integer.MAX_VALUE, -1, 0).withIndictedObjects(entity3)); // Incremental; all entities are still present, but the indexes are different. scoreDirector.beforeVariableChanged(entity2, "value"); entity2.setValue(value1); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(entity1, 0, 1, 2), - assertMatch(entity2, 0, 1, 2), - assertMatch(entity3, Integer.MAX_VALUE, -1, 0)); + assertMatch(entity1, 0, 1, 2).withIndictedObjects(entity1), + assertMatch(entity2, 0, 1, 2).withIndictedObjects(entity2), + assertMatch(entity3, Integer.MAX_VALUE, -1, 0).withIndictedObjects(entity3)); } @Override diff --git a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/tri/AbstractTriConstraintStreamPrecomputeTest.java b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/tri/AbstractTriConstraintStreamPrecomputeTest.java index d80cfab093a..56b83bddd81 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/tri/AbstractTriConstraintStreamPrecomputeTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/tri/AbstractTriConstraintStreamPrecomputeTest.java @@ -1,5 +1,7 @@ package ai.timefold.solver.core.impl.score.stream.common.tri; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.function.Function; @@ -162,9 +164,27 @@ public void filter_2_changed() { (entity, value, entityGroup) -> new Triple<>(value, entityGroup, entity)); } + record ExpectedTri(A a, B b, C c, Object... indicted) { + ExpectedTri withIndictedObject(Object indictedObject) { + for (var obj : indicted) { + if (obj == indictedObject) { + return this; + } + } + var newIndicted = Arrays.copyOf(indicted, indicted.length + 1); + newIndicted[indicted.length] = indictedObject; + return new ExpectedTri<>(a, b, c, newIndicted); + } + } + + ExpectedTri expected(A a, B b, C c, Object... indicted) { + return new ExpectedTri<>(a, b, c, indicted); + } + private void assertPrecompute(TestdataLavishSolution solution, - List> expectedValues, + List> expectedValues, Function> entityStreamSupplier) { + expectedValues = new ArrayList<>(expectedValues); var scoreDirector = buildScoreDirector(factory -> factory.precompute(entityStreamSupplier) .ifExists(TestdataLavishEntity.class) @@ -179,11 +199,16 @@ private void assertPrecompute(TestdataLavishSolution solution, scoreDirector.beforeVariableChanged(entity, "value"); entity.setValue(solution.getFirstValue()); scoreDirector.afterVariableChanged(entity, "value"); + var listIterator = expectedValues.listIterator(); + while (listIterator.hasNext()) { + var expected = listIterator.next(); + listIterator.set(expected.withIndictedObject(entity)); + } } assertScore(scoreDirector, expectedValues.stream() - .map(triple -> new Object[] { triple.a(), triple.b(), triple.c() }) - .map(AbstractConstraintStreamTest::assertMatch) + .map(expected -> assertMatch(expected.a, expected.b, expected.c) + .withIndictedObjects(expected.indicted)) .toArray(AssertableMatch[]::new)); } @@ -200,7 +225,7 @@ public void ifExists() { var value = new TestdataLavishValue(); solution.getValueList().add(value); - assertPrecompute(solution, List.of(new Triple<>(entityWithGroup, value, value)), + assertPrecompute(solution, List.of(expected(entityWithGroup, value, value, entityWithGroup, value, entityGroup)), pf -> pf.forEachUnfiltered(TestdataLavishEntity.class) .join(TestdataLavishValue.class) .join(TestdataLavishValue.class) @@ -222,7 +247,7 @@ public void ifNotExists() { var value = new TestdataLavishValue(); solution.getValueList().add(value); - assertPrecompute(solution, List.of(new Triple<>(entityWithoutGroup, value, value)), + assertPrecompute(solution, List.of(expected(entityWithoutGroup, value, value, entityWithoutGroup, value)), pf -> pf.forEachUnfiltered(TestdataLavishEntity.class) .join(TestdataLavishValue.class) .join(TestdataLavishValue.class) @@ -244,7 +269,7 @@ public void groupBy() { var value = new TestdataLavishValue(); solution.getValueList().add(value); - assertPrecompute(solution, List.of(new Triple<>(entityGroup, 1L, 1L)), + assertPrecompute(solution, List.of(expected(entityGroup, 1L, 1L)), pf -> pf.forEachUnfiltered(TestdataLavishEntity.class) .filter(entity -> entity.getEntityGroup() != null) .groupBy(TestdataLavishEntity::getEntityGroup, @@ -265,8 +290,9 @@ public void flatten() { var value = new TestdataLavishValue(); solution.getValueList().add(value); - assertPrecompute(solution, List.of(new Triple<>(entityWithoutGroup, entityWithoutGroup, value), - new Triple<>(entityWithGroup, entityWithoutGroup, value)), + assertPrecompute(solution, List.of( + expected(entityWithoutGroup, entityWithoutGroup, value, entityWithoutGroup, value), + expected(entityWithGroup, entityWithoutGroup, value, entityWithGroup, value)), pf -> pf.forEachUnfiltered(TestdataLavishEntity.class) .flatten(List::of) .join(TestdataLavishValue.class)); @@ -288,8 +314,9 @@ record ValueHolder(int value) { var value = new TestdataLavishValue(); solution.getValueList().add(value); - assertPrecompute(solution, List.of(new Triple<>(entity1, new ValueHolder(entity1.getIntegerProperty()), value), - new Triple<>(entity2, new ValueHolder(entity2.getIntegerProperty()), value)), + assertPrecompute(solution, List.of( + expected(entity1, new ValueHolder(entity1.getIntegerProperty()), value, entity1, value), + expected(entity2, new ValueHolder(entity2.getIntegerProperty()), value, entity2, value)), pf -> pf.forEachUnfiltered(TestdataLavishEntity.class) .flatten(entity -> List.of(new ValueHolder(entity.getIntegerProperty()))) .join(TestdataLavishValue.class)); @@ -308,8 +335,9 @@ public void flattenLast() { var value = new TestdataLavishValue(); solution.getValueList().add(value); - assertPrecompute(solution, List.of(new Triple<>(entityWithoutGroup, value, value), - new Triple<>(entityWithGroup, value, value)), + assertPrecompute(solution, List.of( + expected(entityWithoutGroup, value, value, entityWithoutGroup, value), + expected(entityWithGroup, value, value, entityWithGroup, value)), pf -> pf.forEachUnfiltered(TestdataLavishEntity.class) .groupBy(ConstraintCollectors.toList()) .flattenLast(entityList -> entityList) @@ -333,8 +361,9 @@ record ValueHolder(int value) { var value = new TestdataLavishValue(); solution.getValueList().add(value); - assertPrecompute(solution, List.of(new Triple<>(new ValueHolder(1), value, value), - new Triple<>(new ValueHolder(2), value, value)), + assertPrecompute(solution, List.of( + expected(new ValueHolder(1), value, value, entity1, value), + expected(new ValueHolder(2), value, value, entity2, value)), pf -> pf.forEachUnfiltered(TestdataLavishEntity.class) .groupBy(ConstraintCollectors.toList()) .flattenLast(entityList -> entityList @@ -360,8 +389,9 @@ public void map() { var value = new TestdataLavishValue(); solution.getValueList().add(value); - assertPrecompute(solution, List.of(new Triple<>(entityGroup, value, value), - new Triple<>(entityGroup, value, value)), + assertPrecompute(solution, List.of( + expected(entityGroup, value, value, entityWithGroup1, value), + expected(entityGroup, value, value, entityWithGroup2, value)), pf -> pf.forEachUnfiltered(TestdataLavishEntity.class) .join(TestdataLavishValue.class) .join(TestdataLavishValue.class) @@ -385,7 +415,8 @@ public void concat() { solution.getValueList().add(value); assertPrecompute(solution, - List.of(new Triple<>(entityWithoutGroup, value, value), new Triple<>(entityWithGroup, value, value)), + List.of(expected(entityWithoutGroup, value, value, entityWithoutGroup, value), + expected(entityWithGroup, value, value, entityWithGroup, value)), pf -> pf.forEachUnfiltered(TestdataLavishEntity.class) .join(TestdataLavishValue.class) .join(TestdataLavishValue.class) @@ -411,7 +442,8 @@ public void distinct() { var value = new TestdataLavishValue(); solution.getValueList().add(value); - assertPrecompute(solution, List.of(new Triple<>(entityGroup, value, value)), + assertPrecompute(solution, List.of( + expected(entityGroup, value, value, entityWithGroup1, entityWithGroup2, value)), pf -> pf.forEachUnfiltered(TestdataLavishEntity.class) .join(TestdataLavishValue.class) .join(TestdataLavishValue.class) @@ -438,9 +470,9 @@ public void complement() { solution.getValueList().add(value); assertPrecompute(solution, List.of( - new Triple<>(entityWithGroup1, value, value), - new Triple<>(entityWithGroup2, value, value), - new Triple<>(entityWithoutGroup, null, null)), + expected(entityWithGroup1, value, value, entityWithGroup1, value), + expected(entityWithGroup2, value, value, entityWithGroup2, value), + expected(entityWithoutGroup, null, null, entityWithoutGroup)), pf -> pf.forEachUnfiltered(TestdataLavishEntity.class) .join(TestdataLavishValue.class) .join(TestdataLavishValue.class) diff --git a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/tri/AbstractTriConstraintStreamTest.java b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/tri/AbstractTriConstraintStreamTest.java index 90724a2057f..6e79563b8c7 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/tri/AbstractTriConstraintStreamTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/tri/AbstractTriConstraintStreamTest.java @@ -129,7 +129,7 @@ public void filter_consecutive() { // From scratch scoreDirector.setWorkingSolution(solution); - assertScore(scoreDirector, assertMatch(entity4, entity5, entity4)); + assertScore(scoreDirector, assertMatch(entity4, entity5, entity4).withIndictedObjects(entity4, entity5)); // Remove entity scoreDirector.beforeEntityRemoved(entity4); @@ -328,24 +328,24 @@ public void joinAfterGroupBy() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(1L, 1L, 1L, extra1), - assertMatch(1L, 1L, 1L, extra2)); + assertMatch(1L, 1L, 1L, extra1).withIndictedObjects(entity1, entity2, extra1), + assertMatch(1L, 1L, 1L, extra2).withIndictedObjects(entity1, entity2, extra2)); // Incremental scoreDirector.beforeVariableChanged(entity2, "value"); entity2.setValue(value2); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(2L, 2L, 2L, extra1), - assertMatch(2L, 2L, 2L, extra2)); + assertMatch(2L, 2L, 2L, extra1).withIndictedObjects(entity1, entity2, extra1), + assertMatch(2L, 2L, 2L, extra2).withIndictedObjects(entity1, entity2, extra2)); // Incremental scoreDirector.beforeEntityRemoved(entity2); solution.getEntityList().remove(entity2); scoreDirector.afterEntityRemoved(entity2); assertScore(scoreDirector, - assertMatch(1L, 1L, 1L, extra1), - assertMatch(1L, 1L, 1L, extra2)); + assertMatch(1L, 1L, 1L, extra1).withIndictedObjects(entity1, extra1), + assertMatch(1L, 1L, 1L, extra2).withIndictedObjects(entity1, extra2)); } @Override @@ -414,7 +414,8 @@ public void ifExists_0Join1Filter() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, entity2, solution.getFirstEntity())); + assertMatch(entity1, entity2, solution.getFirstEntity()) + .withIndictedObjects(entity1, entity2, solution.getFirstEntity(), solution.getFirstEntityGroup())); // Incremental var toRemove = solution.getFirstEntityGroup(); @@ -449,17 +450,22 @@ public void ifExists_1Join0Filter() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity2, entity1, solution.getFirstEntity()), - assertMatch(entity1, entity2, solution.getFirstEntity()), - assertMatch(solution.getFirstEntity(), entity1, entity2)); + assertMatch(entity2, entity1, solution.getFirstEntity()) + .withIndictedObjects(solution.getFirstEntityGroup(), entity2, entity1, solution.getFirstEntity()), + assertMatch(entity1, entity2, solution.getFirstEntity()) + .withIndictedObjects(entityGroup, entity1, entity2, solution.getFirstEntity()), + assertMatch(solution.getFirstEntity(), entity1, entity2) + .withIndictedObjects(solution.getFirstEntityGroup(), solution.getFirstEntity(), entity1, entity2)); // Incremental scoreDirector.beforeProblemFactRemoved(entityGroup); solution.getEntityGroupList().remove(entityGroup); scoreDirector.afterProblemFactRemoved(entityGroup); assertScore(scoreDirector, - assertMatch(entity2, entity1, solution.getFirstEntity()), - assertMatch(entity1, entity2, solution.getFirstEntity())); + assertMatch(entity2, entity1, solution.getFirstEntity()) + .withIndictedObjects(solution.getFirstEntityGroup(), entity2, entity1, solution.getFirstEntity()), + assertMatch(entity1, entity2, solution.getFirstEntity()) + .withIndictedObjects(solution.getFirstEntityGroup(), entity1, entity2, solution.getFirstEntity())); } @Override @@ -489,7 +495,8 @@ public void ifExists_1Join1Filter() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(solution.getFirstEntity(), entity2, entity1)); + assertMatch(solution.getFirstEntity(), entity2, entity1) + .withIndictedObjects(solution.getFirstEntity(), entity2, entity1, entityGroup)); // Incremental scoreDirector.beforeProblemFactRemoved(entityGroup); @@ -753,21 +760,21 @@ public void ifExistsAfterGroupBy() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(1L, 1L, 1L)); + assertMatch(1L, 1L, 1L).withIndictedObjects(entity1, entity2)); // Incremental scoreDirector.beforeVariableChanged(entity2, "value"); entity2.setValue(value2); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(2L, 2L, 2L)); + assertMatch(2L, 2L, 2L).withIndictedObjects(entity1, entity2)); // Incremental scoreDirector.beforeEntityRemoved(entity2); solution.getEntityList().remove(entity2); scoreDirector.afterEntityRemoved(entity2); assertScore(scoreDirector, - assertMatch(1L, 1L, 1L)); + assertMatch(1L, 1L, 1L).withIndictedObjects(entity1)); } @Override @@ -783,18 +790,26 @@ public void groupBy_0Mapping1Collector() { .penalize(SimpleScore.ONE, count -> count) .asConstraint(TEST_CONSTRAINT_ID)); + var entity = solution.getFirstEntity(); + var entity2 = solution.getEntityList().get(1); + var entity3 = solution.getEntityList().get(2); + var entityGroup1 = solution.getFirstEntityGroup(); + var entityGroup2 = solution.getEntityGroupList().get(1); + var value1 = solution.getFirstValue(); + var value2 = solution.getValueList().get(1); + // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-3, 3L)); + assertMatchWithScore(-3, 3L).withIndictedObjects(entity, entity2, entity3, entityGroup1, entityGroup2, value1, + value2)); // Incremental - var entity = solution.getFirstEntity(); scoreDirector.beforeEntityRemoved(entity); solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); assertScore(scoreDirector, - assertMatchWithScore(-2, 2L)); + assertMatchWithScore(-2, 2L).withIndictedObjects(entity2, entity3, entityGroup1, entityGroup2, value1, value2)); } @Override @@ -810,16 +825,18 @@ public void groupBy_0Mapping2Collector() { .asConstraint(TEST_CONSTRAINT_ID)); var entity1 = solution.getFirstEntity(); + var entity2 = solution.getEntityList().get(1); + var entity3 = solution.getEntityList().get(2); // From scratch scoreDirector.setWorkingSolution(solution); - assertScore(scoreDirector, assertMatchWithScore(-1, 3L, 2L)); + assertScore(scoreDirector, assertMatchWithScore(-1, 3L, 2L).withIndictedObjects(entity1, entity2, entity3)); // Incremental scoreDirector.beforeEntityRemoved(entity1); solution.getEntityList().remove(entity1); scoreDirector.afterEntityRemoved(entity1); - assertScore(scoreDirector, assertMatchWithScore(-1, 1L, 1L)); + assertScore(scoreDirector, assertMatchWithScore(-1, 1L, 1L).withIndictedObjects(entity2, entity3)); } @Override @@ -847,14 +864,14 @@ public void groupBy_0Mapping3Collector() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, 3L, 0L, 1L)); + assertMatchWithScore(-1, 3L, 0L, 1L).withIndictedObjects(entity1, entity2, entity3)); // Incremental scoreDirector.beforeEntityRemoved(entity1); solution.getEntityList().remove(entity1); scoreDirector.afterEntityRemoved(entity1); assertScore(scoreDirector, - assertMatchWithScore(-1, 1L, 1L, 1L)); + assertMatchWithScore(-1, 1L, 1L, 1L).withIndictedObjects(entity2, entity3)); } @Override @@ -883,14 +900,14 @@ public void groupBy_0Mapping4Collector() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, 3L, 0L, 1L, asSet(entity1, entity2))); + assertMatchWithScore(-1, 3L, 0L, 1L, asSet(entity1, entity2)).withIndictedObjects(entity1, entity2, entity3)); // Incremental scoreDirector.beforeEntityRemoved(entity1); solution.getEntityList().remove(entity1); scoreDirector.afterEntityRemoved(entity1); assertScore(scoreDirector, - assertMatchWithScore(-1, 1L, 1L, 1L, asSet(entity2))); + assertMatchWithScore(-1, 1L, 1L, 1L, asSet(entity2)).withIndictedObjects(entity2, entity3)); } @Override @@ -906,13 +923,18 @@ public void groupBy_1Mapping0Collector() { .penalize(SimpleScore.ONE) .asConstraint(TEST_CONSTRAINT_ID)); + var entity1 = solution.getFirstEntity(); + var entity2 = solution.getEntityList().get(1); + var entity3 = solution.getEntityList().get(2); + var entityGroup1 = solution.getFirstEntityGroup(); + var entityGroup2 = solution.getEntityGroupList().get(1); var value1 = solution.getFirstValue(); var value2 = solution.getValueList().get(1); scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, value2), - assertMatchWithScore(-1, value1)); + assertMatchWithScore(-1, value2).withIndictedObjects(entity2, entityGroup2, value2), + assertMatchWithScore(-1, value1).withIndictedObjects(entity1, entity3, entityGroup1, value1)); } @Override @@ -928,23 +950,27 @@ public void groupBy_1Mapping1Collector() { .penalize(SimpleScore.ONE, (group, count) -> count) .asConstraint(TEST_CONSTRAINT_ID)); + var entity = solution.getFirstEntity(); + var entity2 = solution.getEntityList().get(1); + var entity3 = solution.getEntityList().get(2); + var entityGroup1 = solution.getFirstEntityGroup(); + var entityGroup2 = solution.getEntityGroupList().get(1); var value1 = solution.getFirstValue(); var value2 = solution.getValueList().get(1); // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, value2, 1L), - assertMatchWithScore(-2, value1, 2L)); + assertMatchWithScore(-1, value2, 1L).withIndictedObjects(entity2, entityGroup2, value2), + assertMatchWithScore(-2, value1, 2L).withIndictedObjects(entity, entity3, entityGroup1, value1)); // Incremental - var entity = solution.getFirstEntity(); scoreDirector.beforeEntityRemoved(entity); solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); assertScore(scoreDirector, - assertMatchWithScore(-1, value2, 1L), - assertMatchWithScore(-1, value1, 1L)); + assertMatchWithScore(-1, value2, 1L).withIndictedObjects(entity2, entityGroup2, value2), + assertMatchWithScore(-1, value1, 1L).withIndictedObjects(entity3, entityGroup1, value1)); } @Override @@ -963,12 +989,14 @@ public void groupBy_1Mapping2Collector() { var entity1 = solution.getFirstEntity(); var entity2 = solution.getEntityList().get(1); + var entity3 = solution.getEntityList().get(2); // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, entity1.toString(), 2L, singleton(entity1)), - assertMatchWithScore(-1, entity2.toString(), 1L, singleton(entity2))); + assertMatchWithScore(-1, entity1.toString(), 2L, singleton(entity1)).withIndictedObjects(entity1, entity2, + entity3), + assertMatchWithScore(-1, entity2.toString(), 1L, singleton(entity2)).withIndictedObjects(entity2, entity3)); // Incremental var entity = solution.getFirstEntity(); @@ -976,7 +1004,7 @@ public void groupBy_1Mapping2Collector() { solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); assertScore(scoreDirector, - assertMatchWithScore(-1, entity2.toString(), 1L, singleton(entity2))); + assertMatchWithScore(-1, entity2.toString(), 1L, singleton(entity2)).withIndictedObjects(entity2, entity3)); } @Override @@ -1000,14 +1028,15 @@ public void groupBy_1Mapping3Collector() { entity1.setLongProperty(Long.MAX_VALUE); var entity2 = solution.getEntityList().get(1); entity2.setLongProperty(Long.MIN_VALUE); + var entity3 = solution.getEntityList().get(2); // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, assertMatchWithScore(-1, entity1.toString(), Long.MAX_VALUE, Long.MAX_VALUE, - singleton(entity1)), + singleton(entity1)).withIndictedObjects(entity1, entity2, entity3), assertMatchWithScore(-1, entity2.toString(), Long.MIN_VALUE, Long.MIN_VALUE, - singleton(entity2))); + singleton(entity2)).withIndictedObjects(entity2, entity3)); // Incremental var entity = solution.getFirstEntity(); @@ -1016,7 +1045,7 @@ public void groupBy_1Mapping3Collector() { scoreDirector.afterEntityRemoved(entity); assertScore(scoreDirector, assertMatchWithScore(-1, entity2.toString(), Long.MIN_VALUE, Long.MIN_VALUE, - singleton(entity2))); + singleton(entity2)).withIndictedObjects(entity2, entity3)); } @Override @@ -1031,6 +1060,9 @@ public void groupBy_2Mapping0Collector() { .penalize(SimpleScore.ONE) .asConstraint(TEST_CONSTRAINT_ID)); + var entity = solution.getFirstEntity(); + var entity2 = solution.getEntityList().get(1); + var entity3 = solution.getEntityList().get(2); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); var value1 = solution.getFirstValue(); @@ -1039,17 +1071,16 @@ public void groupBy_2Mapping0Collector() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, group2, value2), - assertMatchWithScore(-1, group1, value1)); + assertMatchWithScore(-1, group2, value2).withIndictedObjects(entity2, group2, value2), + assertMatchWithScore(-1, group1, value1).withIndictedObjects(entity, entity3, group1, value1)); // Incremental - var entity = solution.getFirstEntity(); scoreDirector.beforeEntityRemoved(entity); solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); assertScore(scoreDirector, - assertMatchWithScore(-1, group2, value2), - assertMatchWithScore(-1, group1, value1)); + assertMatchWithScore(-1, group2, value2).withIndictedObjects(entity2, group2, value2), + assertMatchWithScore(-1, group1, value1).withIndictedObjects(entity3, group1, value1)); } @Override @@ -1065,6 +1096,9 @@ public void groupBy_2Mapping1Collector() { .penalize(SimpleScore.ONE, (group, value, count) -> count) .asConstraint(TEST_CONSTRAINT_ID)); + var entity = solution.getFirstEntity(); + var entity2 = solution.getEntityList().get(1); + var entity3 = solution.getEntityList().get(2); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); var value1 = solution.getFirstValue(); @@ -1073,17 +1107,16 @@ public void groupBy_2Mapping1Collector() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, group2, value2, 1L), - assertMatchWithScore(-2, group1, value1, 2L)); + assertMatchWithScore(-1, group2, value2, 1L).withIndictedObjects(entity2, group2, value2), + assertMatchWithScore(-2, group1, value1, 2L).withIndictedObjects(entity, entity3, group1, value1)); // Incremental - var entity = solution.getFirstEntity(); scoreDirector.beforeEntityRemoved(entity); solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); assertScore(scoreDirector, - assertMatchWithScore(-1, group2, value2, 1L), - assertMatchWithScore(-1, group1, value1, 1L)); + assertMatchWithScore(-1, group2, value2, 1L).withIndictedObjects(entity2, group2, value2), + assertMatchWithScore(-1, group1, value1, 1L).withIndictedObjects(entity3, group1, value1)); } @Override @@ -1100,6 +1133,9 @@ public void groupBy_2Mapping2Collector() { (group, value, count, sameCount) -> count + sameCount) .asConstraint(TEST_CONSTRAINT_ID)); + var entity = solution.getFirstEntity(); + var entity2 = solution.getEntityList().get(1); + var entity3 = solution.getEntityList().get(2); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); var value1 = solution.getFirstValue(); @@ -1108,17 +1144,16 @@ public void groupBy_2Mapping2Collector() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-2, group2, value2, 1L, 1L), - assertMatchWithScore(-4, group1, value1, 2L, 2L)); + assertMatchWithScore(-2, group2, value2, 1L, 1L).withIndictedObjects(entity2, group2, value2), + assertMatchWithScore(-4, group1, value1, 2L, 2L).withIndictedObjects(entity, entity3, group1, value1)); // Incremental - var entity = solution.getFirstEntity(); scoreDirector.beforeEntityRemoved(entity); solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); assertScore(scoreDirector, - assertMatchWithScore(-2, group2, value2, 1L, 1L), - assertMatchWithScore(-2, group1, value1, 1L, 1L)); + assertMatchWithScore(-2, group2, value2, 1L, 1L).withIndictedObjects(entity2, group2, value2), + assertMatchWithScore(-2, group1, value1, 1L, 1L).withIndictedObjects(entity3, group1, value1)); } @Override @@ -1133,6 +1168,9 @@ public void groupBy_3Mapping0Collector() { .penalize(SimpleScore.ONE) .asConstraint(TEST_CONSTRAINT_ID)); + var entity = solution.getFirstEntity(); + var entity2 = solution.getEntityList().get(1); + var entity3 = solution.getEntityList().get(2); var group1 = solution.getEntityGroupList().get(0); var group2 = solution.getEntityGroupList().get(1); var group3 = solution.getEntityGroupList().get(2); @@ -1140,12 +1178,11 @@ public void groupBy_3Mapping0Collector() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, group1, group2, group3), - assertMatchWithScore(-1, group1, group3, group2), - assertMatchWithScore(-1, group2, group3, group1)); + assertMatchWithScore(-1, group1, group2, group3).withIndictedObjects(entity, entity2, entity3), + assertMatchWithScore(-1, group1, group3, group2).withIndictedObjects(entity, entity2, entity3), + assertMatchWithScore(-1, group2, group3, group1).withIndictedObjects(entity, entity2, entity3)); // Incremental - var entity = solution.getFirstEntity(); scoreDirector.beforeEntityRemoved(entity); solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); @@ -1165,6 +1202,9 @@ public void groupBy_3Mapping1Collector() { .penalize(SimpleScore.ONE) .asConstraint(TEST_CONSTRAINT_ID)); + var entity = solution.getFirstEntity(); + var entity2 = solution.getEntityList().get(1); + var entity3 = solution.getEntityList().get(2); var group1 = solution.getEntityGroupList().get(0); var group2 = solution.getEntityGroupList().get(1); var group3 = solution.getEntityGroupList().get(2); @@ -1172,12 +1212,11 @@ public void groupBy_3Mapping1Collector() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, group1, group2, group3, 1L), - assertMatchWithScore(-1, group1, group3, group2, 1L), - assertMatchWithScore(-1, group2, group3, group1, 1L)); + assertMatchWithScore(-1, group1, group2, group3, 1L).withIndictedObjects(entity, entity2, entity3), + assertMatchWithScore(-1, group1, group3, group2, 1L).withIndictedObjects(entity, entity2, entity3), + assertMatchWithScore(-1, group2, group3, group1, 1L).withIndictedObjects(entity, entity2, entity3)); // Incremental - var entity = solution.getFirstEntity(); scoreDirector.beforeEntityRemoved(entity); solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); @@ -1197,6 +1236,9 @@ public void groupBy_4Mapping0Collector() { .penalize(SimpleScore.ONE) .asConstraint(TEST_CONSTRAINT_ID)); + var entity = solution.getFirstEntity(); + var entity2 = solution.getEntityList().get(1); + var entity3 = solution.getEntityList().get(2); var group1 = solution.getEntityGroupList().get(0); var group2 = solution.getEntityGroupList().get(1); var group3 = solution.getEntityGroupList().get(2); @@ -1206,12 +1248,11 @@ public void groupBy_4Mapping0Collector() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, group1, group2, group3, value1), - assertMatchWithScore(-1, group1, group3, group2, value2), - assertMatchWithScore(-1, group2, group3, group1, value1)); + assertMatchWithScore(-1, group1, group2, group3, value1).withIndictedObjects(entity, entity2, entity3), + assertMatchWithScore(-1, group1, group3, group2, value2).withIndictedObjects(entity, entity2, entity3), + assertMatchWithScore(-1, group2, group3, group1, value1).withIndictedObjects(entity, entity2, entity3)); // Incremental - var entity = solution.getFirstEntity(); scoreDirector.beforeEntityRemoved(entity); solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); @@ -1252,17 +1293,18 @@ public void mapToUniWithDuplicates() { .penalize(SimpleScore.ONE) .asConstraint(TEST_CONSTRAINT_ID)); + var entity = solution.getFirstEntity(); + var entity2 = solution.getEntityList().get(1); + var entity3 = solution.getEntityList().get(2); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(asSet(group1, group2)), - assertMatch(asSet(group1, group2)), - assertMatch(asSet(group1, group2))); - - var entity = solution.getFirstEntity(); + assertMatch(asSet(group1, group2)).withIndictedObjects(entity, entity2, entity3), + assertMatch(asSet(group1, group2)).withIndictedObjects(entity, entity2, entity3), + assertMatch(asSet(group1, group2)).withIndictedObjects(entity, entity2, entity3)); // Incremental scoreDirector.beforeEntityRemoved(entity); @@ -1282,6 +1324,9 @@ public void mapToUniWithoutDuplicates() { .penalize(SimpleScore.ONE) .asConstraint(TEST_CONSTRAINT_ID)); + var entity = solution.getFirstEntity(); + var entity2 = solution.getEntityList().get(1); + var entity3 = solution.getEntityList().get(2); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); var group3 = solution.getEntityGroupList().get(2); @@ -1289,11 +1334,9 @@ public void mapToUniWithoutDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(asSet(group1, group2)), - assertMatch(asSet(group1, group3)), - assertMatch(asSet(group2, group3))); - - var entity = solution.getFirstEntity(); + assertMatch(asSet(group1, group2)).withIndictedObjects(entity, entity2, entity3), + assertMatch(asSet(group1, group3)).withIndictedObjects(entity, entity2, entity3), + assertMatch(asSet(group2, group3)).withIndictedObjects(entity, entity2, entity3)); // Incremental scoreDirector.beforeEntityRemoved(entity); @@ -1314,15 +1357,16 @@ public void mapToUniAndDistinctWithDuplicates() { .penalize(SimpleScore.ONE) .asConstraint(TEST_CONSTRAINT_ID)); + var entity = solution.getFirstEntity(); + var entity2 = solution.getEntityList().get(1); + var entity3 = solution.getEntityList().get(2); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(asSet(group1, group2))); - - var entity = solution.getFirstEntity(); + assertMatch(asSet(group1, group2)).withIndictedObjects(entity, entity2, entity3)); // Incremental scoreDirector.beforeEntityRemoved(entity); @@ -1343,6 +1387,9 @@ public void mapToUniAndDistinctWithoutDuplicates() { .penalize(SimpleScore.ONE) .asConstraint(TEST_CONSTRAINT_ID)); + var entity = solution.getFirstEntity(); + var entity2 = solution.getEntityList().get(1); + var entity3 = solution.getEntityList().get(2); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); var group3 = solution.getEntityGroupList().get(2); @@ -1350,11 +1397,9 @@ public void mapToUniAndDistinctWithoutDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(asSet(group1, group2)), - assertMatch(asSet(group1, group3)), - assertMatch(asSet(group2, group3))); - - var entity = solution.getFirstEntity(); + assertMatch(asSet(group1, group2)).withIndictedObjects(entity, entity2, entity3), + assertMatch(asSet(group1, group3)).withIndictedObjects(entity, entity2, entity3), + assertMatch(asSet(group2, group3)).withIndictedObjects(entity, entity2, entity3)); // Incremental scoreDirector.beforeEntityRemoved(entity); @@ -1375,6 +1420,9 @@ public void mapToBi() { .penalize(SimpleScore.ONE) .asConstraint(TEST_CONSTRAINT_ID)); + var entity = solution.getFirstEntity(); + var entity2 = solution.getEntityList().get(1); + var entity3 = solution.getEntityList().get(2); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); var group3 = solution.getEntityGroupList().get(2); @@ -1382,11 +1430,9 @@ public void mapToBi() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(group1, group2), - assertMatch(group1, group3), - assertMatch(group2, group3)); - - var entity = solution.getFirstEntity(); + assertMatch(group1, group2).withIndictedObjects(entity, entity2, entity3), + assertMatch(group1, group3).withIndictedObjects(entity, entity2, entity3), + assertMatch(group2, group3).withIndictedObjects(entity, entity2, entity3)); // Incremental scoreDirector.beforeEntityRemoved(entity); @@ -1408,6 +1454,9 @@ public void mapToTri() { .penalize(SimpleScore.ONE) .asConstraint(TEST_CONSTRAINT_ID)); + var entity = solution.getFirstEntity(); + var entity2 = solution.getEntityList().get(1); + var entity3 = solution.getEntityList().get(2); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); var group3 = solution.getEntityGroupList().get(2); @@ -1415,11 +1464,9 @@ public void mapToTri() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(group1, group2, group3), - assertMatch(group1, group3, group2), - assertMatch(group2, group3, group1)); - - var entity = solution.getFirstEntity(); + assertMatch(group1, group2, group3).withIndictedObjects(entity, entity2, entity3), + assertMatch(group1, group3, group2).withIndictedObjects(entity, entity2, entity3), + assertMatch(group2, group3, group1).withIndictedObjects(entity, entity2, entity3)); // Incremental scoreDirector.beforeEntityRemoved(entity); @@ -1442,6 +1489,9 @@ public void mapToQuad() { .penalize(SimpleScore.ONE) .asConstraint(TEST_CONSTRAINT_ID)); + var entity = solution.getFirstEntity(); + var entity2 = solution.getEntityList().get(1); + var entity3 = solution.getEntityList().get(2); var group1 = solution.getFirstEntityGroup(); var group2 = solution.getEntityGroupList().get(1); var group3 = solution.getEntityGroupList().get(2); @@ -1452,11 +1502,9 @@ public void mapToQuad() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(group1, group2, group3, sum), - assertMatch(group1, group3, group2, sum), - assertMatch(group2, group3, group1, sum)); - - var entity = solution.getFirstEntity(); + assertMatch(group1, group2, group3, sum).withIndictedObjects(entity, entity2, entity3), + assertMatch(group1, group3, group2, sum).withIndictedObjects(entity, entity2, entity3), + assertMatch(group2, group3, group1, sum).withIndictedObjects(entity, entity2, entity3)); // Incremental scoreDirector.beforeEntityRemoved(entity); @@ -1486,16 +1534,14 @@ public void expandToQuad() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, entity2, entity3, sum), - assertMatch(entity1, entity3, entity2, sum), - assertMatch(entity2, entity3, entity1, sum)); - - var entity = solution.getFirstEntity(); + assertMatch(entity1, entity2, entity3, sum).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity1, entity3, entity2, sum).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity2, entity3, entity1, sum).withIndictedObjects(entity1, entity2, entity3)); // Incremental - scoreDirector.beforeEntityRemoved(entity); - solution.getEntityList().remove(entity); - scoreDirector.afterEntityRemoved(entity); + scoreDirector.beforeEntityRemoved(entity1); + solution.getEntityList().remove(entity1); + scoreDirector.afterEntityRemoved(entity1); assertScore(scoreDirector); } @@ -1519,15 +1565,15 @@ public void flatten() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, entity2, entity3, group1), - assertMatch(entity1, entity2, entity3, group2), - assertMatch(entity1, entity2, entity3, group1), - assertMatch(entity1, entity3, entity2, group1), - assertMatch(entity1, entity3, entity2, group1), - assertMatch(entity1, entity3, entity2, group2), - assertMatch(entity2, entity3, entity1, group2), - assertMatch(entity2, entity3, entity1, group1), - assertMatch(entity2, entity3, entity1, group1)); + assertMatch(entity1, entity2, entity3, group1).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity1, entity2, entity3, group2).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity1, entity2, entity3, group1).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity1, entity3, entity2, group1).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity1, entity3, entity2, group1).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity1, entity3, entity2, group2).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity2, entity3, entity1, group2).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity2, entity3, entity1, group1).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity2, entity3, entity1, group1).withIndictedObjects(entity1, entity2, entity3)); // Incremental scoreDirector.beforeEntityRemoved(entity1); @@ -1556,15 +1602,15 @@ public void flattenLastWithDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, entity2, group1), - assertMatch(entity1, entity2, group1), - assertMatch(entity1, entity2, group2), - assertMatch(entity1, entity3, group1), - assertMatch(entity1, entity3, group2), - assertMatch(entity1, entity3, group2), - assertMatch(entity2, entity3, group1), - assertMatch(entity2, entity3, group1), - assertMatch(entity2, entity3, group2)); + assertMatch(entity1, entity2, group1).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity1, entity2, group1).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity1, entity2, group2).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity1, entity3, group1).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity1, entity3, group2).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity1, entity3, group2).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity2, entity3, group1).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity2, entity3, group1).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity2, entity3, group2).withIndictedObjects(entity1, entity2, entity3)); // Incremental scoreDirector.beforeEntityRemoved(entity1); @@ -1593,12 +1639,12 @@ public void flattenLastWithoutDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, entity2, group1), - assertMatch(entity1, entity2, group2), - assertMatch(entity1, entity3, group1), - assertMatch(entity1, entity3, group2), - assertMatch(entity2, entity3, group1), - assertMatch(entity2, entity3, group2)); + assertMatch(entity1, entity2, group1).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity1, entity2, group2).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity1, entity3, group1).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity1, entity3, group2).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity2, entity3, group1).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity2, entity3, group2).withIndictedObjects(entity1, entity2, entity3)); // Incremental scoreDirector.beforeEntityRemoved(entity1); @@ -1628,12 +1674,12 @@ public void flattenLastAndDistinctWithDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, entity2, group1), - assertMatch(entity1, entity2, group2), - assertMatch(entity1, entity3, group1), - assertMatch(entity1, entity3, group2), - assertMatch(entity2, entity3, group1), - assertMatch(entity2, entity3, group2)); + assertMatch(entity1, entity2, group1).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity1, entity2, group2).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity1, entity3, group1).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity1, entity3, group2).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity2, entity3, group1).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity2, entity3, group2).withIndictedObjects(entity1, entity2, entity3)); // Incremental scoreDirector.beforeEntityRemoved(entity1); @@ -1663,12 +1709,12 @@ public void flattenLastAndDistinctWithoutDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, entity2, group1), - assertMatch(entity1, entity2, group2), - assertMatch(entity1, entity3, group1), - assertMatch(entity1, entity3, group2), - assertMatch(entity2, entity3, group1), - assertMatch(entity2, entity3, group2)); + assertMatch(entity1, entity2, group1).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity1, entity2, group2).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity1, entity3, group1).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity1, entity3, group2).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity2, entity3, group1).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity2, entity3, group2).withIndictedObjects(entity1, entity2, entity3)); // Incremental scoreDirector.beforeEntityRemoved(entity1); @@ -1709,8 +1755,8 @@ public void concatUniWithoutValueDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, entity2, entity3), - assertMatch(entity2, null, null)); + assertMatch(entity1, entity2, entity3).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity2, null, null).withIndictedObjects(entity2)); // Incremental scoreDirector.beforeVariableChanged(entity3, "value"); @@ -1721,8 +1767,8 @@ public void concatUniWithoutValueDuplicates() { entity2.setValue(value3); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(entity1, entity3, entity2), - assertMatch(entity3, null, null)); + assertMatch(entity1, entity3, entity2).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity3, null, null).withIndictedObjects(entity3)); } @Override @@ -1758,8 +1804,8 @@ public void concatAndDistinctUniWithoutValueDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, entity2, entity3), - assertMatch(entity2, null, null)); + assertMatch(entity1, entity2, entity3).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity2, null, null).withIndictedObjects(entity2)); // Incremental scoreDirector.beforeVariableChanged(entity3, "value"); @@ -1770,8 +1816,8 @@ public void concatAndDistinctUniWithoutValueDuplicates() { entity2.setValue(value3); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(entity1, entity3, entity2), - assertMatch(entity3, null, null)); + assertMatch(entity1, entity3, entity2).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity3, null, null).withIndictedObjects(entity3)); } @Override @@ -1808,8 +1854,8 @@ public void concatBiWithoutValueDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, entity2, entity3), - assertMatch(entity2, entity3, null)); + assertMatch(entity1, entity2, entity3).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity2, entity3, null).withIndictedObjects(entity2, entity3)); // Incremental scoreDirector.beforeVariableChanged(entity3, "value"); @@ -1820,8 +1866,8 @@ public void concatBiWithoutValueDuplicates() { entity2.setValue(value3); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(entity1, entity3, entity2), - assertMatch(entity3, entity2, null)); + assertMatch(entity1, entity3, entity2).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity3, entity2, null).withIndictedObjects(entity3, entity2)); } @Override @@ -1859,8 +1905,8 @@ public void concatAndDistinctBiWithoutValueDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, entity2, entity3), - assertMatch(entity2, entity3, null)); + assertMatch(entity1, entity2, entity3).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity2, entity3, null).withIndictedObjects(entity2, entity3)); // Incremental scoreDirector.beforeVariableChanged(entity3, "value"); @@ -1871,8 +1917,8 @@ public void concatAndDistinctBiWithoutValueDuplicates() { entity2.setValue(value3); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(entity1, entity3, entity2), - assertMatch(entity3, entity2, null)); + assertMatch(entity1, entity3, entity2).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity3, entity2, null).withIndictedObjects(entity3, entity2)); } @Override @@ -2121,8 +2167,8 @@ public void concatQuadWithoutValueDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, entity2, entity3, null), - assertMatch(entity2, entity3, entity1, entity2)); + assertMatch(entity1, entity2, entity3, null).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity2, entity3, entity1, entity2).withIndictedObjects(entity2, entity3, entity1)); // Incremental scoreDirector.beforeVariableChanged(entity3, "value"); @@ -2133,8 +2179,8 @@ public void concatQuadWithoutValueDuplicates() { entity2.setValue(value3); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(entity1, entity3, entity2, null), - assertMatch(entity3, entity2, entity1, entity3)); + assertMatch(entity1, entity3, entity2, null).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity3, entity2, entity1, entity3).withIndictedObjects(entity3, entity2, entity1)); } @Override @@ -2176,8 +2222,8 @@ public void concatAndDistinctQuadWithoutValueDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, entity2, entity3, null), - assertMatch(entity2, entity3, entity1, entity2)); + assertMatch(entity1, entity2, entity3, null).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity2, entity3, entity1, entity2).withIndictedObjects(entity2, entity3, entity1)); // Incremental scoreDirector.beforeVariableChanged(entity3, "value"); @@ -2188,8 +2234,8 @@ public void concatAndDistinctQuadWithoutValueDuplicates() { entity2.setValue(value3); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(entity1, entity3, entity2, null), - assertMatch(entity3, entity2, entity1, entity3)); + assertMatch(entity1, entity3, entity2, null).withIndictedObjects(entity1, entity2, entity3), + assertMatch(entity3, entity2, entity1, entity3).withIndictedObjects(entity3, entity2, entity1)); } @Override @@ -2233,8 +2279,8 @@ public void concatAfterGroupBy() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, value1, value2, value3, 1L), - assertMatchWithScore(-1, value3, value2, value1, 1L)); + assertMatchWithScore(-1, value1, value2, value3, 1L).withIndictedObjects(entity1, entity2, entity3), + assertMatchWithScore(-1, value3, value2, value1, 1L).withIndictedObjects(entity3, entity2, entity1)); // Incremental scoreDirector.beforeVariableChanged(entity3, "value"); @@ -2250,8 +2296,8 @@ public void concatAfterGroupBy() { entity3.setValue(value1); scoreDirector.afterVariableChanged(entity3, "value"); assertScore(scoreDirector, - assertMatchWithScore(-1, value1, value2, value3, 1L), - assertMatchWithScore(-1, value3, value2, value1, 1L)); + assertMatchWithScore(-1, value1, value2, value3, 1L).withIndictedObjects(entity1, entity2, entity3), + assertMatchWithScore(-1, value3, value2, value1, 1L).withIndictedObjects(entity3, entity2, entity1)); } @Override @@ -2285,18 +2331,18 @@ public void complement() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, 0, 1), - assertMatch(entity2, Integer.MAX_VALUE, -1), - assertMatch(entity3, Integer.MAX_VALUE, -1)); + assertMatch(entity1, 0, 1).withIndictedObjects(entity1), + assertMatch(entity2, Integer.MAX_VALUE, -1).withIndictedObjects(entity2), + assertMatch(entity3, Integer.MAX_VALUE, -1).withIndictedObjects(entity3)); // Incremental; all entities are still present, but the indexes are different. scoreDirector.beforeVariableChanged(entity2, "value"); entity2.setValue(value1); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(entity1, 0, 1), - assertMatch(entity2, 0, 1), - assertMatch(entity3, Integer.MAX_VALUE, -1)); + assertMatch(entity1, 0, 1).withIndictedObjects(entity1), + assertMatch(entity2, 0, 1).withIndictedObjects(entity2), + assertMatch(entity3, Integer.MAX_VALUE, -1).withIndictedObjects(entity3)); } @Override diff --git a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/uni/AbstractUniConstraintStreamPrecomputeTest.java b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/uni/AbstractUniConstraintStreamPrecomputeTest.java index 65b3f749e48..cc9fb27620f 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/uni/AbstractUniConstraintStreamPrecomputeTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/uni/AbstractUniConstraintStreamPrecomputeTest.java @@ -1,5 +1,7 @@ package ai.timefold.solver.core.impl.score.stream.common.uni; +import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.function.Function; @@ -13,7 +15,6 @@ import ai.timefold.solver.core.impl.score.stream.common.ConstraintStreamImplSupport; import ai.timefold.solver.core.impl.score.stream.common.ConstraintStreamPrecomputeTest; import ai.timefold.solver.core.impl.score.stream.common.ConstraintStreamTestExtension; -import ai.timefold.solver.core.impl.util.Pair; import ai.timefold.solver.core.testdomain.score.lavish.TestdataLavishEntity; import ai.timefold.solver.core.testdomain.score.lavish.TestdataLavishEntityGroup; import ai.timefold.solver.core.testdomain.score.lavish.TestdataLavishSolution; @@ -132,9 +133,27 @@ public void filter_0_changed() { assertMatch(entity3)); } - private void assertPrecompute(TestdataLavishSolution solution, - List expectedValues, - Function> entityStreamSupplier) { + record ExpectedUni(A a, Object... indicted) { + ExpectedUni addIndicted(Object indictedObject) { + for (var object : indicted) { + if (object == indictedObject) { + return this; + } + } + var indictments = Arrays.copyOf(indicted, indicted.length + 1); + indictments[indicted.length] = indictedObject; + return new ExpectedUni<>(a, indictments); + } + } + + ExpectedUni expect(A a, Object... indicted) { + return new ExpectedUni<>(a, indicted); + } + + private void assertPrecompute(TestdataLavishSolution solution, + List> expectedTuples, + Function> entityStreamSupplier) { + expectedTuples = new ArrayList<>(expectedTuples); var scoreDirector = buildScoreDirector(factory -> factory.precompute(entityStreamSupplier) .ifExists(TestdataLavishEntity.class) @@ -149,10 +168,16 @@ private void assertPrecompute(TestdataLavishSolution solution, scoreDirector.beforeVariableChanged(entity, "value"); entity.setValue(solution.getFirstValue()); scoreDirector.afterVariableChanged(entity, "value"); + var listIterator = expectedTuples.listIterator(); + while (listIterator.hasNext()) { + var expectedTuple = listIterator.next(); + listIterator.set(expectedTuple.addIndicted(entity)); + } } - assertScore(scoreDirector, expectedValues.stream() - .map(AbstractConstraintStreamTest::assertMatch) + assertScore(scoreDirector, expectedTuples.stream() + .map(expected -> AbstractConstraintStreamTest.assertMatch(expected.a) + .withIndictedObjects(expected.indicted)) .toArray(AssertableMatch[]::new)); } @@ -161,14 +186,17 @@ private void assertPrecompute(TestdataLavishSolution solution, public void ifExists() { var solution = TestdataLavishSolution.generateEmptySolution(); var entityWithoutGroup = new TestdataLavishEntity(); + entityWithoutGroup.setCode("A"); var entityWithGroup = new TestdataLavishEntity(); + entityWithGroup.setCode("B"); var entityGroup = new TestdataLavishEntityGroup(); + entityGroup.setCode("C"); entityWithGroup.setEntityGroup(entityGroup); solution.getEntityList().addAll(List.of(entityWithoutGroup, entityWithGroup)); solution.getEntityGroupList().add(entityGroup); solution.getValueList().add(new TestdataLavishValue()); - assertPrecompute(solution, List.of(entityWithGroup), + assertPrecompute(solution, List.of(expect(entityWithGroup, entityWithGroup, entityGroup)), pf -> pf.forEachUnfiltered(TestdataLavishEntity.class) .ifExists(TestdataLavishEntityGroup.class, Joiners.equal( TestdataLavishEntity::getEntityGroup, Function.identity()))); @@ -186,7 +214,7 @@ public void ifNotExists() { solution.getEntityGroupList().add(entityGroup); solution.getValueList().add(new TestdataLavishValue()); - assertPrecompute(solution, List.of(entityWithoutGroup), + assertPrecompute(solution, List.of(expect(entityWithoutGroup, entityWithoutGroup)), pf -> pf.forEachUnfiltered(TestdataLavishEntity.class) .ifNotExists(TestdataLavishEntityGroup.class, Joiners.equal( TestdataLavishEntity::getEntityGroup, Function.identity()))); @@ -204,7 +232,7 @@ public void groupBy() { solution.getEntityGroupList().add(entityGroup); solution.getValueList().add(new TestdataLavishValue()); - assertPrecompute(solution, List.of(entityGroup), + assertPrecompute(solution, List.of(expect(entityGroup, entityWithGroup)), pf -> pf.forEachUnfiltered(TestdataLavishEntity.class) .filter(entity -> entity.getEntityGroup() != null) .groupBy(TestdataLavishEntity::getEntityGroup)); @@ -215,21 +243,42 @@ public void groupBy() { public void flatten() { var solution = TestdataLavishSolution.generateEmptySolution(); var entityWithoutGroup = new TestdataLavishEntity(); + entityWithoutGroup.setCode("A"); var entityWithGroup = new TestdataLavishEntity(); + entityWithGroup.setCode("B"); var entityGroup = new TestdataLavishEntityGroup(); + entityGroup.setCode("C"); entityWithGroup.setEntityGroup(entityGroup); solution.getEntityList().addAll(List.of(entityWithoutGroup, entityWithGroup)); solution.getEntityGroupList().add(entityGroup); solution.getValueList().add(new TestdataLavishValue()); - assertPrecomputeBi(solution, List.of(new Pair<>(entityWithoutGroup, entityWithoutGroup), - new Pair<>(entityWithGroup, entityWithGroup)), + assertPrecomputeBi(solution, List.of(expectBi(entityWithoutGroup, entityWithoutGroup, entityWithoutGroup), + expectBi(entityWithGroup, entityWithGroup, entityWithGroup)), pf -> pf.forEachUnfiltered(TestdataLavishEntity.class) .flatten(List::of)); } - private void assertPrecomputeBi(TestdataLavishSolution solution, List> expectedValues, + record ExpectedBi(A a, B b, Object... indicted) { + ExpectedBi addIndicted(Object indictedObject) { + for (var object : indicted) { + if (object == indictedObject) { + return this; + } + } + var indictments = Arrays.copyOf(indicted, indicted.length + 1); + indictments[indicted.length] = indictedObject; + return new ExpectedBi<>(a, b, indictments); + } + } + + ExpectedBi expectBi(A a, B b, Object... indicted) { + return new ExpectedBi<>(a, b, indicted); + } + + private void assertPrecomputeBi(TestdataLavishSolution solution, List> expectedValues, Function> entityStreamSupplier) { + expectedValues = new ArrayList<>(expectedValues); var scoreDirector = buildScoreDirector(factory -> factory.precompute(entityStreamSupplier) .ifExists(TestdataLavishEntity.class) @@ -244,11 +293,16 @@ private void assertPrecomputeBi(TestdataLavishSolution solution, List new Object[] { pair.key(), pair.value() }) - .map(AbstractConstraintStreamTest::assertMatch) + .map(expectedBi -> assertMatch(expectedBi.a(), expectedBi.b()) + .withIndictedObjects(expectedBi.indicted)) .toArray(AssertableMatch[]::new)); } @@ -268,8 +322,8 @@ record ValueHolder(int value) { solution.getValueList().add(new TestdataLavishValue()); assertPrecomputeBi(solution, List.of( - new Pair<>(entity1, new ValueHolder(1)), - new Pair<>(entity2, new ValueHolder(2))), + expectBi(entity1, new ValueHolder(1), entity1), + expectBi(entity2, new ValueHolder(2), entity2)), pf -> pf.forEachUnfiltered(TestdataLavishEntity.class) .flatten(entity -> List.of(new ValueHolder(entity.getIntegerProperty())))); } @@ -286,7 +340,8 @@ public void flattenLast() { solution.getEntityGroupList().add(entityGroup); solution.getValueList().add(new TestdataLavishValue()); - assertPrecompute(solution, List.of(entityWithoutGroup, entityWithGroup), + assertPrecompute(solution, + List.of(expect(entityWithoutGroup, entityWithoutGroup), expect(entityWithGroup, entityWithGroup)), pf -> pf.forEachUnfiltered(TestdataLavishEntity.class) .groupBy(ConstraintCollectors.toList()) .flattenLast(entityList -> entityList)); @@ -307,7 +362,8 @@ record ValueHolder(int value) { solution.getEntityList().addAll(List.of(entity1, entity2)); solution.getValueList().add(new TestdataLavishValue()); - assertPrecompute(solution, List.of(new ValueHolder(1), new ValueHolder(2)), + assertPrecompute(solution, List.of(expect(new ValueHolder(1), entity1), + expect(new ValueHolder(2), entity2)), pf -> pf.forEachUnfiltered(TestdataLavishEntity.class) .groupBy(ConstraintCollectors.toList()) .flattenLast(entityList -> entityList @@ -330,7 +386,8 @@ public void map() { solution.getEntityGroupList().add(entityGroup); solution.getValueList().add(new TestdataLavishValue()); - assertPrecompute(solution, List.of(entityGroup, entityGroup), + assertPrecompute(solution, List.of(expect(entityGroup, entityWithGroup1), + expect(entityGroup, entityWithGroup2)), pf -> pf.forEachUnfiltered(TestdataLavishEntity.class) .filter(entity -> entity.getEntityGroup() != null) .map(TestdataLavishEntity::getEntityGroup)); @@ -341,14 +398,18 @@ public void map() { public void concat() { var solution = TestdataLavishSolution.generateEmptySolution(); var entityWithoutGroup = new TestdataLavishEntity(); + entityWithoutGroup.setCode("EntityWithoutGroup"); var entityWithGroup = new TestdataLavishEntity(); + entityWithGroup.setCode("EntityWithGroup"); var entityGroup = new TestdataLavishEntityGroup(); + entityGroup.setCode("EntityGroup"); entityWithGroup.setEntityGroup(entityGroup); solution.getEntityList().addAll(List.of(entityWithoutGroup, entityWithGroup)); solution.getEntityGroupList().add(entityGroup); solution.getValueList().add(new TestdataLavishValue()); - assertPrecompute(solution, List.of(entityWithoutGroup, entityWithGroup), + assertPrecompute(solution, List.of(expect(entityWithoutGroup, entityWithoutGroup), + expect(entityWithGroup, entityWithGroup)), pf -> pf.forEachUnfiltered(TestdataLavishEntity.class) .filter(entity -> entity.getEntityGroup() == null) .concat(pf.forEachUnfiltered(TestdataLavishEntity.class) @@ -369,7 +430,7 @@ public void distinct() { solution.getEntityGroupList().add(entityGroup); solution.getValueList().add(new TestdataLavishValue()); - assertPrecompute(solution, List.of(entityGroup), + assertPrecompute(solution, List.of(expect(entityGroup, entityWithGroup1, entityWithGroup2)), pf -> pf.forEachUnfiltered(TestdataLavishEntity.class) .filter(entity -> entity.getEntityGroup() != null) .map(TestdataLavishEntity::getEntityGroup) @@ -390,7 +451,9 @@ public void complement() { solution.getEntityGroupList().add(entityGroup); solution.getValueList().add(new TestdataLavishValue()); - assertPrecompute(solution, List.of(entityWithGroup1, entityWithGroup2, entityWithoutGroup), + assertPrecompute(solution, List.of(expect(entityWithGroup1, entityWithGroup1), + expect(entityWithGroup2, entityWithGroup2), + expect(entityWithoutGroup, entityWithoutGroup)), pf -> pf.forEachUnfiltered(TestdataLavishEntity.class) .filter(entity -> entity.getEntityGroup() != null) .complement(TestdataLavishEntity.class)); diff --git a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/uni/AbstractUniConstraintStreamTest.java b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/uni/AbstractUniConstraintStreamTest.java index 883b8856886..da4c87235a5 100644 --- a/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/uni/AbstractUniConstraintStreamTest.java +++ b/core/src/test/java/ai/timefold/solver/core/impl/score/stream/common/uni/AbstractUniConstraintStreamTest.java @@ -163,7 +163,7 @@ public void filter_entity() { @TestTemplate public void filter_consecutive() { var solution = TestdataLavishSolution.generateSolution(4, 4); - var entity1 = solution.getEntityList().get(0); + var entity1 = solution.getEntityList().getFirst(); var entity2 = solution.getEntityList().get(1); var entity3 = solution.getEntityList().get(2); var entity4 = solution.getEntityList().get(3); @@ -255,22 +255,22 @@ public void join_1Equal() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, entity1), - assertMatch(entity1, entity3), - assertMatch(entity2, entity2), - assertMatch(entity3, entity1), - assertMatch(entity3, entity3)); + assertMatch(entity1, entity1).withIndictedObjects(entity1), + assertMatch(entity1, entity3).withIndictedObjects(entity1, entity3), + assertMatch(entity2, entity2).withIndictedObjects(entity2), + assertMatch(entity3, entity1).withIndictedObjects(entity3, entity1), + assertMatch(entity3, entity3).withIndictedObjects(entity3)); // Incremental scoreDirector.beforeVariableChanged(entity3, "value"); entity3.setValue(value2); scoreDirector.afterVariableChanged(entity3, "value"); assertScore(scoreDirector, - assertMatch(entity1, entity1), - assertMatch(entity2, entity2), - assertMatch(entity2, entity3), - assertMatch(entity3, entity2), - assertMatch(entity3, entity3)); + assertMatch(entity1, entity1).withIndictedObjects(entity1), + assertMatch(entity2, entity2).withIndictedObjects(entity2), + assertMatch(entity2, entity3).withIndictedObjects(entity2, entity3), + assertMatch(entity3, entity2).withIndictedObjects(entity3, entity2), + assertMatch(entity3, entity3).withIndictedObjects(entity3)); // Incremental for which the first change matches a join that doesn't survive the second change scoreDirector.beforeVariableChanged(entity1, "value"); @@ -280,11 +280,11 @@ public void join_1Equal() { entity3.setValue(value1); scoreDirector.afterVariableChanged(entity3, "value"); assertScore(scoreDirector, - assertMatch(entity1, entity1), - assertMatch(entity2, entity2), - assertMatch(entity1, entity2), - assertMatch(entity2, entity1), - assertMatch(entity3, entity3)); + assertMatch(entity1, entity1).withIndictedObjects(entity1), + assertMatch(entity2, entity2).withIndictedObjects(entity2), + assertMatch(entity1, entity2).withIndictedObjects(entity1, entity2), + assertMatch(entity2, entity1).withIndictedObjects(entity2, entity1), + assertMatch(entity3, entity3).withIndictedObjects(entity3)); } /** @@ -365,24 +365,26 @@ public void join_2Equal() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(solution.getFirstEntity(), solution.getFirstEntity()), - assertMatch(entity1, entity1), - assertMatch(entity1, entity2), - assertMatch(entity2, entity1), - assertMatch(entity2, entity2), - assertMatch(entity3, entity3)); + assertMatch(solution.getFirstEntity(), solution.getFirstEntity()) + .withIndictedObjects(solution.getFirstEntity()), + assertMatch(entity1, entity1).withIndictedObjects(entity1), + assertMatch(entity1, entity2).withIndictedObjects(entity1, entity2), + assertMatch(entity2, entity1).withIndictedObjects(entity2, entity1), + assertMatch(entity2, entity2).withIndictedObjects(entity2), + assertMatch(entity3, entity3).withIndictedObjects(entity3)); // Incremental scoreDirector.beforeProblemPropertyChanged(entity1); entity1.setIntegerProperty(8); scoreDirector.afterProblemPropertyChanged(entity1); assertScore(scoreDirector, - assertMatch(solution.getFirstEntity(), solution.getFirstEntity()), - assertMatch(entity1, entity1), - assertMatch(entity1, entity3), - assertMatch(entity2, entity2), - assertMatch(entity3, entity1), - assertMatch(entity3, entity3)); + assertMatch(solution.getFirstEntity(), solution.getFirstEntity()) + .withIndictedObjects(solution.getFirstEntity()), + assertMatch(entity1, entity1).withIndictedObjects(entity1), + assertMatch(entity1, entity3).withIndictedObjects(entity1, entity3), + assertMatch(entity2, entity2).withIndictedObjects(entity2), + assertMatch(entity3, entity1).withIndictedObjects(entity3, entity1), + assertMatch(entity3, entity3).withIndictedObjects(entity3)); } @Override @@ -412,24 +414,24 @@ public void joinAfterGroupBy() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(1L, extra1), - assertMatch(1L, extra2)); + assertMatch(1L, extra1).withIndictedObjects(entity1, entity2, extra1), + assertMatch(1L, extra2).withIndictedObjects(entity1, entity2, extra2)); // Incremental scoreDirector.beforeVariableChanged(entity2, "value"); entity2.setValue(value2); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(2L, extra1), - assertMatch(2L, extra2)); + assertMatch(2L, extra1).withIndictedObjects(entity1, entity2, extra1), + assertMatch(2L, extra2).withIndictedObjects(entity1, entity2, extra2)); // Incremental scoreDirector.beforeEntityRemoved(entity2); solution.getEntityList().remove(entity2); scoreDirector.afterEntityRemoved(entity2); assertScore(scoreDirector, - assertMatch(1L, extra1), - assertMatch(1L, extra2)); + assertMatch(1L, extra1).withIndictedObjects(entity1, extra1), + assertMatch(1L, extra2).withIndictedObjects(entity1, extra2)); } @Override @@ -462,16 +464,31 @@ public void ifExists_0Joiner0Filter() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(solution.getFirstValueGroup()), - assertMatch(valueGroup)); + assertMatch(solution.getFirstValueGroup()) + .withIndictedObjects( + solution.getFirstValueGroup(), + solution.getFirstEntityGroup(), + entityGroup), + assertMatch(valueGroup).withIndictedObjects( + valueGroup, + solution.getFirstEntityGroup(), + entityGroup)); // Incremental scoreDirector.beforeProblemFactRemoved(entityGroup); solution.getEntityGroupList().remove(entityGroup); scoreDirector.afterProblemFactRemoved(entityGroup); + + // Note: entityGroup is still indicted, despite being removed, + // since only the right tuple of the ifExists changed, so no + // change was propagated. assertScore(scoreDirector, - assertMatch(solution.getFirstValueGroup()), - assertMatch(valueGroup)); + assertMatch(solution.getFirstValueGroup()).withIndictedObjects( + solution.getFirstValueGroup(), + solution.getFirstEntityGroup()), + assertMatch(valueGroup).withIndictedObjects( + valueGroup, + solution.getFirstEntityGroup())); } @Override @@ -496,17 +513,19 @@ public void ifExists_0Join1Filter() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(solution.getFirstEntity()), - assertMatch(entity1), - assertMatch(entity2)); + assertMatch(solution.getFirstEntity()).withIndictedObjects(solution.getFirstEntity(), + solution.getFirstEntityGroup()), + assertMatch(entity1).withIndictedObjects(entity1, entityGroup), + assertMatch(entity2).withIndictedObjects(entity2, solution.getFirstEntityGroup())); // Incremental scoreDirector.beforeProblemFactRemoved(entityGroup); solution.getEntityGroupList().remove(entityGroup); scoreDirector.afterProblemFactRemoved(entityGroup); assertScore(scoreDirector, - assertMatch(solution.getFirstEntity()), - assertMatch(entity2)); + assertMatch(solution.getFirstEntity()).withIndictedObjects(solution.getFirstEntity(), + solution.getFirstEntityGroup()), + assertMatch(entity2).withIndictedObjects(entity2, solution.getFirstEntityGroup())); } @Override @@ -530,17 +549,19 @@ public void ifExists_1Join0Filter() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(solution.getFirstEntity()), - assertMatch(entity1), - assertMatch(entity2)); + assertMatch(solution.getFirstEntity()).withIndictedObjects(solution.getFirstEntity(), + solution.getFirstEntityGroup()), + assertMatch(entity1).withIndictedObjects(entity1, entityGroup), + assertMatch(entity2).withIndictedObjects(entity2, solution.getFirstEntityGroup())); // Incremental scoreDirector.beforeProblemFactRemoved(entityGroup); solution.getEntityGroupList().remove(entityGroup); scoreDirector.afterProblemFactRemoved(entityGroup); assertScore(scoreDirector, - assertMatch(solution.getFirstEntity()), - assertMatch(entity2)); + assertMatch(solution.getFirstEntity()).withIndictedObjects(solution.getFirstEntity(), + solution.getFirstEntityGroup()), + assertMatch(entity2).withIndictedObjects(entity2, solution.getFirstEntityGroup())); } @Override @@ -567,15 +588,15 @@ public void ifExists_1Join1Filter() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1), - assertMatch(entity2)); + assertMatch(entity1).withIndictedObjects(entity1, entityGroup), + assertMatch(entity2).withIndictedObjects(entity2, solution.getFirstEntityGroup())); // Incremental scoreDirector.beforeProblemFactRemoved(entityGroup); solution.getEntityGroupList().remove(entityGroup); scoreDirector.afterProblemFactRemoved(entityGroup); assertScore(scoreDirector, - assertMatch(entity2)); + assertMatch(entity2).withIndictedObjects(entity2, solution.getFirstEntityGroup())); } @TestTemplate @@ -598,16 +619,16 @@ public void ifExistsOther_1Join0Filter() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(solution.getFirstEntity()), - assertMatch(entity2)); + assertMatch(solution.getFirstEntity()).withIndictedObjects(solution.getFirstEntity(), entity2), + assertMatch(entity2).withIndictedObjects(solution.getFirstEntity(), entity2)); // Incremental scoreDirector.beforeProblemPropertyChanged(entity2); entity2.setEntityGroup(entityGroup); scoreDirector.afterProblemPropertyChanged(entity2); assertScore(scoreDirector, - assertMatch(entity1), - assertMatch(entity2)); + assertMatch(entity1).withIndictedObjects(entity1, entity2), + assertMatch(entity2).withIndictedObjects(entity1, entity2)); } @Override @@ -634,16 +655,16 @@ public void ifExistsDoesNotIncludeUnassigned() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(solution.getFirstEntity()), - assertMatch(entity2)); + assertMatch(solution.getFirstEntity()).withIndictedObjects(solution.getFirstEntity(), entity2), + assertMatch(entity2).withIndictedObjects(solution.getFirstEntity(), entity2)); // Incremental scoreDirector.beforeProblemPropertyChanged(entity2); entity2.setEntityGroup(entityGroup); scoreDirector.afterProblemPropertyChanged(entity2); assertScore(scoreDirector, - assertMatch(entity1), - assertMatch(entity2)); + assertMatch(entity1).withIndictedObjects(entity1, entity2), + assertMatch(entity2).withIndictedObjects(entity1, entity2)); } @Override @@ -870,21 +891,21 @@ public void ifExistsAfterGroupBy() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(1L)); + assertMatch(1L).withIndictedObjects(entity1, entity2)); // Incremental scoreDirector.beforeVariableChanged(entity2, "value"); entity2.setValue(value2); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(2L)); + assertMatch(2L).withIndictedObjects(entity1, entity2)); // Incremental scoreDirector.beforeEntityRemoved(entity2); solution.getEntityList().remove(entity2); scoreDirector.afterEntityRemoved(entity2); assertScore(scoreDirector, - assertMatch(1L)); + assertMatch(1L).withIndictedObjects(entity1)); } @TestTemplate @@ -1036,7 +1057,7 @@ public void forEach_listVarNotAllowsUnassignedValues_noInverseVar() { // v2 is not assigned, so it should not be matched scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(v1)); + assertMatch(v1).withIndictedObjects(v1, e1)); // Incremental scoreDirector.beforeListVariableChanged(e2, "valueList", 0, 1); @@ -1045,8 +1066,8 @@ public void forEach_listVarNotAllowsUnassignedValues_noInverseVar() { scoreDirector.afterListVariableElementAssigned(e2, "valueList", v2); scoreDirector.afterListVariableChanged(e2, "valueList", 0, 1); assertScore(scoreDirector, - assertMatch(v1), - assertMatch(v2)); + assertMatch(v1).withIndictedObjects(v1, e1), + assertMatch(v2).withIndictedObjects(v2, e2)); scoreDirector.beforeListVariableChanged(e1, "valueList", 0, 0); scoreDirector.beforeListVariableElementUnassigned(e1, "valueList", v1); @@ -1054,7 +1075,7 @@ public void forEach_listVarNotAllowsUnassignedValues_noInverseVar() { scoreDirector.afterListVariableElementUnassigned(e1, "valueList", v1); scoreDirector.afterListVariableChanged(e1, "valueList", 0, 0); assertScore(scoreDirector, - assertMatch(v2)); + assertMatch(v2).withIndictedObjects(v2, e2)); } @TestTemplate @@ -1407,7 +1428,7 @@ public void groupBy_1Mapping0Collect_filtered() { .asConstraint(TEST_CONSTRAINT_ID)); scoreDirector.setWorkingSolution(solution); - assertScore(scoreDirector, assertMatchWithScore(-1, entityGroup1)); + assertScore(scoreDirector, assertMatchWithScore(-1, entityGroup1).withIndictedObjects(entity1, entity2)); } @TestTemplate @@ -1432,8 +1453,16 @@ public void groupBy_1Mapping1Collect_filtered() { scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, entityGroup1, 2L), - assertMatchWithScore(-1, solution.getFirstEntityGroup(), 8L)); + assertMatchWithScore(-1, entityGroup1, 2L).withIndictedObjects(entity1, entity2), + assertMatchWithScore(-1, solution.getFirstEntityGroup(), 8L).withIndictedObjects( + solution.getFirstEntity(), + solution.getEntityList().get(1), + solution.getEntityList().get(2), + solution.getEntityList().get(3), + solution.getEntityList().get(4), + solution.getEntityList().get(5), + solution.getEntityList().get(6), + entity3)); } @TestTemplate @@ -1459,8 +1488,8 @@ public void groupBy_joinedAndFiltered() { scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, entityGroup1, entity1), - assertMatchWithScore(-1, entityGroup1, entity2)); + assertMatchWithScore(-1, entityGroup1, entity1).withIndictedObjects(entity1, entity2), + assertMatchWithScore(-1, entityGroup1, entity2).withIndictedObjects(entity1, entity2)); } @Override @@ -1486,8 +1515,16 @@ public void groupBy_1Mapping0Collector() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, solution.getFirstEntityGroup()), - assertMatchWithScore(-1, entityGroup1)); + assertMatchWithScore(-1, solution.getFirstEntityGroup()).withIndictedObjects( + solution.getFirstEntity(), + solution.getEntityList().get(1), + solution.getEntityList().get(2), + solution.getEntityList().get(3), + solution.getEntityList().get(4), + solution.getEntityList().get(5), + solution.getEntityList().get(6), + entity3), + assertMatchWithScore(-1, entityGroup1).withIndictedObjects(entity1, entity2)); // Incremental Stream.of(entity1, entity2).forEach(entity -> { @@ -1495,7 +1532,15 @@ public void groupBy_1Mapping0Collector() { solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); }); - assertScore(scoreDirector, assertMatchWithScore(-1, solution.getFirstEntityGroup())); + assertScore(scoreDirector, assertMatchWithScore(-1, solution.getFirstEntityGroup()).withIndictedObjects( + solution.getFirstEntity(), + solution.getEntityList().get(1), + solution.getEntityList().get(2), + solution.getEntityList().get(3), + solution.getEntityList().get(4), + solution.getEntityList().get(5), + solution.getEntityList().get(6), + entity3)); } @Override @@ -1511,18 +1556,22 @@ public void groupBy_1Mapping1Collector() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, solution.getFirstEntityGroup(), 2L), - assertMatchWithScore(-1, solution.getEntityGroupList().get(1), 1L)); + assertMatchWithScore(-1, solution.getFirstEntityGroup(), 2L).withIndictedObjects(solution.getFirstEntity(), + solution.getEntityList().get(2)), + assertMatchWithScore(-1, solution.getEntityGroupList().get(1), 1L) + .withIndictedObjects(solution.getEntityList().get(1))); // Incremental - Stream.of(solution.getEntityList().get(0), solution.getEntityList().get(1)) + var originalFirstEntity = solution.getFirstEntity(); + Stream.of(solution.getEntityList().getFirst(), solution.getEntityList().get(1)) .forEach(entity -> { scoreDirector.beforeEntityRemoved(entity); solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); }); assertScore(scoreDirector, - assertMatchWithScore(-1, solution.getFirstEntityGroup(), 1L)); + assertMatchWithScore(-1, solution.getFirstEntityGroup(), 1L) + .withIndictedObjects(solution.getEntityList().getFirst())); } @Override @@ -1544,16 +1593,20 @@ public void groupBy_1Mapping2Collector() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, solution.getFirstEntityGroup(), 2L, asSet(entity1, entity3)), - assertMatchWithScore(-1, solution.getEntityGroupList().get(1), 1L, Collections.singleton(entity2))); + assertMatchWithScore(-1, solution.getFirstEntityGroup(), 2L, asSet(entity1, entity3)) + .withIndictedObjects(solution.getFirstEntity(), solution.getEntityList().get(2)), + assertMatchWithScore(-1, solution.getEntityGroupList().get(1), 1L, Collections.singleton(entity2)) + .withIndictedObjects(solution.getEntityList().get(1))); // Incremental scoreDirector.beforeEntityRemoved(entity1); solution.getEntityList().remove(entity1); scoreDirector.afterEntityRemoved(entity1); assertScore(scoreDirector, - assertMatchWithScore(-1, solution.getFirstEntityGroup(), 1L, Collections.singleton(entity3)), - assertMatchWithScore(-1, solution.getEntityGroupList().get(1), 1L, Collections.singleton(entity2))); + assertMatchWithScore(-1, solution.getFirstEntityGroup(), 1L, Collections.singleton(entity3)) + .withIndictedObjects(entity3), + assertMatchWithScore(-1, solution.getEntityGroupList().get(1), 1L, Collections.singleton(entity2)) + .withIndictedObjects(entity2)); } @Override @@ -1576,16 +1629,20 @@ public void groupBy_1Mapping3Collector() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, solution.getFirstEntityGroup(), 2L, 2, asSet(entity1, entity3)), - assertMatchWithScore(-1, solution.getEntityGroupList().get(1), 1L, 1, Collections.singleton(entity2))); + assertMatchWithScore(-1, solution.getFirstEntityGroup(), 2L, 2, asSet(entity1, entity3)) + .withIndictedObjects(solution.getFirstEntity(), solution.getEntityList().get(2)), + assertMatchWithScore(-1, solution.getEntityGroupList().get(1), 1L, 1, Collections.singleton(entity2)) + .withIndictedObjects(solution.getEntityList().get(1))); // Incremental scoreDirector.beforeEntityRemoved(entity1); solution.getEntityList().remove(entity1); scoreDirector.afterEntityRemoved(entity1); assertScore(scoreDirector, - assertMatchWithScore(-1, solution.getFirstEntityGroup(), 1L, 1, Collections.singleton(entity3)), - assertMatchWithScore(-1, solution.getEntityGroupList().get(1), 1L, 1, Collections.singleton(entity2))); + assertMatchWithScore(-1, solution.getFirstEntityGroup(), 1L, 1, Collections.singleton(entity3)) + .withIndictedObjects(entity3), + assertMatchWithScore(-1, solution.getEntityGroupList().get(1), 1L, 1, Collections.singleton(entity2)) + .withIndictedObjects(entity2)); } @Override @@ -1610,13 +1667,29 @@ public void groupBy_0Mapping1Collector() { // From scratch scoreDirector.setWorkingSolution(solution); - assertScore(scoreDirector, assertMatchWithScore(-10, 10L)); + assertScore(scoreDirector, assertMatchWithScore(-10, 10L).withIndictedObjects( + solution.getFirstEntity(), + solution.getEntityList().get(1), + solution.getEntityList().get(2), + solution.getEntityList().get(3), + solution.getEntityList().get(4), + solution.getEntityList().get(5), + solution.getEntityList().get(6), + entity1, entity2, entity3)); // Incremental scoreDirector.beforeEntityRemoved(entity3); solution.getEntityList().remove(entity3); scoreDirector.afterEntityRemoved(entity3); - assertScore(scoreDirector, assertMatchWithScore(-9, 9L)); + assertScore(scoreDirector, assertMatchWithScore(-9, 9L).withIndictedObjects( + solution.getFirstEntity(), + solution.getEntityList().get(1), + solution.getEntityList().get(2), + solution.getEntityList().get(3), + solution.getEntityList().get(4), + solution.getEntityList().get(5), + solution.getEntityList().get(6), + entity1, entity2)); } @Override @@ -1634,13 +1707,15 @@ public void groupBy_0Mapping2Collector() { // From scratch scoreDirector.setWorkingSolution(solution); - assertScore(scoreDirector, assertMatchWithScore(-1, 3L, 3)); + assertScore(scoreDirector, assertMatchWithScore(-1, 3L, 3).withIndictedObjects(solution.getFirstEntity(), + solution.getEntityList().get(1), solution.getEntityList().get(2))); // Incremental scoreDirector.beforeEntityRemoved(entity1); solution.getEntityList().remove(entity1); scoreDirector.afterEntityRemoved(entity1); - assertScore(scoreDirector, assertMatchWithScore(-1, 2L, 2)); + assertScore(scoreDirector, assertMatchWithScore(-1, 2L, 2).withIndictedObjects(solution.getEntityList().getFirst(), + solution.getEntityList().get(1))); } @Override @@ -1665,14 +1740,16 @@ public void groupBy_0Mapping3Collector() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, 3L, 0, 2)); + assertMatchWithScore(-1, 3L, 0, 2).withIndictedObjects(solution.getFirstEntity(), + solution.getEntityList().get(1), solution.getEntityList().get(2))); // Incremental scoreDirector.beforeEntityRemoved(entity1); solution.getEntityList().remove(entity1); scoreDirector.afterEntityRemoved(entity1); assertScore(scoreDirector, - assertMatchWithScore(-1, 2L, 1, 2)); + assertMatchWithScore(-1, 2L, 1, 2).withIndictedObjects(solution.getEntityList().getFirst(), + solution.getEntityList().get(1))); } @Override @@ -1698,14 +1775,17 @@ public void groupBy_0Mapping4Collector() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, 3L, 0, 2, asSet(entity1, entity2, entity3))); + assertMatchWithScore(-1, 3L, 0, 2, asSet(entity1, entity2, entity3)).withIndictedObjects( + solution.getFirstEntity(), solution.getEntityList().get(1), solution.getEntityList().get(2))); // Incremental scoreDirector.beforeEntityRemoved(entity1); solution.getEntityList().remove(entity1); scoreDirector.afterEntityRemoved(entity1); assertScore(scoreDirector, - assertMatchWithScore(-1, 2L, 1, 2, asSet(entity2, entity3))); + assertMatchWithScore(-1, 2L, 1, 2, asSet(entity2, entity3)).withIndictedObjects( + solution.getEntityList().getFirst(), + solution.getEntityList().get(1))); } @TestTemplate @@ -1733,17 +1813,33 @@ public void groupBy_1Mapping1Collector_groupingOnPrimitives() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-8, 1, 8L), - assertMatchWithScore(-1, 2, 1L), - assertMatchWithScore(-1, 3, 1L)); + assertMatchWithScore(-8, 1, 8L).withIndictedObjects( + solution.getFirstEntity(), + solution.getEntityList().get(1), + solution.getEntityList().get(2), + solution.getEntityList().get(3), + solution.getEntityList().get(4), + solution.getEntityList().get(5), + solution.getEntityList().get(6), + entity1), + assertMatchWithScore(-1, 2, 1L).withIndictedObjects(entity2), + assertMatchWithScore(-1, 3, 1L).withIndictedObjects(entity3)); // Incremental scoreDirector.beforeEntityRemoved(entity3); solution.getEntityList().remove(entity3); scoreDirector.afterEntityRemoved(entity3); assertScore(scoreDirector, - assertMatchWithScore(-8, 1, 8L), - assertMatchWithScore(-1, 2, 1L)); + assertMatchWithScore(-8, 1, 8L).withIndictedObjects( + solution.getFirstEntity(), + solution.getEntityList().get(1), + solution.getEntityList().get(2), + solution.getEntityList().get(3), + solution.getEntityList().get(4), + solution.getEntityList().get(5), + solution.getEntityList().get(6), + entity1), + assertMatchWithScore(-1, 2, 1L).withIndictedObjects(entity2)); } @Override @@ -1769,37 +1865,52 @@ public void groupBy_2Mapping0Collector() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, entityGroup1, solution.getFirstValue()), - assertMatchWithScore(-1, entityGroup1, secondValue), - assertMatchWithScore(-1, solution.getFirstEntityGroup(), solution.getValueList().get(0)), - assertMatchWithScore(-1, solution.getFirstEntityGroup(), solution.getValueList().get(1)), - assertMatchWithScore(-1, solution.getFirstEntityGroup(), solution.getValueList().get(2)), - assertMatchWithScore(-1, solution.getFirstEntityGroup(), solution.getValueList().get(3)), - assertMatchWithScore(-1, solution.getFirstEntityGroup(), solution.getValueList().get(4))); + assertMatchWithScore(-1, entityGroup1, solution.getFirstValue()).withIndictedObjects(entity1, entity2), + assertMatchWithScore(-1, entityGroup1, secondValue).withIndictedObjects(entity3), + assertMatchWithScore(-1, solution.getFirstEntityGroup(), solution.getValueList().getFirst()) + .withIndictedObjects(solution.getFirstEntity(), solution.getEntityList().get(5)), + assertMatchWithScore(-1, solution.getFirstEntityGroup(), solution.getValueList().get(1)) + .withIndictedObjects(solution.getEntityList().get(1), solution.getEntityList().get(6)), + assertMatchWithScore(-1, solution.getFirstEntityGroup(), solution.getValueList().get(2)) + .withIndictedObjects(solution.getEntityList().get(2)), + assertMatchWithScore(-1, solution.getFirstEntityGroup(), solution.getValueList().get(3)) + .withIndictedObjects(solution.getEntityList().get(3)), + assertMatchWithScore(-1, solution.getFirstEntityGroup(), solution.getValueList().get(4)) + .withIndictedObjects(solution.getEntityList().get(4))); // Incremental scoreDirector.beforeEntityRemoved(entity3); solution.getEntityList().remove(entity3); scoreDirector.afterEntityRemoved(entity3); assertScore(scoreDirector, - assertMatchWithScore(-1, entityGroup1, solution.getFirstValue()), - assertMatchWithScore(-1, solution.getFirstEntityGroup(), solution.getValueList().get(0)), - assertMatchWithScore(-1, solution.getFirstEntityGroup(), solution.getValueList().get(1)), - assertMatchWithScore(-1, solution.getFirstEntityGroup(), solution.getValueList().get(2)), - assertMatchWithScore(-1, solution.getFirstEntityGroup(), solution.getValueList().get(3)), - assertMatchWithScore(-1, solution.getFirstEntityGroup(), solution.getValueList().get(4))); + assertMatchWithScore(-1, entityGroup1, solution.getFirstValue()).withIndictedObjects(entity1, entity2), + assertMatchWithScore(-1, solution.getFirstEntityGroup(), solution.getValueList().getFirst()) + .withIndictedObjects(solution.getFirstEntity(), solution.getEntityList().get(5)), + assertMatchWithScore(-1, solution.getFirstEntityGroup(), solution.getValueList().get(1)) + .withIndictedObjects(solution.getEntityList().get(1), solution.getEntityList().get(6)), + assertMatchWithScore(-1, solution.getFirstEntityGroup(), solution.getValueList().get(2)) + .withIndictedObjects(solution.getEntityList().get(2)), + assertMatchWithScore(-1, solution.getFirstEntityGroup(), solution.getValueList().get(3)) + .withIndictedObjects(solution.getEntityList().get(3)), + assertMatchWithScore(-1, solution.getFirstEntityGroup(), solution.getValueList().get(4)) + .withIndictedObjects(solution.getEntityList().get(4))); // Ensure that the first match is still there when entity2, as it still has entity1 scoreDirector.beforeEntityRemoved(entity2); solution.getEntityList().remove(entity2); scoreDirector.afterEntityRemoved(entity2); assertScore(scoreDirector, - assertMatchWithScore(-1, entityGroup1, solution.getFirstValue()), - assertMatchWithScore(-1, solution.getFirstEntityGroup(), solution.getValueList().get(0)), - assertMatchWithScore(-1, solution.getFirstEntityGroup(), solution.getValueList().get(1)), - assertMatchWithScore(-1, solution.getFirstEntityGroup(), solution.getValueList().get(2)), - assertMatchWithScore(-1, solution.getFirstEntityGroup(), solution.getValueList().get(3)), - assertMatchWithScore(-1, solution.getFirstEntityGroup(), solution.getValueList().get(4))); + assertMatchWithScore(-1, entityGroup1, solution.getFirstValue()).withIndictedObjects(entity1), + assertMatchWithScore(-1, solution.getFirstEntityGroup(), solution.getValueList().getFirst()) + .withIndictedObjects(solution.getFirstEntity(), solution.getEntityList().get(5)), + assertMatchWithScore(-1, solution.getFirstEntityGroup(), solution.getValueList().get(1)) + .withIndictedObjects(solution.getEntityList().get(1), solution.getEntityList().get(6)), + assertMatchWithScore(-1, solution.getFirstEntityGroup(), solution.getValueList().get(2)) + .withIndictedObjects(solution.getEntityList().get(2)), + assertMatchWithScore(-1, solution.getFirstEntityGroup(), solution.getValueList().get(3)) + .withIndictedObjects(solution.getEntityList().get(3)), + assertMatchWithScore(-1, solution.getFirstEntityGroup(), solution.getValueList().get(4)) + .withIndictedObjects(solution.getEntityList().get(4))); } @Override @@ -1826,17 +1937,32 @@ public void groupBy_2Mapping1Collector() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-7, solution.getFirstEntityGroup(), solution.getFirstValue(), 7L), - assertMatchWithScore(-2, entityGroup1, value1, 2L), - assertMatchWithScore(-1, entityGroup1, solution.getFirstValue(), 1L)); + assertMatchWithScore(-7, solution.getFirstEntityGroup(), solution.getFirstValue(), 7L).withIndictedObjects( + solution.getFirstEntity(), + solution.getEntityList().get(1), + solution.getEntityList().get(2), + solution.getEntityList().get(3), + solution.getEntityList().get(4), + solution.getEntityList().get(5), + solution.getEntityList().get(6)), + assertMatchWithScore(-2, entityGroup1, value1, 2L).withIndictedObjects(entity1, entity3), + assertMatchWithScore(-1, entityGroup1, solution.getFirstValue(), 1L).withIndictedObjects(entity2)); // Incremental scoreDirector.beforeProblemPropertyChanged(entity2); entity2.setEntityGroup(solution.getFirstEntityGroup()); scoreDirector.afterProblemPropertyChanged(entity2); assertScore(scoreDirector, - assertMatchWithScore(-8, solution.getFirstEntityGroup(), solution.getFirstValue(), 8L), - assertMatchWithScore(-2, entityGroup1, value1, 2L)); + assertMatchWithScore(-8, solution.getFirstEntityGroup(), solution.getFirstValue(), 8L).withIndictedObjects( + solution.getFirstEntity(), + solution.getEntityList().get(1), + solution.getEntityList().get(2), + solution.getEntityList().get(3), + solution.getEntityList().get(4), + solution.getEntityList().get(5), + solution.getEntityList().get(6), + entity2), + assertMatchWithScore(-2, entityGroup1, value1, 2L).withIndictedObjects(entity1, entity3)); } @Override @@ -1863,17 +1989,32 @@ public void groupBy_2Mapping2Collector() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-7, solution.getFirstEntityGroup(), solution.getFirstValue(), 7L, 7L), - assertMatchWithScore(-2, entityGroup1, value1, 2L, 2L), - assertMatchWithScore(-1, entityGroup1, solution.getFirstValue(), 1L, 1L)); + assertMatchWithScore(-7, solution.getFirstEntityGroup(), solution.getFirstValue(), 7L, 7L).withIndictedObjects( + solution.getFirstEntity(), + solution.getEntityList().get(1), + solution.getEntityList().get(2), + solution.getEntityList().get(3), + solution.getEntityList().get(4), + solution.getEntityList().get(5), + solution.getEntityList().get(6)), + assertMatchWithScore(-2, entityGroup1, value1, 2L, 2L).withIndictedObjects(entity1, entity3), + assertMatchWithScore(-1, entityGroup1, solution.getFirstValue(), 1L, 1L).withIndictedObjects(entity2)); // Incremental scoreDirector.beforeProblemPropertyChanged(entity2); entity2.setEntityGroup(solution.getFirstEntityGroup()); scoreDirector.afterProblemPropertyChanged(entity2); assertScore(scoreDirector, - assertMatchWithScore(-8, solution.getFirstEntityGroup(), solution.getFirstValue(), 8L, 8L), - assertMatchWithScore(-2, entityGroup1, value1, 2L, 2L)); + assertMatchWithScore(-8, solution.getFirstEntityGroup(), solution.getFirstValue(), 8L, 8L).withIndictedObjects( + solution.getFirstEntity(), + solution.getEntityList().get(1), + solution.getEntityList().get(2), + solution.getEntityList().get(3), + solution.getEntityList().get(4), + solution.getEntityList().get(5), + solution.getEntityList().get(6), + entity2), + assertMatchWithScore(-2, entityGroup1, value1, 2L, 2L).withIndictedObjects(entity1, entity3)); } @Override @@ -1902,21 +2043,21 @@ public void groupBy_3Mapping0Collector() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, group1, value1, entity1.getCode()), - assertMatchWithScore(-1, group2, value2, entity2.getCode()), - assertMatchWithScore(-1, group1, value3, entity3.getCode()), - assertMatchWithScore(-1, group2, value1, entity4.getCode()), - assertMatchWithScore(-1, group1, value2, entity5.getCode())); + assertMatchWithScore(-1, group1, value1, entity1.getCode()).withIndictedObjects(entity1), + assertMatchWithScore(-1, group2, value2, entity2.getCode()).withIndictedObjects(entity2), + assertMatchWithScore(-1, group1, value3, entity3.getCode()).withIndictedObjects(entity3), + assertMatchWithScore(-1, group2, value1, entity4.getCode()).withIndictedObjects(entity4), + assertMatchWithScore(-1, group1, value2, entity5.getCode()).withIndictedObjects(entity5)); // Incremental scoreDirector.beforeEntityRemoved(entity1); solution.getEntityList().remove(entity1); scoreDirector.afterEntityRemoved(entity1); assertScore(scoreDirector, - assertMatchWithScore(-1, group2, value2, entity2.getCode()), - assertMatchWithScore(-1, group1, value3, entity3.getCode()), - assertMatchWithScore(-1, group2, value1, entity4.getCode()), - assertMatchWithScore(-1, group1, value2, entity5.getCode())); + assertMatchWithScore(-1, group2, value2, entity2.getCode()).withIndictedObjects(entity2), + assertMatchWithScore(-1, group1, value3, entity3.getCode()).withIndictedObjects(entity3), + assertMatchWithScore(-1, group2, value1, entity4.getCode()).withIndictedObjects(entity4), + assertMatchWithScore(-1, group1, value2, entity5.getCode()).withIndictedObjects(entity5)); } @Override @@ -1945,21 +2086,30 @@ public void groupBy_3Mapping1Collector() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, group1, value1, entity1.getCode(), Collections.singleton(entity1)), - assertMatchWithScore(-1, group2, value2, entity2.getCode(), Collections.singleton(entity2)), - assertMatchWithScore(-1, group1, value3, entity3.getCode(), Collections.singleton(entity3)), - assertMatchWithScore(-1, group2, value1, entity4.getCode(), Collections.singleton(entity4)), - assertMatchWithScore(-1, group1, value2, entity5.getCode(), Collections.singleton(entity5))); + assertMatchWithScore(-1, group1, value1, entity1.getCode(), Collections.singleton(entity1)) + .withIndictedObjects(entity1), + assertMatchWithScore(-1, group2, value2, entity2.getCode(), Collections.singleton(entity2)) + .withIndictedObjects(entity2), + assertMatchWithScore(-1, group1, value3, entity3.getCode(), Collections.singleton(entity3)) + .withIndictedObjects(entity3), + assertMatchWithScore(-1, group2, value1, entity4.getCode(), Collections.singleton(entity4)) + .withIndictedObjects(entity4), + assertMatchWithScore(-1, group1, value2, entity5.getCode(), Collections.singleton(entity5)) + .withIndictedObjects(entity5)); // Incremental scoreDirector.beforeEntityRemoved(entity1); solution.getEntityList().remove(entity1); scoreDirector.afterEntityRemoved(entity1); assertScore(scoreDirector, - assertMatchWithScore(-1, group2, value2, entity2.getCode(), Collections.singleton(entity2)), - assertMatchWithScore(-1, group1, value3, entity3.getCode(), Collections.singleton(entity3)), - assertMatchWithScore(-1, group2, value1, entity4.getCode(), Collections.singleton(entity4)), - assertMatchWithScore(-1, group1, value2, entity5.getCode(), Collections.singleton(entity5))); + assertMatchWithScore(-1, group2, value2, entity2.getCode(), Collections.singleton(entity2)) + .withIndictedObjects(entity2), + assertMatchWithScore(-1, group1, value3, entity3.getCode(), Collections.singleton(entity3)) + .withIndictedObjects(entity3), + assertMatchWithScore(-1, group2, value1, entity4.getCode(), Collections.singleton(entity4)) + .withIndictedObjects(entity4), + assertMatchWithScore(-1, group1, value2, entity5.getCode(), Collections.singleton(entity5)) + .withIndictedObjects(entity5)); } @Override @@ -1988,21 +2138,21 @@ public void groupBy_4Mapping0Collector() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, entity1, group1, value1, entity1.getCode()), - assertMatchWithScore(-1, entity2, group2, value2, entity2.getCode()), - assertMatchWithScore(-1, entity3, group1, value3, entity3.getCode()), - assertMatchWithScore(-1, entity4, group2, value1, entity4.getCode()), - assertMatchWithScore(-1, entity5, group1, value2, entity5.getCode())); + assertMatchWithScore(-1, entity1, group1, value1, entity1.getCode()).withIndictedObjects(entity1), + assertMatchWithScore(-1, entity2, group2, value2, entity2.getCode()).withIndictedObjects(entity2), + assertMatchWithScore(-1, entity3, group1, value3, entity3.getCode()).withIndictedObjects(entity3), + assertMatchWithScore(-1, entity4, group2, value1, entity4.getCode()).withIndictedObjects(entity4), + assertMatchWithScore(-1, entity5, group1, value2, entity5.getCode()).withIndictedObjects(entity5)); // Incremental scoreDirector.beforeEntityRemoved(entity1); solution.getEntityList().remove(entity1); scoreDirector.afterEntityRemoved(entity1); assertScore(scoreDirector, - assertMatchWithScore(-1, entity2, group2, value2, entity2.getCode()), - assertMatchWithScore(-1, entity3, group1, value3, entity3.getCode()), - assertMatchWithScore(-1, entity4, group2, value1, entity4.getCode()), - assertMatchWithScore(-1, entity5, group1, value2, entity5.getCode())); + assertMatchWithScore(-1, entity2, group2, value2, entity2.getCode()).withIndictedObjects(entity2), + assertMatchWithScore(-1, entity3, group1, value3, entity3.getCode()).withIndictedObjects(entity3), + assertMatchWithScore(-1, entity4, group2, value1, entity4.getCode()).withIndictedObjects(entity4), + assertMatchWithScore(-1, entity5, group1, value2, entity5.getCode()).withIndictedObjects(entity5)); } @Override @@ -2040,8 +2190,8 @@ public void mapToUniWithDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(group), - assertMatch(group)); + assertMatch(group).withIndictedObjects(solution.getFirstEntity()), + assertMatch(group).withIndictedObjects(solution.getEntityList().get(1))); var entity = solution.getFirstEntity(); @@ -2050,7 +2200,7 @@ public void mapToUniWithDuplicates() { solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); assertScore(scoreDirector, - assertMatch(group)); + assertMatch(group).withIndictedObjects(solution.getEntityList().getFirst())); } @Override @@ -2069,8 +2219,8 @@ public void mapToUniWithoutDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(group1), - assertMatch(group2)); + assertMatch(group1).withIndictedObjects(solution.getFirstEntity()), + assertMatch(group2).withIndictedObjects(solution.getEntityList().get(1))); var entity = solution.getFirstEntity(); @@ -2079,7 +2229,7 @@ public void mapToUniWithoutDuplicates() { solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); assertScore(scoreDirector, - assertMatch(group2)); + assertMatch(group2).withIndictedObjects(solution.getEntityList().getFirst())); } @Override @@ -2098,7 +2248,7 @@ public void mapToUniAndDistinctWithDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(group)); + assertMatch(group).withIndictedObjects(solution.getFirstEntity(), solution.getEntityList().get(1))); var entity = solution.getFirstEntity(); @@ -2107,7 +2257,7 @@ public void mapToUniAndDistinctWithDuplicates() { solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); assertScore(scoreDirector, - assertMatch(group)); + assertMatch(group).withIndictedObjects(solution.getEntityList().getFirst())); } @Override @@ -2127,8 +2277,8 @@ public void mapToUniAndDistinctWithoutDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(group1), - assertMatch(group2)); + assertMatch(group1).withIndictedObjects(solution.getFirstEntity()), + assertMatch(group2).withIndictedObjects(solution.getEntityList().get(1))); var entity = solution.getFirstEntity(); @@ -2137,7 +2287,7 @@ public void mapToUniAndDistinctWithoutDuplicates() { solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); assertScore(scoreDirector, - assertMatch(group2)); + assertMatch(group2).withIndictedObjects(solution.getEntityList().getFirst())); } @Override @@ -2159,8 +2309,8 @@ public void mapToBi() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(group1, value1), - assertMatch(group2, value2)); + assertMatch(group1, value1).withIndictedObjects(solution.getFirstEntity()), + assertMatch(group2, value2).withIndictedObjects(solution.getEntityList().get(1))); var entity = solution.getFirstEntity(); @@ -2169,7 +2319,7 @@ public void mapToBi() { solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); assertScore(scoreDirector, - assertMatch(group2, value2)); + assertMatch(group2, value2).withIndictedObjects(solution.getEntityList().getFirst())); } @Override @@ -2194,8 +2344,8 @@ public void mapToTri() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(group1, value1, code1), - assertMatch(group2, value2, code2)); + assertMatch(group1, value1, code1).withIndictedObjects(solution.getFirstEntity()), + assertMatch(group2, value2, code2).withIndictedObjects(solution.getEntityList().get(1))); var entity = solution.getFirstEntity(); @@ -2204,7 +2354,7 @@ public void mapToTri() { solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); assertScore(scoreDirector, - assertMatch(group2, value2, code2)); + assertMatch(group2, value2, code2).withIndictedObjects(solution.getEntityList().getFirst())); } @Override @@ -2232,8 +2382,8 @@ public void mapToQuad() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(group1, value1, code1, property1), - assertMatch(group2, value2, code2, property2)); + assertMatch(group1, value1, code1, property1).withIndictedObjects(solution.getFirstEntity()), + assertMatch(group2, value2, code2, property2).withIndictedObjects(solution.getEntityList().get(1))); var entity = solution.getFirstEntity(); @@ -2242,7 +2392,7 @@ public void mapToQuad() { solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); assertScore(scoreDirector, - assertMatch(group2, value2, code2, property2)); + assertMatch(group2, value2, code2, property2).withIndictedObjects(solution.getEntityList().getFirst())); } @Override @@ -2261,8 +2411,8 @@ public void expandToBi() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(solution.getFirstEntity(), group1), - assertMatch(solution.getEntityList().get(1), group2)); + assertMatch(solution.getFirstEntity(), group1).withIndictedObjects(solution.getFirstEntity()), + assertMatch(solution.getEntityList().get(1), group2).withIndictedObjects(solution.getEntityList().get(1))); var entity = solution.getFirstEntity(); @@ -2271,7 +2421,7 @@ public void expandToBi() { solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); assertScore(scoreDirector, - assertMatch(solution.getFirstEntity(), group2)); + assertMatch(solution.getFirstEntity(), group2).withIndictedObjects(solution.getEntityList().getFirst())); } @Override @@ -2292,8 +2442,9 @@ public void expandToTri() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(solution.getFirstEntity(), group1, value1), - assertMatch(solution.getEntityList().get(1), group2, value2)); + assertMatch(solution.getFirstEntity(), group1, value1).withIndictedObjects(solution.getFirstEntity()), + assertMatch(solution.getEntityList().get(1), group2, value2) + .withIndictedObjects(solution.getEntityList().get(1))); var entity = solution.getFirstEntity(); @@ -2302,7 +2453,8 @@ public void expandToTri() { solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); assertScore(scoreDirector, - assertMatch(solution.getFirstEntity(), group2, value2)); + assertMatch(solution.getFirstEntity(), group2, value2) + .withIndictedObjects(solution.getEntityList().getFirst())); } @Override @@ -2326,8 +2478,9 @@ public void expandToQuad() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(solution.getFirstEntity(), group1, value1, code1), - assertMatch(solution.getEntityList().get(1), group2, value2, code2)); + assertMatch(solution.getFirstEntity(), group1, value1, code1).withIndictedObjects(solution.getFirstEntity()), + assertMatch(solution.getEntityList().get(1), group2, value2, code2) + .withIndictedObjects(solution.getEntityList().get(1))); var entity = solution.getFirstEntity(); @@ -2336,7 +2489,8 @@ public void expandToQuad() { solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); assertScore(scoreDirector, - assertMatch(solution.getFirstEntity(), group2, value2, code2)); + assertMatch(solution.getFirstEntity(), group2, value2, code2) + .withIndictedObjects(solution.getEntityList().getFirst())); } @Override @@ -2362,11 +2516,11 @@ public void flattenLastWithDuplicates() { // group1 is used instead of group1Dup because it equals to it scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(group1), - assertMatch(group1), - assertMatch(group2), - assertMatch(group1), - assertMatch(group2)); + assertMatch(group1).withIndictedObjects(solution.getFirstEntity()), + assertMatch(group1).withIndictedObjects(solution.getFirstEntity()), + assertMatch(group2).withIndictedObjects(solution.getFirstEntity()), + assertMatch(group1).withIndictedObjects(solution.getEntityList().get(1)), + assertMatch(group2).withIndictedObjects(solution.getEntityList().get(1))); var entity = solution.getFirstEntity(); @@ -2376,17 +2530,17 @@ public void flattenLastWithDuplicates() { scoreDirector.afterVariableChanged(entity, "value"); assertScore(scoreDirector, - assertMatch(group1), - assertMatch(group2), - assertMatch(group1), - assertMatch(group2)); + assertMatch(group1).withIndictedObjects(solution.getFirstEntity()), + assertMatch(group2).withIndictedObjects(solution.getFirstEntity()), + assertMatch(group1).withIndictedObjects(solution.getEntityList().get(1)), + assertMatch(group2).withIndictedObjects(solution.getEntityList().get(1))); scoreDirector.beforeEntityRemoved(entity); solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); assertScore(scoreDirector, - assertMatch(group1), - assertMatch(group2)); + assertMatch(group1).withIndictedObjects(solution.getEntityList().getFirst()), + assertMatch(group2).withIndictedObjects(solution.getEntityList().getFirst())); } @Override @@ -2405,8 +2559,8 @@ public void flattenLastWithoutDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(group1), - assertMatch(group2)); + assertMatch(group1).withIndictedObjects(solution.getFirstEntity()), + assertMatch(group2).withIndictedObjects(solution.getEntityList().get(1))); var entity = solution.getFirstEntity(); @@ -2415,7 +2569,7 @@ public void flattenLastWithoutDuplicates() { solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); assertScore(scoreDirector, - assertMatch(group2)); + assertMatch(group2).withIndictedObjects(solution.getEntityList().getFirst())); } @Override @@ -2437,18 +2591,18 @@ public void flatten() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, group1), - assertMatch(entity1, group2), - assertMatch(entity2, group1), - assertMatch(entity2, group2)); + assertMatch(entity1, group1).withIndictedObjects(entity1), + assertMatch(entity1, group2).withIndictedObjects(entity1), + assertMatch(entity2, group1).withIndictedObjects(entity2), + assertMatch(entity2, group2).withIndictedObjects(entity2)); // Incremental scoreDirector.beforeEntityRemoved(entity1); solution.getEntityList().remove(entity1); scoreDirector.afterEntityRemoved(entity1); assertScore(scoreDirector, - assertMatch(entity2, group1), - assertMatch(entity2, group2)); + assertMatch(entity2, group1).withIndictedObjects(entity2), + assertMatch(entity2, group2).withIndictedObjects(entity2)); } @Override @@ -2468,8 +2622,8 @@ public void flattenLastAndDistinctWithDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(group1), - assertMatch(group2)); + assertMatch(group1).withIndictedObjects(solution.getFirstEntity(), solution.getEntityList().get(1)), + assertMatch(group2).withIndictedObjects(solution.getFirstEntity(), solution.getEntityList().get(1))); var entity = solution.getFirstEntity(); @@ -2478,8 +2632,8 @@ public void flattenLastAndDistinctWithDuplicates() { solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); assertScore(scoreDirector, - assertMatch(group1), - assertMatch(group2)); + assertMatch(group1).withIndictedObjects(solution.getEntityList().getFirst()), + assertMatch(group2).withIndictedObjects(solution.getEntityList().getFirst())); } @Override @@ -2499,8 +2653,8 @@ public void flattenLastAndDistinctWithoutDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(group1), - assertMatch(group2)); + assertMatch(group1).withIndictedObjects(solution.getFirstEntity()), + assertMatch(group2).withIndictedObjects(solution.getEntityList().get(1))); var entity = solution.getFirstEntity(); @@ -2509,7 +2663,7 @@ public void flattenLastAndDistinctWithoutDuplicates() { solution.getEntityList().remove(entity); scoreDirector.afterEntityRemoved(entity); assertScore(scoreDirector, - assertMatch(group2)); + assertMatch(group2).withIndictedObjects(solution.getEntityList().getFirst())); } @Override @@ -2718,8 +2872,8 @@ public void concatBiWithoutValueDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, null), - assertMatch(entity2, entity3)); + assertMatch(entity1, null).withIndictedObjects(entity1), + assertMatch(entity2, entity3).withIndictedObjects(entity2, entity3)); // Incremental scoreDirector.beforeVariableChanged(entity3, "value"); @@ -2730,8 +2884,8 @@ public void concatBiWithoutValueDuplicates() { entity2.setValue(value3); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(entity1, null), - assertMatch(entity3, entity2)); + assertMatch(entity1, null).withIndictedObjects(entity1), + assertMatch(entity3, entity2).withIndictedObjects(entity3, entity2)); } @Override @@ -2765,8 +2919,8 @@ public void concatAndDistinctBiWithoutValueDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, null), - assertMatch(entity2, entity3)); + assertMatch(entity1, null).withIndictedObjects(entity1), + assertMatch(entity2, entity3).withIndictedObjects(entity2, entity3)); // Incremental scoreDirector.beforeVariableChanged(entity3, "value"); @@ -2777,8 +2931,8 @@ public void concatAndDistinctBiWithoutValueDuplicates() { entity2.setValue(value3); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(entity1, null), - assertMatch(entity3, entity2)); + assertMatch(entity1, null).withIndictedObjects(entity1), + assertMatch(entity3, entity2).withIndictedObjects(entity3, entity2)); } @Override @@ -2813,8 +2967,8 @@ public void concatTriWithoutValueDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, null, null), - assertMatch(entity2, entity3, entity1)); + assertMatch(entity1, null, null).withIndictedObjects(entity1), + assertMatch(entity2, entity3, entity1).withIndictedObjects(entity2, entity3, entity1)); // Incremental scoreDirector.beforeVariableChanged(entity3, "value"); @@ -2825,8 +2979,8 @@ public void concatTriWithoutValueDuplicates() { entity2.setValue(value3); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(entity1, null, null), - assertMatch(entity3, entity2, entity1)); + assertMatch(entity1, null, null).withIndictedObjects(entity1), + assertMatch(entity3, entity2, entity1).withIndictedObjects(entity3, entity2, entity1)); } @Override @@ -2862,8 +3016,8 @@ public void concatAndDistinctTriWithoutValueDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, null, null), - assertMatch(entity2, entity3, entity1)); + assertMatch(entity1, null, null).withIndictedObjects(entity1), + assertMatch(entity2, entity3, entity1).withIndictedObjects(entity2, entity3, entity1)); // Incremental scoreDirector.beforeVariableChanged(entity3, "value"); @@ -2874,8 +3028,8 @@ public void concatAndDistinctTriWithoutValueDuplicates() { entity2.setValue(value3); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(entity1, null, null), - assertMatch(entity3, entity2, entity1)); + assertMatch(entity1, null, null).withIndictedObjects(entity1), + assertMatch(entity3, entity2, entity1).withIndictedObjects(entity3, entity2, entity1)); } @Override @@ -2912,8 +3066,8 @@ public void concatQuadWithoutValueDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, null, null, null), - assertMatch(entity2, entity3, entity1, entity2)); + assertMatch(entity1, null, null, null).withIndictedObjects(entity1), + assertMatch(entity2, entity3, entity1, entity2).withIndictedObjects(entity2, entity3, entity1)); // Incremental scoreDirector.beforeVariableChanged(entity3, "value"); @@ -2924,8 +3078,8 @@ public void concatQuadWithoutValueDuplicates() { entity2.setValue(value3); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(entity1, null, null, null), - assertMatch(entity3, entity2, entity1, entity3)); + assertMatch(entity1, null, null, null).withIndictedObjects(entity1), + assertMatch(entity3, entity2, entity1, entity3).withIndictedObjects(entity3, entity2, entity1)); } @Override @@ -2963,8 +3117,8 @@ public void concatAndDistinctQuadWithoutValueDuplicates() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatch(entity1, null, null, null), - assertMatch(entity2, entity3, entity1, entity2)); + assertMatch(entity1, null, null, null).withIndictedObjects(entity1), + assertMatch(entity2, entity3, entity1, entity2).withIndictedObjects(entity2, entity3, entity1)); // Incremental scoreDirector.beforeVariableChanged(entity3, "value"); @@ -2975,8 +3129,8 @@ public void concatAndDistinctQuadWithoutValueDuplicates() { entity2.setValue(value3); scoreDirector.afterVariableChanged(entity2, "value"); assertScore(scoreDirector, - assertMatch(entity1, null, null, null), - assertMatch(entity3, entity2, entity1, entity3)); + assertMatch(entity1, null, null, null).withIndictedObjects(entity1), + assertMatch(entity3, entity2, entity1, entity3).withIndictedObjects(entity3, entity2, entity1)); } @Override @@ -3009,16 +3163,16 @@ public void concatAfterGroupBy() { // From scratch scoreDirector.setWorkingSolution(solution); assertScore(scoreDirector, - assertMatchWithScore(-1, value1, 1L), - assertMatchWithScore(-1, value2, 1L)); + assertMatchWithScore(-1, value1, 1L).withIndictedObjects(entity1), + assertMatchWithScore(-1, value2, 1L).withIndictedObjects(entity2)); // Incremental scoreDirector.beforeVariableChanged(entity3, "value"); entity3.setValue(value2); scoreDirector.afterVariableChanged(entity3, "value"); assertScore(scoreDirector, - assertMatchWithScore(-1, value1, 1L), - assertMatchWithScore(-2, value2, 2L)); + assertMatchWithScore(-1, value1, 1L).withIndictedObjects(entity1), + assertMatchWithScore(-2, value2, 2L).withIndictedObjects(entity2, entity3)); // Incremental for which the first change matches a join that doesn't survive the second change scoreDirector.beforeVariableChanged(entity1, "value"); @@ -3028,8 +3182,8 @@ public void concatAfterGroupBy() { entity3.setValue(value1); scoreDirector.afterVariableChanged(entity3, "value"); assertScore(scoreDirector, - assertMatchWithScore(-1, value1, 1L), - assertMatchWithScore(-1, value2, 1L)); + assertMatchWithScore(-1, value1, 1L).withIndictedObjects(entity3), + assertMatchWithScore(-1, value2, 1L).withIndictedObjects(entity2)); } @Override