diff --git a/core/constraint-streams/src/test/java/ai/timefold/solver/constraint/streams/common/ConstraintStreamFunctionalTest.java b/core/constraint-streams/src/test/java/ai/timefold/solver/constraint/streams/common/ConstraintStreamFunctionalTest.java index b2a29664bf..25d88c6281 100644 --- a/core/constraint-streams/src/test/java/ai/timefold/solver/constraint/streams/common/ConstraintStreamFunctionalTest.java +++ b/core/constraint-streams/src/test/java/ai/timefold/solver/constraint/streams/common/ConstraintStreamFunctionalTest.java @@ -194,6 +194,10 @@ default void expandToQuad() { void penalizeUnweighted(); + void penalizeUnweightedLong(); + + void penalizeUnweightedBigDecimal(); + void penalize(); void penalizeLong(); diff --git a/core/constraint-streams/src/test/java/ai/timefold/solver/constraint/streams/common/bi/AbstractBiConstraintStreamTest.java b/core/constraint-streams/src/test/java/ai/timefold/solver/constraint/streams/common/bi/AbstractBiConstraintStreamTest.java index f734afe0b0..711e1cae72 100644 --- a/core/constraint-streams/src/test/java/ai/timefold/solver/constraint/streams/common/bi/AbstractBiConstraintStreamTest.java +++ b/core/constraint-streams/src/test/java/ai/timefold/solver/constraint/streams/common/bi/AbstractBiConstraintStreamTest.java @@ -2341,6 +2341,42 @@ public void penalizeUnweighted() { assertDefaultJustifications(scoreDirector, solution.getEntityList()); } + @Override + @TestTemplate + public void penalizeUnweightedLong() { + TestdataSimpleLongScoreSolution solution = TestdataSimpleLongScoreSolution.generateSolution(); + + InnerScoreDirector scoreDirector = buildScoreDirector( + TestdataSimpleLongScoreSolution.buildSolutionDescriptor(), + factory -> new Constraint[] { + factory.forEachUniquePair(TestdataEntity.class) + .penalizeLong(SimpleLongScore.ONE) + .asConstraint(TEST_CONSTRAINT_NAME) + }); + + scoreDirector.setWorkingSolution(solution); + scoreDirector.calculateScore(); + assertThat(scoreDirector.calculateScore()).isEqualTo(SimpleLongScore.of(-21)); + } + + @Override + @TestTemplate + public void penalizeUnweightedBigDecimal() { + TestdataSimpleBigDecimalScoreSolution solution = TestdataSimpleBigDecimalScoreSolution.generateSolution(); + + InnerScoreDirector scoreDirector = + buildScoreDirector(TestdataSimpleBigDecimalScoreSolution.buildSolutionDescriptor(), + factory -> new Constraint[] { + factory.forEachUniquePair(TestdataEntity.class) + .penalizeBigDecimal(SimpleBigDecimalScore.ONE) + .asConstraint(TEST_CONSTRAINT_NAME) + }); + + scoreDirector.setWorkingSolution(solution); + scoreDirector.calculateScore(); + assertThat(scoreDirector.calculateScore()).isEqualTo(SimpleBigDecimalScore.of(BigDecimal.valueOf(-21))); + } + private , Solution_, Entity_> void assertDefaultJustifications( InnerScoreDirector scoreDirector, List entityList) { if (!implSupport.isConstreamMatchEnabled()) diff --git a/core/constraint-streams/src/test/java/ai/timefold/solver/constraint/streams/common/quad/AbstractQuadConstraintStreamTest.java b/core/constraint-streams/src/test/java/ai/timefold/solver/constraint/streams/common/quad/AbstractQuadConstraintStreamTest.java index d14e030aa8..9dad83866a 100644 --- a/core/constraint-streams/src/test/java/ai/timefold/solver/constraint/streams/common/quad/AbstractQuadConstraintStreamTest.java +++ b/core/constraint-streams/src/test/java/ai/timefold/solver/constraint/streams/common/quad/AbstractQuadConstraintStreamTest.java @@ -2028,6 +2028,48 @@ public void penalizeUnweighted() { assertDefaultJustifications(scoreDirector, solution.getEntityList(), solution.getValueList()); } + @Override + @TestTemplate + public void penalizeUnweightedLong() { + TestdataSimpleLongScoreSolution solution = TestdataSimpleLongScoreSolution.generateSolution(); + + InnerScoreDirector scoreDirector = buildScoreDirector( + TestdataSimpleLongScoreSolution.buildSolutionDescriptor(), + factory -> new Constraint[] { + factory.forEachUniquePair(TestdataEntity.class, equal(TestdataEntity::getValue)) + .join(TestdataValue.class, equal((entity, entity2) -> entity.getValue(), identity())) + .join(TestdataValue.class, equal((entity, entity2, value) -> value, identity())) + .penalizeLong(SimpleLongScore.ONE) + .asConstraint(TEST_CONSTRAINT_NAME) + }); + + scoreDirector.setWorkingSolution(solution); + scoreDirector.calculateScore(); + assertThat(scoreDirector.calculateScore()).isEqualTo(SimpleLongScore.of(-2)); + assertDefaultJustifications(scoreDirector, solution.getEntityList(), solution.getValueList()); + } + + @Override + @TestTemplate + public void penalizeUnweightedBigDecimal() { + TestdataSimpleBigDecimalScoreSolution solution = TestdataSimpleBigDecimalScoreSolution.generateSolution(); + + InnerScoreDirector scoreDirector = + buildScoreDirector(TestdataSimpleBigDecimalScoreSolution.buildSolutionDescriptor(), + factory -> new Constraint[] { + factory.forEachUniquePair(TestdataEntity.class, equal(TestdataEntity::getValue)) + .join(TestdataValue.class, equal((entity, entity2) -> entity.getValue(), identity())) + .join(TestdataValue.class, equal((entity, entity2, value) -> value, identity())) + .penalizeBigDecimal(SimpleBigDecimalScore.ONE) + .asConstraint(TEST_CONSTRAINT_NAME) + }); + + scoreDirector.setWorkingSolution(solution); + scoreDirector.calculateScore(); + assertThat(scoreDirector.calculateScore()).isEqualTo(SimpleBigDecimalScore.of(BigDecimal.valueOf(-2))); + assertDefaultJustifications(scoreDirector, solution.getEntityList(), solution.getValueList()); + } + private , Solution_, Entity_, Value_> void assertDefaultJustifications( InnerScoreDirector scoreDirector, List entityList, List valueList) { if (!implSupport.isConstreamMatchEnabled()) diff --git a/core/constraint-streams/src/test/java/ai/timefold/solver/constraint/streams/common/tri/AbstractTriConstraintStreamTest.java b/core/constraint-streams/src/test/java/ai/timefold/solver/constraint/streams/common/tri/AbstractTriConstraintStreamTest.java index 903dd6f92c..a2a92bf51e 100644 --- a/core/constraint-streams/src/test/java/ai/timefold/solver/constraint/streams/common/tri/AbstractTriConstraintStreamTest.java +++ b/core/constraint-streams/src/test/java/ai/timefold/solver/constraint/streams/common/tri/AbstractTriConstraintStreamTest.java @@ -2322,6 +2322,46 @@ public void penalizeUnweighted() { assertDefaultJustifications(scoreDirector, solution.getEntityList(), solution.getValueList()); } + @Override + @TestTemplate + public void penalizeUnweightedLong() { + TestdataSimpleLongScoreSolution solution = TestdataSimpleLongScoreSolution.generateSolution(); + + InnerScoreDirector scoreDirector = buildScoreDirector( + TestdataSimpleLongScoreSolution.buildSolutionDescriptor(), + factory -> new Constraint[] { + factory.forEachUniquePair(TestdataEntity.class, equal(TestdataEntity::getValue)) + .join(TestdataValue.class, equal((entity, entity2) -> entity.getValue(), identity())) + .penalizeLong(SimpleLongScore.ONE) + .asConstraint(TEST_CONSTRAINT_NAME) + }); + + scoreDirector.setWorkingSolution(solution); + scoreDirector.calculateScore(); + assertThat(scoreDirector.calculateScore()).isEqualTo(SimpleLongScore.of(-2)); + assertDefaultJustifications(scoreDirector, solution.getEntityList(), solution.getValueList()); + } + + @Override + @TestTemplate + public void penalizeUnweightedBigDecimal() { + TestdataSimpleBigDecimalScoreSolution solution = TestdataSimpleBigDecimalScoreSolution.generateSolution(); + + InnerScoreDirector scoreDirector = + buildScoreDirector(TestdataSimpleBigDecimalScoreSolution.buildSolutionDescriptor(), + factory -> new Constraint[] { + factory.forEachUniquePair(TestdataEntity.class, equal(TestdataEntity::getValue)) + .join(TestdataValue.class, equal((entity, entity2) -> entity.getValue(), identity())) + .penalizeBigDecimal(SimpleBigDecimalScore.ONE) + .asConstraint(TEST_CONSTRAINT_NAME) + }); + + scoreDirector.setWorkingSolution(solution); + scoreDirector.calculateScore(); + assertThat(scoreDirector.calculateScore()).isEqualTo(SimpleBigDecimalScore.of(BigDecimal.valueOf(-2))); + assertDefaultJustifications(scoreDirector, solution.getEntityList(), solution.getValueList()); + } + private , Solution_, Entity_, Value_> void assertDefaultJustifications( InnerScoreDirector scoreDirector, List entityList, List valueList) { if (!implSupport.isConstreamMatchEnabled()) diff --git a/core/constraint-streams/src/test/java/ai/timefold/solver/constraint/streams/common/uni/AbstractUniConstraintStreamTest.java b/core/constraint-streams/src/test/java/ai/timefold/solver/constraint/streams/common/uni/AbstractUniConstraintStreamTest.java index 17907d59e8..0237a9490b 100644 --- a/core/constraint-streams/src/test/java/ai/timefold/solver/constraint/streams/common/uni/AbstractUniConstraintStreamTest.java +++ b/core/constraint-streams/src/test/java/ai/timefold/solver/constraint/streams/common/uni/AbstractUniConstraintStreamTest.java @@ -2668,6 +2668,42 @@ public void penalizeUnweighted() { assertDefaultJustifications(scoreDirector, solution.getEntityList()); } + @Override + @TestTemplate + public void penalizeUnweightedLong() { + TestdataSimpleLongScoreSolution solution = TestdataSimpleLongScoreSolution.generateSolution(); + + InnerScoreDirector scoreDirector = buildScoreDirector( + TestdataSimpleLongScoreSolution.buildSolutionDescriptor(), + factory -> new Constraint[] { + factory.forEach(TestdataEntity.class) + .penalizeLong(SimpleLongScore.ONE) + .asConstraint(TEST_CONSTRAINT_NAME) + }); + + scoreDirector.setWorkingSolution(solution); + scoreDirector.calculateScore(); + assertThat(scoreDirector.calculateScore()).isEqualTo(SimpleLongScore.of(-7)); + assertDefaultJustifications(scoreDirector, solution.getEntityList()); + } + + @Override + @TestTemplate + public void penalizeUnweightedBigDecimal() { + TestdataSimpleBigDecimalScoreSolution solution = TestdataSimpleBigDecimalScoreSolution.generateSolution(); + + InnerScoreDirector scoreDirector = + buildScoreDirector(TestdataSimpleBigDecimalScoreSolution.buildSolutionDescriptor(), + factory -> new Constraint[] { factory.forEach(TestdataEntity.class) + .penalizeBigDecimal(SimpleBigDecimalScore.ONE) + .asConstraint(TEST_CONSTRAINT_NAME) }); + + scoreDirector.setWorkingSolution(solution); + scoreDirector.calculateScore(); + assertThat(scoreDirector.calculateScore()).isEqualTo(SimpleBigDecimalScore.of(BigDecimal.valueOf(-7))); + assertDefaultJustifications(scoreDirector, solution.getEntityList()); + } + private , Solution_, Entity_> void assertDefaultJustifications( InnerScoreDirector scoreDirector, List entityList) { if (!implSupport.isConstreamMatchEnabled()) diff --git a/core/core-impl/src/main/java/ai/timefold/solver/core/api/score/stream/bi/BiConstraintStream.java b/core/core-impl/src/main/java/ai/timefold/solver/core/api/score/stream/bi/BiConstraintStream.java index 1a900c61d6..c73da7d876 100644 --- a/core/core-impl/src/main/java/ai/timefold/solver/core/api/score/stream/bi/BiConstraintStream.java +++ b/core/core-impl/src/main/java/ai/timefold/solver/core/api/score/stream/bi/BiConstraintStream.java @@ -1175,6 +1175,24 @@ default > BiConstraintBuilder penaliz return penalize(constraintWeight, ConstantLambdaUtils.biConstantOne()); } + /** + * As defined by {@link #penalizeLong(Score, ToLongBiFunction)}, where the match weight is one (1). + * + * @return never null + */ + default > BiConstraintBuilder penalizeLong(Score_ constraintWeight) { + return penalizeLong(constraintWeight, ConstantLambdaUtils.biConstantOneLong()); + } + + /** + * As defined by {@link #penalizeBigDecimal(Score, BiFunction)}, where the match weight is one (1). + * + * @return never null + */ + default > BiConstraintBuilder penalizeBigDecimal(Score_ constraintWeight) { + return penalizeBigDecimal(constraintWeight, ConstantLambdaUtils.biConstantOneBigDecimal()); + } + /** * Applies a negative {@link Score} impact, * subtracting the constraintWeight multiplied by the match weight, diff --git a/core/core-impl/src/main/java/ai/timefold/solver/core/api/score/stream/quad/QuadConstraintStream.java b/core/core-impl/src/main/java/ai/timefold/solver/core/api/score/stream/quad/QuadConstraintStream.java index 422c9badfb..cf5276d471 100644 --- a/core/core-impl/src/main/java/ai/timefold/solver/core/api/score/stream/quad/QuadConstraintStream.java +++ b/core/core-impl/src/main/java/ai/timefold/solver/core/api/score/stream/quad/QuadConstraintStream.java @@ -921,6 +921,25 @@ default > QuadConstraintBuilder return penalize(constraintWeight, ConstantLambdaUtils.quadConstantOne()); } + /** + * As defined by {@link #penalizeLong(Score, ToLongQuadFunction)}, where the match weight is one (1). + * + * @return never null + */ + default > QuadConstraintBuilder penalizeLong(Score_ constraintWeight) { + return penalizeLong(constraintWeight, ConstantLambdaUtils.quadConstantOneLong()); + } + + /** + * As defined by {@link #penalizeBigDecimal(Score, QuadFunction)}, where the match weight is one (1). + * + * @return never null + */ + default > QuadConstraintBuilder + penalizeBigDecimal(Score_ constraintWeight) { + return penalizeBigDecimal(constraintWeight, ConstantLambdaUtils.quadConstantOneBigDecimal()); + } + /** * Applies a negative {@link Score} impact, * subtracting the constraintWeight multiplied by the match weight, diff --git a/core/core-impl/src/main/java/ai/timefold/solver/core/api/score/stream/tri/TriConstraintStream.java b/core/core-impl/src/main/java/ai/timefold/solver/core/api/score/stream/tri/TriConstraintStream.java index 67c2faaa4c..6fc478f71b 100644 --- a/core/core-impl/src/main/java/ai/timefold/solver/core/api/score/stream/tri/TriConstraintStream.java +++ b/core/core-impl/src/main/java/ai/timefold/solver/core/api/score/stream/tri/TriConstraintStream.java @@ -1151,6 +1151,24 @@ default > TriConstraintBuilder pen return penalize(constraintWeight, ConstantLambdaUtils.triConstantOne()); } + /** + * As defined by {@link #penalizeLong(Score, ToLongTriFunction)}, where the match weight is one (1). + * + * @return never null + */ + default > TriConstraintBuilder penalizeLong(Score_ constraintWeight) { + return penalizeLong(constraintWeight, ConstantLambdaUtils.triConstantOneLong()); + } + + /** + * As defined by {@link #penalizeBigDecimal(Score, TriFunction)}, where the match weight is one (1). + * + * @return never null + */ + default > TriConstraintBuilder penalizeBigDecimal(Score_ constraintWeight) { + return penalizeBigDecimal(constraintWeight, ConstantLambdaUtils.triConstantOneBigDecimal()); + } + /** * Applies a negative {@link Score} impact, * subtracting the constraintWeight multiplied by the match weight, diff --git a/core/core-impl/src/main/java/ai/timefold/solver/core/api/score/stream/uni/UniConstraintStream.java b/core/core-impl/src/main/java/ai/timefold/solver/core/api/score/stream/uni/UniConstraintStream.java index d63e54903a..b14f788f72 100644 --- a/core/core-impl/src/main/java/ai/timefold/solver/core/api/score/stream/uni/UniConstraintStream.java +++ b/core/core-impl/src/main/java/ai/timefold/solver/core/api/score/stream/uni/UniConstraintStream.java @@ -1633,6 +1633,24 @@ default > UniConstraintBuilder penalize( return penalize(constraintWeight, ConstantLambdaUtils.uniConstantOne()); } + /** + * As defined by {@link #penalizeLong(Score, ToLongFunction)}, where the match weight is one (1). + * + * @return never null + */ + default > UniConstraintBuilder penalizeLong(Score_ constraintWeight) { + return penalizeLong(constraintWeight, ConstantLambdaUtils.uniConstantOneLong()); + } + + /** + * As defined by {@link #penalizeBigDecimal(Score, Function)}, where the match weight is one (1). + * + * @return never null + */ + default > UniConstraintBuilder penalizeBigDecimal(Score_ constraintWeight) { + return penalizeBigDecimal(constraintWeight, ConstantLambdaUtils.uniConstantOneBigDecimal()); + } + /** * Applies a negative {@link Score} impact, * subtracting the constraintWeight multiplied by the match weight, diff --git a/core/core-impl/src/main/java/ai/timefold/solver/core/impl/util/ConstantLambdaUtils.java b/core/core-impl/src/main/java/ai/timefold/solver/core/impl/util/ConstantLambdaUtils.java index 919a991391..73534be28f 100644 --- a/core/core-impl/src/main/java/ai/timefold/solver/core/impl/util/ConstantLambdaUtils.java +++ b/core/core-impl/src/main/java/ai/timefold/solver/core/impl/util/ConstantLambdaUtils.java @@ -1,15 +1,20 @@ package ai.timefold.solver.core.impl.util; +import java.math.BigDecimal; import java.util.Objects; import java.util.function.BiFunction; import java.util.function.BiPredicate; import java.util.function.Function; import java.util.function.ToIntBiFunction; import java.util.function.ToIntFunction; +import java.util.function.ToLongBiFunction; +import java.util.function.ToLongFunction; import ai.timefold.solver.core.api.function.QuadFunction; import ai.timefold.solver.core.api.function.ToIntQuadFunction; import ai.timefold.solver.core.api.function.ToIntTriFunction; +import ai.timefold.solver.core.api.function.ToLongQuadFunction; +import ai.timefold.solver.core.api.function.ToLongTriFunction; import ai.timefold.solver.core.api.function.TriFunction; /** @@ -59,17 +64,41 @@ public final class ConstantLambdaUtils { private static final QuadFunction QUAD_PICK_FOURTH = (a, b, c, d) -> d; @SuppressWarnings("rawtypes") - private static final ToIntFunction UNI_CONSTANT_ONE = (a) -> 1; + private static final ToIntFunction UNI_CONSTANT_ONE = a -> 1; + + @SuppressWarnings("rawtypes") + private static final ToLongFunction UNI_CONSTANT_ONE_LONG = a -> 1L; + + @SuppressWarnings("rawtypes") + private static final Function UNI_CONSTANT_ONE_BIG_DECIMAL = a -> BigDecimal.ONE; @SuppressWarnings("rawtypes") private static final ToIntBiFunction BI_CONSTANT_ONE = (a, b) -> 1; + @SuppressWarnings("rawtypes") + private static final ToLongBiFunction BI_CONSTANT_ONE_LONG = (a, b) -> 1L; + + @SuppressWarnings("rawtypes") + private static final BiFunction BI_CONSTANT_ONE_BIG_DECIMAL = (a, b) -> BigDecimal.ONE; + @SuppressWarnings("rawtypes") private static final ToIntTriFunction TRI_CONSTANT_ONE = (a, b, c) -> 1; + @SuppressWarnings("rawtypes") + private static final ToLongTriFunction TRI_CONSTANT_ONE_LONG = (a, b, c) -> 1L; + + @SuppressWarnings("rawtypes") + private static final TriFunction TRI_CONSTANT_ONE_BIG_DECIMAL = (a, b, c) -> BigDecimal.ONE; + @SuppressWarnings("rawtypes") private static final ToIntQuadFunction QUAD_CONSTANT_ONE = (a, b, c, d) -> 1; + @SuppressWarnings("rawtypes") + private static final ToLongQuadFunction QUAD_CONSTANT_ONE_LONG = (a, b, c, d) -> 1L; + + @SuppressWarnings("rawtypes") + private static final QuadFunction QUAD_CONSTANT_ONE_BIG_DECiMAL = (a, b, c, d) -> BigDecimal.ONE; + /** * Returns a {@link Runnable} that does nothing. * @@ -200,6 +229,26 @@ public static ToIntFunction uniConstantOne() { return UNI_CONSTANT_ONE; } + /** + * Returns a {@link ToLongFunction} that returns the constant 1. + * + * @return never null + */ + @SuppressWarnings("unchecked") + public static ToLongFunction uniConstantOneLong() { + return UNI_CONSTANT_ONE_LONG; + } + + /** + * Returns a {@link Function} that returns the constant 1. + * + * @return never null + */ + @SuppressWarnings("unchecked") + public static Function uniConstantOneBigDecimal() { + return UNI_CONSTANT_ONE_BIG_DECIMAL; + } + /** * Returns a {@link ToIntBiFunction} that returns the constant 1. * @@ -210,6 +259,26 @@ public static ToIntBiFunction biConstantOne() { return BI_CONSTANT_ONE; } + /** + * Returns a {@link ToLongBiFunction} that returns the constant 1. + * + * @return never null + */ + @SuppressWarnings("unchecked") + public static ToLongBiFunction biConstantOneLong() { + return BI_CONSTANT_ONE_LONG; + } + + /** + * Returns a {@link BiFunction} that returns the constant 1. + * + * @return never null + */ + @SuppressWarnings("unchecked") + public static BiFunction biConstantOneBigDecimal() { + return BI_CONSTANT_ONE_BIG_DECIMAL; + } + /** * Returns a {@link ToIntTriFunction} that returns the constant 1. * @@ -220,6 +289,26 @@ public static ToIntTriFunction triConstantOne() { return TRI_CONSTANT_ONE; } + /** + * Returns a {@link ToLongTriFunction} that returns the constant 1. + * + * @return never null + */ + @SuppressWarnings("unchecked") + public static ToLongTriFunction triConstantOneLong() { + return TRI_CONSTANT_ONE_LONG; + } + + /** + * Returns a {@link TriFunction} that returns the constant 1. + * + * @return never null + */ + @SuppressWarnings("unchecked") + public static TriFunction triConstantOneBigDecimal() { + return TRI_CONSTANT_ONE_BIG_DECIMAL; + } + /** * Returns a {@link ToIntQuadFunction} that returns the constant 1. * @@ -230,6 +319,26 @@ public static ToIntQuadFunction quadConstantOne() { return QUAD_CONSTANT_ONE; } + /** + * Returns a {@link ToLongQuadFunction} that returns the constant 1. + * + * @return never null + */ + @SuppressWarnings("unchecked") + public static ToLongQuadFunction quadConstantOneLong() { + return QUAD_CONSTANT_ONE_LONG; + } + + /** + * Returns a {@link QuadFunction} that returns the constant 1. + * + * @return never null + */ + @SuppressWarnings("unchecked") + public static QuadFunction quadConstantOneBigDecimal() { + return QUAD_CONSTANT_ONE_BIG_DECiMAL; + } + private ConstantLambdaUtils() { // No external instances. }