Skip to content

Commit b537d6e

Browse files
authored
Merge pull request #46 from arnaud-m/refactorNode
Refactor the interface ICryptaNode close #41
2 parents 9bb4497 + 80fc389 commit b537d6e

16 files changed

+177
-206
lines changed

.github/workflows/CryptatorTest.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,11 @@ jobs :
66
steps:
77
- name: Checkhout the repository
88
uses: actions/checkout@v3
9-
- name: Set up JDK 17
9+
- name: Set up JDK 11
1010
uses: actions/setup-java@v3
1111
with:
12-
java-version: '17'
12+
java-version: '11'
1313
distribution: 'adopt'
1414
cache: 'maven'
1515
- name: Test with Maven
16-
run: mvn -B test -Dtest=ExtensiveTesting --file pom.xml
16+
run: mvn -B test -Dtest=ExtensiveTesting --file pom.xml

pom.xml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@
5050
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
5151
<maven.build.timestamp.format>yyyy</maven.build.timestamp.format>
5252
<!-- Java source/target to use for compilation. -->
53-
<javac.target>17</javac.target>
54-
<javac.source>17</javac.source>
53+
<javac.target>11</javac.target>
54+
<javac.source>11</javac.source>
5555
</properties>
5656
<dependencies>
5757
<!-- https://mvnrepository.com/artifact/args4j/args4j -->
@@ -126,11 +126,11 @@
126126
<!-- Only required when JAVA_HOME isn't at least Java 9 and when haven't
127127
configured the maven-toolchains-plugin -->
128128
<jdkToolchain>
129-
<version>17</version>
129+
<version>11</version>
130130
</jdkToolchain>
131-
<release>17</release>
132-
<source>16</source>
133-
<target>16</target>
131+
<release>11</release>
132+
<source>11</source>
133+
<target>11</target>
134134
</configuration>
135135
</plugin>
136136
<plugin>

src/main/java/cryptator/solver/AbstractModelerNodeConsumer.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ private IntVar[] getGCCOccs(int lb, int ub) {
5353

5454
@Override
5555
public void accept(ICryptaNode node, int numNode) {
56-
if (node.isWordLeaf()) {
56+
if (node.isWord()) {
5757
final char[] w = node.getWord();
5858
if (w.length > 0) firstSymbols.add(node.getWord()[0]);
5959
}

src/main/java/cryptator/solver/AdaptiveSolver.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ public final int getMaxLength() {
6868

6969
@Override
7070
public void accept(ICryptaNode node, int numNode) {
71-
if (node.isWordLeaf()) {
71+
if (node.isWord()) {
7272
final int len = node.getWord().length;
7373
if (len > maxLen) maxLen = len;
7474
}

src/main/java/cryptator/solver/CryptaBignumModeler.java

Lines changed: 52 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,23 @@
88
*/
99
package cryptator.solver;
1010

11+
import java.math.BigInteger;
12+
import java.util.ArrayDeque;
13+
import java.util.ArrayList;
14+
import java.util.Deque;
15+
import java.util.List;
16+
17+
import org.chocosolver.solver.Model;
18+
import org.chocosolver.solver.expression.discrete.arithmetic.ArExpression;
19+
import org.chocosolver.solver.variables.IntVar;
20+
1121
import cryptator.CryptaOperator;
1222
import cryptator.config.CryptaConfig;
1323
import cryptator.specs.ICryptaModeler;
1424
import cryptator.specs.ICryptaNode;
15-
import cryptator.tree.CryptaConstant;
1625
import cryptator.tree.CryptaOperatorDetection;
1726
import cryptator.tree.TreeTraversals;
1827
import cryptator.tree.TreeUtils;
19-
import org.chocosolver.solver.Model;
20-
import org.chocosolver.solver.expression.discrete.arithmetic.ArExpression;
21-
import org.chocosolver.solver.variables.IntVar;
22-
23-
import java.util.ArrayDeque;
24-
import java.util.Deque;
2528

2629
public class CryptaBignumModeler implements ICryptaModeler {
2730

@@ -60,14 +63,17 @@ private ArExpression[] makeWordVars(char[] word) {
6063
return vars;
6164
}
6265

63-
private ArExpression[] makeConstantVars(CryptaConstant constant) {
64-
var ints =constant.changeBaseLittleEndian(config.getArithmeticBase());
65-
final int n = ints.length;
66-
ArExpression[] vars = new ArExpression[n];
67-
for (int i = 0; i < n; i++) {
68-
vars[i] = model.intVar(ints[i]);
69-
}
70-
return vars;
66+
private ArExpression[] makeConstVars(char[] word) {
67+
List<ArExpression> vars = new ArrayList<>();
68+
BigInteger n = new BigInteger(new String(word));
69+
BigInteger b = BigInteger.valueOf(config.getArithmeticBase());
70+
while (n.compareTo(BigInteger.ZERO) > 0) {
71+
BigInteger[] r = n.divideAndRemainder(b);
72+
n = r[0];
73+
vars.add(model.intVar(r[1].intValueExact()));
74+
}
75+
ArExpression[] res = new ArExpression[vars.size()];
76+
return vars.toArray(res);
7177
}
7278

7379

@@ -98,29 +104,40 @@ private void applyEQ(ArExpression[] a, ArExpression[] b) {
98104
a1.carries[n - 1].eq(b1.carries[n - 1]).decompose().post();
99105
}
100106

107+
private void apply(CryptaOperator op, ArExpression[] a, ArExpression[] b) {
108+
switch (op) {
109+
case ADD: {
110+
stack.push(applyADD(a, b));
111+
break;
112+
}
113+
case EQ: {
114+
applyEQ(a, b);
115+
if (!stack.isEmpty()) // TODO Raise CryptaModelException instead !
116+
throw new IllegalStateException("Stack is not empty after accepting a relational operator.");
117+
else break;
118+
}
119+
default :
120+
// Should never be in the default case, this exception is
121+
// a program break in order to recall to modify the switch if
122+
// a new operator in BigNum is added.
123+
// Example case : we remove the MUL operator in computeUnsupportedBignumOperator
124+
throw new IllegalStateException("Bignum operator is not yet implemented");
125+
}
126+
}
101127
@Override
102128
public void accept(ICryptaNode node, int numNode) {
103129
super.accept(node, numNode);
104-
if (node.isConstantLeaf()){
105-
stack.push(makeConstantVars((CryptaConstant) node));
106-
} else if (node.isWordLeaf()){
107-
stack.push(makeWordVars(node.getWord()));
108-
} else if (!node.getOperator().equals(CryptaOperator.AND)) {
109-
final ArExpression[] b = stack.pop();
110-
final ArExpression[] a = stack.pop();
111-
switch (node.getOperator()) {
112-
case ADD -> stack.push(applyADD(a, b));
113-
case EQ -> {
114-
applyEQ(a, b);
115-
if (!stack.isEmpty())
116-
throw new IllegalStateException("Stack is not empty after accepting a relational operator.");
117-
}
118-
default ->
119-
// Should never be in the default case, this exception is
120-
// a program break in order to recall to modify the switch if
121-
// a new operator in BigNum is added.
122-
// Example case : we remove the MUL operator in computeUnsupportedBignumOperator
123-
throw new IllegalStateException("Bignum operator is not yet implemented");
130+
if(node.isInternalNode()) {
131+
if(! node.getOperator().equals(CryptaOperator.AND)) {
132+
final ArExpression[] b = stack.pop();
133+
final ArExpression[] a = stack.pop();
134+
apply(node.getOperator(), a, b);
135+
} // else do nothing ; constraint are posted when the relational operator is popped.
136+
} else {
137+
if (node.isConstant()){
138+
stack.push(makeConstVars(node.getWord()));
139+
} else {
140+
stack.push(makeWordVars(node.getWord()));
124141
}
125142
}
126143
}

src/main/java/cryptator/solver/CryptaModeler.java

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,19 @@
88
*/
99
package cryptator.solver;
1010

11-
import cryptator.config.CryptaConfig;
12-
import cryptator.specs.ICryptaModeler;
13-
import cryptator.specs.ICryptaNode;
14-
import cryptator.tree.CryptaConstant;
15-
import cryptator.tree.TreeTraversals;
11+
import java.util.ArrayDeque;
12+
import java.util.Deque;
13+
import java.util.function.Function;
14+
1615
import org.chocosolver.solver.Model;
1716
import org.chocosolver.solver.expression.discrete.arithmetic.ArExpression;
1817
import org.chocosolver.solver.expression.discrete.relational.ReExpression;
1918
import org.chocosolver.solver.variables.IntVar;
2019

21-
import java.util.ArrayDeque;
22-
import java.util.Deque;
23-
import java.util.function.Function;
20+
import cryptator.config.CryptaConfig;
21+
import cryptator.specs.ICryptaModeler;
22+
import cryptator.specs.ICryptaNode;
23+
import cryptator.tree.TreeTraversals;
2424

2525
public class CryptaModeler implements ICryptaModeler {
2626

@@ -46,22 +46,28 @@ public ModelerConsumer(Model model, CryptaConfig config) {
4646
wordVarBuilder = config.getHornerScheme() ? new HornerVarBuilder() : new ExponentiationVarBuilder();
4747
}
4848

49-
private IntVar makeWordVar(char[] word) {
50-
return wordVarBuilder.apply(word);
49+
private IntVar makeWordVar(ICryptaNode node) {
50+
return wordVarBuilder.apply(node.getWord());
51+
}
52+
53+
private IntVar makeWordConst(ICryptaNode node) {
54+
return model.intVar(Integer.parseInt(new String(node.getWord())));
5155
}
5256

5357
@Override
5458
public void accept(ICryptaNode node, int numNode) {
55-
super.accept(node, numNode);
56-
if (node.isConstantLeaf()) {
57-
stack.push(model.intVar(((CryptaConstant) node).getConstant()));
58-
} else if (node.isWordLeaf()) {
59-
stack.push(makeWordVar(node.getWord()));
60-
} else {
61-
final ArExpression b = stack.pop();
62-
final ArExpression a = stack.pop();
63-
stack.push(node.getOperator().getExpression().apply(a, b));
64-
}
59+
super.accept(node, numNode);
60+
if(node.isInternalNode()) {
61+
final ArExpression b = stack.pop();
62+
final ArExpression a = stack.pop();
63+
stack.push(node.getOperator().getExpression().apply(a, b));
64+
} else {
65+
if (node.isConstant()) {
66+
stack.push(makeWordConst(node));
67+
} else {
68+
stack.push(makeWordVar(node));
69+
}
70+
}
6571
}
6672

6773
@Override

src/main/java/cryptator/specs/ICryptaNode.java

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -39,42 +39,44 @@ public interface ICryptaNode {
3939
* @return the left child
4040
*/
4141
ICryptaNode getLeftChild();
42-
// FIXME Use java.util.Optional ?
4342

4443
/**
4544
* Gets the right child if any.
4645
*
4746
* @return the right child
4847
*/
4948
ICryptaNode getRightChild();
50-
// FIXME Use java.util.Optional ?
5149

5250
/**
53-
* Checks if the node is a constant leaf of the tree.
51+
* Checks if the node is an internal node of the tree.
5452
*
55-
* @return true, if it is a constant leaf
53+
* @return true, if it is internal node, otherwise it is a leaf
5654
*/
57-
boolean isConstantLeaf();
58-
55+
boolean isInternalNode();
56+
57+
5958
/**
60-
* Checks if the node is a word leaf of the tree.
59+
* Checks if the subtree is a constant of the tree.
6160
*
62-
* @return true, if it is a word leaf
61+
* @return true, if it is constant, otherwise false.
6362
*/
64-
boolean isWordLeaf();
63+
boolean isConstant();
6564

6665
/**
67-
* Checks if the node is an internal node of the tree.
66+
* Checks if the node represents a word, i.e. it is a leaf and not constant.
6867
*
69-
* @return true, if it is internal node
68+
* @return true, if it is a word leaf, otherwise false.
7069
*/
71-
boolean isInternalNode();
70+
default boolean isWord() {
71+
return !isInternalNode() && !isConstant();
72+
}
73+
7274

7375
/**
74-
* Transform parsed node to string.
75-
* For example parse(inOrderPrint(parse("a+'8'=b"))) is the same as parse("a+'8'=b")
76+
* Transform parsed node to string recognized by the grammar.
7677
*
77-
* @return the string representation of the node accepted by the parser
78+
* @return the string representation of the node accepted by the grammar
7879
*/
79-
String write();
80+
String toGrammarString();
81+
8082
}

src/main/java/cryptator/tree/CryptaConstant.java

Lines changed: 4 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -8,63 +8,23 @@
88
*/
99
package cryptator.tree;
1010

11-
import java.util.ArrayList;
12-
import java.util.List;
13-
1411
public class CryptaConstant extends CryptaLeaf {
15-
public static final int DEFAULT_BASE = 10;
16-
private final int constant;
17-
public CryptaConstant() {
18-
super();
19-
constant = Integer.parseInt(new String(getWord()));
20-
}
21-
12+
2213
public CryptaConstant(String word) {
2314
super(word);
24-
constant = Integer.parseInt(new String(getWord()));
2515
}
2616

2717
public CryptaConstant(char[] word) {
2818
super(word);
29-
constant = Integer.parseInt(new String(getWord()));
3019
}
3120

3221
@Override
33-
public boolean isConstantLeaf() {
22+
public boolean isConstant() {
3423
return true;
3524
}
3625

3726
@Override
38-
public boolean isWordLeaf() {
39-
return false;
40-
}
41-
42-
public int getConstant() {
43-
return constant;
44-
}
45-
46-
/**
47-
* @param newBase : the new base to which convert the current constant (Note the current constant is by default in base 10)
48-
* @return an Integer[] of the little representation of the int in the newBase
49-
*
50-
* For example, the constant 178829 will be res = [13, 8, 10, 11, 2] since
51-
* sum_{i from 0 to newBase - 1} res[i] * newBase^i = 178829
52-
*/
53-
public Integer[] changeBaseLittleEndian(int newBase){
54-
var length = getWord().length;
55-
List<Integer> newInt = new ArrayList<>();
56-
var n = this.constant;
57-
while (n > 0){
58-
var reminder = n % newBase;
59-
var divRes = n / newBase;
60-
newInt.add(reminder);
61-
n = divRes;
62-
}
63-
return newInt.toArray(new Integer[0]);
64-
}
65-
66-
@Override
67-
public String write(){
68-
return "'" + constant + "'";
27+
public String toGrammarString(){
28+
return "'" + new String(getWord()) + "'";
6929
}
7030
}

0 commit comments

Comments
 (0)