diff --git a/src/main/benchmarks/README.org b/src/main/benchmarks/README.org index 4f152fd..317b4b8 100644 --- a/src/main/benchmarks/README.org +++ b/src/main/benchmarks/README.org @@ -44,7 +44,7 @@ # Read the csf files with a list of country codes, and languages codes while IFS="," read ctry lang ; do INSTANCE="instances/$lang-$1-$2.dat" - echo -ctry $ctry -lang $lang $1 $2 > $INSTANCE + echo --ctry $ctry --lang $lang $1 $2 > $INSTANCE done < <(grep -v "^#\|^$" $LANGUAGES) } #+END_SRC @@ -77,17 +77,13 @@ exit $? *** Best algorithm #+BEGIN_SRC sh - echo -v normal -d TRUE -lightM FALSE -lightP FALSE > algorithms/FF.dat + echo -v normal --dry-run > algorithms/HEAVY.dat #+END_SRC *** All algorithms #+BEGIN_SRC sh - echo -v normal -d TRUE -lightM FALSE -lightP FALSE > algorithms/FF.dat - echo -v normal -d TRUE -lightM FALSE -lightP TRUE > algorithms/FT.dat - echo -v normal -d TRUE -lightM TRUE -lightP FALSE -newHeavyP FALSE > algorithms/TF.dat - echo -v normal -d TRUE -lightM TRUE -lightP FALSE -newHeavyP TRUE > algorithms/TF_NEW.dat - echo -v normal -d TRUE -lightM TRUE -lightP TRUE > algorithms/TT.dat + echo -v normal --dry-run --light > algorithms/LIGHT.dat #+END_SRC @@ -139,7 +135,7 @@ The word lists ~colors.txt~ and ~monsters.txt~ takes the most time by far (aroun #+BEGIN_SRC sh function generateLong() { - echo -minop $1 -maxop $1 words/$2 > instances/`basename -s .txt $2`-$1.dat + echo --min $1 --max $1 words/$2 > instances/`basename -s .txt $2`-$1.dat } # generateLong 9 9 monsters.txt @@ -169,7 +165,7 @@ The word lists ~colors.txt~ and ~monsters.txt~ takes the most time by far (aroun function generateMaxLong() { find samples/ -type f -print | while read PATHNAME ; do INSTANCE="instances/"`basename -s .txt $PATHNAME`-$1-$2.dat - echo -minop $1 -maxop $2 $PATHNAME > $INSTANCE + echo --min $1 --max $2 $PATHNAME > $INSTANCE done } @@ -218,7 +214,7 @@ The word lists ~colors.txt~ and ~monsters.txt~ takes the most time by far (aroun function generateCrossword() { find samples/ -type f -print | while read PATHNAME ; do INSTANCE="instances/"`basename -s .txt $PATHNAME`-$1.dat - echo -grid $1 $PATHNAME > $INSTANCE + echo --generate CROSS --cross $1 $PATHNAME > $INSTANCE done } @@ -230,18 +226,18 @@ The word lists ~colors.txt~ and ~monsters.txt~ takes the most time by far (aroun *** Addition #+BEGIN_SRC sh - echo -v quiet -d FALSE -l TRUE -lightM FALSE -lightP FALSE > algorithms/ALL.dat + echo -v quiet --generate ADD -s BIGNUM > algorithms/ALL.dat #+END_SRC *** Multiplication #+BEGIN_SRC sh - echo -v quiet -d FALSE -mult TRUE -multUnique TRUE -lightP FALSE > algorithms/ALL.dat + echo -v quiet --generate MUL --right UNIQUE > algorithms/ALL.dat #+END_SRC *** Long Multiplication #+BEGIN_SRC sh - echo -v quiet -d FALSE -longMult TRUE -lightM FALSE -lightP FALSE > algorithms/ALL.dat + echo -v quiet --generate LMUL > algorithms/ALL.dat #+END_SRC * Solve @@ -301,14 +297,10 @@ Do not remove the content of the directory! ** Configure the algorithm #+BEGIN_SRC sh - ARGS=" -v verbose -t 100" - echo $ARGS -l TRUE > algorithms/BIGNUM.dat - echo $ARGS -l FALSE -h FALSE > algorithms/SCALAR.dat - echo $ARGS -l FALSE -h TRUE > algorithms/HORNER.dat - -# echo $ARGS -l TRUE -search 1 > algorithms/BIGNUM-1.dat -# echo $ARGS -l FALSE -h FALSE -search 1 > algorithms/SCALAR-1.dat -# echo $ARGS -l FALSE -h TRUE -search 1 > algorithms/HORNER-1.dat + ARGS=" -v verbose" + echo $ARGS -s BIGNUM > algorithms/BIGNUM.dat + echo $ARGS -s SCALAR > algorithms/SCALAR.dat + echo $ARGS -s SCALAR --horner > algorithms/HORNER.dat #+END_SRC diff --git a/src/main/java/cryptator/Cryptagen.java b/src/main/java/cryptator/Cryptagen.java index bc7a867..fd28bf4 100644 --- a/src/main/java/cryptator/Cryptagen.java +++ b/src/main/java/cryptator/Cryptagen.java @@ -12,6 +12,7 @@ import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.List; +import java.util.OptionalInt; import java.util.Scanner; import java.util.logging.Level; import java.util.logging.Logger; @@ -37,8 +38,12 @@ public static void main(final String[] args) { } public static int doMain(final String[] args) { - CryptagenOptionsParser optparser = new CryptagenOptionsParser(); - if (optparser.parseOptions(args)) { + try { + CryptagenOptionsParser optparser = new CryptagenOptionsParser(); + final OptionalInt parserExitCode = optparser.parseOptions(args); + if (parserExitCode.isPresent()) { + return parserExitCode.getAsInt(); + } final CryptagenConfig config = optparser.getConfig(); final WordArray words = buildWords(config.getArguments(), config); if (words != null) { @@ -46,9 +51,10 @@ public static int doMain(final String[] args) { } else { LOGGER.log(Level.WARNING, "Empty word list."); } + return -1; + } finally { + JULogUtil.flushLogs(); } - JULogUtil.flushLogs(); - return -1; } private static List readWords(final String filename) { diff --git a/src/main/java/cryptator/Cryptamancer.java b/src/main/java/cryptator/Cryptamancer.java index 998844f..ec41f06 100644 --- a/src/main/java/cryptator/Cryptamancer.java +++ b/src/main/java/cryptator/Cryptamancer.java @@ -8,6 +8,7 @@ */ package cryptator; +import java.util.OptionalInt; import java.util.Scanner; import java.util.logging.Level; import java.util.logging.Logger; @@ -98,7 +99,8 @@ public static void main(final String[] args) throws Exception { JULogUtil.configureDefaultLoggers(); CryptamancerOptionsParser optparser = new CryptamancerOptionsParser(); - if (!optparser.parseOptions(args)) { + final OptionalInt parserExitCode = optparser.parseOptions(args); + if (parserExitCode.isPresent()) { return; } final CryptaLogConfig config = optparser.getConfig(); diff --git a/src/main/java/cryptator/Cryptator.java b/src/main/java/cryptator/Cryptator.java index 9e5f9f5..48e2fe5 100644 --- a/src/main/java/cryptator/Cryptator.java +++ b/src/main/java/cryptator/Cryptator.java @@ -8,6 +8,7 @@ */ package cryptator; +import java.util.OptionalInt; import java.util.logging.Level; import java.util.logging.Logger; @@ -37,22 +38,26 @@ public static void main(final String[] args) { } public static int doMain(final String[] args) { - CryptatorOptionsParser optparser = new CryptatorOptionsParser(); - if (!optparser.parseOptions(args)) { - return -1; - } - final CryptatorConfig config = optparser.getConfig(); + try { + final CryptatorOptionsParser optparser = new CryptatorOptionsParser(); + final OptionalInt parserExitCode = optparser.parseOptions(args); + if (parserExitCode.isPresent()) { + return parserExitCode.getAsInt(); + } + final CryptatorConfig config = optparser.getConfig(); - final ICryptaSolver solver = buildSolver(config); + final ICryptaSolver solver = buildSolver(config); - final CryptaParserWrapper parser = new CryptaParserWrapper(); + final CryptaParserWrapper parser = new CryptaParserWrapper(); - int exitCode = 0; - for (String cryptarithm : config.getArguments()) { - exitCode += solve(cryptarithm, parser, solver, config); + int exitCode = 0; + for (String cryptarithm : config.getArguments()) { + exitCode += solve(cryptarithm, parser, solver, config); + } + return exitCode; + } finally { + JULogUtil.flushLogs(); } - JULogUtil.flushLogs(); - return exitCode; } private static class CryptatorOptionsParser extends OptionsParserWithLog { diff --git a/src/main/java/cryptator/cmd/AbstractOptionsParser.java b/src/main/java/cryptator/cmd/AbstractOptionsParser.java index 3156e20..4bba789 100644 --- a/src/main/java/cryptator/cmd/AbstractOptionsParser.java +++ b/src/main/java/cryptator/cmd/AbstractOptionsParser.java @@ -8,19 +8,31 @@ */ package cryptator.cmd; +import java.io.BufferedReader; import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.InputStreamReader; import java.io.OutputStreamWriter; +import java.util.Comparator; +import java.util.OptionalInt; import java.util.logging.Level; import java.util.logging.Logger; import org.kohsuke.args4j.CmdLineException; import org.kohsuke.args4j.CmdLineParser; +import org.kohsuke.args4j.OptionDef; import org.kohsuke.args4j.OptionHandlerFilter; +import org.kohsuke.args4j.ParserProperties; +import org.kohsuke.args4j.spi.OptionHandler; import cryptator.config.CryptaConfig; public abstract class AbstractOptionsParser { + private static final OptionalInt ERROR_CODE = OptionalInt.of(64); + private static final OptionalInt EXIT_CODE = OptionalInt.of(0); + private static final OptionalInt EMPTY_CODE = OptionalInt.empty(); + private final Class mainClass; protected final E config; @@ -53,9 +65,6 @@ protected boolean checkConfiguration() { getLogger().severe("The Arithmetic base must be greater than 1."); return false; } - if ((config.getRelaxMinDigitOccurence() < 0) || (config.getRelaxMaxDigitOccurence() < 0)) { - getLogger().severe("Min/Max digit occurence is ignored because it cannot be negative."); - } return true; } @@ -67,17 +76,28 @@ public final E getConfig() { return config; } - public final boolean parseOptions(final String[] args) { - final CmdLineParser parser = new CmdLineParser(config); + public enum CmdLineParserStatus { + ERROR, EXIT, CONTINUE + } + + public final OptionalInt parseOptions(final String[] args) { + final ParserProperties properties = ParserProperties.defaults().withOptionSorter(new CryptaOptionSorter()); + final CmdLineParser parser = new CmdLineParser(config, properties); try { // parse the arguments. parser.parseArgument(args); configureLoggers(); - if (checkConfiguration()) { + if (config.isDisplayHelp()) { + logManual(parser); + return EXIT_CODE; + } else if (config.getArguments().isEmpty()) { + logHelp(parser); + return EXIT_CODE; + } else if (checkConfiguration()) { if (checkArguments()) { getLogger().log(Level.CONFIG, "Parse options [OK]"); - getLogger().log(Level.FINE, "Configuration:\n{0}", config); - return true; + getLogger().log(Level.FINE, "Parse configuration [OK]\n{0}", config); + return EMPTY_CODE; } else { getLogger().log(Level.SEVERE, "Parse arguments [FAIL]\n{0}", config.getArguments()); } @@ -88,12 +108,8 @@ public final boolean parseOptions(final String[] args) { } catch (CmdLineException e) { getLogger().log(Level.SEVERE, "Parse options [FAIL]", e); } - - if (getLogger().isLoggable(Level.INFO)) { - getLogger().info(buildHelpMessage(parser, OptionHandlerFilter.PUBLIC)); - } - return false; - + logHelp(parser); + return ERROR_CODE; } private String printUsage(final CmdLineParser parser, final OptionHandlerFilter filter) { @@ -102,23 +118,99 @@ private String printUsage(final CmdLineParser parser, final OptionHandlerFilter return os.toString(); } - private String printExample(final String options) { - return "java " + getCommandName() + " " + options + " " + getArgumentName(); + private void appendExample(final StringBuilder b, final CmdLineParser parser, final OptionHandlerFilter filter) { + b.append("java ").append(getCommandName()).append(' '); + final String opts = parser.printExample(filter); + if (opts.length() > 0) + b.append(opts).append(" "); + if (filter == OptionHandlerFilter.REQUIRED) { + b.append("[options...] "); + } + b.append(getArgumentName()).append('\n'); + } + + private void appendHeader(final StringBuilder b) { + b.append("Help of ").append(getCommandName()).append("\n"); + } + + private void appendMessage(final StringBuilder b) { + appendResource(b, "help/" + getCommandName() + ".txt"); + } + + private void appendResource(final StringBuilder b, final String resourceName) { + final InputStream in = mainClass.getClassLoader().getResourceAsStream(resourceName); + final BufferedReader reader = new BufferedReader(new InputStreamReader(in)); + reader.lines().forEach(x -> b.append(x).append('\n')); + } + + private void appendFooter(final StringBuilder b) { + b.append("\nReport bugs: \n"); + b.append("Cryptator home page: \n"); + } + + private void appendShortFooter(final StringBuilder b) { + b.append("\nTry the option '--help' for more information.\n"); + } + + private void logHelp(final CmdLineParser parser) { + if (getLogger().isLoggable(Level.INFO)) { + final StringBuilder b = new StringBuilder(); + appendHeader(b); + appendExample(b, parser, OptionHandlerFilter.REQUIRED); + b.append("\n"); + appendMessage(b); + b.append("\nPublic options:\n"); + b.append(printUsage(parser, OptionHandlerFilter.PUBLIC)); + appendShortFooter(b); + getLogger().info(b.toString()); + } } - private String printExample(final CmdLineParser parser, final OptionHandlerFilter filter) { - return printExample(parser.printExample(filter)); + private void logManual(final CmdLineParser parser) { + if (getLogger().isLoggable(Level.INFO)) { + final StringBuilder b = new StringBuilder(); + appendHeader(b); + b.append("SYNOPSIS\n"); + appendExample(b, parser, OptionHandlerFilter.REQUIRED); + appendExample(b, parser, OptionHandlerFilter.PUBLIC); + appendExample(b, parser, OptionHandlerFilter.ALL); + b.append("\nDESCRIPTION\n"); + appendMessage(b); + b.append("\nOPTIONS\n"); + b.append(printUsage(parser, OptionHandlerFilter.ALL)); + b.append("\nEXAMPLE\n"); + appendFooter(b); + getLogger().info(b.toString()); + } + } - private String buildHelpMessage(final CmdLineParser parser, final OptionHandlerFilter filter) { - StringBuilder b = new StringBuilder(); - b.append(" Help message:\n"); - b.append(printExample("[options...]")).append("\n"); - b.append(printUsage(parser, filter)); - b.append("\nExamples:"); - b.append("\n").append(printExample(parser, OptionHandlerFilter.REQUIRED)); - b.append("\n").append(printExample(parser, filter)); - return b.toString(); + private static class CryptaOptionSorter implements Comparator { + + private final boolean longOption(OptionDef x) { + return x.toString().charAt(1) == '-'; + } + + @Override + public int compare(OptionHandler arg0, OptionHandler arg1) { + final OptionDef x = arg0.option; + final OptionDef y = arg1.option; + // Required option + int comp = Boolean.compare(x.required(), y.required()); + if (comp == 0) { + // Hidden options + comp = Boolean.compare(x.hidden(), y.hidden()); + if (comp == 0) { + // Long options + comp = Boolean.compare(longOption(x), longOption(y)); + if (comp == 0) { + // Option strings + comp = x.toString().compareTo(y.toString()); + } + } + } + return comp; + } } } diff --git a/src/main/java/cryptator/config/CryptaCmdConfig.java b/src/main/java/cryptator/config/CryptaCmdConfig.java index 3337ad8..19e8873 100644 --- a/src/main/java/cryptator/config/CryptaCmdConfig.java +++ b/src/main/java/cryptator/config/CryptaCmdConfig.java @@ -9,24 +9,32 @@ package cryptator.config; import org.kohsuke.args4j.Option; -import org.kohsuke.args4j.spi.ExplicitBooleanOptionHandler; public class CryptaCmdConfig extends CryptaLogConfig { - @Option(name = "-c", handler = ExplicitBooleanOptionHandler.class, usage = "check solutions by evaluation") + public enum SolverType { + SCALAR, BIGNUM, CRYPT, ADAPT, ADPATC + } + + @Option(name = "-s", aliases = {"--solver"}, usage = "Select the type of solver.") + private SolverType solverType = SolverType.ADAPT; + + @Option(name = "--check", usage = "Check solutions by evaluation.") private boolean checkSolution; - @Option(name = "-g", handler = ExplicitBooleanOptionHandler.class, usage = "export solutions to graphviz format") + @Option(name = "--graphviz", usage = "Export solutions to graphviz format.") private boolean exportGraphiz; - @Option(name = "-l", handler = ExplicitBooleanOptionHandler.class, usage = "use the bignum model (only +, *, and =)") - private boolean useBigNum; - - @Option(name = "--crypt-command", usage = "the crypt command") + @Option(name = "--crypt", hidden = true, usage = "Set the crypt command.") private String cryptCommand = "crypt"; - @Option(name = "-crypt", handler = ExplicitBooleanOptionHandler.class, usage = "use the crypt solver (only + and =)") - private boolean useCrypt; + public final SolverType getSolverType() { + return solverType; + } + + public final void setSolverType(SolverType solverType) { + this.solverType = solverType; + } public final boolean isExportGraphiz() { return exportGraphiz; @@ -36,20 +44,19 @@ public final boolean isCheckSolution() { return checkSolution; } + @Deprecated(forRemoval = true) public final boolean useBignum() { - return useBigNum; + return solverType == SolverType.BIGNUM; } + @Deprecated(forRemoval = true) public final void setUseBigNum(final boolean useBigNum) { - this.useBigNum = useBigNum; + this.solverType = useBigNum ? SolverType.BIGNUM : SolverType.SCALAR; } + @Deprecated(forRemoval = true) public final boolean useCrypt() { - return useCrypt; - } - - public final void setUseCrypt(final boolean useCrypt) { - this.useCrypt = useCrypt; + return solverType == SolverType.CRYPT; } public final String getCryptCommand() { @@ -58,6 +65,6 @@ public final String getCryptCommand() { @Override public String toString() { - return super.toString() + "\nc BIGNUM " + useBignum(); + return super.toString() + "\nc SOLVER " + getSolverType(); } } diff --git a/src/main/java/cryptator/config/CryptaConfig.java b/src/main/java/cryptator/config/CryptaConfig.java index 0a0506f..a68fe78 100644 --- a/src/main/java/cryptator/config/CryptaConfig.java +++ b/src/main/java/cryptator/config/CryptaConfig.java @@ -15,7 +15,6 @@ import org.kohsuke.args4j.Argument; import org.kohsuke.args4j.Option; -import org.kohsuke.args4j.spi.ExplicitBooleanOptionHandler; /** * A bean object that stores the common configuration. This is designed for @@ -23,22 +22,19 @@ */ public class CryptaConfig { - @Option(name = "-b", usage = "Base (or radix) of the positional numeral system used for the cryptarithm (> 1)") + @Option(name = "-b", aliases = {"--base", "--radix"}, usage = "Base of the positional numeral system (> 1).") private int arithmeticBase = BigInteger.TEN.intValue(); - @Option(name = "-z", handler = ExplicitBooleanOptionHandler.class, usage = "allow leading zeros in the cryptarithm solution") + @Option(name = "-h", aliases = {"--help"}, usage = "Output a usage message and exit.") + private boolean displayHelp; + + @Option(name = "--zeros", hidden = true, usage = "Allow leading zeros in the cryptarithm solution") private boolean allowLeadingZeros; - @Option(name = "-h", handler = ExplicitBooleanOptionHandler.class, usage = "use the horner scheme to model the numbers repsented by the cryptarithm words") + @Option(name = "--horner", hidden = true, usage = "Use the horner scheme to model finite precision words.") private boolean hornerScheme; - @Option(name = "-min", usage = "relaxation of the minimum number of occurences of a digit (>=0)") - private int relaxMinDigitOccurence = 0; - - @Option(name = "-max", usage = "relaxation of the maximum number of occurences of a digit (>=0)") - private int relaxMaxDigitOccurence = 0; - - @Option(name = "-search", usage = "identifier of the search strategy") + @Option(name = "--search", hidden = true, usage = "Set the search strategy.") private int searchStrategy = 0; /** @@ -71,22 +67,6 @@ public final void setHornerScheme(final boolean useHornerScheme) { this.hornerScheme = useHornerScheme; } - public final int getRelaxMinDigitOccurence() { - return relaxMinDigitOccurence; - } - - public final void setRelaxMinDigitOccurence(final int relaxMinDigitOccurence) { - this.relaxMinDigitOccurence = relaxMinDigitOccurence; - } - - public final int getRelaxMaxDigitOccurence() { - return relaxMaxDigitOccurence; - } - - public final void setRelaxMaxDigitOccurence(final int relaxMaxDigitOccurence) { - this.relaxMaxDigitOccurence = relaxMaxDigitOccurence; - } - public final int getSearchStrategy() { return searchStrategy; } @@ -95,22 +75,16 @@ public final void setSearchStrategy(final int searchStrategy) { this.searchStrategy = searchStrategy; } + public final boolean isDisplayHelp() { + return displayHelp; + } + public final int getMinDigitOccurence(final int n) { - int minOcc = n / getArithmeticBase(); - final int deltaMin = getRelaxMinDigitOccurence(); - if (deltaMin > 0) { - minOcc = Math.max(0, minOcc - deltaMin); - } - return minOcc; + return n / getArithmeticBase(); } public final int getMaxDigitOccurence(final int n) { - int maxOcc = ((n + getArithmeticBase()) - 1) / getArithmeticBase(); - final int deltaMax = getRelaxMaxDigitOccurence(); - if (deltaMax > 0) { - maxOcc = Math.min(n, maxOcc + deltaMax); - } - return maxOcc; + return ((n + getArithmeticBase()) - 1) / getArithmeticBase(); } public final List getArguments() { @@ -120,8 +94,7 @@ public final List getArguments() { @Override public String toString() { return "c BASE " + arithmeticBase + "\nc ALLOW_LEADING_0 " + allowLeadingZeros + "\nc HORNER_SCHEME " - + hornerScheme + "\nc RELAX_MIN_DIGIT_OCC " + relaxMinDigitOccurence + "\nc RELAX_MAX_DIGIT_OCC " - + relaxMaxDigitOccurence; + + hornerScheme; } } diff --git a/src/main/java/cryptator/config/CryptaLogConfig.java b/src/main/java/cryptator/config/CryptaLogConfig.java index 978c603..7358200 100644 --- a/src/main/java/cryptator/config/CryptaLogConfig.java +++ b/src/main/java/cryptator/config/CryptaLogConfig.java @@ -14,7 +14,7 @@ public class CryptaLogConfig extends CryptaConfig { - @Option(name = "-v", usage = "increase the verbosity of the program") + @Option(name = "-v", aliases = {"--verbose"}, usage = "Increase the verbosity of the program.") private Verbosity verbosity = Verbosity.NORMAL; public final Verbosity getVerbosity() { diff --git a/src/main/java/cryptator/config/CryptagenConfig.java b/src/main/java/cryptator/config/CryptagenConfig.java index c376d08..d4e3d5f 100644 --- a/src/main/java/cryptator/config/CryptagenConfig.java +++ b/src/main/java/cryptator/config/CryptagenConfig.java @@ -9,42 +9,62 @@ package cryptator.config; import org.kohsuke.args4j.Option; -import org.kohsuke.args4j.spi.ExplicitBooleanOptionHandler; public class CryptagenConfig extends CryptaCmdConfig { - @Option(name = "-d", handler = ExplicitBooleanOptionHandler.class, usage = "dry run (generate but do not solve candidate cryptarithms)") - private boolean dryRun; + public enum GenerateType { + ADD, MUL, LMUL, CROSS, + } - @Option(name = "-grid", usage = "grid size for crossword cryptarithm)") - private int gridSize = 0; + @Option(name = "-g", aliases = {"--generate"}, usage = "Select the type of generator.") + private GenerateType generateType; + + public enum RightMemberType { + FREE, UNIQUE, FIXED + } - @Option(name = "-mult", handler = ExplicitBooleanOptionHandler.class, usage = "generate multiplication cryptarithms") - private boolean multModel = false; + @Option(name = "--right", usage = "Select the right member type.") + private RightMemberType rightMemberType = RightMemberType.UNIQUE; - @Option(name = "-longMult", handler = ExplicitBooleanOptionHandler.class, usage = "generate long multiplication cryptarithms") - private boolean longMultModel = false; + @Option(name = "-d", aliases = {"--dry-run"}, usage = "Dry run (generate but do not solve candidate cryptarithms).") + private boolean dryRun; + + @Option(name = "--cross", usage = "Set the grid size of a crossword.") + private int gridSize = 0; - @Option(name = "-ctry", usage = "country code for doubly true cryptarithms)") + @Option(name = "--ctry", usage = "Country code for doubly true cryptarithms.") private String countryCode = "EN"; - @Option(name = "-lang", usage = "language code for doubly true cryptarithms)") + @Option(name = "--lang", usage = "Language code for doubly true cryptarithms.") private String langCode = "en"; - @Option(name = "-t", usage = "number of threads (experimental)") - private int nthreads = 1; - - @Option(name = "-minop", usage = "minimum number of left operands") + @Option(name = "--min", usage = "Minimum number of left operands.") private int minLeftOperands = 2; - @Option(name = "-maxop", usage = "maximum number of left operands") + @Option(name = "--max", usage = "Maximum number of left operands.") private int maxLeftOperands = -1; - @Option(name = "-lightP", handler = ExplicitBooleanOptionHandler.class, usage = "use weak consistency") - private boolean lightPropagation; + @Option(name = "--light", hidden = true, usage = "Use a light CP model.") + private boolean lightModel; - @Option(name = "-multUnique", handler = ExplicitBooleanOptionHandler.class, usage = "use weak consistency") - private boolean multUnique; + @Option(name = "--threads", hidden = true, usage = "Number of threads (experimental).") + private int nthreads = 1; + + public final GenerateType getGenerateType() { + return generateType; + } + + public final void setGenerateType(GenerateType generateType) { + this.generateType = generateType; + } + + public final RightMemberType getRightMemberType() { + return rightMemberType; + } + + public final void setRightMemberType(RightMemberType rightMemberType) { + this.rightMemberType = rightMemberType; + } public final boolean isDryRun() { return dryRun; @@ -78,38 +98,43 @@ public final int getMaxLeftOperands() { return maxLeftOperands; } - public final boolean isLightPropagation() { - return lightPropagation; + public final boolean isLightModel() { + return lightModel; } + @Deprecated(forRemoval = true) public final boolean isMultUnique() { - return multUnique; + return rightMemberType == RightMemberType.UNIQUE; } - public final void setLightPropagation(final boolean lightPropagation) { - this.lightPropagation = lightPropagation; + public final void setLightModel(final boolean lightModel) { + this.lightModel = lightModel; } + @Deprecated(forRemoval = true) public final boolean isMultModel() { - return multModel; + return generateType == GenerateType.MUL; } + @Deprecated(forRemoval = true) public final void setMultModel(final boolean multModel) { - this.multModel = multModel; + this.generateType = multModel ? GenerateType.MUL : GenerateType.ADD; } + @Deprecated(forRemoval = true) public final boolean isLongMultModel() { - return longMultModel; + return generateType == GenerateType.LMUL; } + @Deprecated(forRemoval = true) public final void setLongMultModel(final boolean longMultModel) { - this.longMultModel = longMultModel; + this.generateType = longMultModel ? GenerateType.LMUL : GenerateType.ADD; } @Override public String toString() { - return super.toString() + "\nc LANG " + langCode + "\nc THREADS " + nthreads + "\nc LIGHT_PROPAG " - + lightPropagation; + return super.toString() + "\nc GENERATE " + generateType + "\nc LANG " + langCode + "\nc THREADS " + nthreads + + "\nc LIGHT_PROPAG " + lightModel; } } diff --git a/src/main/java/cryptator/config/CryptatorConfig.java b/src/main/java/cryptator/config/CryptatorConfig.java index 1946356..90eab08 100644 --- a/src/main/java/cryptator/config/CryptatorConfig.java +++ b/src/main/java/cryptator/config/CryptatorConfig.java @@ -12,10 +12,10 @@ public class CryptatorConfig extends CryptaCmdConfig { - @Option(name = "-s", usage = "limit the number of solutions returned by the solver") + @Option(name = "--solution", usage = "Limit the number of solutions returned by the solver.") private int solutionLimit; - @Option(name = "-t", usage = "limit the time taken by a solver (in seconds)") + @Option(name = "--time", usage = "Limit the time taken by the solver (in seconds)") private int timeLimit; public final int getSolutionLimit() { diff --git a/src/main/java/cryptator/gen/CryptaListGenerator.java b/src/main/java/cryptator/gen/CryptaListGenerator.java index 1ec2948..746191a 100644 --- a/src/main/java/cryptator/gen/CryptaListGenerator.java +++ b/src/main/java/cryptator/gen/CryptaListGenerator.java @@ -113,7 +113,7 @@ private ICryptaGenSolver buildGenSolver() { gen.buildModel(); gen.postWordCountConstraints(Math.max(config.getMinLeftOperands(), 2) + 1, config.getMaxLeftOperands() + 1); gen.postMaxSymbolCountConstraint(config.getArithmeticBase()); - if (!config.isLightPropagation()) { + if (!config.isLightModel()) { gen.postHeavyConstraints(config.getArithmeticBase()); } if (words.hasRightMember()) { diff --git a/src/main/resources/help/cryptator.Cryptagen.txt b/src/main/resources/help/cryptator.Cryptagen.txt new file mode 100644 index 0000000..261fea8 --- /dev/null +++ b/src/main/resources/help/cryptator.Cryptagen.txt @@ -0,0 +1 @@ +Help message in a file \ No newline at end of file diff --git a/src/main/resources/help/cryptator.Cryptamancer.txt b/src/main/resources/help/cryptator.Cryptamancer.txt new file mode 100644 index 0000000..261fea8 --- /dev/null +++ b/src/main/resources/help/cryptator.Cryptamancer.txt @@ -0,0 +1 @@ +Help message in a file \ No newline at end of file diff --git a/src/main/resources/help/cryptator.Cryptator.txt b/src/main/resources/help/cryptator.Cryptator.txt new file mode 100644 index 0000000..261fea8 --- /dev/null +++ b/src/main/resources/help/cryptator.Cryptator.txt @@ -0,0 +1 @@ +Help message in a file \ No newline at end of file diff --git a/src/main/shell/demo-generate.sh b/src/main/shell/demo-generate.sh index e8fa6ef..7bd44a4 100755 --- a/src/main/shell/demo-generate.sh +++ b/src/main/shell/demo-generate.sh @@ -15,7 +15,7 @@ DIR="../words" ## Execute the command. ## Filter the output: print only the cryptarithm. function solve() { - java -cp $JAR cryptator.Cryptagen -c TRUE -v quiet $* | sed -n 's/\(.*+.*=.*\)/ - \1/p' + java -cp $JAR cryptator.Cryptagen -v quiet $* | sed -n 's/\(.*+.*=.*\)/ - \1/p' } echo "# Search cryptarithms with a UNIQUE solution" @@ -36,7 +36,7 @@ echo -e "\n## Generate from a list with a fixed right member\n" echo "- Planets" solve $DIR/planets.txt planets echo "- Greek alphabet" -solve -minop 6 $DIR/alpha.txt greeks +solve --min 6 $DIR/alpha.txt greeks ###### echo -e "\n## Generate doubly true cryptarithms\n\nEach number in [0, 100] appears at most once.\n" diff --git a/src/test/java/cryptator/CommandTest.java b/src/test/java/cryptator/CommandTest.java index ae0b601..c6479aa 100644 --- a/src/test/java/cryptator/CommandTest.java +++ b/src/test/java/cryptator/CommandTest.java @@ -37,8 +37,8 @@ public void testCryptatorException2() throws FileNotFoundException { @Test public void testCryptator() throws FileNotFoundException { - String[] args = {"-b", "10", "-c", "TRUE", "-g", "FALSE", "-h", "TRUE", "-l", "FALSE", "-max", "0", "-min", "0", - "-s", "1", "-t", "5", "-v", "SILENT", "-z", "FALSE", "www+imac=crash "}; + String[] args = {"-b", "10", "--check", "--horner", "-s", "SCALAR", "--solution", "1", "--time", "5", "-v", + "SILENT", "--zeros", "www+imac=crash "}; assertEquals(0, Cryptator.doMain(args)); } @@ -50,13 +50,13 @@ public void testCryptagenException() throws FileNotFoundException { @Test public void testCryptagen() throws FileNotFoundException { - String[] args = {"-c", "FALSE", "-g", "FALSE", "-v", "SILENT", "www", "imac", "crash"}; + String[] args = {"-v", "SILENT", "www", "imac", "crash"}; assertEquals(0, Cryptagen.doMain(args)); } @Test public void testCryptagenDoublyTrue() throws FileNotFoundException { - String[] args = {"-c", "FALSE", "-g", "FALSE", "-v", "SILENT", "3", "4"}; + String[] args = {"-v", "SILENT", "3", "4"}; assertEquals(0, Cryptagen.doMain(args)); } diff --git a/src/test/java/cryptator/GenerateTest.java b/src/test/java/cryptator/GenerateTest.java index 3b3c7bc..ce5cd92 100644 --- a/src/test/java/cryptator/GenerateTest.java +++ b/src/test/java/cryptator/GenerateTest.java @@ -20,6 +20,7 @@ import cryptator.cmd.CryptaBiConsumer; import cryptator.cmd.WordArray; import cryptator.config.CryptagenConfig; +import cryptator.config.CryptagenConfig.RightMemberType; import cryptator.gen.CryptaListGenerator; import cryptator.solver.CryptaModelException; @@ -34,7 +35,7 @@ public static void configureTestLoggers() { private void configure(final int gridSize, final boolean lightPropagation) { config.setGridSize(gridSize); - config.setLightPropagation(lightPropagation); + config.setLightModel(lightPropagation); } private void testGenerate(final int expectedSolCount, final OptionalInt expectedCandCount, @@ -72,12 +73,14 @@ private void testHeavyGenerate(final int expectedSolCount, final OptionalInt exp private void testMultGenerateLH(final int expectedSolCount, final WordArray wordArray) throws CryptaModelException { JULogUtil.configureSilentLoggers(); + config.setRightMemberType(RightMemberType.FREE); config.setMultModel(true); configure(0, false); testGenerate(expectedSolCount, OptionalInt.empty(), wordArray); configure(0, true); testGenerate(expectedSolCount, OptionalInt.empty(), wordArray); config.setMultModel(false); + config.setRightMemberType(RightMemberType.UNIQUE); } private void testMultGenerate(final int expectedSolCount, final WordArray wordArray) throws CryptaModelException { diff --git a/src/test/java/cryptator/SolverTest.java b/src/test/java/cryptator/SolverTest.java index e54bd05..58a7ace 100644 --- a/src/test/java/cryptator/SolverTest.java +++ b/src/test/java/cryptator/SolverTest.java @@ -8,6 +8,20 @@ */ package cryptator; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.InputStream; +import java.math.BigInteger; +import java.util.Scanner; +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; + import cryptator.config.CryptaConfig; import cryptator.parser.CryptaParserException; import cryptator.parser.CryptaParserWrapper; @@ -19,17 +33,6 @@ import cryptator.specs.ICryptaSolver; import cryptator.tree.CryptaEvaluation; import cryptator.tree.CryptaEvaluationException; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Ignore; -import org.junit.Test; - -import java.io.InputStream; -import java.math.BigInteger; -import java.util.Scanner; -import java.util.concurrent.atomic.AtomicInteger; - -import static org.junit.Assert.*; final class CryptaSolvingTester { @@ -250,23 +253,6 @@ public void testDonaldGeraldRobert3() throws CryptaParserException, CryptaModelE t.testUNSAT("donald + gerald = robert"); } - @Test - public void testDonaldGeraldRobert4() throws CryptaParserException, CryptaModelException, CryptaSolverException { - t.config.setArithmeticBase(2); - t.config.getAllowLeadingZeros(); - t.config.setRelaxMinDigitOccurence(1); - t.testUNSAT("donald + gerald = robert"); - } - - @Test - public void testDonaldGeraldRobert5() throws CryptaParserException, CryptaModelException, CryptaSolverException { - t.config.setArithmeticBase(2); - t.config.getAllowLeadingZeros(); - t.config.setRelaxMinDigitOccurence(1); - t.config.setRelaxMaxDigitOccurence(1); - t.testUNSAT("donald + gerald = robert"); - } - @Test @Ignore("It is a choco issue.") public void testEMC2() throws CryptaParserException, CryptaModelException, CryptaSolverException {