Skip to content

Commit 1b1602f

Browse files
committed
Define hasMaximumSize Rater #37
Signed-off-by: Mārtiņš Avots <martins.avots@splitcells.net>
1 parent e7ba144 commit 1b1602f

4 files changed

Lines changed: 203 additions & 1 deletion

File tree

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
/* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
2+
* SPDX-FileCopyrightText: Contributors To The `net.splitcells.*` Projects
3+
*/
4+
package net.splitcells.gel.rating.rater.lib;
5+
6+
import net.splitcells.dem.data.set.Set;
7+
import net.splitcells.dem.data.set.list.List;
8+
import net.splitcells.dem.lang.dom.Domable;
9+
import net.splitcells.dem.object.Discoverable;
10+
import net.splitcells.dem.utils.CommonFunctions;
11+
import net.splitcells.gel.constraint.Constraint;
12+
import net.splitcells.gel.constraint.GroupId;
13+
import net.splitcells.gel.data.view.Line;
14+
import net.splitcells.gel.data.view.View;
15+
import net.splitcells.gel.rating.framework.Rating;
16+
import net.splitcells.gel.rating.rater.framework.Rater;
17+
import net.splitcells.gel.rating.rater.framework.RatingEvent;
18+
19+
import static net.splitcells.dem.data.set.Sets.toSetOfUniques;
20+
import static net.splitcells.dem.data.set.list.Lists.list;
21+
import static net.splitcells.dem.lang.tree.TreeI.tree;
22+
import static net.splitcells.dem.resource.communication.log.Logs.logs;
23+
import static net.splitcells.gel.rating.framework.LocalRatingI.localRating;
24+
import static net.splitcells.gel.rating.rater.framework.RatingEventI.ratingEvent;
25+
import static net.splitcells.gel.rating.type.Cost.cost;
26+
import static net.splitcells.gel.rating.type.Cost.noCost;
27+
28+
29+
public class HasMaximalSize implements Rater {
30+
public static HasMaximalSize hasMaximalSize(int minimalSize) {
31+
return new HasMaximalSize(minimalSize);
32+
}
33+
34+
private final int maximalSize;
35+
private final List<Discoverable> contexts = list();
36+
37+
private HasMaximalSize(int maximalSize) {
38+
this.maximalSize = maximalSize;
39+
}
40+
41+
@Override
42+
public RatingEvent ratingAfterAddition(View lines, Line addition, List<Constraint> children
43+
, View ratingsBeforeAddition) {
44+
final var individualRating = rating(lines, false);
45+
final var additionalRatings
46+
= rateLines(lines, addition, children, individualRating);
47+
additionalRatings.additions().put(addition
48+
, localRating()
49+
.withPropagationTo(children)
50+
.withRating(individualRating)
51+
.withResultingGroupId(addition.value(Constraint.INCOMING_CONSTRAINT_GROUP))
52+
);
53+
return additionalRatings;
54+
}
55+
56+
private RatingEvent rateLines(View lines, Line changed, List<Constraint> children, Rating cost) {
57+
final RatingEvent linesRating = ratingEvent();
58+
lines.rawLinesView().stream()
59+
.filter(e -> e != null)
60+
.filter(e -> e.index() != changed.index())
61+
.forEach(e -> {
62+
linesRating.updateRating_withReplacement(e,
63+
localRating().
64+
withPropagationTo(children).
65+
withRating(cost).
66+
withResultingGroupId(changed.value(Constraint.INCOMING_CONSTRAINT_GROUP))
67+
);
68+
});
69+
return linesRating;
70+
}
71+
72+
@Override
73+
public String toSimpleDescription(Line line, View groupsLineProcessing, GroupId incomingGroup) {
74+
return "Group size should be at most " + maximalSize + ", but is " + groupsLineProcessing.size() + " instead.";
75+
}
76+
77+
@Override
78+
public RatingEvent rating_before_removal
79+
(View lines
80+
, Line removal
81+
, List<Constraint> children
82+
, View ratingsBeforeRemoval) {
83+
return rateLines(lines, removal, children, rating(lines, true));
84+
}
85+
86+
private Rating rating(View lines, boolean beforeRemoval) {
87+
final Rating rating;
88+
final int actualSize;
89+
if (beforeRemoval) {
90+
actualSize = lines.size() - 1;
91+
} else {
92+
actualSize = lines.size();
93+
}
94+
if (actualSize == 0) {
95+
logs().warn(getClass().getName() + " should not be run on empty groups.");
96+
rating = noCost();
97+
} else if (actualSize > maximalSize) {
98+
final int difference = actualSize - maximalSize;
99+
rating = cost(difference / ((double) actualSize));
100+
} else {
101+
rating = noCost();
102+
}
103+
return rating;
104+
}
105+
106+
@Override
107+
public Class<? extends Rater> type() {
108+
return HasMaximalSize.class;
109+
}
110+
111+
@Override
112+
public List<Domable> arguments() {
113+
return list(() -> tree(HasMaximalSize.class.getSimpleName()).withChild(tree("" + maximalSize)));
114+
}
115+
116+
@Override
117+
public boolean equals(Object arg) {
118+
if (arg instanceof HasMaximalSize cArg) {
119+
return this.maximalSize == cArg.maximalSize;
120+
}
121+
return false;
122+
}
123+
124+
@Override
125+
public int hashCode() {
126+
return CommonFunctions.hashCode(maximalSize);
127+
}
128+
129+
@Override
130+
public void addContext(Discoverable context) {
131+
contexts.add(context);
132+
}
133+
134+
@Override
135+
public Set<List<String>> paths() {
136+
return contexts.stream().map(Discoverable::path).collect(toSetOfUniques());
137+
}
138+
139+
@Override
140+
public String toString() {
141+
return getClass().getSimpleName() + ", " + maximalSize;
142+
}
143+
}

projects/net.splitcells.gel.editor/src/main/java/net/splitcells/gel/editor/geal/runners/FunctionCallMetaExecutor.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import static net.splitcells.gel.editor.geal.runners.HasMinimalDistanceOfCallRunner.hasMinimalDistanceOfCallRunner;
3636
import static net.splitcells.gel.editor.geal.runners.HasSizeCallRunner.hasSizeCallRunner;
3737
import static net.splitcells.gel.editor.geal.runners.ImportCsvDataRunner.importCsvDataRunner;
38+
import static net.splitcells.gel.editor.geal.runners.MaxSizeCallRunner.maxSizeCallRunner;
3839
import static net.splitcells.gel.editor.geal.runners.MinSizeCallRunner.minSizeCallRunner;
3940
import static net.splitcells.gel.editor.geal.runners.NotRunner.notRunner;
4041
import static net.splitcells.gel.editor.geal.runners.OutputFormatCallRunner.outputFormatCallRunner;
@@ -68,6 +69,7 @@ public static FunctionCallMetaExecutor functionCallMetaExecutor() {
6869
.registerExecutor(notRunner())
6970
.registerExecutor(equalsRunner())
7071
.registerExecutor(minSizeCallRunner())
72+
.registerExecutor(maxSizeCallRunner())
7173
;
7274
}
7375

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
2+
* SPDX-FileCopyrightText: Contributors To The `net.splitcells.*` Projects
3+
*/
4+
package net.splitcells.gel.editor.geal.runners;
5+
6+
import lombok.val;
7+
import net.splitcells.dem.data.set.list.List;
8+
import net.splitcells.gel.editor.Editor;
9+
import net.splitcells.gel.editor.geal.lang.FunctionCallDesc;
10+
11+
import java.util.Optional;
12+
13+
import static net.splitcells.dem.data.set.list.Lists.list;
14+
import static net.splitcells.gel.editor.geal.runners.FunctionCallRun.functionCallRun;
15+
import static net.splitcells.gel.editor.geal.runners.FunctionCallRunnerParser.functionCallRunnerParser;
16+
import static net.splitcells.gel.rating.rater.lib.HasMaximalSize.hasMaximalSize;
17+
import static net.splitcells.gel.rating.rater.lib.HasMinimalSize.hasMinimalSize;
18+
19+
public class MaxSizeCallRunner implements FunctionCallRunner {
20+
public static FunctionCallRunner maxSizeCallRunner() {
21+
return new MaxSizeCallRunner();
22+
}
23+
24+
private static final String MAX_SIZE_NAME = "hasMaximumSize";
25+
26+
private static class Args {
27+
int maximalSize;
28+
}
29+
30+
private static final FunctionCallRunnerParser<Args> PARSER = functionCallRunnerParser(MAX_SIZE_NAME
31+
, 1
32+
, fcr -> {
33+
val args = new Args();
34+
fcr.requireSubjectAbsence();
35+
args.maximalSize = fcr.parseArgument(Integer.class, 0, "maximalSize");
36+
fcr.requireArgumentCount(1);
37+
return args;
38+
});
39+
40+
private MaxSizeCallRunner() {
41+
42+
}
43+
44+
@Override public FunctionCallRun execute(FunctionCallDesc functionCall, Optional<Object> subject, Editor context) {
45+
final var run = functionCallRun(subject, context);
46+
if (!functionCall.getName().getValue().equals(MAX_SIZE_NAME) || subject.isPresent() || functionCall.getArguments().size() != 1) {
47+
return run;
48+
}
49+
val args = PARSER.parse(subject, context, functionCall);
50+
run.setResult(Optional.of(hasMaximalSize(args.maximalSize)));
51+
return run;
52+
}
53+
54+
@Override public List<FunctionCallRunnerParser<?>> parsers() {
55+
return list(PARSER);
56+
}
57+
}

projects/net.splitcells.gel.editor/src/main/resources/html/net/splitcells/gel/editor/geal/examples/sports-course-planning.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ courseScheduling . forEach(not(equals(assignedSport, 'Dummy Course')))
4949
. then(hasSize(optCourseSize));
5050
courseScheduling . forEach(not(equals(assignedSport, 'Dummy Course')))
5151
. forEach(assignedSport, assignedHalfYear)
52-
. then(atMost(maxCourseSize, size));
52+
. then(hasMaximumSize(maxCourseSize));
5353
courseScheduling . forEach(not(equals(assignedSport, 'Dummy Course')))
5454
. forEach(assignedHalfYear)
5555
. then(atMost(maxCoursesPerHalfYear, uniqueValueCount(assignedSport)));

0 commit comments

Comments
 (0)