Skip to content

Commit cda8258

Browse files
authored
Merge pull request #73 from arnaud-m/bug-68
Post missing symmetry breaking constraint close #69
2 parents 42911e5 + 0936d93 commit cda8258

File tree

6 files changed

+72
-29
lines changed

6 files changed

+72
-29
lines changed

src/main/java/cryptator/cmd/CryptaBiConsumer.java

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import cryptator.specs.ICryptaEvaluation;
2020
import cryptator.specs.ICryptaNode;
2121
import cryptator.specs.ICryptaSolution;
22+
import cryptator.specs.ICryptaSolutionStore;
2223
import cryptator.tree.CryptaEvaluation;
2324
import cryptator.tree.CryptaEvaluationException;
2425
import cryptator.tree.GraphvizExport;
@@ -27,11 +28,11 @@
2728
import guru.nidi.graphviz.engine.Graphviz;
2829
import guru.nidi.graphviz.model.Graph;
2930

30-
public class CryptaBiConsumer implements BiConsumer<ICryptaNode, ICryptaSolution> {
31+
public class CryptaBiConsumer implements BiConsumer<ICryptaNode, ICryptaSolution>, ICryptaSolutionStore {
3132

3233
private final Logger logger;
3334

34-
private long solutionCount;
35+
private int solutionCount;
3536

3637
private Optional<ICryptaSolution> lastSolution;
3738

@@ -46,18 +47,15 @@ public CryptaBiConsumer(final Logger logger) {
4647
internal = new SolutionCounter();
4748
}
4849

49-
public final long getSolutionCount() {
50+
@Override
51+
public final int getSolutionCount() {
5052
return solutionCount;
5153
}
5254

5355
public final Optional<ICryptaSolution> getLastSolution() {
5456
return lastSolution;
5557
}
5658

57-
public final Optional<ICryptaSolution> getUniqueSolution() {
58-
return solutionCount <= 1 ? lastSolution : Optional.empty();
59-
}
60-
6159
public final int getErrorCount() {
6260
return errorCount;
6361
}
@@ -94,8 +92,8 @@ private class SolutionCounter implements BiConsumer<ICryptaNode, ICryptaSolution
9492

9593
@Override
9694
public void accept(final ICryptaNode t, final ICryptaSolution u) {
97-
lastSolution = Optional.of(u);
9895
solutionCount++;
96+
lastSolution = Optional.of(u);
9997
}
10098
}
10199

src/main/java/cryptator/gen/CryptaGenModel.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ private void postLeftOrRightConstraints() {
5454
}
5555

5656
private void postSymBreakLengthLenConstraint() {
57-
left.getMaxLength().le(right.getMaxLength());
57+
left.getMaxLength().le(right.getMaxLength()).post();
5858

5959
}
6060

src/main/java/cryptator/gen/CryptaListGenerator.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ private static void parallelSolve(final CryptaGenModel gen, final Consumer<ICryp
106106
}
107107

108108
@Override
109-
public void generate(final BiConsumer<ICryptaNode, ICryptaSolution> consumer) throws CryptaModelException {
109+
public long generate(final BiConsumer<ICryptaNode, ICryptaSolution> consumer) throws CryptaModelException {
110110
final CryptaGenModel gen = buildModel();
111111
clog.logOnModel(gen);
112112

@@ -119,6 +119,7 @@ public void generate(final BiConsumer<ICryptaNode, ICryptaSolution> consumer) th
119119
parallelSolve(gen, cons, nthreads);
120120
}
121121
clog.logOnSolver(gen);
122+
return gen.getModel().getSolver().getSolutionCount();
122123
}
123124

124125
// FIXME are consumers thread-safe ? they are used in parallel !
@@ -167,13 +168,17 @@ public void accept(final ICryptaNode t) {
167168
try {
168169
final CryptaBiConsumer collect = buildBiConsumer();
169170
solver.solve(t, config, collect);
170-
Optional<ICryptaSolution> solution = collect.getUniqueSolution();
171-
if (solution.isPresent()) {
172-
internal.accept(t, solution.get());
171+
if (collect.getErrorCount() == 0) {
172+
final Optional<ICryptaSolution> solution = collect.getUniqueSolution();
173+
if (solution.isPresent()) {
174+
internal.accept(t, solution.get());
175+
}
176+
} else {
177+
logger.log(Level.WARNING, "Solve the candidate cryptarithm [ERROR]");
173178
}
174179
} catch (CryptaModelException | CryptaSolverException e) {
175180
errorCount.incrementAndGet();
176-
logger.log(Level.WARNING, "Fail to solve the cryptarithm", e);
181+
logger.log(Level.WARNING, "Solve the candidate cryptarithm [FAIL]", e);
177182
}
178183
}
179184
}

src/main/java/cryptator/specs/ICryptaGenerator.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@ public interface ICryptaGenerator {
2323
*
2424
* @param consumer the consumer that handles the generated cryptarithm along
2525
* with its solution.
26+
* @return the number of candidate cryptarithm
2627
* @throws CryptaModelException if there was an error during the generation.
2728
*/
28-
void generate(BiConsumer<ICryptaNode, ICryptaSolution> consumer) throws CryptaModelException;
29+
long generate(BiConsumer<ICryptaNode, ICryptaSolution> consumer) throws CryptaModelException;
2930

3031
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/**
2+
* This file is part of cryptator, https://github.com/arnaud-m/cryptator
3+
*
4+
* Copyright (c) 2022, Université Côte d'Azur. All rights reserved.
5+
*
6+
* Licensed under the BSD 3-clause license.
7+
* See LICENSE file in the project root for full license information.
8+
*/
9+
package cryptator.specs;
10+
11+
import java.util.Optional;
12+
13+
public interface ICryptaSolutionStore {
14+
15+
int getSolutionCount();
16+
17+
Optional<ICryptaSolution> getLastSolution();
18+
19+
default Optional<ICryptaSolution> getUniqueSolution() {
20+
return getSolutionCount() <= 1 ? getLastSolution() : Optional.empty();
21+
}
22+
23+
}

src/test/java/cryptator/GenerateTest.java

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import static org.junit.Assert.assertEquals;
1212

1313
import java.util.Arrays;
14+
import java.util.OptionalInt;
1415

1516
import org.junit.BeforeClass;
1617
import org.junit.Test;
@@ -28,44 +29,59 @@ public static void configureTestLoggers() {
2829
JULogUtil.configureTestLoggers();
2930
}
3031

31-
public long testGenerate(final WordArray wordArray, final boolean lightModel, final boolean lightPropagation)
32+
private void testGenerate(final int expectedSolCount, final OptionalInt expectedCandCount,
33+
final WordArray wordArray, final boolean lightModel, final boolean lightPropagation)
3234
throws CryptaModelException {
3335
final CryptagenConfig config = new CryptagenConfig();
3436
config.setLightModel(lightModel);
3537
config.setLightPropagation(lightPropagation);
3638
final CryptaListGenerator gen = new CryptaListGenerator(wordArray, config, Cryptagen.LOGGER);
3739
CryptaBiConsumer cons = new CryptaBiConsumer(Cryptagen.LOGGER);
40+
cons.withSolutionLog();
3841
cons.withSolutionCheck(config.getArithmeticBase());
39-
gen.generate(cons);
4042
assertEquals(0, cons.getErrorCount());
41-
return cons.getSolutionCount();
43+
long actualCandCount = gen.generate(cons);
44+
if (expectedCandCount.isPresent()) {
45+
assertEquals(expectedCandCount.getAsInt(), actualCandCount);
46+
}
47+
assertEquals(expectedSolCount, cons.getSolutionCount());
4248
}
4349

44-
public void testGenerate(final int expectedSolCount, final WordArray wordArray) throws CryptaModelException {
45-
assertEquals(expectedSolCount, testGenerate(wordArray, false, false));
46-
assertEquals(expectedSolCount, testGenerate(wordArray, false, true));
47-
assertEquals(expectedSolCount, testGenerate(wordArray, true, false));
48-
assertEquals(expectedSolCount, testGenerate(wordArray, true, true));
50+
private void testGenerate(final int expectedSolCount, final WordArray wordArray) throws CryptaModelException {
51+
testGenerate(expectedSolCount, OptionalInt.empty(), wordArray);
4952
}
5053

51-
public void testGenerate(final int expectedSolCount, final String rightMember, final String... words)
52-
throws CryptaModelException {
53-
testGenerate(expectedSolCount, new WordArray(Arrays.asList(words), rightMember));
54+
private void testGenerate(final int expectedSolCount, final OptionalInt expectedCandCount,
55+
final WordArray wordArray) throws CryptaModelException {
56+
testGenerate(expectedSolCount, expectedCandCount, wordArray, false, false);
57+
testGenerate(expectedSolCount, expectedCandCount, wordArray, false, true);
58+
testGenerate(expectedSolCount, expectedCandCount, wordArray, true, false);
59+
testGenerate(expectedSolCount, expectedCandCount, wordArray, true, true);
5460
}
5561

5662
@Test
5763
public void testSendMoreMoney() throws CryptaModelException {
58-
testGenerate(1, null, "send", "more", "money");
64+
final WordArray words = new WordArray(Arrays.asList("send", "more", "money"), null);
65+
testGenerate(1, OptionalInt.of(1), words);
66+
}
67+
68+
@Test
69+
public void testSendMuchMoreMoney() throws CryptaModelException {
70+
WordArray words = new WordArray(Arrays.asList("send", "much", "more", "money"), null);
71+
testGenerate(1, OptionalInt.of(6), words, false, true);
72+
5973
}
6074

6175
@Test
6276
public void testPlanets1() throws CryptaModelException {
63-
testGenerate(2, null, "venus", "earth", "uranus", "saturn");
77+
WordArray words = new WordArray(Arrays.asList("venus", "earth", "uranus", "saturn"), null);
78+
testGenerate(2, words);
6479
}
6580

6681
@Test
6782
public void testPlanets2() throws CryptaModelException {
68-
testGenerate(1, "planets", "venus", "earth", "uranus", "saturn");
83+
WordArray words = new WordArray(Arrays.asList("venus", "earth", "uranus", "saturn"), "planets");
84+
testGenerate(1, words);
6985
}
7086

7187
@Test

0 commit comments

Comments
 (0)