diff --git a/src/main/benchmarks/README.org b/src/main/benchmarks/README.org index d573ad0..d9b00c1 100644 --- a/src/main/benchmarks/README.org +++ b/src/main/benchmarks/README.org @@ -224,6 +224,7 @@ The word lists ~colors.txt~ and ~monsters.txt~ takes the most time by far (aroun generateCrossword 3 #+END_SRC + ** Configure the algorithm *** Addition @@ -245,7 +246,6 @@ The word lists ~colors.txt~ and ~monsters.txt~ takes the most time by far (aroun * Solve ** Create the solver *** Cryptator -**** Simple Precision #+BEGIN_SRC sh :tangle solver.sh JAR=cryptator-0.6.0-SNAPSHOT-with-dependencies.jar diff --git a/src/main/cryptarithms/long-multiplications/samples.db.txt b/src/main/cryptarithms/long-multiplications/samples.db.txt new file mode 100644 index 0000000..ac7b52a --- /dev/null +++ b/src/main/cryptarithms/long-multiplications/samples.db.txt @@ -0,0 +1,6 @@ +in * gag = vain && in * '1' + kan * '10' + in * '100' = vain && in * g = in && in * a = kan && in * g = in +in * gag = vain && in * '1' + km * '10' + in * '100' = vain && in * g = in && in * a = km && in * g = in +in * go = kan && in * '1' + km * '10' = kan && in * g = km && in * o = in +in * ive = olim && km * '1' + go * '10' + in * '100' = olim && in * i = in && in * v = go && in * e = km +km * km = kan && in * '1' + km * '10' = kan && km * k = km && km * m = in +surs * cc = relus && surs * '1' + surs * '10' = relus && surs * c = surs && surs * c = surs diff --git a/src/main/java/cryptator/config/CryptaLogConfig.java b/src/main/java/cryptator/config/CryptaLogConfig.java index 371a456..978c603 100644 --- a/src/main/java/cryptator/config/CryptaLogConfig.java +++ b/src/main/java/cryptator/config/CryptaLogConfig.java @@ -21,7 +21,7 @@ public final Verbosity getVerbosity() { return verbosity; } - public final void setVerbosity(Verbosity verbosity) { + public final void setVerbosity(final Verbosity verbosity) { this.verbosity = verbosity; } diff --git a/src/main/java/cryptator/config/CryptagenConfig.java b/src/main/java/cryptator/config/CryptagenConfig.java index e861f75..373401f 100644 --- a/src/main/java/cryptator/config/CryptagenConfig.java +++ b/src/main/java/cryptator/config/CryptagenConfig.java @@ -54,7 +54,7 @@ public final int getGridSize() { return gridSize; } - public final void setGridSize(int gridSize) { + public final void setGridSize(final int gridSize) { this.gridSize = gridSize; } @@ -98,7 +98,7 @@ public final boolean isMultModel() { return multModel; } - public final void setMultModel(boolean multModel) { + public final void setMultModel(final boolean multModel) { this.multModel = multModel; } @@ -106,7 +106,7 @@ public final boolean isLongMultModel() { return longMultModel; } - public final void setLongMultModel(boolean longMultModel) { + public final void setLongMultModel(final boolean longMultModel) { this.longMultModel = longMultModel; } diff --git a/src/main/java/cryptator/gen/AbstractCryptaGenModel.java b/src/main/java/cryptator/gen/AbstractCryptaGenModel.java index f7af6df..1272cc9 100644 --- a/src/main/java/cryptator/gen/AbstractCryptaGenModel.java +++ b/src/main/java/cryptator/gen/AbstractCryptaGenModel.java @@ -37,6 +37,13 @@ public abstract class AbstractCryptaGenModel implements ICryptaGenModel { /** The maximum length of a present word. */ protected final IntVar maxLength; + /** + * Instantiates a new model for the generation. + * + * @param model the model + * @param words the words + * @param prefix the prefix for variable names + */ protected AbstractCryptaGenModel(final Model model, final String[] words, final String prefix) { super(); this.model = model; @@ -46,6 +53,11 @@ protected AbstractCryptaGenModel(final Model model, final String[] words, final this.maxLength = model.intVar(prefix + "maxLength", 0, AbstractCryptaGenModel.getMaxLength(words), false); } + /** + * Gets the model. + * + * @return the model + */ @Override public final Model getModel() { return model; @@ -87,14 +99,26 @@ private static BoolVar[] buildWordVars(final Model model, final String[] words, return vars; } + /** + * Post constraints for the presence of words. + */ protected abstract void postWordConstraints(); + /** + * Post word count constraint. + */ protected void postWordCountConstraint() { model.sum(vwords, "=", wordCount).post(); } + /** + * Post the constraints that set the maximum length. + */ protected abstract void postMaxLengthConstraints(); + /** + * Builds the model. + */ @Override public void buildModel() { postWordConstraints(); @@ -115,6 +139,11 @@ public void postWordCountConstraints(final int min, final int max) { } } + /** + * Post a constraint that set the number of words. + * + * @param val the number of words + */ public void postWordCountConstraints(final int val) { wordCount.eq(val).post(); } @@ -124,18 +153,44 @@ public String toString() { return GenerateUtil.recordString(this, " "); } + /** + * Gets the maximum length of the words. + * + * @param words the words + * @return the maximum length + */ public static final int getMaxLength(final String[] words) { return Arrays.stream(words).mapToInt(String::length).max().orElse(0); } + /** + * Gets the sum of the word lengths. + * + * @param words the words + * @return the sum of the lengths + */ public static final int getSumLength(final String[] words) { return Arrays.stream(words).mapToInt(String::length).reduce(0, Integer::sum); } + /** + * Gets the array of word lengths. + * + * @param words the words + * @return the array of lengths + */ public static final int[] getLengths(final String[] words) { return Arrays.stream(words).mapToInt(String::length).toArray(); } + /** + * Gets the array of length cardinalities. + * + * The cardinality of a word is its number of distinct characters. + * + * @param words the words + * @return the array of cardinalities + */ public static final int[] getCards(final String[] words) { final ToIntFunction distinctCharCount = s -> (int) s.chars().distinct().count(); return Arrays.stream(words).mapToInt(distinctCharCount).toArray(); diff --git a/src/main/java/cryptator/gen/AbstractCryptaListModel.java b/src/main/java/cryptator/gen/AbstractCryptaListModel.java index 5a658f7..3f7a5ed 100644 --- a/src/main/java/cryptator/gen/AbstractCryptaListModel.java +++ b/src/main/java/cryptator/gen/AbstractCryptaListModel.java @@ -45,10 +45,18 @@ protected AbstractCryptaListModel(final Model model, final String[] words) { symbolCount = model.intVar("symbCount", 0, symbolsToVariables.size()); } + /** + * Gets the symbol count. + * + * @return the symbol count + */ public final IntVar getSymbolCount() { return symbolCount; } + /** + * Builds the model. + */ @Override public void buildModel() { super.buildModel(); @@ -56,13 +64,31 @@ public void buildModel() { postChannelingConstraints(); } + /** + * Post the constraint for the fixed right member. + */ public abstract void postFixedRightMemberConstraints(); - public abstract void postDoublyTrueConstraints(final int lowerBound); + /** + * Post constraints for doubly true cryptarithms. + * + * @param lowerBound the lower bound for the sum + */ + public abstract void postDoublyTrueConstraints(int lowerBound); + /** + * Post optional constraints for the numerical precision. + * + * @param base the arithmetic base + */ public abstract void postPrecisionConstraints(int base); - public abstract void postHeavyConstraints(final int base); + /** + * Post heavy constraints for filtering more candidates. + * + * @param base the arithmetic base + */ + public abstract void postHeavyConstraints(int base); /** * Post a constraint over the maximum number of distinct symbols in the words. @@ -73,6 +99,9 @@ public void postMaxSymbolCountConstraint(final int max) { symbolCount.le(max).post(); } + /** + * Post symbol count constraint. + */ private void postSymbolCountConstraint() { final BoolVar[] symbols = AbstractCryptaListModel.toArray(symbolsToVariables.values()); model.sum(symbols, "=", symbolCount).post(); @@ -125,6 +154,12 @@ private void postChannelingConstraints() { } } + /** + * Transform a collection of boolean variables into an array. + * + * @param vars the boolean variable collection + * @return the array + */ private static BoolVar[] toArray(final Collection vars) { return vars.toArray(new BoolVar[vars.size()]); } diff --git a/src/main/java/cryptator/gen/CryptaGenAdd.java b/src/main/java/cryptator/gen/CryptaGenAdd.java index 70db936..afb73b6 100644 --- a/src/main/java/cryptator/gen/CryptaGenAdd.java +++ b/src/main/java/cryptator/gen/CryptaGenAdd.java @@ -68,7 +68,7 @@ public void postDoublyTrueConstraints(final int lb) { } @Override - public void postPrecisionConstraints(int base) { + public void postPrecisionConstraints(final int base) { // Nothing to do. } diff --git a/src/main/java/cryptator/gen/CryptaGenCrossword.java b/src/main/java/cryptator/gen/CryptaGenCrossword.java index 1c3c707..9ca3c7f 100644 --- a/src/main/java/cryptator/gen/CryptaGenCrossword.java +++ b/src/main/java/cryptator/gen/CryptaGenCrossword.java @@ -23,7 +23,8 @@ class CryptaCrossPair extends CryptaMemberPair { private final IntVar[] indices; - public CryptaCrossPair(final IntVar[] indices, final String[] words, final String prefix, boolean useLenModel) { + public CryptaCrossPair(final IntVar[] indices, final String[] words, final String prefix, + final boolean useLenModel) { super(indices[indices.length - 1], words, prefix, useLenModel); this.indices = indices; } @@ -56,7 +57,7 @@ public class CryptaGenCrossword extends AbstractCryptaListModel { private final CryptaCrossPair[] additions; - public CryptaGenCrossword(int n, String[] words, boolean useLenModel) { + public CryptaGenCrossword(final int n, final String[] words, final boolean useLenModel) { super(new Model("Generate-Crossword"), words); this.n = n; this.grid = new CryptaGridModel(model, n, words.length); @@ -64,7 +65,7 @@ public CryptaGenCrossword(int n, String[] words, boolean useLenModel) { createMembers(useLenModel); } - private void createMembers(boolean useLenModel) { + private void createMembers(final boolean useLenModel) { for (int i = 0; i < n; i++) { final String prefix = "R" + (i + 1) + "_"; additions[i] = new CryptaCrossPair(grid.getRow(i), words, prefix, useLenModel); @@ -76,24 +77,12 @@ private void createMembers(boolean useLenModel) { } } - private void setSolution() { - int[][] solgrid = new int[][] {new int[] {0, 3, 4}, new int[] {1, 5, 7}, new int[] {2, 6, 8}}; - for (int i = 0; i < solgrid.length; i++) { - for (int j = 0; j < solgrid[i].length; j++) { - grid.getCell(i, j).eq(solgrid[i][j]).post(); - } - } - } - @Override public void buildModel() { super.buildModel(); grid.buildModel(); Stream.of(additions).forEach(CryptaCrossPair::buildModel); - // setSolution(); - // TODO Set search strategy ? - // getSolver().setSearch(Search.intVarSearch(ArrayUtils.flatten(grid.getMatrix()))); - + // TODO change search strategy ? } @Override @@ -102,17 +91,17 @@ public void postFixedRightMemberConstraints() { } @Override - public void postDoublyTrueConstraints(int lowerBound) { + public void postDoublyTrueConstraints(final int lowerBound) { throw new UnsupportedOperationException("Not yet implemented"); } @Override - public void postHeavyConstraints(int base) { + public void postHeavyConstraints(final int base) { Stream.of(additions).forEach(m -> m.postHeavyConstraints(base)); } @Override - public void postPrecisionConstraints(int base) { + public void postPrecisionConstraints(final int base) { // Nothing to do. } diff --git a/src/main/java/cryptator/gen/CryptaGenLongMult.java b/src/main/java/cryptator/gen/CryptaGenLongMult.java index 59ab38c..25f1bdd 100644 --- a/src/main/java/cryptator/gen/CryptaGenLongMult.java +++ b/src/main/java/cryptator/gen/CryptaGenLongMult.java @@ -33,7 +33,7 @@ public class CryptaGenLongMult extends AbstractCryptaListModel { private int arithmeticBase; - public CryptaGenLongMult(String[] words, int arithmeticBase) { + public CryptaGenLongMult(final String[] words, final int arithmeticBase) { super(new Model("Generate-Long-Multiplication"), words); this.arithmeticBase = arithmeticBase; final int[] lengths = getLengths(words); @@ -45,7 +45,7 @@ public final int getArithmeticBase() { return arithmeticBase; } - public final void setArithmeticBase(int arithmeticBase) { + public final void setArithmeticBase(final int arithmeticBase) { this.arithmeticBase = arithmeticBase; } @@ -77,17 +77,17 @@ public void postFixedRightMemberConstraints() { } @Override - public void postDoublyTrueConstraints(int lowerBound) { + public void postDoublyTrueConstraints(final int lowerBound) { throw new UnsupportedOperationException("Doubly true long multiplication is undefined."); } @Override - public void postHeavyConstraints(int base) { + public void postHeavyConstraints(final int base) { // Nothing to do } @Override - public void postPrecisionConstraints(int base) { + public void postPrecisionConstraints(final int base) { final int thresh = AdaptiveSolver.computeThreshold(base); longMult.getProductLength().le(thresh).post(); } @@ -107,7 +107,7 @@ private String[] getTermWords() { return twords; } - private static ICryptaNode recordTermAddition(final String[] terms, String product, int base) { + private static ICryptaNode recordTermAddition(final String[] terms, final String product, final int base) { ArrayList nodes = new ArrayList<>(); int exponent = 1; for (int i = terms.length - 1; i >= 0; i--) { @@ -119,7 +119,8 @@ private static ICryptaNode recordTermAddition(final String[] terms, String produ return new CryptaNode(CryptaOperator.EQ, addition, new CryptaLeaf(product)); } - private static ICryptaNode recordTermMultiplications(String[] terms, String multiplicand, String multiplier) { + private static ICryptaNode recordTermMultiplications(final String[] terms, final String multiplicand, + final String multiplier) { final char[] multipliers = multiplier.toCharArray(); final ArrayList nodes = new ArrayList<>(); for (int i = 0; i < terms.length; i++) { diff --git a/src/main/java/cryptator/gen/CryptaGenMult.java b/src/main/java/cryptator/gen/CryptaGenMult.java index a608c66..0bfa9b1 100644 --- a/src/main/java/cryptator/gen/CryptaGenMult.java +++ b/src/main/java/cryptator/gen/CryptaGenMult.java @@ -23,7 +23,7 @@ class CryptaMemberMult extends CryptaMemberPair { private final IntVar sumR; - public CryptaMemberMult(Model model, String[] words, String prefix) { + public CryptaMemberMult(final Model model, final String[] words, final String prefix) { super(new CryptaMemberLen(model, words, prefix + "L_"), new CryptaMemberLen(model, words, prefix + "R_")); final int ub = AbstractCryptaGenModel.getSumLength(words); this.sumL = model.intVar("L_sumLength", 0, ub); @@ -48,7 +48,7 @@ protected void postSymBreakLengthConstraint() { getModel().lexLess(left.getWordVars(), right.getWordVars()).post(); } - public void postMultPrecisionConstraints(int base) { + public void postMultPrecisionConstraints(final int base) { getModel().sum(left.lengths, "=", sumL).post(); getModel().sum(((CryptaMemberLen) right).lengths, "=", sumR).post(); final int thresh = AdaptiveSolver.computeThreshold(base) + 1; @@ -56,7 +56,7 @@ public void postMultPrecisionConstraints(int base) { sumR.le(thresh).post(); } - public void postMultHeavyConstraints(int base) { + public void postMultHeavyConstraints(final int base) { final ArExpression minL = sumL.sub(left.getWordCount()).add(1); final ArExpression minR = sumR.sub(right.getWordCount()).add(1); minL.le(sumR).post(); @@ -67,6 +67,8 @@ public void postMultHeavyConstraints(int base) { public class CryptaGenMult extends AbstractCryptaListModel { + private final int PRECISION = 1000; + private final CryptaMemberMult multiplication; public CryptaGenMult(final String[] words) { @@ -98,9 +100,9 @@ public void postFixedRightMemberConstraints() { multiplication.postFixedRightMemberConstraints(); } - private int getDoublyCoeff(int i) { + private int getDoublyCoeff(final int i) { double coeff = Math.log(i) / Math.log((getN() - 1)); - return (int) Math.round(1000 * coeff); + return (int) Math.round(PRECISION * coeff); } @@ -131,7 +133,7 @@ public void postDoublyTrueConstraints(final int lb) { } @Override - public void postPrecisionConstraints(int base) { + public void postPrecisionConstraints(final int base) { final int thresh = AdaptiveSolver.computeThreshold(base); getMaxLength().le(thresh).post(); multiplication.postMultPrecisionConstraints(base); diff --git a/src/main/java/cryptator/gen/CryptaListGenerator.java b/src/main/java/cryptator/gen/CryptaListGenerator.java index 736766c..49188f5 100644 --- a/src/main/java/cryptator/gen/CryptaListGenerator.java +++ b/src/main/java/cryptator/gen/CryptaListGenerator.java @@ -37,18 +37,35 @@ import cryptator.specs.ICryptaSolver; import cryptator.tree.TreeUtils; +/** + * The Class CryptaListGenerator is the root class for generation. + * + * It builds and solves a generation model for cryptarithms. + */ public class CryptaListGenerator implements ICryptaGenerator { + /** The words. */ private final WordArray words; + /** The config. */ private final CryptagenConfig config; + /** The logger. */ private final Logger logger; + /** The clog. */ private final ChocoLogger clog; + /** The error count. */ private final AtomicInteger errorCount; + /** + * Instantiates a new cryptarithm generator. + * + * @param words the word array + * @param config the configuration + * @param logger the logger + */ public CryptaListGenerator(final WordArray words, final CryptagenConfig config, final Logger logger) { super(); this.words = words; @@ -58,10 +75,20 @@ public CryptaListGenerator(final WordArray words, final CryptagenConfig config, this.errorCount = new AtomicInteger(); } + /** + * Gets the error count. + * + * @return the error count + */ public final int getErrorCount() { return errorCount.intValue(); } + /** + * Creates the generation model with respect to the configuration. + * + * @return the generation model + */ private AbstractCryptaListModel createGenModel() { if (config.getGridSize() > 0) { return new CryptaGenCrossword(config.getGridSize(), words.getWords(), config.isLightModel()); @@ -74,6 +101,13 @@ private AbstractCryptaListModel createGenModel() { } } + /** + * Builds the generation solver. + * + * It creates, builds and configures the generation model. + * + * @return the generation solver + */ private ICryptaGenSolver buildGenSolver() { final AbstractCryptaListModel gen = createGenModel(); gen.buildModel(); @@ -93,6 +127,13 @@ private ICryptaGenSolver buildGenSolver() { return gen; } + /** + * Builds the consumer for candidate cryptarithms . + * + * @param gen the generation model + * @param consumer the consumer for valid cryptarithms + * @return the candidate consumer + */ private Consumer buildConsumer(final IChocoModel gen, final BiConsumer consumer) { final Consumer cons = new LogConsumer(gen); @@ -100,6 +141,12 @@ private Consumer buildConsumer(final IChocoModel gen, return config.isDryRun() ? cons : cons.andThen(new GenerateConsumer(solver, consumer)); } + /** + * Solve the candidates in sequential. + * + * @param gen the generation solver + * @param cons the candidate consumer + */ private void sequentialSolve(final ICryptaGenSolver gen, final Consumer cons) { final Solver s = gen.getSolver(); while (s.solve()) { @@ -107,6 +154,13 @@ private void sequentialSolve(final ICryptaGenSolver gen, final Consumer cons, final int nthreads) { final ExecutorService executor = Executors.newFixedThreadPool(nthreads); @@ -142,16 +196,30 @@ public long generate(final BiConsumer consumer) th return gen.getSolver().getSolutionCount(); } + /** + * The LogConsumer logs the candidate cryptarithm. + */ // FIXME are consumers thread-safe ? they are used in parallel ! private class LogConsumer implements Consumer { + /** The solution. */ private final Solution solution; + /** + * Instantiates a new log consumer. + * + * @param gen the generation model + */ LogConsumer(final IChocoModel gen) { super(); solution = new Solution(gen.getModel()); } + /** + * Accept the candidate cryptarithm. + * + * @param t the candidate cryptarithm + */ @Override public void accept(final ICryptaNode t) { clog.logOnSolution(solution); @@ -161,12 +229,23 @@ public void accept(final ICryptaNode t) { } } + /** + * The Class GenerateConsumer solves the candidate cryptarithm. + */ private class GenerateConsumer implements Consumer { + /** The solver. */ private final ICryptaSolver solver; + /** The internal consumer for valid cryptarithm. */ private final BiConsumer internal; + /** + * Instantiates a new generate consumer. + * + * @param solver the cryptarithm solver + * @param internal the internal consumer for valid cryptarithm + */ GenerateConsumer(final ICryptaSolver solver, final BiConsumer internal) { super(); this.solver = solver; @@ -174,6 +253,11 @@ private class GenerateConsumer implements Consumer { this.solver.limitSolution(2); } + /** + * Builds the consumer of valid cryptarithms. + * + * @return the cryptarithm consumer + */ private CryptaBiConsumer buildBiConsumer() { CryptaBiConsumer consumer = new CryptaBiConsumer(logger); if (config.isCheckSolution()) { @@ -182,6 +266,11 @@ private CryptaBiConsumer buildBiConsumer() { return consumer; } + /** + * Accept the candidate cryptarithm. + * + * @param t the candidate + */ @Override public void accept(final ICryptaNode t) { try { diff --git a/src/main/java/cryptator/gen/GenerateUtil.java b/src/main/java/cryptator/gen/GenerateUtil.java index 91f7bc2..55e5117 100644 --- a/src/main/java/cryptator/gen/GenerateUtil.java +++ b/src/main/java/cryptator/gen/GenerateUtil.java @@ -21,30 +21,73 @@ import cryptator.tree.CryptaLeaf; import cryptator.tree.CryptaNode; -public class GenerateUtil { +/** + * The Class GenerateUtil provides utilities for building cryptarithm tree. + * + * It is used by generation models for recording their solutions. + */ +public final class GenerateUtil { + /** + * Unusable private constructor. + */ private GenerateUtil() { super(); } + /** + * The stream of present words of the model. + * + * @param model the generation model + * @return the stream of present words + */ public static Stream wordStream(final ICryptaGenModel model) { final BoolVar[] v = model.getWordVars(); final String[] w = model.getWords(); return IntStream.range(0, model.getN()).filter(i -> v[i].isInstantiatedTo(1)).mapToObj(i -> w[i]); } + /** + * The stream of present word leaves. + * + * A leaf is created for each present word of the model. + * + * @param model the generation model + * @return the stream of present leaves + */ public static Stream leafStream(final ICryptaGenModel model) { return wordStream(model).map(CryptaLeaf::new).map(ICryptaNode.class::cast); } + /** + * Record a string with the present words. + * + * @param model the generation model + * @param separator the word separator + * @return the string of present words + */ public static String recordString(final ICryptaGenModel model, final String separator) { return wordStream(model).collect(Collectors.joining(separator)); } + /** + * Reduce the nodes by an operation. + * + * @param operator the operator + * @param nodes the nodes to reduce + * @return the reduced node + */ public static ICryptaNode reduceOperation(final CryptaOperator operator, final ICryptaNode... nodes) { return reduceOperation(operator, Stream.of(nodes)); } + /** + * Reduce the nodes by an operation. + * + * @param operator the operator + * @param nodeStream the stream of nodes to reduce + * @return the reduced node + */ public static ICryptaNode reduceOperation(final CryptaOperator operator, final Stream nodeStream) { BinaryOperator binop = (a, b) -> { if (a == null) { @@ -55,23 +98,58 @@ public static ICryptaNode reduceOperation(final CryptaOperator operator, final S return nodeStream.reduce(null, binop); } + /** + * Record the addition of present words. + * + * @param model the generation model + * @return the sum node + */ public static ICryptaNode recordAddition(final ICryptaGenModel model) { return reduceOperation(CryptaOperator.ADD, leafStream(model)); } + /** + * Record the addition of two nodes. + * + * @param left the left node + * @param right the right node + * @return the sum node + */ public static ICryptaNode recordAddition(final ICryptaGenModel left, final ICryptaGenModel right) { return reduceOperation(CryptaOperator.EQ, recordAddition(left), recordAddition(right)); } + /** + * Record the multiplication of present words. + * + * @param model the generation model + * @return the product node + */ public static ICryptaNode recordMultiplication(final ICryptaGenModel model) { return reduceOperation(CryptaOperator.MUL, leafStream(model)); } + /** + * Record the multiplication of two nodes. + * + * @param left the left node + * @param right the right node + * @return the product node + */ public static ICryptaNode recordMultiplication(final ICryptaGenModel left, final ICryptaGenModel right) { return reduceOperation(CryptaOperator.EQ, recordMultiplication(left), recordMultiplication(right)); } - public static ICryptaNode recordMultiplication(String multiplicand, String multiplier, String product) { + /** + * Record a short multiplication. + * + * @param multiplicand the multiplicand word + * @param multiplier the multiplier word + * @param product the product word + * @return the short multiplication node + */ + public static ICryptaNode recordMultiplication(final String multiplicand, final String multiplier, + final String product) { return new CryptaNode(CryptaOperator.EQ, new CryptaNode(CryptaOperator.MUL, new CryptaLeaf(multiplicand), new CryptaLeaf(multiplier)), new CryptaLeaf(product)); diff --git a/src/main/java/cryptator/gen/member/CryptaMemberPair.java b/src/main/java/cryptator/gen/member/CryptaMemberPair.java index 5a33368..f520411 100644 --- a/src/main/java/cryptator/gen/member/CryptaMemberPair.java +++ b/src/main/java/cryptator/gen/member/CryptaMemberPair.java @@ -23,26 +23,26 @@ public class CryptaMemberPair implements ICryptaGenSolver { protected final AbstractCryptaGenModel right; - protected CryptaMemberPair(CryptaMemberLen left, CryptaMemberLen right) { + protected CryptaMemberPair(final CryptaMemberLen left, final CryptaMemberLen right) { super(); this.left = left; this.right = right; } - public CryptaMemberPair(final Model model, final String[] words, final String prefix, boolean useMemberLen) { + public CryptaMemberPair(final Model model, final String[] words, final String prefix, final boolean useMemberLen) { super(); left = buildLeftMember(model, words, prefix, useMemberLen); right = new CryptaMemberElt(model, words, prefix + "R_"); } - public CryptaMemberPair(final IntVar index, final String[] words, final String prefix, boolean useMemberLen) { + public CryptaMemberPair(final IntVar index, final String[] words, final String prefix, final boolean useMemberLen) { super(); left = buildLeftMember(index.getModel(), words, prefix, useMemberLen); right = new CryptaMemberElt(index, words, prefix + "R_"); } private static final CryptaMemberLen buildLeftMember(final Model model, final String[] words, final String prefix, - boolean useMemberLen) { + final boolean useMemberLen) { return useMemberLen ? new CryptaMemberLen(model, words, prefix + "L_") : new CryptaMemberCard(model, words, prefix + "L_"); } diff --git a/src/main/java/cryptator/gen/pattern/CryptaGridModel.java b/src/main/java/cryptator/gen/pattern/CryptaGridModel.java index f3511f3..132ea56 100644 --- a/src/main/java/cryptator/gen/pattern/CryptaGridModel.java +++ b/src/main/java/cryptator/gen/pattern/CryptaGridModel.java @@ -15,16 +15,30 @@ import cryptator.gen.AbstractCryptaGenModel; import cryptator.specs.IChocoModel; +/** + * The Class CryptaGridModel assigns distinct words to a square grid. + */ public final class CryptaGridModel implements IChocoModel { + /** The model. */ private final Model model; + /** The grid of word indices. */ private final IntVar[][] grid; + /** The transposed grid of word indices. */ private final IntVar[][] tgrid; + /** The format for printing the grid. */ private final String format; + /** + * Instantiates a new grid model. + * + * @param model the model + * @param n the grid dimension is 'n x n' + * @param m the number of words + */ public CryptaGridModel(final Model model, final int n, final int m) { super(); this.model = model; @@ -34,10 +48,16 @@ public CryptaGridModel(final Model model, final int n, final int m) { this.format = " %" + precision + "d"; } + /** + * Post all different constraint for the word indices. + */ private void postAllDifferentConstraint() { model.allDifferent(ArrayUtils.flatten(grid)).post(); } + /** + * Post symmetry breaking constraints over the word indices. + */ private void postSymmetryBreakingConstraints() { final int n = size(); if (n > 1) { @@ -51,6 +71,9 @@ private void postSymmetryBreakingConstraints() { } } + /** + * Builds the grid model. + */ public void buildModel() { postAllDifferentConstraint(); postSymmetryBreakingConstraints(); @@ -61,31 +84,71 @@ public Model getModel() { return model; } + /** + * Gets the grid size + * + * @return the grid size + */ public int size() { return grid.length; } - public IntVar getCell(int i, int j) { + /** + * Gets the variable of the grid. + * + * @param i the row + * @param j the column + * @return the grid variable + */ + public IntVar getCell(final int i, final int j) { return grid[i][j]; } - public IntVar[] getRow(int i) { + /** + * Gets the i-th row of the grid. + * + * @param i the row index + * @return the row array of variables + */ + public IntVar[] getRow(final int i) { return grid[i]; } - public IntVar[] getCol(int i) { + /** + * Gets the i-th column of the grid. + * + * @param i the column index + * @return the row array of variables + */ + public IntVar[] getCol(final int i) { return tgrid[i]; } + /** + * Gets the matrix of grid variables. + * + * @return the matrix of word indices + */ public IntVar[][] getMatrix() { return grid; } + /** + * Gets the transposed matrix. + * + * @return the transpose + */ public IntVar[][] getTranspose() { return tgrid; } - public String toString(String[] words) { + /** + * To string with word mapping. + * + * @param words the words mapped to the indices + * @return the grid of words + */ + public String toString(final String[] words) { final int n = size(); final StringBuilder b = new StringBuilder(); final int maxlen = AbstractCryptaGenModel.getMaxLength(words); @@ -99,6 +162,11 @@ public String toString(String[] words) { return b.toString(); } + /** + * To string with indices. + * + * @return the grid of indices + */ @Override public String toString() { final StringBuilder b = new StringBuilder(); diff --git a/src/main/java/cryptator/gen/pattern/CryptaLongMultModel.java b/src/main/java/cryptator/gen/pattern/CryptaLongMultModel.java index dae858b..9633488 100644 --- a/src/main/java/cryptator/gen/pattern/CryptaLongMultModel.java +++ b/src/main/java/cryptator/gen/pattern/CryptaLongMultModel.java @@ -20,37 +20,65 @@ import cryptator.specs.IChocoModel; +/** + * The Class builds a generation model for long multiplication. + * + * It does not inherit nor use members because of the strength of the + * constraints over the operands. + */ public class CryptaLongMultModel implements IChocoModel { + /** The word cardinalities, i.e. the number of distinct characters. */ private final int[] cards; + /** The word lengths, i.e. the number of characters. */ private final int[] lengths; + /** The model. */ private final Model model; + /** The multiplicand index. */ private final IntVar multiplicand; + /** The multiplier index. */ private final IntVar multiplier; + /** The product index. */ private final IntVar product; + /** The term indices. */ private final IntVar[] terms; + /** The term presences. */ private final BoolVar[] presences; + /** The multiplicand length. */ private final IntVar mdLength; + /** The multiplier length. */ private final IntVar mrLength; + /** The product length. */ private final IntVar prLength; + /** The term lengths. */ private final IntVar[] termLengths; + /** The multiplier cardinality, its number of distinct characters. */ private final IntVar mrCard; - private int maxProductLength = 5; - - public CryptaLongMultModel(Model model, final int[] lengths, final int[] cards) { + /** The Constant maxProductLength. */ + // TODO Define properly + private static final int maxProductLength = 5; + + /** + * Instantiates a new model for the long multiplication. + * + * @param model the model + * @param lengths the word lengths + * @param cards the word cardinalities + */ + public CryptaLongMultModel(final Model model, final int[] lengths, final int[] cards) { this.model = model; this.lengths = lengths; this.cards = cards; @@ -74,30 +102,65 @@ public Model getModel() { return model; } + /** + * Gets the multiplicand index. + * + * @return the multiplicand index + */ public int getMultiplicandIndex() { return multiplicand.getValue(); } + /** + * Gets the multiplier index. + * + * @return the multiplier index + */ public int getMultiplierIndex() { return multiplier.getValue(); } + /** + * Gets the product index. + * + * @return the product index + */ public int getProductIndex() { return product.getValue(); } + /** + * Gets the product length. + * + * @return the product length + */ public IntVar getProductLength() { return prLength; } + /** + * Gets the product. + * + * @return the product + */ public IntVar getProduct() { return product; } + /** + * Gets the word indices. + * + * @return the word indices + */ public IntVar[] getWordIndices() { return ArrayUtils.append(ArrayUtils.toArray(multiplicand, multiplier, product), terms); } + /** + * Gets the term indices. + * + * @return the term indices + */ public int[] getTermIndices() { int n = mrCard.getValue(); int[] indices = new int[n]; @@ -107,16 +170,22 @@ public int[] getTermIndices() { return indices; } - private static void ge1(ArExpression e1, ArExpression e2) { + private static void ge1(final ArExpression e1, final ArExpression e2) { e1.ge(e2).post(); e1.le(e2.add(1)).post(); } + /** + * Post the product length constraints. + */ private void postPRLenConstraints() { ge1(prLength, mdLength.add(mrLength).sub(1)); prLength.ge(termLengths[0].add(mrLength).sub(1)).post(); } + /** + * Post the all different constraint on the word indices. + */ private void postAllDifferent() { final int n = lengths.length; model.allDifferentUnderCondition(ArrayUtils.concat(terms, product), new ExceptN(n), true).post(); @@ -124,6 +193,9 @@ private void postAllDifferent() { terms[maxProductLength].eq(n).post(); } + /** + * Post the channeling between the term indices and lengths. + */ private void postTermLenChanneling() { final int n = lengths.length; final IntVar[] vars = new IntVar[n + 1]; @@ -137,18 +209,28 @@ private void postTermLenChanneling() { } } + /** + * Post the channeling constraint between the indices and lengths of the + * operands of the short multiplication. + */ private void postMultLenChanneling() { model.element(mdLength, lengths, multiplicand).post(); model.element(mrLength, lengths, multiplier).post(); model.element(prLength, lengths, product).post(); } + /** + * Post the constraint on the term lengths. + */ private void postTermLenConstraints() { for (int i = 0; i < termLengths.length; i++) { ge1(termLengths[i], mdLength); } } + /** + * Post symmetry breaking constraint by ordering terms. + */ private void postTermOrderingConstraints() { for (int i = 0; i < terms.length; i++) { model.reifyXneC(terms[i], lengths.length, presences[i]); @@ -156,11 +238,17 @@ private void postTermOrderingConstraints() { model.decreasing(presences, 0).post(); } + /** + * Post the constraint for counting the terms. + */ private void postTermCountConstraints() { model.element(mrCard, cards, multiplier).post(); model.sum("presences", presences).eq(mrCard).post(); } + /** + * Builds the model. + */ public void buildModel() { postAllDifferent(); @@ -174,17 +262,23 @@ public void buildModel() { postTermOrderingConstraints(); } - private void toString(StringBuilder b, IntVar idx, IntVar len) { + private void toString(final StringBuilder b, final IntVar idx, final IntVar len) { toString(b, idx, len, Optional.empty()); } - private void toString(StringBuilder b, IntVar idx, IntVar len, Optional card) { + private void toString(final StringBuilder b, final IntVar idx, final IntVar len, final Optional card) { b.append(idx.getValue()).append("(").append(len.getValue()); - if (card.isPresent()) + if (card.isPresent()) { b.append("-").append(card.get().getValue()); + } b.append(") "); } + /** + * To string. + * + * @return the string + */ @Override public String toString() { StringBuilder b = new StringBuilder(); @@ -202,17 +296,26 @@ public String toString() { return b.toString(); } + /** + * The Class ExceptN for the all different constraint + */ private static class ExceptN implements Condition { + /** The dummy index. */ private final int n; - protected ExceptN(int n) { + /** + * Instantiates a new condition. + * + * @param n the dummy index + */ + protected ExceptN(final int n) { super(); this.n = n; } @Override - public boolean holdOnVar(IntVar x) { + public boolean holdOnVar(final IntVar x) { return !x.contains(n); } diff --git a/src/main/java/cryptator/solver/AbstractCryptaSolution.java b/src/main/java/cryptator/solver/AbstractCryptaSolution.java index 4820023..3bea9bd 100644 --- a/src/main/java/cryptator/solver/AbstractCryptaSolution.java +++ b/src/main/java/cryptator/solver/AbstractCryptaSolution.java @@ -9,6 +9,7 @@ package cryptator.solver; import java.util.Map; +import java.util.Map.Entry; import java.util.TreeMap; import java.util.function.BiConsumer; @@ -53,11 +54,11 @@ public void forEach(final BiConsumer action) { public String toString() { final StringBuilder b1 = new StringBuilder(); final StringBuilder b2 = new StringBuilder(); - for (Character symbol : symbolsToDigits.keySet()) { - final String domain = getDomain(symbolsToDigits.get(symbol)); + for (Entry e : symbolsToDigits.entrySet()) { + final String domain = getDomain(e.getValue()); final int length = Math.max(1, domain.length()); final String format = " %" + length + "s|"; - b1.append(String.format(format, symbol)); + b1.append(String.format(format, e.getKey())); b2.append(String.format(format, domain)); } return b1.toString() + "\n" + b2.toString(); diff --git a/src/main/java/cryptator/solver/AbstractModelerNodeConsumer.java b/src/main/java/cryptator/solver/AbstractModelerNodeConsumer.java index 34f43ce..28cc073 100644 --- a/src/main/java/cryptator/solver/AbstractModelerNodeConsumer.java +++ b/src/main/java/cryptator/solver/AbstractModelerNodeConsumer.java @@ -101,7 +101,6 @@ public void postConstraints() throws CryptaModelException { } public void configureSearch() { - // model.getSolver().showDecisions(); final int searchType = config.getSearchStrategy(); if (searchType == 1) { IntVar[] vars = symbolsToVariables.values().toArray(new IntVar[symbolsToVariables.size()]); diff --git a/src/main/java/cryptator/solver/CryptaBignumModeler.java b/src/main/java/cryptator/solver/CryptaBignumModeler.java index 6ae5374..99b0db0 100644 --- a/src/main/java/cryptator/solver/CryptaBignumModeler.java +++ b/src/main/java/cryptator/solver/CryptaBignumModeler.java @@ -90,12 +90,10 @@ private ArExpression[] applyADD(final ArExpression[] a, final ArExpression[] b) for (int i = 0; i < m; i++) { c[i] = CryptaOperator.ADD.getExpression().apply(a[i], b[i]); } - // Can only enter in one loop - for (int i = m; i < a.length; i++) { - c[i] = a[i]; - } - for (int i = m; i < b.length; i++) { - c[i] = b[i]; + if (a.length > m) { + System.arraycopy(a, m, c, m, a.length - m); + } else { + System.arraycopy(b, m, c, m, b.length - m); } return c; } diff --git a/src/test/java/cryptator/BignumTest.java b/src/test/java/cryptator/BignumTest.java index b1bb888..730172a 100644 --- a/src/test/java/cryptator/BignumTest.java +++ b/src/test/java/cryptator/BignumTest.java @@ -199,31 +199,16 @@ public void testSetBase2() throws CryptaParserException, CryptaModelException, C // Tests from SolverTest class @Test public void testSendMoreMoneyList2() throws CryptaParserException, CryptaModelException, CryptaSolverException { - t.config.setHornerScheme(true); t.testNotUNIQUE("send+more=money; a+b=c"); } @Test public void testSendMoreMoneyList3() throws CryptaParserException, CryptaModelException, CryptaSolverException { - t.config.setHornerScheme(true); t.testSAT("send+more=money; a+b=c"); } @Test - public void testSendMoreMoneyList4() throws CryptaParserException, CryptaModelException, CryptaSolverException { - t.config.setHornerScheme(true); - t.testUNSAT("send+more=money; s+e=n"); - } - - @Test - public void testSendMoreMoneyList5() throws CryptaParserException, CryptaModelException, CryptaSolverException { - t.config.setHornerScheme(true); - t.testUNSAT("send+more=money;;; s+e=n"); - } - - @Test - public void testSendMoreMoneyList6() throws CryptaParserException, CryptaModelException, CryptaSolverException { - t.config.setHornerScheme(true); - t.testUNSAT("send+more=money; s+e=n;"); + public void testUnsatSendMoreMoneyList() throws CryptaParserException, CryptaModelException, CryptaSolverException { + t.testUNSAT("send+more=money; s+e=n", "send+more=money;;; s+e=n", "send+more=money; s+e=n;"); } } diff --git a/src/test/java/cryptator/CrossModelTest.java b/src/test/java/cryptator/CrossModelTest.java index 09c6958..e820e56 100644 --- a/src/test/java/cryptator/CrossModelTest.java +++ b/src/test/java/cryptator/CrossModelTest.java @@ -17,25 +17,27 @@ public class CrossModelTest { - private void testCrosswordModel(int size, String[] words, int expectedSolutionCount) { + private void testCrosswordModel(final int size, final String[] words, final int expectedSolutionCount) { testCrosswordModel(size, words, true, expectedSolutionCount); testCrosswordModel(size, words, false, expectedSolutionCount); } - private void testCrosswordModel(int size, String[] words, boolean useLenModel, int expectedSolutionCount) { + private void testCrosswordModel(final int size, final String[] words, final boolean useLenModel, + final int expectedSolutionCount) { final CryptaGenCrossword m = new CryptaGenCrossword(size, words, useLenModel); m.buildModel(); // System.out.println(m.getModel()); assertEquals(expectedSolutionCount, m.getModel().getSolver().streamSolutions().count()); } - private void testHeavyCrosswordModel(int size, String[] words, int expectedSolutionCount) { + private void testHeavyCrosswordModel(final int size, final String[] words, final int expectedSolutionCount) { testHeavyCrosswordModel(size, words, true, expectedSolutionCount); testHeavyCrosswordModel(size, words, false, expectedSolutionCount); } - private void testHeavyCrosswordModel(int size, String[] words, boolean useLenModel, int expectedSolutionCount) { + private void testHeavyCrosswordModel(final int size, final String[] words, final boolean useLenModel, + final int expectedSolutionCount) { final CryptaGenCrossword m = new CryptaGenCrossword(size, words, useLenModel); m.buildModel(); m.postHeavyConstraints(10); diff --git a/src/test/java/cryptator/GenModelTest.java b/src/test/java/cryptator/GenModelTest.java index 7af49b0..739a6c0 100644 --- a/src/test/java/cryptator/GenModelTest.java +++ b/src/test/java/cryptator/GenModelTest.java @@ -43,17 +43,17 @@ public void postFixedRightMemberConstraints() { } @Override - public void postDoublyTrueConstraints(int lowerBound) { + public void postDoublyTrueConstraints(final int lowerBound) { // Nothing to do. } @Override - public void postPrecisionConstraints(int base) { + public void postPrecisionConstraints(final int base) { // Nothing to do. } @Override - public void postHeavyConstraints(int base) { + public void postHeavyConstraints(final int base) { // Nothing to do. } diff --git a/src/test/java/cryptator/GenerateTest.java b/src/test/java/cryptator/GenerateTest.java index 1262ead..d47d311 100644 --- a/src/test/java/cryptator/GenerateTest.java +++ b/src/test/java/cryptator/GenerateTest.java @@ -52,13 +52,13 @@ private void testGenerate(final int expectedSolCount, final OptionalInt expected assertEquals(expectedSolCount, cons.getSolutionCount()); } - private void testGenerate(final int expectedSolCount, final WordArray wordArray, int gridSize) + private void testGenerate(final int expectedSolCount, final WordArray wordArray, final int gridSize) throws CryptaModelException { testGenerate(expectedSolCount, OptionalInt.empty(), wordArray, 0); } private void testGenerate(final int expectedSolCount, final OptionalInt expectedCandCount, - final WordArray wordArray, int gridSize) throws CryptaModelException { + final WordArray wordArray, final int gridSize) throws CryptaModelException { configure(gridSize, false, false); testGenerate(expectedSolCount, expectedCandCount, wordArray); configure(gridSize, false, true); @@ -70,7 +70,7 @@ private void testGenerate(final int expectedSolCount, final OptionalInt expected } private void testHeavyGenerate(final int expectedSolCount, final OptionalInt expectedCandCount, - final WordArray wordArray, int gridSize) throws CryptaModelException { + final WordArray wordArray, final int gridSize) throws CryptaModelException { configure(gridSize, false, false); testGenerate(expectedSolCount, expectedCandCount, wordArray); configure(gridSize, true, false); @@ -87,6 +87,17 @@ private void testMultGenerate(final int expectedSolCount, final WordArray wordAr config.setMultModel(false); } + private void testLongMultGenerate(final int expectedSolCount, final WordArray wordArray) + throws CryptaModelException { + JULogUtil.configureSilentLoggers(); + config.setLongMultModel(true); + configure(0, false, false); + testGenerate(expectedSolCount, OptionalInt.empty(), wordArray); + configure(0, false, true); + testGenerate(expectedSolCount, OptionalInt.empty(), wordArray); + config.setLongMultModel(false); + } + @Test public void testSendMoreMoney() throws CryptaModelException { final WordArray words = new WordArray(Arrays.asList("send", "more", "money"), null); @@ -191,4 +202,16 @@ public void testMult5() throws CryptaModelException { testMultGenerate(2, words); } + @Test + public void testLongMult1() throws CryptaModelException { + WordArray words = new WordArray(Arrays.asList("who", "is", "hobs", "hawi", "mosis"), null); + testLongMultGenerate(1, words); + } + + @Test + public void testLongMult3() throws CryptaModelException { + WordArray words = new WordArray(Arrays.asList("get", "by", "babe", "beare"), null); + testLongMultGenerate(1, words); + } + } diff --git a/src/test/java/cryptator/GridModelTest.java b/src/test/java/cryptator/GridModelTest.java index f441bec..62bb0f4 100644 --- a/src/test/java/cryptator/GridModelTest.java +++ b/src/test/java/cryptator/GridModelTest.java @@ -17,7 +17,7 @@ public class GridModelTest { - private void testGridModel(int size, int words, int expectedSolutionCount) { + private void testGridModel(final int size, final int words, final int expectedSolutionCount) { CryptaGridModel m = new CryptaGridModel(new Model(), size, words); m.buildModel(); assertEquals(expectedSolutionCount, m.getModel().getSolver().streamSolutions().count()); diff --git a/src/test/java/cryptator/LongMultModelTest.java b/src/test/java/cryptator/LongMultModelTest.java index f357595..fe55caf 100644 --- a/src/test/java/cryptator/LongMultModelTest.java +++ b/src/test/java/cryptator/LongMultModelTest.java @@ -16,11 +16,11 @@ public class LongMultModelTest { - public void testLongMultModel(int expectedSolutionCount, int[] lengths) { + public void testLongMultModel(final int expectedSolutionCount, final int[] lengths) { testLongMultModel(expectedSolutionCount, lengths, lengths); } - public void testLongMultModel(int expectedSolutionCount, int[] lengths, int[] cards) { + public void testLongMultModel(final int expectedSolutionCount, final int[] lengths, final int[] cards) { CryptaLongMultModel m = new CryptaLongMultModel(new Model(), lengths, cards); m.buildModel(); Assert.assertEquals(expectedSolutionCount, m.getSolver().streamSolutions().count()); diff --git a/src/test/java/cryptator/MultTest.java b/src/test/java/cryptator/MultTest.java index 75c3b87..fd42fb8 100644 --- a/src/test/java/cryptator/MultTest.java +++ b/src/test/java/cryptator/MultTest.java @@ -17,11 +17,11 @@ public class MultTest { - private void testMultModel(int expectedSolutionCount, String[] words) { + private void testMultModel(final int expectedSolutionCount, final String[] words) { testMultModel(expectedSolutionCount, words, false); } - private void testMultModel(int expectedSolutionCount, String[] words, boolean isDoublyTrue) { + private void testMultModel(final int expectedSolutionCount, final String[] words, final boolean isDoublyTrue) { final CryptaGenMult m = new CryptaGenMult(words); m.buildModel(); m.postPrecisionConstraints(10); @@ -41,10 +41,11 @@ private void testMultModel(int expectedSolutionCount, String[] words, boolean is } - private void testGenLongMultModel(int expectedSolutionCount, String[] words) { + private void testGenLongMultModel(final int expectedSolutionCount, final String[] words) { final CryptaGenLongMult m = new CryptaGenLongMult(words, 10); m.buildModel(); - + m.postPrecisionConstraints(10); + m.postHeavyConstraints(10); // System.out.println(m.getModel()); // Solution sol = new Solution(m.getModel()); // m.getSolver().streamSolutions().forEach(s -> { @@ -94,7 +95,7 @@ public void testLongMult1() { testGenLongMultModel(22, words); } - @Test // (expected = InvalidSolutionException.class) + @Test public void testLongMult2() { final String[] words = new String[] {"who", "is", "hobs", "hawi", "mosis"}; testGenLongMultModel(18, words); diff --git a/src/test/java/cryptator/ProductTest.java b/src/test/java/cryptator/ProductTest.java index ae5da23..c4a6ba1 100644 --- a/src/test/java/cryptator/ProductTest.java +++ b/src/test/java/cryptator/ProductTest.java @@ -19,11 +19,14 @@ public class ProductTest { private Model model; - private IntVar copper, neon, iron, silver, result; + private IntVar copper; + private IntVar neon; + private IntVar iron; + private IntVar silver; + private IntVar result; /*** - * Tests if choco can solve "copper*neon=iron*silver" via various way of writing - * constraints + * Tests "copper*neon=iron*silver" with different models. */ @Before public void setup() { @@ -89,7 +92,7 @@ public void model2() { } /*** - * Creating a model using the operators function given by IntVar + * Create a model using the operators function given by IntVar. */ @Test @Ignore("It is a choco issue") @@ -101,8 +104,9 @@ public void model3() { } /*** - * Creating a model using the operators function given by IntVar without using - * result as an intermediary + * Create a model using the operators function given by the IntVar. + * + * It does not use result as an intermediary */ @Test @Ignore("It is a choco issue") diff --git a/src/test/java/cryptator/SolverTest.java b/src/test/java/cryptator/SolverTest.java index 02a3d8b..d6027f4 100644 --- a/src/test/java/cryptator/SolverTest.java +++ b/src/test/java/cryptator/SolverTest.java @@ -82,19 +82,25 @@ public void testSAT(final String cryptarithm, final int solutionCount) assertEquals("solution count " + cryptarithm, solutionCount, testSolve(cryptarithm, true)); } - public void testUNSAT(final String cryptarithm) throws CryptaModelException, CryptaSolverException { - assertEquals("solution count " + cryptarithm, 0, testSolve(cryptarithm, false)); + public void testUNSAT(final String... cryptarithms) throws CryptaModelException, CryptaSolverException { + for (String cryptarithm : cryptarithms) { + assertEquals("solution count " + cryptarithm, 0, testSolve(cryptarithm, false)); + } } - public void testUNIQUE(final String cryptarithm) throws CryptaModelException, CryptaSolverException { - testSAT(cryptarithm, 1); + public void testUNIQUE(final String... cryptarithms) throws CryptaModelException, CryptaSolverException { + for (String cryptarithm : cryptarithms) { + testSAT(cryptarithm, 1); + } } - public void testNotUNIQUE(final String cryptarithm) throws CryptaModelException, CryptaSolverException { - assertTrue("solution count " + cryptarithm, testSolve(cryptarithm, true) > 1); + public void testNotUNIQUE(final String... cryptarithms) throws CryptaModelException, CryptaSolverException { + for (String cryptarithm : cryptarithms) { + assertTrue("solution count " + cryptarithm, testSolve(cryptarithm, true) > 1); + } } - public void testResource(String resourcePath) + public void testResource(final String resourcePath) throws CryptaParserException, CryptaModelException, CryptaSolverException { final InputStream in = getClass().getClassLoader().getResourceAsStream(resourcePath); final Scanner s = new Scanner(in); @@ -178,38 +184,11 @@ public void testSendMoreMoney3() throws CryptaParserException, CryptaModelExcept } @Test - public void testSendMoreMoney4() throws CryptaParserException, CryptaModelException, CryptaSolverException { - t.testUNIQUE(" -send -more= -money"); - } - - @Test - public void testSendMoreMoney5() throws CryptaParserException, CryptaModelException, CryptaSolverException { - t.testUNIQUE(" (-send) + (-more) = (-money)"); - } - - @Test - public void testSendMoreMoney6() throws CryptaParserException, CryptaModelException, CryptaSolverException { - t.testUNIQUE("(-(-send)) + (-(-more)) = (-(-money))"); - } - - @Test - public void testSendMoreMoney7() throws CryptaParserException, CryptaModelException, CryptaSolverException { - t.testUNIQUE("(-send) - more = -money"); - } - - @Test - public void testSendMoreMoney8() throws CryptaParserException, CryptaModelException, CryptaSolverException { - t.testUNIQUE("(-(-send)) - (-more) = money"); - } - - @Test - public void testSendMoreMoney9() throws CryptaParserException, CryptaModelException, CryptaSolverException { - t.testUNIQUE("(send+more=money)"); - } - - @Test - public void testSendMoreMoney10() throws CryptaParserException, CryptaModelException, CryptaSolverException { - t.testUNIQUE("(((send+more)=(money)))"); + public void testSendMoreMoneyParenthesis() + throws CryptaParserException, CryptaModelException, CryptaSolverException { + t.testUNIQUE(" -send -more= -money", " (-send) + (-more) = (-money)", "(-(-send)) + (-(-more)) = (-(-money))", + "(-send) - more = -money", "(-(-send)) - (-more) = money", "(send+more=money)", + "(((send+more)=(money)))"); } @Test @@ -510,21 +489,10 @@ public void testSendMoreMoneyList3() throws CryptaParserException, CryptaModelEx } @Test - public void testSendMoreMoneyList4() throws CryptaParserException, CryptaModelException, CryptaSolverException { - t.config.setHornerScheme(true); - t.testUNSAT("send+more=money; s+e=n"); - } - - @Test - public void testSendMoreMoneyList5() throws CryptaParserException, CryptaModelException, CryptaSolverException { - t.config.setHornerScheme(true); - t.testUNSAT("send+more=money;;; s+e=n"); - } - - @Test - public void testSendMoreMoneyList6() throws CryptaParserException, CryptaModelException, CryptaSolverException { + public void testUnsatSendMoreMoneyList() throws CryptaParserException, CryptaModelException, CryptaSolverException { t.config.setHornerScheme(true); - t.testUNSAT("send+more=money; s+e=n;"); + t.testUNSAT("send+more=money; s+e=n", "send+more=money;;; s+e=n", "send+more=money; s+e=n;", + "send+more=money&& s+e=n", "send+more=money&&&&&& s+e=n", "send+more=money&& s+e=n&&"); } // Long multiplication with integer @@ -532,37 +500,19 @@ public void testSendMoreMoneyList6() throws CryptaParserException, CryptaModelEx * SEE * SO = MIMEO MIMEO = EMOO + 10*MESS SEE * O = EMOO SEE * S = MESS */ @Test - public void testEvaluationLongMultiplication1() - throws CryptaParserException, CryptaSolverException, CryptaModelException { - var cryptarithm = "SEE * SO = MIMEO; MIMEO = EMOO + '10'*MESS;SEE * O = EMOO;SEE * S = MESS"; - t.testUNIQUE(cryptarithm); - } + public void testUniqueLongMultiplications() throws CryptaModelException, CryptaSolverException { + String[] cryptarithms = {"SEE * SO = MIMEO; MIMEO = EMOO + '10'*MESS;SEE * O = EMOO;SEE * S = MESS", + "SEE * SO = MIMEO; MIMEO = EMOO + \"10\"*MESS;SEE * O = EMOO;SEE * S = MESS", + "CUT * T = BUST; CUT * I = TNNT; TNNT * '10' + BUST = TENET; TENET = CUT * IT", + "RED * S = ARCS; RED * A = RED; RED * '10' + ARCS = CDTS; CDTS = RED * AS", + "HOW * E = HAIL; HOW * W = PAL; HAIL + PAL * '10' = LHAL; HOW * WE = LHAL", + "SEE * SO = MIMEO&& MIMEO = EMOO + '10'*MESS&&SEE * O = EMOO&&SEE * S = MESS", + "CUT * T = BUST&& CUT * I = TNNT&& TNNT * '10' + BUST = TENET&& TENET = CUT * IT", + "RED * S = ARCS&& RED * A = RED&& RED * '10' + ARCS = CDTS&& CDTS = RED * AS", + "HOW * E = HAIL&& HOW * W = PAL&& HAIL + PAL * '10' = LHAL&& HOW * WE = LHAL" - @Test - public void testEvaluationLongMultiplication1doubleticks() - throws CryptaParserException, CryptaSolverException, CryptaModelException { - var cryptarithm = "SEE * SO = MIMEO; MIMEO = EMOO + \"10\"*MESS;SEE * O = EMOO;SEE * S = MESS"; - t.testUNIQUE(cryptarithm); - } - - /* - * C U T I T --------- B U S T T N N T ----------- T E N E T - */ - @Test - public void testEvaluationLongMultiplication2() - throws CryptaParserException, CryptaSolverException, CryptaModelException { - var cryptarithm = "CUT * T = BUST; CUT * I = TNNT; TNNT * '10' + BUST = TENET; TENET = CUT * IT"; - t.testUNIQUE(cryptarithm); - } - - /* - * R E D A S --------- A R C S R E D --------- C D T S - */ - @Test - public void testEvaluationLongMultiplication3() - throws CryptaParserException, CryptaSolverException, CryptaModelException { - var cryptarithm = "RED * S = ARCS; RED * A = RED; RED * '10' + ARCS = CDTS; CDTS = RED * AS"; - t.testUNIQUE(cryptarithm); + }; + t.testUNIQUE(cryptarithms); } @Test @@ -572,16 +522,6 @@ public void testEvaluationLongMultiplicationFail3() t.testUNSAT(cryptarithm); } - /* - * H O W W E --------- H A I L P A L --------- L H A L - */ - @Test - public void testEvaluationLongMultiplication4() - throws CryptaParserException, CryptaSolverException, CryptaModelException { - var cryptarithm = "HOW * E = HAIL; HOW * W = PAL; HAIL + PAL * '10' = LHAL; HOW * WE = LHAL"; - t.testUNIQUE(cryptarithm); - } - // LONG DIVISION /* * K M ----------- A K A / D A D D Y D Y N A ----------- A R M Y A R K A @@ -670,50 +610,8 @@ public void testSendMoreMoneyList3symbol() t.testSAT("send+more=money&& a+b=c"); } - @Test - public void testSendMoreMoneyList4symbol() - throws CryptaParserException, CryptaModelException, CryptaSolverException { - t.config.setHornerScheme(true); - t.testUNSAT("send+more=money&& s+e=n"); - } - - @Test - public void testSendMoreMoneyList5symbol() - throws CryptaParserException, CryptaModelException, CryptaSolverException { - t.config.setHornerScheme(true); - t.testUNSAT("send+more=money&&&&&& s+e=n"); - } - - @Test - public void testSendMoreMoneyList6symbol() - throws CryptaParserException, CryptaModelException, CryptaSolverException { - t.config.setHornerScheme(true); - t.testUNSAT("send+more=money&& s+e=n&&"); - } - // Long multiplication with integer - @Test - public void testEvaluationLongMultiplication1symbol() - throws CryptaParserException, CryptaSolverException, CryptaModelException { - var cryptarithm = "SEE * SO = MIMEO&& MIMEO = EMOO + '10'*MESS&&SEE * O = EMOO&&SEE * S = MESS"; - t.testUNIQUE(cryptarithm); - } - - @Test - public void testEvaluationLongMultiplication2symbol() - throws CryptaParserException, CryptaSolverException, CryptaModelException { - var cryptarithm = "CUT * T = BUST&& CUT * I = TNNT&& TNNT * '10' + BUST = TENET&& TENET = CUT * IT"; - t.testUNIQUE(cryptarithm); - } - - @Test - public void testEvaluationLongMultiplication3symbol() - throws CryptaParserException, CryptaSolverException, CryptaModelException { - var cryptarithm = "RED * S = ARCS&& RED * A = RED&& RED * '10' + ARCS = CDTS&& CDTS = RED * AS"; - t.testUNIQUE(cryptarithm); - } - @Test public void testEvaluationLongMultiplicationFail3symbol() throws CryptaParserException, CryptaSolverException, CryptaModelException { @@ -721,13 +619,6 @@ public void testEvaluationLongMultiplicationFail3symbol() t.testUNSAT(cryptarithm); } - @Test - public void testEvaluationLongMultiplication4symbol() - throws CryptaParserException, CryptaSolverException, CryptaModelException { - var cryptarithm = "HOW * E = HAIL&& HOW * W = PAL&& HAIL + PAL * '10' = LHAL&& HOW * WE = LHAL"; - t.testUNIQUE(cryptarithm); - } - // LONG DIVISION @Test