Let me start off by saying that this project is a mess, and I know it. I believe it is a mess because I threw it together in a few days just to get it working. Unfortunately, my undergraduate education hasn't taught me design patterns (yet), so there is really no organized structure.
If I want to develop this into a more organized project, and perhaps a more general math library, with more functionality (integration, statistics, etc), what can I do to design it very well? Where do I put the logic, etc. I have been reading up on design patterns but I still feel that I need direction.
MathApp.java
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javax.swing.*;
public class MathApp extends Application {
private CalculusTool cT;
private String expression;
private String deriv;
@Override
public void start(Stage primaryStage) {
primaryStage.setTitle("MathApp");
GridPane grid = new GridPane();
grid.setAlignment(Pos.CENTER);
grid.setHgap(10);
grid.setVgap(10);
grid.setPadding(new Insets(25, 25, 25, 25));
Text scenetitle = new Text("Enter an expression");
scenetitle.setFont(Font.font("Tahoma", FontWeight.NORMAL, 15));
grid.add(scenetitle, 0, 0, 2, 1);
TextField expBox = new TextField();
grid.add(expBox, 1, 2);
Text error = new Text();
grid.add(error, 1, 6);
error.setVisible(false);
Button btn = new Button("Derive");
HBox hbBtn = new HBox(10);
hbBtn.setAlignment(Pos.BOTTOM_RIGHT);
hbBtn.getChildren().add(btn);
grid.add(hbBtn, 1, 4);
final Text actiontarget = new Text();
grid.add(actiontarget, 1, 6);
btn.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent e) {
//same expression - derive again when clicked
if(expBox.hasProperties() && expBox.getText().equals(expression)) {
deriv = cT.readable(cT.derive());
actiontarget.setVisible(true);
actiontarget.setFill(Color.FIREBRICK);
actiontarget.setText(deriv);
}
else { // new expression, create new object
expression = expBox.getText();
if (!CalculusTool.isValidExpression(expression)) {
actiontarget.setVisible(false);
error.setText(CalculusTool.errorMessage);
error.setVisible(true);
expBox.clear();
} else {
error.setVisible(false);
cT = new CalculusTool(expression);
deriv = cT.readable(cT.derive());
actiontarget.setVisible(true);
actiontarget.setFill(Color.FIREBRICK);
actiontarget.setText(deriv);
}
}
}
});
Scene scene = new Scene(grid, 300, 275);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
ExpNode.java
abstract class ExpNode {
abstract String getVal();
abstract String getType();
abstract ExpNode getLeftChild();
abstract ExpNode getRightChild();
abstract void setLeftChild(ExpNode l);
abstract void setRightChild(ExpNode r);
abstract ExpNode derivative();
}
class OpNode extends ExpNode
{
String operator;
ExpNode leftChild;
ExpNode rightChild;
OpNode mult1;
OpNode mult2;
OpNode mult3;
OpNode div;
OpNode plus;
OpNode minus;
OpNode exponent;
NegateNode unary;
OpNode(String op)
{
operator = op;
leftChild = null;
rightChild = null;
}
String getVal() { return operator; }
String getType() { return "operator"; }
ExpNode getLeftChild() { return leftChild; }
ExpNode getRightChild() { return rightChild; }
void setLeftChild(ExpNode lc) { leftChild = lc; }
void setRightChild(ExpNode rc) { rightChild = rc; }
ExpNode derivative() {
switch(operator) {
case "+": // h(x) = f(x)+g(x) then h′(x) = f′(x)+g′(x)
plus = new OpNode("+");
plus.setLeftChild(getLeftChild().derivative());
plus.setRightChild(getRightChild().derivative());
return plus;
case "-": // h(x) = f(x)-g(x) then h′(x) = f′(x)-g′(x)
minus = new OpNode("-");
minus.setLeftChild(getLeftChild().derivative());
minus.setRightChild(getRightChild().derivative());
return minus;
case "*": // h(x) = f(x)g(x) then h′(x) = f′(x)g(x) + f(x)g′(x)
mult1 = new OpNode("*");
mult2 = new OpNode("*");
plus = new OpNode("+");
mult1.setLeftChild(getLeftChild());
mult1.setRightChild(getRightChild().derivative());
mult2.setLeftChild(getRightChild());
mult2.setRightChild(getLeftChild().derivative());
plus.setLeftChild(mult1);
plus.setRightChild(mult2);
return plus;
case "/": // h(x) = f(x)/g(x) then h′(x) = (f′(x)g(x) − f(x)g′(x))/( g(x)^2)
mult1 = new OpNode("*");
mult2 = new OpNode("*");
mult3 = new OpNode("*");
div = new OpNode("/");
minus = new OpNode("-");
mult1.setLeftChild(getRightChild());
mult1.setRightChild(getLeftChild().derivative());
mult2.setLeftChild(getLeftChild());
mult2.setRightChild(getRightChild().derivative());
minus.setLeftChild(mult1);
minus.setRightChild(mult2);
mult3.setLeftChild(getRightChild());
mult3.setRightChild(getRightChild());
div.setLeftChild(minus);
div.setRightChild(mult3);
return div;
case "^":
/*y = xsin x
ln y = (sin x) ln x ← taking the natural log of both sides.
y'/y = (cos x) ln x + (sin x)/x ← Using the product rule and implicit differentiation.
y' = y((cos x) ln x + (sin x)/x) ← multiplying by y.
y' = xsin x ((cos x) ln x + (sin x)/x) ← substitution.
*/
if(getRightChild().getVal().equals("0")) {
return new ConstNode("1");
}
if(getLeftChild().getType().equals("function")) {
if(getRightChild().getType().equals("variable")) {
}
if(getRightChild().getType().equals("function")) {
}
if(getRightChild().getType().equals("constant")) {
int temp = Integer.parseInt(getRightChild().getVal());
temp--;
ConstNode decrement = new ConstNode(String.valueOf(temp));
mult1 = new OpNode("*");
mult2 = new OpNode("*");
exponent = new OpNode("^");
//getLeftChild() = func(x)
//getLeftChild().derivative() = dx*dfunc(x)
//getRightChild() = C
//decrement = C - 1
//getLeftChild().getRightChild() = x
// func(x)^C ==>> C*dx*func(x)^(C-1)*dfunc(x)
exponent.setLeftChild(getLeftChild());
exponent.setRightChild(decrement);
mult1.setLeftChild(exponent);
mult1.setRightChild(getLeftChild().derivative());
mult2.setLeftChild(getRightChild());
mult2.setRightChild(mult1);
return mult2;
}
}
else if(getLeftChild().getType().equals("variable")) {
if(getRightChild().getType().equals("unaryMinus")) {
if(getRightChild().getRightChild().getType().equals("constant")) { // Ex: x^-3 ==> -3*(x^-4)
int temp = Integer.parseInt(getRightChild().getRightChild().getVal());
temp++;
ConstNode decrement = new ConstNode(String.valueOf(temp));
mult1 = new OpNode("*");
exponent = new OpNode("^");
unary.setRightChild(getRightChild().getRightChild());
exponent.setLeftChild(getLeftChild());
exponent.setRightChild(decrement);
mult1.setLeftChild(unary);
mult1.setRightChild(exponent);
return mult1;
}
}
// if not unaryMinus
if(getRightChild().getType().equals("variable")) {
}
if(getRightChild().getType().equals("function")) {
}
if(getRightChild().getType().equals("constant")) { // Ex: x^3 ==> 3*(x^2)
int temp = Integer.parseInt(getRightChild().getVal());
temp--;
ConstNode decrement = new ConstNode(String.valueOf(temp));
mult1 = new OpNode("*");
exponent = new OpNode("^");
exponent.setLeftChild(getLeftChild());
exponent.setRightChild(decrement);
mult1.setLeftChild(getRightChild());
mult1.setRightChild(exponent);
return mult1;
}
}
else if(getLeftChild().getType().equals("constant")) {
if(getRightChild().getType().equals("variable")) {
}
if(getRightChild().getType().equals("function")) {
}
if(getRightChild().getType().equals("constant")) {
return new ConstNode("0");
}
}
return new ConstNode("0");
}
return null;
}
}
// only has right child
class FuncNode extends ExpNode
{
String func;
ExpNode leftChild;
ExpNode rightChild;
NegateNode unary;
OpNode exponent;
OpNode mult;
OpNode mult2;
OpNode div;
FuncNode sin;
FuncNode cos;
FuncNode sec;
FuncNode cot;
FuncNode csc;
FuncNode tan;
FuncNode ln;
ConstNode two;
ConstNode ten;
FuncNode(String f)
{
func = f;
leftChild = null;
rightChild = null;
}
String getVal() { return func; }
String getType() { return "function"; }
ExpNode getLeftChild() { return leftChild; }
ExpNode getRightChild() { return rightChild; }
void setLeftChild(ExpNode lc) { leftChild = lc; }
void setRightChild(ExpNode rc) { rightChild = rc; }
ExpNode derivative() {
switch(func) {
case "sin":
mult = new OpNode("*");
cos = new FuncNode("cos");
cos.setRightChild(getRightChild());
mult.setLeftChild(getRightChild().derivative());
mult.setRightChild(cos);
return mult;
case "cos":
unary = new NegateNode();
mult = new OpNode("*");
sin = new FuncNode("sin");
sin.setRightChild(getRightChild());
mult.setLeftChild(getRightChild().derivative());
mult.setRightChild(sin);
unary.setRightChild(mult);
return unary;
case "tan":
sec = new FuncNode("sec");
mult = new OpNode("*");
exponent = new OpNode("^");
two = new ConstNode("2");
sec.setRightChild(getRightChild());
exponent.setLeftChild(sec);
exponent.setRightChild(two);
mult.setLeftChild(getRightChild().derivative());
mult.setRightChild(exponent);
return mult;
case "csc":
unary = new NegateNode();
mult = new OpNode("*");
mult2 = new OpNode("*");
cot = new FuncNode("cot");
csc = new FuncNode("csc");
cot.setRightChild(getRightChild());
csc.setRightChild(getRightChild());
mult.setLeftChild(cot);
mult.setRightChild(csc);
mult2.setLeftChild(getRightChild().derivative());
mult2.setRightChild(mult);
unary.setRightChild(mult2);
return unary;
case "sec":
mult = new OpNode("*");
mult2 = new OpNode("*");
sec = new FuncNode("sec");
tan = new FuncNode("tan");
sec.setRightChild(getRightChild());
tan.setRightChild(getRightChild());
mult.setLeftChild(sec);
mult.setRightChild(tan);
mult2.setLeftChild(getRightChild().derivative());
mult2.setRightChild(mult);
return mult2;
case "cot":
unary = new NegateNode();
exponent = new OpNode("^");
mult = new OpNode("*");
csc = new FuncNode("csc");
two = new ConstNode("2");
csc.setRightChild(getRightChild());
exponent.setLeftChild(csc);
exponent.setRightChild(two);
mult.setLeftChild(getRightChild().derivative());
mult.setRightChild(exponent);
unary.setRightChild(mult);
return unary;
case "log":
div = new OpNode("/");
mult = new OpNode("*");
ln = new FuncNode("ln");
ten = new ConstNode("10");
ln.setRightChild(ten);
mult.setLeftChild(getRightChild());
mult.setRightChild(ln);
div.setLeftChild(getRightChild().derivative());
div.setRightChild(mult);
return div;
case "ln":
div = new OpNode("/");
div.setLeftChild(getRightChild().derivative());
div.setRightChild(getRightChild());
return div;
//case "arcsin":
//case "arccos":
}
return null;
}
}
class ConstNode extends ExpNode
{
int num;
String strNum;
ExpNode leftChild;
ExpNode rightChild;
boolean string = false;
ConstNode(int val)
{
num = val;
strNum = "";
leftChild = null;
rightChild = null;
}
ConstNode(String val)
{
strNum = val;
num = 0;
leftChild = null;
rightChild = null;
string = true;
}
String getVal() { return strNum; }
String getType() { return "constant"; }
ExpNode getLeftChild() { return leftChild; }
ExpNode getRightChild() { return rightChild; }
void setLeftChild(ExpNode lc) { leftChild = lc; }
void setRightChild(ExpNode rc) { rightChild = rc; }
ExpNode derivative() { return new ConstNode("0"); }
}
class VarNode extends ExpNode
{
String variable;
ExpNode leftChild;
ExpNode rightChild;
VarNode(String v)
{
variable = v;
leftChild = null;
rightChild = null;
}
String getVal() { return variable; }
String getType() { return "variable"; }
ExpNode getLeftChild() { return leftChild; }
ExpNode getRightChild() { return rightChild; }
void setLeftChild(ExpNode lc) { leftChild = lc; }
void setRightChild(ExpNode rc) { rightChild = rc; }
ExpNode derivative() {
return new ConstNode("1");
}
}
// $ only has a right child
class NegateNode extends ExpNode
{
String unaryMinus;
ExpNode leftChild;
ExpNode rightChild;
NegateNode unary;
NegateNode()
{
unaryMinus = "$";
leftChild = null;
rightChild = null;
}
NegateNode(String un)
{
unaryMinus = un;
leftChild = null;
rightChild = null;
}
String getVal() { return unaryMinus; }
String getType() { return "unaryMinus"; }
ExpNode getLeftChild() { return leftChild; }
ExpNode getRightChild() { return rightChild; }
void setLeftChild(ExpNode lc) { leftChild = lc; }
void setRightChild(ExpNode rc) { rightChild = rc; }
ExpNode derivative() {
if(getRightChild().getType().equals("constant")) {
return new ConstNode("0");
}
unary = new NegateNode("$");
unary.setRightChild(getRightChild().derivative());
return unary;
}
}
CalculusTool.java
import java.util.ArrayList;
import java.util.Stack;
public class CalculusTool {
public static String var = "";
private String expression;
private String derivative;
private ArrayList<String> postFixTokens;
private ExpNode root;
private ExpNode derivativeRoot;
private int COUNT = 10;
static String errorMessage;
public CalculusTool(String exp)
{
expression = formatString(exp);
postFixTokens = tokenize(expression);
root = constructTree(postFixTokens);
derivativeRoot = root;
derivative = expression;
}
public String getExpression()
{
return expression;
}
public void resetDerivative()
{
derivativeRoot = root;
derivative = expression;
}
public static boolean isValidExpression(String exp)
{
exp = exp.replaceAll("\\s","");
exp = exp.toLowerCase();
if(exp.length() == 0) {
errorMessage = "Nothing Entered";
return false;
}
if(!exp.matches("[a-zA-Z0-9+*/^()-]+")) { // contains only operators, numbers, or letters
errorMessage = "Syntax Error";
return false;
}
if(exp.matches("[+*/^()-]+")) { // doesn't contain any operands
errorMessage = "Syntax Error";
return false;
}
String firstChar = exp.substring(0, 1);
String lastChar = exp.substring(exp.length() - 1, exp.length());
if(!firstChar.equals("-") && isOperator(firstChar) || firstChar.equals(")") || isOperator(lastChar) || lastChar.equals("(")) {
errorMessage = "Syntax Error"; //starts with operator or close parenthesis, or ends with operator or open parenthesis
return false;
}
for(int i = 0; i < exp.length(); i++) {
String temp = "";
while(i < exp.length() && exp.substring(i, i + 1).matches("[a-zA-Z]")) {
temp += exp.substring(i, i + 1);
i++;
}
if(temp.length() == 1) {
//i--; // ?? i must be decremented from the above while loop in this if block so the program can check the last character in the string
if(var.length() == 0)
var = temp;
if(!temp.equals(var)) {
errorMessage = "Your expression cannot contain two variables";
return false;
}
else if(i < exp.length() && exp.substring(i, i + 1).matches("[0-9]+")) {
errorMessage = "Can't do this: " + temp + exp.substring(i, i + 1);
return false;
}
}
else if(isFunction(temp)) {
if(i < exp.length()) {
if(!exp.substring(i, i + 1).equals("(")) {
System.out.println("Syntax Error: " + temp + " needs a parenthesis after it");// no parenthesis after function (EX: sin5)
return false;
}
}
else {
System.out.println("Syntax Error: " + temp + " needs an input"); // nothing after function (EX: 5 + sin)
return false;
}
}
else if(temp.length() != 0){
System.out.println(temp + ": function not found");
return false;
}
//i--; // ?? i must be decremented since it was left incremented in the above while loop
}
int cntOpenParen = 0;
int cntCloseParen = 0;
for(int i = 0; i < exp.length() - 1; i++) {
String tmp1 = exp.substring(i, i + 1);
String tmp2 = exp.substring(i + 1, i + 2);
if(tmp1.equals("-")) {
if(isOperator(tmp2) || tmp2.equals(")")) {
System.out.println("Syntax Error: " + tmp1 + tmp2);
return false;
}
}
else if(tmp2.equals("-")) { // Also prevents next else if from rejecting an operator followed by a unary minus
if(tmp1.equals("(")) {
System.out.println("Syntax Error: " + tmp1 + tmp2);
return false;
}
}
else if((isOperator(tmp1) || tmp1.equals("(")) && (isOperator(tmp2) || tmp2.equals(")"))) {
System.out.println("Syntax Error: " + tmp1 + tmp2); // two operands in a row (examples: ++, (+, ())
return false;
}
else if(exp.substring(i, i + 1).equals("("))
cntOpenParen++;
else if(exp.substring(i, i + 1).equals(")"))
cntCloseParen++;
}
if(cntOpenParen < cntCloseParen) { // found a ")" when the end of the expression was expected
System.out.println("Syntax Error: found ')' but expected end of expression");
return false;
}
return true;
}
public static boolean isOperator(String str)
{
if(str.matches("[+*/^-]"))
return true;
return false;
}
public boolean isOperand(String str)
{
if(str.matches("[0-9]+") || str.equals(var))
return true;
return false;
}
public static boolean isFunction(String str)
{
String[] funcs = {"sin", "cos", "tan", "csc", "sec", "cot", "log", "ln"};
for(String temp: funcs)
if(str.equals(temp))
return true;
return false;
}
// "simplifies" the tree by deleting, replacing and merging nodes
public ExpNode simplify(ExpNode root)
{
if(root == null)
{
return root;
}
else if(root.getType().equals("unaryMinus") && root.getRightChild().getType().equals("unaryMinus")) {
return root.getRightChild().getRightChild();
}
else if(root.getVal().equals("*")) {
if(root.getLeftChild().getVal().equals("1")) {
return root.getRightChild();
}
else if(root.getRightChild().getVal().equals("1")) {
return root.getLeftChild();
}
else if(root.getLeftChild().getVal().equals("0")) {
return root.getLeftChild();
}
else if(root.getRightChild().getVal().equals("0")) {
return root.getRightChild();
}
else if(root.getLeftChild().getVal().equals("*")) {
if(root.getRightChild().getType().equals("constant")) {
if(root.getLeftChild().getLeftChild().getType().equals("constant")) { // Ex: (5*x)*6 ==> 30*x
int num1 = Integer.parseInt(root.getRightChild().getVal());
int num2 = Integer.parseInt(root.getLeftChild().getLeftChild().getVal());
OpNode mult = new OpNode("*");
mult.setLeftChild(new ConstNode(String.valueOf(num1 * num2)));
mult.setRightChild(root.getLeftChild().getRightChild());
return mult;
}
if(root.getLeftChild().getRightChild().getType().equals("constant")) { // Ex: (x*5)*6 ==> 30*x
int num1 = Integer.parseInt(root.getRightChild().getVal());
int num2 = Integer.parseInt(root.getLeftChild().getRightChild().getVal());
OpNode mult = new OpNode("*");
mult.setLeftChild(new ConstNode(String.valueOf(num1 * num2)));
mult.setRightChild(root.getLeftChild().getLeftChild());
return mult;
}
}
if(root.getRightChild().getType().equals("variable")) {
if(root.getLeftChild().getLeftChild().getType().equals("variable")) {
OpNode exponent = new OpNode("^");
exponent.setLeftChild(root.getRightChild());
exponent.setRightChild(new ConstNode("2"));
if(root.getLeftChild().getRightChild().getType().equals("function")) { // Ex: (x*sin(x))*x ==> (x^2)*sin(x)
root.setRightChild(root.getLeftChild().getRightChild());
root.setLeftChild(exponent);
}
else { // Ex: (x*5)*x ==> 5*x^2
root.setRightChild(exponent);
root.setLeftChild(root.getLeftChild().getRightChild());
}
return root;
}
if(root.getLeftChild().getRightChild().getType().equals("variable")) {
OpNode exponent = new OpNode("^");
exponent.setLeftChild(root.getRightChild());
exponent.setRightChild(new ConstNode("2"));
if(root.getLeftChild().getLeftChild().getType().equals("function")) { // Ex: (sin(x)*x)*x ==> (x^2)*sin(x)
root.setRightChild(root.getLeftChild().getLeftChild());
root.setLeftChild(exponent);
}
else { // Ex: (5*x)*x ==> 5*x^2
root.setRightChild(exponent);
root.setLeftChild(root.getLeftChild().getLeftChild());
}
return root;
}
}
}
else if(root.getRightChild().getVal().equals("*")) {
if(root.getLeftChild().getType().equals("constant")) {
if(root.getRightChild().getLeftChild().getType().equals("constant")) { // Ex: 5*(6*x) ==> 30*x
int num1 = Integer.parseInt(root.getLeftChild().getVal());
int num2 = Integer.parseInt(root.getRightChild().getLeftChild().getVal());
OpNode mult = new OpNode("*");
mult.setLeftChild(new ConstNode(String.valueOf(num1 * num2)));
mult.setRightChild(root.getRightChild().getRightChild());
return mult;
}
if(root.getRightChild().getRightChild().getType().equals("constant")) { // Ex: 5*(x*6) ==> 30*x
int num1 = Integer.parseInt(root.getLeftChild().getVal());
int num2 = Integer.parseInt(root.getRightChild().getRightChild().getVal());
OpNode mult = new OpNode("*");
mult.setLeftChild(new ConstNode(String.valueOf(num1 * num2)));
mult.setRightChild(root.getRightChild().getLeftChild());
return mult;
}
}
if(root.getLeftChild().getType().equals("variable")) {
if(root.getRightChild().getLeftChild().getType().equals("variable")) {
OpNode exponent = new OpNode("^");
exponent.setLeftChild(root.getLeftChild());
exponent.setRightChild(new ConstNode("2"));
if(root.getRightChild().getRightChild().getType().equals("function")) { // Ex: x*(x*sin(x)) ==> (x^2)*sin(x)
root.setRightChild(root.getRightChild().getRightChild());
root.setLeftChild(exponent);
}
else { // Ex: x*(x*5) ==> 5*x^2
root.setRightChild(exponent);
root.setLeftChild(root.getRightChild().getRightChild());
}
return root;
}
if(root.getRightChild().getRightChild().getType().equals("variable")) {
OpNode exponent = new OpNode("^");
exponent.setLeftChild(root.getLeftChild());
exponent.setRightChild(new ConstNode("2"));
if(root.getRightChild().getLeftChild().getType().equals("function")) { // Ex: x*(sin(x)*x) ==> (x^2)*sin(x)
root.setRightChild(root.getRightChild().getLeftChild());
root.setLeftChild(exponent);
}
else {
root.setRightChild(exponent);
root.setLeftChild(root.getRightChild().getLeftChild()); // Ex: x*(5*x) ==> 5*x^2
}
return root;
}
}
}
else if(root.getLeftChild().getVal().equals("^") && root.getRightChild().getVal().equals("^")) {
}
else if(root.getLeftChild().getVal().equals("^")) { // Ex: (x^2)*x
}
else if(root.getRightChild().getVal().equals("^")) { // Ex: x*(x^2)
if(root.getLeftChild().getType().equals("variable") && root.getRightChild().getLeftChild().getType().equals("variable")) {
}
}
else if(root.getLeftChild().getType().equals("unaryMinus")) {
if(root.getRightChild().getType().equals("constant") && root.getLeftChild().getRightChild().getType().equals("constant")) {
int num1 = Integer.parseInt(root.getRightChild().getVal());
int num2 = Integer.parseInt(root.getLeftChild().getRightChild().getVal());
NegateNode unary = new NegateNode("$");
unary.setRightChild(new ConstNode(String.valueOf(num1 * num2)));
return unary;
}
}
else if(root.getRightChild().getType().equals("unaryMinus")) {
if(root.getLeftChild().getType().equals("constant") && root.getRightChild().getRightChild().getType().equals("constant")) {
int num1 = Integer.parseInt(root.getLeftChild().getVal());
int num2 = Integer.parseInt(root.getRightChild().getRightChild().getVal());
NegateNode unary = new NegateNode("$");
unary.setRightChild(new ConstNode(String.valueOf(num1 * num2)));
return unary;
}
}
// Ex: x*x ==> x^2
else if(root.getLeftChild().getType().equals("variable") && root.getRightChild().getType().equals("variable")) {
OpNode exponent = new OpNode("^");
exponent.setLeftChild(root.getLeftChild());
exponent.setRightChild(new ConstNode("2"));
return exponent;
}
else if(root.getLeftChild().getType().equals("constant") && root.getRightChild().getType().equals("constant")) {
int num1 = Integer.parseInt(root.getLeftChild().getVal());
int num2 = Integer.parseInt(root.getRightChild().getVal());
return new ConstNode(String.valueOf(num1 * num2));
}
}
else if(root.getVal().equals("+")) {
if(root.getLeftChild().getVal().equals("0")) {
return root.getRightChild();
}
else if(root.getRightChild().getVal().equals("0")) {
return root.getLeftChild();
}
}
else if(root.getVal().equals("^")) {
if(root.getRightChild().getVal().equals("1")) {
return root.getLeftChild();
}
}
root.setLeftChild(simplify(root.getLeftChild()));
root.setRightChild(simplify(root.getRightChild()));
return root;
}
public String derive()
{
derivativeRoot = derivativeRoot.derivative();
// need to change simplfy to do depth-first traversal so that the
// tree will be fully "simplified" after one call
derivativeRoot = simplify(derivativeRoot);
derivativeRoot = simplify(derivativeRoot);
derivativeRoot = simplify(derivativeRoot);
return createInfix(derivativeRoot);
}
public int getPrecedence(String str)
{
int val = 0;
if(str.equals("+"))
val = 2;
else if(str.equals("-"))
val = 2;
else if(str.equals("*") || str.equals(("/")))
val = 3;
else if(str.equals("^") || str.equals("$"))
val = 4;
return val;
}
public boolean isLeftAssociative(String s)
{
if(s.equals("^") || s.equals("$") || s.equals("+") || s.equals("*"))
return false;
return true;
}
// modifies the string to look more like it would if someone wrote the expression
// out on paper
public String readable(String s)
{
for(int i = 0; i < s.length(); i++) {
if(s.substring(i, i + 1).equals("*") && i > 0)
if(isOperand(s.substring(i - 1, i)) && i < s.length() - 1 && s.substring(i + 1, i + 2).equals(var))
s = s.substring(0, i) + s.substring(i + 1);
}
return s;
}
// adds and deletes characters to aid in the creation of the binary expression tree
public String formatString(String exp)
{
exp = exp.replaceAll("\\s",""); // why
exp = exp.toLowerCase();
int count = 0;
if(exp.substring(0, 1).equals("-")) { // if expression starts with a minus sign, it is a unary one
exp = "$" + exp.substring(1); // replace
}
for(int i = 0; i < exp.length(); i++) {
if(exp.substring(i, i + 1).equals("("))
count++;
else if(exp.substring(i, i + 1).equals(")"))
count--;
}
while(count > 0) {
exp += ")";
count--;
}
// At the operators, when the operator is "-" and it is preceded by another operator,
// or preceded by a left parenthesis, or when it is the first character of the input
// it is a unary minus rather than binary. In this case, I change it to another
// character, '$', and make its precedence the same as that of '^'.
for(int i = 0; i < exp.length() - 1; i++) {
String tmp1 = exp.substring(i, i + 1);
String tmp2 = exp.substring(i + 1, i + 2);
if(tmp2.equals("-") && (isOperator(tmp1) || tmp1.equals("(")))
exp = exp.substring(0, i + 1) + "$" + exp.substring(i + 2);
else if((tmp1.matches("[0-9]+") || tmp1.equals(var)) && (tmp2.equals("(") || tmp2.equals(var)))
exp = exp.substring(0, i + 1) + "*" + exp.substring(i + 1);
}
return exp;
}
//creates string representing infix expression
public String createInfix(ExpNode root)
{
String str = "";
if(root == null) {
return str;
}
if (root != null) {
if (root.getRightChild() == null) {
str += root.getVal();
}
else if(root.getType().equals("function")) {
str += root.getVal();
//str += "(";
}
else if(root.getType().equals("unaryMinus")) {
str += "-";
}
else {
int parentPrecedence = getPrecedence(root.getVal());
str += root.getVal();
if(root.getLeftChild() != null && (getPrecedence(root.getLeftChild().getVal()) < parentPrecedence || isLeftAssociative(root.getLeftChild().getVal()))) {
//str += "(";
}
/*if(getPrecedence(root.getRightChild().getVal()) < parentPrecedence) {
str += ")";
}*/
}
}
return createInfix(root.getLeftChild()) + str + createInfix(root.getRightChild());
}
// separates the expression string into "tokens" and sorts them in
// postfix order
public ArrayList<String> tokenize(String exp)
{
ArrayList<String> tokens = new ArrayList<>();
Stack<String> stack = new Stack<>();
for(int i = 0; i < exp.length(); i++)
{
String token = "";
if(isOperator(exp.substring(i, i + 1)) || exp.substring(i, i + 1).equals("$")) {
token = exp.substring(i, i + 1);
while ((!stack.isEmpty() && (isOperator(stack.peek()) || stack.peek().equals("$")))
&& ((isLeftAssociative(token) && getPrecedence(token) <= getPrecedence(stack.peek()))
|| (!isLeftAssociative(token) && getPrecedence(token) < getPrecedence(stack.peek())))) {
tokens.add(stack.pop());
}
stack.push(token);
}
else if(exp.substring(i, i + 1).equals(var)) {
token = var;
tokens.add(token);
}
else if(exp.substring(i, i + 1).equals("(")) {
token = exp.substring(i, i + 1);
stack.push(token);
}
else if(exp.substring(i, i + 1).equals(")")) {
while(!stack.isEmpty() && !stack.peek().equals("(")) {
tokens.add(stack.pop());
}
if(!stack.isEmpty())
stack.pop();
if(!stack.isEmpty() && isFunction(stack.peek())) {
tokens.add(stack.pop());
}
}
else if(exp.substring(i, i + 1).matches("[0-9]+")) {
while(i < exp.length() && exp.substring(i, i + 1).matches("[0-9]+")) {
token += exp.substring(i, i + 1);
i++;
}
tokens.add(token);
i--; // i was left incremented after the while loop
}
else if(exp.substring(i, i + 1).equals(var)) {
tokens.add(token);
}
else {
while(i < exp.length() && exp.substring(i, i + 1).matches("[a-zA-Z]+")) {
token += exp.substring(i, i + 1);
i++;
}
if(token.length() != 0) {
stack.push(token);
}
}
}
while(!stack.isEmpty()) {
tokens.add(stack.pop());
}
return tokens;
}
// reads the "tokens" in order from the list and builds a tree
public ExpNode constructTree(ArrayList<String> postTokens)
{
ExpNode root = null;
Stack<ExpNode> nodes = new Stack<>();
for(String str: postTokens)
{
if(str.matches("[0-9]+")) {
nodes.push(new ConstNode(str));
}
else if(str.equals(var))
nodes.push(new VarNode(var));
else if(!nodes.isEmpty() && isFunction(str)) {
FuncNode function = new FuncNode(str);
function.setRightChild(nodes.pop());
nodes.push(function);
}
else if(!nodes.isEmpty() && str.equals("$")) {
NegateNode unary = new NegateNode("$");
unary.setRightChild(nodes.pop());
nodes.push(unary);
}
else if(!nodes.isEmpty()){
OpNode operator = new OpNode(str);
operator.setRightChild(nodes.pop());
operator.setLeftChild(nodes.pop());
nodes.push(operator);
}
}
if(!nodes.isEmpty())
root = nodes.pop();
return root;
}
}
if
conditions together with Boolean operators. Unfortunately, your code is a bit too long for me to review at this time. Overall, this is a great effort! The comments are especially helpful! \$\endgroup\$getType()
(and the uglyString
type). If I have some time I'll write an answer \$\endgroup\$