Skip to content

Commit

Permalink
added support for parentheses
Browse files Browse the repository at this point in the history
  • Loading branch information
Tran-Antoine committed Feb 19, 2019
1 parent 88a5fb0 commit bdfab80
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 66 deletions.
3 changes: 2 additions & 1 deletion src/issues.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
- Multiplication doesn't work with powered expressions, such as x^3, because only "x" is identified
- Division has not been configured for variables, hence all expressions containing '/' and variables
won't be reduced properly
- Pow encounters the same issue as division
- Pow encounters the same issue as division
- Some math shortcuts currently don't work, such as "9x*y" instead of "9x*1y". Same for "(3+2)x" instead of "(3+2)*1x"
20 changes: 18 additions & 2 deletions src/main/java/net/akami/mask/math/Tree.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
import java.util.ArrayList;
import java.util.List;

/**
* A tree is an object that contains a list of branches.
*/
public class Tree {

private List<Branch> branches;
Expand All @@ -23,6 +26,8 @@ public class Branch {
private String reducedValue;

public Branch(String expression) {
//boolean surrounded = expression.charAt(0) == '(' && expression.charAt(expression.length()-1) == ')';
//this.expression = surrounded ? expression.substring(1, expression.length()-1) : expression;
this.expression = expression;
reduced = false;
branches.add(this);
Expand All @@ -38,7 +43,7 @@ public Branch(String expression) {
public void setOperation(char operation) { this.operation = operation; }
public void setLeft(Branch left) { this.left = left; }
public void setRight(Branch right) { this.right = right; }
public void setReducedValue(String value) {
public void setReducedValue(String value){
this.reducedValue = value;
reduced = true;
}
Expand All @@ -51,15 +56,26 @@ public boolean equals(Object obj) {
return false;
}

@Override
public String toString() {
return expression;
}

public boolean hasChildren() {
return left != null || right != null;
}

public boolean doChildrenHaveChildren() {
if(hasChildren())
return left.hasChildren() || right.hasChildren();
return false;
}

public boolean canBeCalculated() {
if(!hasChildren()) {
return false;
}
return !(left.hasChildren() || right.hasChildren());
return !doChildrenHaveChildren();
}
}
}
63 changes: 53 additions & 10 deletions src/main/java/net/akami/mask/utils/ReducerFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,18 @@ public static String reduce(String exp) {

// deletes all the spaces
String localExp = exp.replaceAll("\\s", "");

tree.new Branch(localExp);
LOGGER.debug("Initial branch added : {}", tree.getBranches().get(0));

// split the expression for each pair of signs.
for(int i = 0; i < OPERATIONS.length; i+=2) {
splitBy(tree, OPERATIONS[i].getSign(), OPERATIONS[i+1].getSign());
}
do {
// split the expression for each pair of signs.
for (int i = 0; i < OPERATIONS.length; i += 2) {
splitBy(tree, OPERATIONS[i].getSign(), OPERATIONS[i + 1].getSign());
}
} while(containsParentheses(tree));

TreeUtils.printBranches(tree);
String result;
try {
result = TreeUtils.mergeBranches(tree);
Expand All @@ -56,8 +61,8 @@ private static void splitBy(Tree tree, char c1, char c2) {

while(true) {

Branch actual = branches.get(index);
String exp = actual.getExpression();
Branch actualBranch = branches.get(index);
String exp = actualBranch.getExpression();

/*
We must go from the end to the beginning. Otherwise, operations' priority is not respected.
Expand All @@ -72,13 +77,14 @@ private static void splitBy(Tree tree, char c1, char c2) {
expression '5+3*2', the result will be 5+3*2, 5, and 3*2. When splitting by '*' and '/', we don't
want '5+3*2' to be split, because it contains one of the previous operations (+).
*/
if(actual.hasChildren())
if(actualBranch.hasChildren())
break;

char c = exp.charAt(i);

if(c == c1 || c == c2) {
TreeUtils.createNewBranch(tree, actual, exp, i, c);
if((c == c1 || c == c2) && !isInsideParentheses(i, actualBranch)) {
LOGGER.debug("Found a place to split at index {}, character '{}'",i, c);
TreeUtils.createNewBranch(tree, actualBranch, i, c);
break;
}
}
Expand All @@ -88,7 +94,44 @@ private static void splitBy(Tree tree, char c1, char c2) {
break;
}
}
LOGGER.debug("Separations with the operations {} and {} are now done", c1, c2);
//LOGGER.debug("Separations with the operations {} and {} are now done", c1, c2);
}

private static boolean containsParentheses(Tree self) {
for(Branch branch : self.getBranches()) {
if(branch.hasChildren())
continue;
String exp = branch.getExpression();
if(exp.contains("(") || exp.contains(")")) {
return true;
}
}
return false;
}
public static boolean isInsideParentheses(int index, Branch self) {

String exp = self.getExpression();
if(exp.charAt(0) == '(' && exp.charAt(exp.length()-1) == ')') {
exp = exp.substring(1, exp.length()-1);
}
LOGGER.debug("Checking if sign at index {} is surrounded in {}", index, self);
int leftParenthesis = 0;

for(int i = 0; i < exp.length(); i++) {
if(exp.charAt(i) == '(') {
leftParenthesis++;
}

if(exp.charAt(i) == ')' && i != exp.length()) {
leftParenthesis--;
}
if(leftParenthesis > 0 && i == index) {
LOGGER.debug("- Indeed surrounded");
return true;
}
}
LOGGER.debug("Not surrounded");
return false;
}

}
29 changes: 24 additions & 5 deletions src/main/java/net/akami/mask/utils/TreeUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,12 @@ Merging the branches from the last one to the first one (this initial expression
for (int i = tree.getBranches().size() -1; i >= 0; i--) {

Branch branch = tree.getBranches().get(i);
LOGGER.debug("Actual branch : {}\n", branch.getExpression());
LOGGER.debug("\n");
LOGGER.debug("Actual branch : {}", branch.getExpression());

if(!branch.canBeCalculated()) {
LOGGER.debug("Not calculable.");
LOGGER.debug("Not calculable : hasChildren : {} / children have no children : {}",
branch.hasChildren(), branch.doChildrenHaveChildren());
continue;
}
// We are sure that the branch has a left and a right part, because the branch can be calculated
Expand Down Expand Up @@ -62,12 +64,29 @@ Merging the branches from the last one to the first one (this initial expression
return null;
}

public static void createNewBranch(Tree tree, Branch actual, String exp, int index, char operation) {
public static void createNewBranch(Tree tree, Branch actual, int index, char operation) {

String left = exp.substring(0, index);
String right = exp.substring(index+1);
String exp = actual.getExpression();

int start = 0;

if(exp.charAt(0) == '(' && exp.charAt(exp.length()-1) == ')') {
start = 1;
}

String left = exp.substring(start, index);
String right = exp.substring(index+1, exp.length()-start);
actual.setLeft(tree.new Branch(left));
actual.setRight(tree.new Branch(right));
actual.setOperation(operation);

LOGGER.debug("Successfully created new branches. Left : {}, Right : {}",
actual.getLeft().getExpression(), actual.getRight().getExpression());
}

public static void printBranches(Tree self) {
for(Branch branch : self.getBranches()) {
LOGGER.debug("Branch found : {}", branch);
}
}
}
51 changes: 3 additions & 48 deletions src/test/java/net/akami/mask/MathTest.java
Original file line number Diff line number Diff line change
@@ -1,57 +1,12 @@
package net.akami.mask;

import net.akami.mask.math.MaskExpression;
import net.akami.mask.math.MaskOperator;
import net.akami.mask.utils.ReducerFactory;

public class MathTest {

public static void main(String... args) {

// Creates the math expression
MaskExpression curve = new MaskExpression("3x^2 + 2x + 6 + 2y");
// Prepares the operator. At least the next calculation will be performed from "curve"
MaskOperator operator = MaskOperator.begin(curve);

/* null indicates that the result of the calculation "imageFor" doesn't need to be saved as a mask.
Instead, a final temporary variable will contain the result. The boolean indicates that the mask selected
for calculations need to be changed after the first calculation. In fact, as we want to convert the result to
an integer, we want the result of the calculation to be the actual mask, so that we can convert it to an
integer afterwards
*/
System.out.println("Image : "+operator.imageFor(null, true, 2, 0).asInt());
System.out.println("Expression : "+curve);

// The default mask used for calculation is now set to null
operator.end();
/* Output :
Image : 22
Expression : 3x^2+2x+6+2y
*/

// Let's now imagine we want to change the curve expression, by replacing all the x's by 3.

/* We need to call begin again because we called end() before. If you know that there will be no calculations
between the last one and this one, you don't need to call end(), neither do you need to call begin() again
*/
operator.begin(curve);
operator.imageFor(3);

/*
We use 'false' so the mask used for calculations remains 39+2y, not the result of the calculation.
We use null again so that it affects the temporary variable, and not the mask itself.
When calling asFloat, we need to specify that we want the float value of the temporary variable,
by default the method gives us the value of the default mask, here 39+2y.
*/
float image1 = operator.imageFor(null, false, 0).asFloat(MaskExpression.TEMP);
float image2 = operator.imageFor(null, false, 2).asFloat(MaskExpression.TEMP);
System.out.println(curve);
System.out.println(image1 + " / " + image2);

/* Output : 39+2y
39.0 / 43.0
The expression has indeed been changed. Note that masks are mutable objects, so be careful with constants.
*/
System.out.println(ReducerFactory.reduce("((1+2)*3)*4"));
System.out.println(ReducerFactory.reduce("(5x+4x)*1y"));
}
}

0 comments on commit bdfab80

Please sign in to comment.