diff --git a/core/src/main/java/ai/timefold/solver/core/impl/domain/variable/AbstractExternalizedNextPrevElementVariableProcessor.java b/core/src/main/java/ai/timefold/solver/core/impl/domain/variable/AbstractExternalizedNextPrevElementVariableProcessor.java deleted file mode 100644 index 304fa1fc93..0000000000 --- a/core/src/main/java/ai/timefold/solver/core/impl/domain/variable/AbstractExternalizedNextPrevElementVariableProcessor.java +++ /dev/null @@ -1,36 +0,0 @@ -package ai.timefold.solver.core.impl.domain.variable; - -import java.util.List; -import java.util.Objects; - -import ai.timefold.solver.core.impl.domain.variable.descriptor.ShadowVariableDescriptor; -import ai.timefold.solver.core.impl.score.director.InnerScoreDirector; - -abstract sealed class AbstractExternalizedNextPrevElementVariableProcessor - permits ExternalizedNextElementVariableProcessor, ExternalizedPreviousElementVariableProcessor { - - protected final ShadowVariableDescriptor shadowVariableDescriptor; - - protected AbstractExternalizedNextPrevElementVariableProcessor( - ShadowVariableDescriptor shadowVariableDescriptor) { - this.shadowVariableDescriptor = Objects.requireNonNull(shadowVariableDescriptor); - } - - public abstract void setElement(InnerScoreDirector scoreDirector, List listVariable, Object element, - int index); - - public abstract Object getElement(Object element); - - public void unsetElement(InnerScoreDirector scoreDirector, Object element) { - setValue(scoreDirector, element, null); - } - - protected void setValue(InnerScoreDirector scoreDirector, Object element, Object value) { - if (getElement(element) != value) { - scoreDirector.beforeVariableChanged(shadowVariableDescriptor, element); - shadowVariableDescriptor.setValue(element, value); - scoreDirector.afterVariableChanged(shadowVariableDescriptor, element); - } - } - -} diff --git a/core/src/main/java/ai/timefold/solver/core/impl/domain/variable/ExternalizedListVariableStateSupply.java b/core/src/main/java/ai/timefold/solver/core/impl/domain/variable/ExternalizedListVariableStateSupply.java index 041d44397c..9e9b87f265 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/domain/variable/ExternalizedListVariableStateSupply.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/domain/variable/ExternalizedListVariableStateSupply.java @@ -27,23 +27,23 @@ public ExternalizedListVariableStateSupply(ListVariableDescriptor sou @Override public void externalize(IndexShadowVariableDescriptor shadowVariableDescriptor) { - listVariableState.linkDescriptor(shadowVariableDescriptor); + listVariableState.linkShadowVariable(shadowVariableDescriptor); } @Override public void externalize(InverseRelationShadowVariableDescriptor shadowVariableDescriptor) { - listVariableState.linkDescriptor(shadowVariableDescriptor); + listVariableState.linkShadowVariable(shadowVariableDescriptor); } @Override public void externalize(PreviousElementShadowVariableDescriptor shadowVariableDescriptor) { - listVariableState.linkDescriptor(shadowVariableDescriptor); + listVariableState.linkShadowVariable(shadowVariableDescriptor); previousExternalized = true; } @Override public void externalize(NextElementShadowVariableDescriptor shadowVariableDescriptor) { - listVariableState.linkDescriptor(shadowVariableDescriptor); + listVariableState.linkShadowVariable(shadowVariableDescriptor); nextExternalized = true; } diff --git a/core/src/main/java/ai/timefold/solver/core/impl/domain/variable/ExternalizedNextElementVariableProcessor.java b/core/src/main/java/ai/timefold/solver/core/impl/domain/variable/ExternalizedNextElementVariableProcessor.java deleted file mode 100644 index 7855980f7d..0000000000 --- a/core/src/main/java/ai/timefold/solver/core/impl/domain/variable/ExternalizedNextElementVariableProcessor.java +++ /dev/null @@ -1,26 +0,0 @@ -package ai.timefold.solver.core.impl.domain.variable; - -import java.util.List; - -import ai.timefold.solver.core.impl.domain.variable.nextprev.NextElementShadowVariableDescriptor; -import ai.timefold.solver.core.impl.score.director.InnerScoreDirector; - -final class ExternalizedNextElementVariableProcessor - extends AbstractExternalizedNextPrevElementVariableProcessor { - - public ExternalizedNextElementVariableProcessor(NextElementShadowVariableDescriptor shadowVariableDescriptor) { - super(shadowVariableDescriptor); - } - - @Override - public void setElement(InnerScoreDirector scoreDirector, List listVariable, Object element, - int index) { - var next = index == listVariable.size() - 1 ? null : listVariable.get(index + 1); - setValue(scoreDirector, element, next); - } - - public Object getElement(Object element) { - return shadowVariableDescriptor.getValue(element); - } - -} diff --git a/core/src/main/java/ai/timefold/solver/core/impl/domain/variable/ExternalizedNextPrevElementVariableProcessor.java b/core/src/main/java/ai/timefold/solver/core/impl/domain/variable/ExternalizedNextPrevElementVariableProcessor.java new file mode 100644 index 0000000000..53c844a691 --- /dev/null +++ b/core/src/main/java/ai/timefold/solver/core/impl/domain/variable/ExternalizedNextPrevElementVariableProcessor.java @@ -0,0 +1,58 @@ +package ai.timefold.solver.core.impl.domain.variable; + +import java.util.List; +import java.util.Objects; + +import ai.timefold.solver.core.impl.domain.variable.descriptor.ShadowVariableDescriptor; +import ai.timefold.solver.core.impl.domain.variable.nextprev.NextElementShadowVariableDescriptor; +import ai.timefold.solver.core.impl.domain.variable.nextprev.PreviousElementShadowVariableDescriptor; +import ai.timefold.solver.core.impl.score.director.InnerScoreDirector; + +final class ExternalizedNextPrevElementVariableProcessor { + + public static ExternalizedNextPrevElementVariableProcessor + ofPrevious(PreviousElementShadowVariableDescriptor shadowVariableDescriptor) { + return new ExternalizedNextPrevElementVariableProcessor<>(shadowVariableDescriptor, -1); + } + + public static ExternalizedNextPrevElementVariableProcessor + ofNext(NextElementShadowVariableDescriptor shadowVariableDescriptor) { + return new ExternalizedNextPrevElementVariableProcessor<>(shadowVariableDescriptor, 1); + } + + private final ShadowVariableDescriptor shadowVariableDescriptor; + private final int modifier; + + private ExternalizedNextPrevElementVariableProcessor(ShadowVariableDescriptor shadowVariableDescriptor, + int modifier) { + this.shadowVariableDescriptor = Objects.requireNonNull(shadowVariableDescriptor); + this.modifier = modifier; + } + + public void setElement(InnerScoreDirector scoreDirector, List listVariable, Object element, + int index) { + var target = index + modifier; + if (target < 0 || target >= listVariable.size()) { + setValue(scoreDirector, element, null); + } else { + setValue(scoreDirector, element, listVariable.get(target)); + } + } + + private void setValue(InnerScoreDirector scoreDirector, Object element, Object value) { + if (getElement(element) != value) { + scoreDirector.beforeVariableChanged(shadowVariableDescriptor, element); + shadowVariableDescriptor.setValue(element, value); + scoreDirector.afterVariableChanged(shadowVariableDescriptor, element); + } + } + + public Object getElement(Object element) { + return shadowVariableDescriptor.getValue(element); + } + + public void unsetElement(InnerScoreDirector scoreDirector, Object element) { + setValue(scoreDirector, element, null); + } + +} diff --git a/core/src/main/java/ai/timefold/solver/core/impl/domain/variable/ExternalizedPreviousElementVariableProcessor.java b/core/src/main/java/ai/timefold/solver/core/impl/domain/variable/ExternalizedPreviousElementVariableProcessor.java deleted file mode 100644 index 5c2f309ef9..0000000000 --- a/core/src/main/java/ai/timefold/solver/core/impl/domain/variable/ExternalizedPreviousElementVariableProcessor.java +++ /dev/null @@ -1,27 +0,0 @@ -package ai.timefold.solver.core.impl.domain.variable; - -import java.util.List; - -import ai.timefold.solver.core.impl.domain.variable.nextprev.PreviousElementShadowVariableDescriptor; -import ai.timefold.solver.core.impl.score.director.InnerScoreDirector; - -final class ExternalizedPreviousElementVariableProcessor - extends AbstractExternalizedNextPrevElementVariableProcessor { - - public ExternalizedPreviousElementVariableProcessor( - PreviousElementShadowVariableDescriptor shadowVariableDescriptor) { - super(shadowVariableDescriptor); - } - - @Override - public void setElement(InnerScoreDirector scoreDirector, List listVariable, Object element, - int index) { - var previous = index == 0 ? null : listVariable.get(index - 1); - setValue(scoreDirector, element, previous); - } - - public Object getElement(Object element) { - return shadowVariableDescriptor.getValue(element); - } - -} diff --git a/core/src/main/java/ai/timefold/solver/core/impl/domain/variable/ListVariableState.java b/core/src/main/java/ai/timefold/solver/core/impl/domain/variable/ListVariableState.java index 613ee59f2f..9708dea8a1 100644 --- a/core/src/main/java/ai/timefold/solver/core/impl/domain/variable/ListVariableState.java +++ b/core/src/main/java/ai/timefold/solver/core/impl/domain/variable/ListVariableState.java @@ -20,10 +20,9 @@ final class ListVariableState { private ExternalizedIndexVariableProcessor externalizedIndexProcessor = null; private ExternalizedListInverseVariableProcessor externalizedInverseProcessor = null; - private AbstractExternalizedNextPrevElementVariableProcessor externalizedPreviousElementProcessor = null; - private AbstractExternalizedNextPrevElementVariableProcessor externalizedNextElementProcessor = null; + private ExternalizedNextPrevElementVariableProcessor externalizedPreviousElementProcessor = null; + private ExternalizedNextPrevElementVariableProcessor externalizedNextElementProcessor = null; - private boolean canUseExternalizedLocation = false; private boolean requiresLocationMap = true; private InnerScoreDirector scoreDirector; private int unassignedCount = 0; @@ -33,30 +32,29 @@ public ListVariableState(ListVariableDescriptor sourceVariableDescrip this.sourceVariableDescriptor = sourceVariableDescriptor; } - public void linkDescriptor(IndexShadowVariableDescriptor shadowVariableDescriptor) { + public void linkShadowVariable(IndexShadowVariableDescriptor shadowVariableDescriptor) { this.externalizedIndexProcessor = new ExternalizedIndexVariableProcessor<>(shadowVariableDescriptor); } - public void linkDescriptor(InverseRelationShadowVariableDescriptor shadowVariableDescriptor) { + public void linkShadowVariable(InverseRelationShadowVariableDescriptor shadowVariableDescriptor) { this.externalizedInverseProcessor = new ExternalizedListInverseVariableProcessor<>(shadowVariableDescriptor, sourceVariableDescriptor); } - public void linkDescriptor(PreviousElementShadowVariableDescriptor shadowVariableDescriptor) { + public void linkShadowVariable(PreviousElementShadowVariableDescriptor shadowVariableDescriptor) { this.externalizedPreviousElementProcessor = - new ExternalizedPreviousElementVariableProcessor<>(shadowVariableDescriptor); + ExternalizedNextPrevElementVariableProcessor.ofPrevious(shadowVariableDescriptor); } - public void linkDescriptor(NextElementShadowVariableDescriptor shadowVariableDescriptor) { - this.externalizedNextElementProcessor = new ExternalizedNextElementVariableProcessor<>(shadowVariableDescriptor); + public void linkShadowVariable(NextElementShadowVariableDescriptor shadowVariableDescriptor) { + this.externalizedNextElementProcessor = ExternalizedNextPrevElementVariableProcessor.ofNext(shadowVariableDescriptor); } public void initialize(InnerScoreDirector scoreDirector, int initialUnassignedCount) { this.scoreDirector = scoreDirector; this.unassignedCount = initialUnassignedCount; - this.canUseExternalizedLocation = externalizedIndexProcessor != null && externalizedInverseProcessor != null; - this.requiresLocationMap = !canUseExternalizedLocation + this.requiresLocationMap = externalizedIndexProcessor == null || externalizedInverseProcessor == null || externalizedPreviousElementProcessor == null || externalizedNextElementProcessor == null; if (requiresLocationMap) { if (elementLocationMap == null) { @@ -220,14 +218,14 @@ private enum ChangeType { } public ElementLocation getLocationInList(Object planningValue) { - if (!canUseExternalizedLocation) { + if (requiresLocationMap) { return Objects.requireNonNullElse(elementLocationMap.get(planningValue), ElementLocation.unassigned()); - } else { - var inverse = getInverseSingleton(planningValue); + } else { // At this point, both inverse and index are externalized. + var inverse = externalizedInverseProcessor.getInverseSingleton(planningValue); if (inverse == null) { return ElementLocation.unassigned(); } - return ElementLocation.of(inverse, getIndex(planningValue)); + return ElementLocation.of(inverse, externalizedIndexProcessor.getIndex(planningValue)); } }