added rcalc src

master
Ondřej Hruška 11 years ago
parent ff465b514e
commit cfc55b658f
  1. 1
      src
  2. 26
      src/net/mightypork/rcalc/IDebugable.java
  3. 20
      src/net/mightypork/rcalc/IEvaluable.java
  4. 11
      src/net/mightypork/rcalc/IEvaluableToken.java
  5. 11
      src/net/mightypork/rcalc/IToken.java
  6. 32
      src/net/mightypork/rcalc/ParseError.java
  7. 74
      src/net/mightypork/rcalc/RCalc.java
  8. 172
      src/net/mightypork/rcalc/RCalcSession.java
  9. 268
      src/net/mightypork/rcalc/TokenList.java
  10. 185
      src/net/mightypork/rcalc/Tokenizer.java
  11. 451
      src/net/mightypork/rcalc/numbers/Fraction.java
  12. 37
      src/net/mightypork/rcalc/operations/BinaryOperation.java
  13. 17
      src/net/mightypork/rcalc/operations/Operation.java
  14. 39
      src/net/mightypork/rcalc/operations/OperationAdd.java
  15. 40
      src/net/mightypork/rcalc/operations/OperationDivide.java
  16. 38
      src/net/mightypork/rcalc/operations/OperationFactorial.java
  17. 39
      src/net/mightypork/rcalc/operations/OperationModulo.java
  18. 40
      src/net/mightypork/rcalc/operations/OperationMultiply.java
  19. 40
      src/net/mightypork/rcalc/operations/OperationPower.java
  20. 40
      src/net/mightypork/rcalc/operations/OperationSubtract.java
  21. 33
      src/net/mightypork/rcalc/operations/UnaryOperation.java
  22. 14
      src/net/mightypork/rcalc/tokens/IOperatorToken.java
  23. 23
      src/net/mightypork/rcalc/tokens/TokenBinaryOperator.java
  24. 29
      src/net/mightypork/rcalc/tokens/TokenOperatorAdd.java
  25. 29
      src/net/mightypork/rcalc/tokens/TokenOperatorDivide.java
  26. 29
      src/net/mightypork/rcalc/tokens/TokenOperatorFactorial.java
  27. 29
      src/net/mightypork/rcalc/tokens/TokenOperatorModulo.java
  28. 29
      src/net/mightypork/rcalc/tokens/TokenOperatorMultiply.java
  29. 29
      src/net/mightypork/rcalc/tokens/TokenOperatorPower.java
  30. 29
      src/net/mightypork/rcalc/tokens/TokenOperatorSubtract.java
  31. 20
      src/net/mightypork/rcalc/tokens/TokenParenthesisLeft.java
  32. 20
      src/net/mightypork/rcalc/tokens/TokenParenthesisRight.java
  33. 22
      src/net/mightypork/rcalc/tokens/TokenUnaryOperatorLeft.java
  34. 22
      src/net/mightypork/rcalc/tokens/TokenUnaryOperatorRight.java
  35. 131
      src/net/mightypork/semestralka/CalculatorBatch.java
  36. 192
      src/net/mightypork/semestralka/CalculatorInteractive.java
  37. 36
      src/net/mightypork/semestralka/Main.java
  38. 56
      src/net/mightypork/semestralka/Tests.java

1
src

@ -1 +0,0 @@
/home/ondra/Dokumenty/EclipseWorkspace/RCalc/src

@ -0,0 +1,26 @@
package net.mightypork.rcalc;
/**
* Interface for classes with optional debug mode
*
* @author Ondrej Hruska
*/
public interface IDebugable {
/**
* Get if debug mode is enabled
*
* @return debug enabled
*/
public boolean isDebug();
/**
* Enable / disable debug mode
*
* @param debug enable debug
*/
public void setDebug(boolean debug);
}

@ -0,0 +1,20 @@
package net.mightypork.rcalc;
import net.mightypork.rcalc.numbers.Fraction;
/**
* Interface of an object that can be turned into a fractional value
*
* @author Ondrej Hruska
*/
public interface IEvaluable {
/**
* Get fractional value
*
* @return fractional value
*/
public Fraction evaluate();
}

@ -0,0 +1,11 @@
package net.mightypork.rcalc;
/**
* Evaluable token interface
*
* @author Ondrej Hruska
*/
public interface IEvaluableToken extends IEvaluable, IToken {
}

@ -0,0 +1,11 @@
package net.mightypork.rcalc;
/**
* Token interface
*
* @author Ondrej Hruska
*/
public interface IToken {
}

@ -0,0 +1,32 @@
package net.mightypork.rcalc;
/**
* Error thrown when a syntax error is encountered while parsing an expression
*
* @author Ondrej Hruska
*/
public class ParseError extends RuntimeException {
/**
* Create new parse error
*
* @param message error message
*/
public ParseError(String message) {
super(message);
}
/**
* Wrapper another exception in a parse error
*
* @param e other exception
*/
public ParseError(Exception e) {
super(e);
}
}

@ -0,0 +1,74 @@
package net.mightypork.rcalc;
import net.mightypork.rcalc.numbers.Fraction;
/**
* Rational calculator, expression parser returning fractional results instead
* of imprecise decimal values.
*
* @author Ondrej Hruska
*/
public class RCalc implements IDebugable {
/** Debug mode flag */
private boolean debug = false;
/** Tokenizer instance */
private Tokenizer tokenizer = new Tokenizer();
/**
* Create new rational calculator
*/
public RCalc() {
}
@Override
public boolean isDebug() {
return debug;
}
@Override
public void setDebug(boolean debug) {
tokenizer.setDebug(debug);
this.debug = debug;
}
/**
* Calculate a result of an expression.
*
* @param expression input expression
* @return computed result
*/
public Fraction evaluate(String expression) {
IEvaluable ev;
try {
TokenList tl = tokenizer.tokenize(expression);
if (debug) System.out.println("\nBuilding syntax tree...");
ev = tl.parse();
} catch (ParseError e) {
throw e;
} catch (NumberFormatException e) {
throw new ParseError("Invalid number format.");
} catch (Exception e) {
throw new ParseError(e);
}
if (debug) System.out.println("\nSyntax tree:\n" + ev + "\n");
Fraction out = ev.evaluate();
return out;
}
}

@ -0,0 +1,172 @@
package net.mightypork.rcalc;
import java.util.HashMap;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import net.mightypork.rcalc.numbers.Fraction;
/**
* Rational Calculator Session - interactive RCalc with possibility to set and
* use variables.
*
* @author Ondrej Hruska
*/
public class RCalcSession implements IDebugable {
/** Debug mode flag */
private boolean debug = false;
/** Calculator instance */
private RCalc rcalc = new RCalc();
/** Table of user defined variables */
private HashMap<String, Fraction> symbolTable = new HashMap<String, Fraction>();
@Override
public boolean isDebug() {
return debug;
}
@Override
public void setDebug(boolean debug) {
rcalc.setDebug(debug);
this.debug = debug;
}
/**
* Get variable value
*
* @param name variable name
* @return the value
*/
public Fraction getVariable(String name) {
return symbolTable.get(name.toLowerCase());
}
/**
* Set a variable value
*
* @param name variable name
* @param value value to set
*/
public void setVariable(String name, Fraction value) {
symbolTable.put(name.trim(), value);
}
/**
* Evaluate an expression
*
* @param expression expression
* @return result
*/
public Fraction evaluate(String expression) {
expression = expression.trim().toLowerCase();
if (expression.length() == 0) return null; // no operation
expression.replace(":=", "="); // normalize assignment sign
String assignedVariable = null;
String assignmentOperation = "";
// parse name=value syntax for variables
if (expression.matches("^[a-z]+\\s*[+\\-*/%^]?=\\s*(.+)$")) {
Pattern pattern = Pattern.compile("^([a-z]+)\\s*([+\\-*/%^]?=)\\s*(.+)$");
Matcher matcher = pattern.matcher(expression);
if (!matcher.find() || matcher.groupCount() != 3) {
throw new ParseError("Invalid expression format, check your syntax.");
}
assignedVariable = matcher.group(1).trim();
assignmentOperation = matcher.group(2).trim();
expression = matcher.group(3).trim();
}
// replace variable names with their values
for (Entry<String, Fraction> entry : symbolTable.entrySet()) {
String regex = "(?:(?<![a-z])|^)" + entry.getKey() + "(?:(?![a-z])|$)";
String replacement = "(" + entry.getValue() + ")";
expression = expression.replaceAll(regex, replacement);
}
// calculate result
Fraction result = rcalc.evaluate(expression);
// assign variable value if requested
if (assignedVariable != null) {
Fraction oldValue = symbolTable.get(assignedVariable);
if (oldValue == null) oldValue = Fraction.ZERO;
if (assignmentOperation.equals("=")) {
// no-op
} else if (assignmentOperation.equals("+=")) {
result = oldValue.add(result);
} else if (assignmentOperation.equals("-=")) {
result = oldValue.subtract(result);
} else if (assignmentOperation.equals("*=")) {
result = oldValue.multiply(result);
} else if (assignmentOperation.equals("/=")) {
result = oldValue.divide(result);
} else if (assignmentOperation.equals("%=")) {
result = oldValue.modulo(result);
} else if (assignmentOperation.equals("^=")) {
result = oldValue.power(result);
}
symbolTable.put(assignedVariable, result);
}
return result;
}
/**
* Set symbol table (can be used to share it among multiple instances)
*
* @param table the symbol table
*/
public void setSymbolTable(HashMap<String, Fraction> table) {
this.symbolTable = table;
}
/**
* Get the current symbol table
*
* @return symbol table
*/
public HashMap<String, Fraction> getSymbolTable() {
return symbolTable;
}
}

@ -0,0 +1,268 @@
package net.mightypork.rcalc;
import java.util.ArrayList;
import java.util.Stack;
import net.mightypork.rcalc.operations.Operation;
import net.mightypork.rcalc.tokens.*;
/**
* A list of tokens which can be parsed into a single IEvaluable
*
* @author Ondrej Hruska
*/
public class TokenList extends ArrayList<IToken> implements IToken {
/**
* Create a TokenList with defined initial size
*
* @param i initial size
*/
public TokenList(int i) {
super(i);
}
/**
* Create a blank TokenList
*/
public TokenList() {
super();
}
/**
* Parse the token list.<br>
* After running this method, the list is typically reduced to a single
* token
*
* @return the remaining token
*/
public IEvaluableToken parse() {
try {
// 1. Extract all parentheses into TokenLists
extractParentheses();
// 2. Convert operator tokens to operators, including their arguments
// in the correct order ^ * / + -
extractOperator(TokenOperatorFactorial.class);
extractOperator(TokenOperatorPower.class);
extractOperator(TokenOperatorMultiply.class);
extractOperator(TokenOperatorDivide.class);
extractOperator(TokenOperatorModulo.class);
extractOperator(TokenOperatorAdd.class);
extractOperator(TokenOperatorSubtract.class);
// 3. Check for leftovers, throw error if any
if (this.size() > 1) {
throw new ParseError("TokenList didn't parse entirely, probably a syntax error: " + this);
}
// 4. If last token is a TokenList, use its content instead
if (this.get(0) instanceof TokenList) {
TokenList theList = (TokenList) this.get(0);
if (theList.size() == 1 && (theList.get(0) instanceof IEvaluableToken)) {
IEvaluableToken eval = (IEvaluableToken) theList.get(0);
this.clear();
this.add(eval);
}
}
// 5. Throw error if last token remaining is not evaluable
if (!(this.get(0) instanceof IEvaluableToken)) {
throw new ParseError("Last token in a TokenList is not evaluable: " + this.get(0));
}
} catch (ParseError e) {
throw e;
} catch (IndexOutOfBoundsException e) {
throw new ParseError("Missing operand(s).");
} catch (Exception e) {
throw new ParseError(e);
}
return (IEvaluableToken) this.get(0); // checked earlier
}
/**
* Find tokens of given operator class, and convert them to the operations
* including their operands.
*
* @param operatorClass class of the operator to find
*/
private void extractOperator(Class<? extends IOperatorToken> operatorClass) {
Stack<Integer> positions = new Stack<Integer>();
// find operator positions
for (int i = size() - 1; i >= 0; i--) {
if (operatorClass.isInstance(get(i))) {
positions.push(i);
}
}
// offset, used when positions are updated while replacing tokens
int offset = 0;
while (!positions.isEmpty()) {
// get position of the next token
int pos = positions.pop() + offset;
IToken operatorToken = get(pos);
if (operatorToken instanceof TokenBinaryOperator) {
TokenBinaryOperator opToken = (TokenBinaryOperator) operatorToken;
// variables for tokenLists of the operands
TokenList leftTL, rightTL;
// get left operand
IToken leftArg = get(pos - 1);
if (leftArg instanceof TokenList) {
leftTL = (TokenList) leftArg;
} else {
leftTL = new TokenList(1);
leftTL.add(leftArg);
}
// get right operand
IToken rightArg = get(pos + 1);
if (rightArg instanceof TokenList) {
rightTL = (TokenList) rightArg;
} else {
rightTL = new TokenList(1);
rightTL.add(rightArg);
}
// build an operation
Operation op = opToken.toOperation(leftTL, rightTL);
// discard used tokens
subList(pos - 1, pos + 2).clear();
// put back the operation
add(pos - 1, op);
// shift offset
offset -= 2;
} else if (operatorToken instanceof TokenUnaryOperatorLeft) {
TokenUnaryOperatorLeft opToken = (TokenUnaryOperatorLeft) operatorToken;
// variable for left operand
TokenList leftTL;
// get left operand (the only one)
IToken leftArg = get(pos - 1);
if (leftArg instanceof TokenList) {
leftTL = (TokenList) leftArg;
} else {
leftTL = new TokenList(1);
leftTL.add(leftArg);
}
// build an operation
Operation op = opToken.toOperation(leftTL);
// discard used tokens
subList(pos - 1, pos + 1).clear();
// put back the operation
add(pos - 1, op);
// shift offset
offset -= 1;
} else if (operatorToken instanceof TokenUnaryOperatorRight) {
TokenUnaryOperatorRight opToken = (TokenUnaryOperatorRight) operatorToken;
// variable for left operand
TokenList rightTL;
// get right operand (the only one)
IToken rightArg = get(pos + 1);
if (rightArg instanceof TokenList) {
rightTL = (TokenList) rightArg;
} else {
rightTL = new TokenList(1);
rightTL.add(rightArg);
}
// build an operation
Operation op = opToken.toOperation(rightTL);
// discard used tokens
subList(pos, pos + 2).clear();
// put back the operation
add(pos, op);
// shift offset
offset -= 1;
}
}
}
/**
* Convert outer parenthesized blocks to individual tokens
*/
private void extractParentheses() {
int open = 0; // level of outer parentheses
int openIgnored = 0; // level of inner parentheses, to be skipped
Stack<Integer> lefts = new Stack<Integer>();
Stack<Integer> rights = new Stack<Integer>();
for (int i = 0; i < size(); i++) {
if (get(i) instanceof TokenParenthesisLeft) {
if (open == 0) {
open++;
lefts.push(i);
} else {
openIgnored++;
}
}
if (get(i) instanceof TokenParenthesisRight) {
if (openIgnored > 0) {
openIgnored--;
} else {
open--;
if (open == 0) rights.push(i);
}
}
}
if (open > 0) throw new ParseError("Unbalanced parentheses.");
// extract token lists inside the outer parentheses, put them back as single tokens
while (!lefts.isEmpty()) {
int left = lefts.pop();
int right = rights.pop();
int innerLeft = left + 1;
int innerRight = right - 1;
// prepare tokenList for the inner tokens
TokenList insides = new TokenList();
// add the inner tokens
insides.addAll(subList(innerLeft, innerRight + 1));
// destroy the whole range including parentheses
subList(left, right + 1).clear();
// put replacement token back
add(left, insides.parse());
}
}
}

@ -0,0 +1,185 @@
package net.mightypork.rcalc;
import java.math.BigInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import net.mightypork.rcalc.numbers.Fraction;
import net.mightypork.rcalc.tokens.*;
/**
* Utility for converting an expression from String to a list of tokens
*
* @author Ondrej Hruska
*/
public class Tokenizer implements IDebugable {
/** Debug mode flag */
private boolean debug = false;
/** Deep debug mode flag */
private boolean deepDebug = false;
@Override
public boolean isDebug() {
return debug;
}
@Override
public void setDebug(boolean debug) {
this.debug = debug;
}
/**
* Get if deep debug mode is enabled
*
* @return is in deep debug mode
*/
public boolean isDeepDebug() {
return deepDebug && debug;
}
/**
* Enable / disable deep debug mode
*
* @param debug
*/
public void setDeepDebug(boolean debug) {
this.deepDebug = this.debug = debug;
}
/**
* Parse a string to a list of expression tokens
*
* @param input string with expression to parse
* @return token list
*/
public TokenList tokenize(String input) {
// discard whitespace
input = input.replaceAll("\\s", "");
// check parentheses
int level = 0;
for (char c : input.toCharArray()) {
if (c == '(') level++;
if (c == ')') level--;
if (level < 0) throw new ParseError("Unbalanced parentheses.");
}
if (level != 0) throw new ParseError("Unbalanced parentheses.");
// translate ** to ^
input = input.replaceAll("[*]{2}", "^");
if (debug && deepDebug) System.out.println("\nTranslate '**' to '^'\n" + input);
// enclose ^ left operand in parentheses
input = input.replaceAll("([0-9.]+[!]?)\\^", "($1)^");
if (debug && deepDebug) System.out.println("\nWrap left numeric operand of '^' with parentheses\n" + input);
// add multiplication operator for implicit multiplication
input = input.replaceAll("(?<=[0-9)!])(?=[(])", "*"); // )( -> )*(
input = input.replaceAll("(?<=[)!])(?=[0-9(])", "*");
if (debug && deepDebug) System.out.println("\nApply implicit multiplication\n" + input);
// normalize series of '+' and '-'
StringBuffer outputBuffer = new StringBuffer();
Matcher matcher = Pattern.compile("([+\\-]{2,})").matcher(input);
while (matcher.find()) {
int minus = 0;
for (char c : matcher.group().toCharArray()) {
if (c == '-') minus++;
}
minus = minus % 2;
matcher.appendReplacement(outputBuffer, minus == 0 ? "+" : "-");
}
matcher.appendTail(outputBuffer);
input = outputBuffer.toString();
if (debug && deepDebug) System.out.println("\nNormalize sequences of '+' and '-'\n" + input);
// turn '-' to '+-' between numbers
input = input.replaceAll("(?<=[0-9)!])-(?=[0-9])", "+-");
if (debug && deepDebug) System.out.println("\n'-' to '+-' between numbers\n" + input);
// convert '-' to '+-1*' in front of non-numbers
input = input.replaceAll("-(?=[^0-9])", "+-1*");
if (debug && deepDebug) System.out.println("\n'-' to '+-1*' in front of non-numbers\n" + input);
// discard useless + signs
input = input.replaceAll("(?<=[^)0-9!]|^)\\+", "");
if (debug && deepDebug) System.out.println("\nDiscard '+' at beginning of scope\n" + input);
if (debug) System.out.println("\nParsing tokens...");
TokenList list = new TokenList();
String buffer = "";
boolean parsingNumber = false;
for (Character c : input.toCharArray()) {
if (c.toString().matches("[\\-0-9.]")) {
buffer += c;
parsingNumber = true;
} else {
if (parsingNumber) {
list.add(new Fraction(buffer));
buffer = "";
parsingNumber = false;
}
switch (c) {
case '(':
list.add(new TokenParenthesisLeft());
break;
case ')':
list.add(new TokenParenthesisRight());
break;
case '*':
list.add(new TokenOperatorMultiply());
break;
case '/':
list.add(new TokenOperatorDivide());
break;
case '+':
list.add(new TokenOperatorAdd());
break;
case '!':
list.add(new TokenOperatorFactorial());
break;
case '%':
list.add(new TokenOperatorModulo());
break;
case '^':
list.add(new TokenOperatorPower());
break;
default:
throw new ParseError("Unknown token " + c);
}
}
}
// include possible last number
if (parsingNumber) {
list.add(new Fraction(buffer));
}
if (debug) System.out.println("\nToken list:\n" + list);
return list;
}
}

@ -0,0 +1,451 @@
package net.mightypork.rcalc.numbers;
import java.math.BigInteger;
import net.mightypork.rcalc.IEvaluableToken;
import net.mightypork.rcalc.ParseError;
/**
* A fractional number with methods for basic arithmetics
*
* @author Ondrej Hruska
*/
public class Fraction implements IEvaluableToken {
/** Zero fraction (0/1) */
public static final Fraction ZERO = new Fraction(BigInteger.ZERO);
/** One fraction (1/1) */
public static final Fraction ONE = new Fraction(BigInteger.ZERO);
private BigInteger numerator = BigInteger.ZERO;
private BigInteger denominator = BigInteger.ONE;
/**
* Create fraction from numerator and 1 as denominator
*
* @param numerator numerator
*/
public Fraction(long numerator) {
this.numerator = BigInteger.valueOf(numerator);
this.denominator = BigInteger.ONE;
}
/**
* Create fraction from numerator and 1 as denominator
*
* @param numerator numerator
*/
public Fraction(BigInteger numerator) {
this.numerator = numerator;
this.denominator = BigInteger.ONE;
}
/**
* Create fraction from numerator and denominator
*
* @param numerator numerator
* @param denominator denominator
*/
public Fraction(long numerator, long denominator) {
if (denominator == 0) {
throw new ArithmeticException("Division by zero.");
}
this.numerator = BigInteger.valueOf(numerator);
this.denominator = BigInteger.valueOf(denominator);
}
/**
* Create fraction from numerator and denominator
*
* @param numerator numerator
* @param denominator denominator
*/
public Fraction(BigInteger numerator, BigInteger denominator) {
if (denominator.equals(BigInteger.ZERO)) {
throw new ArithmeticException("Division by zero.");
}
this.numerator = numerator;
this.denominator = denominator;
}
/**
* Create a fraction as a copy of another
*
* @param other other fraction to copy
*/
public Fraction(Fraction other) {
this.numerator = other.numerator;
this.denominator = other.denominator;
}
public Fraction(String number) {
if(number.contains(".")) {
String[] parts = number.split("[.]");
try{
if(parts.length != 2) throw new ParseError(""); // for the catch
this.numerator = new BigInteger(parts[0]+parts[1]);
this.denominator = BigInteger.valueOf(10).pow(parts[1].length());
this.simplify_ip();
}catch(Exception e) {
e.printStackTrace();
throw new ParseError("Invalid number format.");
}
} else {
this.numerator = new BigInteger(number);
this.denominator = BigInteger.ONE;
}
}
/**
* Get numerator number
*
* @return numerator
*/
public BigInteger getNumerator() {
return numerator;
}
/**
* Get denominator number
*
* @return numerator
*/
public BigInteger getDenominator() {
return numerator;
}
/**
* Add a number
*
* @param operand addend
* @return this + addend
*/
public Fraction add(Fraction operand) {
Fraction a = this.getCopy();
Fraction b = operand.getCopy();
a.numerator = a.numerator.multiply(b.denominator);
b.numerator = b.numerator.multiply(a.denominator);
a.denominator = a.denominator.multiply(b.denominator);
a.numerator = a.numerator.add(b.numerator);
return a.simplify();
}
/**
* Subtract a number
*
* @param operand subtrahend
* @return this - subtrahend
*/
public Fraction subtract(Fraction operand) {
Fraction negated = new Fraction(operand.numerator.negate(), operand.denominator);
return this.add(negated);
}
/**
* Multiply by a number
*
* @param operand multiplier
* @return this * multiplier
*/
public Fraction multiply(Fraction operand) {
BigInteger n = numerator.multiply(operand.numerator);
BigInteger d = denominator.multiply(operand.denominator);
return (new Fraction(n, d)).simplify();
}
/**
* Divide by a number
*
* @param operand divisor
* @return this / divisor
*/
public Fraction divide(Fraction operand) {
Fraction flipped = new Fraction(operand.denominator, operand.numerator);
return this.multiply(flipped);
}
/**
* Modulo with a number
*
* @param operand divisor
* @return this % divisor
*/
public Fraction modulo(Fraction operand) {
if (isFractional() || operand.isFractional()) {
throw new ArithmeticException("Modulo is not defined for fractional operands.");
}
BigInteger i = this.getBigIntegerValue();
BigInteger o = operand.getBigIntegerValue();
return new Fraction(i.mod(o));
}
/**
* Get an n-th power of the number
*
* @param operand power
* @return this ^ power
*/
public Fraction power(Fraction operand) {
if (operand.isFractional()) {
throw new ArithmeticException("Can't calculate fractional power.");
}
int power = operand.getIntegerValue();
boolean swap = power < 0;
power = Math.abs(power);
BigInteger n = numerator.pow(power);
BigInteger d = denominator.pow(power);
Fraction out = (new Fraction(n, d)).simplify();
if (swap) out = out.invert();
return out;
}
/**
* Get a fraction with swapped numerator and denominator.
*
* @return inverted fraction
*/
public Fraction invert() {
if (numerator.equals(BigInteger.ZERO)) {
throw new ArithmeticException("Division by zero.");
}
return new Fraction(denominator, numerator);
}
/**
* Simplify the fraction
*
* @return simplified fraction
*/
public Fraction simplify() {
BigInteger gcd = numerator.gcd(denominator);
return new Fraction(numerator.divide(gcd), denominator.divide(gcd));
}
/**
* Simplify the fraction (in place)
*/
private void simplify_ip() {
BigInteger gcd = numerator.gcd(denominator);
numerator = numerator.divide(gcd);
denominator = denominator.divide(gcd);
}
/**
* Check if the number is fractional
*
* @return is fractional
*/
public boolean isFractional() {
return !(numerator.mod(denominator)).equals(BigInteger.ZERO);
}
/**
* Get an integer numeric value
*
* @return value
* @throws UnsupportedOperationException for fractional numbers
*/
public int getIntegerValue() {
if (isFractional()) {
throw new ArithmeticException("Can't take integer value of a fractional number.");
}
BigInteger a = numerator.divide(denominator);
if (a.longValue() > Integer.MAX_VALUE) {
throw new ArithmeticException("Can't convert to integer, number is too big.");
}
return a.intValue();
}
/**
* Get a BigInteger numeric value
*
* @return value
* @throws UnsupportedOperationException for fractional numbers
*/
public BigInteger getBigIntegerValue() {
if (this.isFractional()) {
throw new ArithmeticException("Can't take BigInteger value of a fractional number.");
}
return numerator.divide(denominator);
}
/**
* Get a double-precision numeric value
*
* @return value
*/
public double getDoubleValue() {
double a = numerator.doubleValue();
double b = denominator.doubleValue();
if (Double.isInfinite(a) || Double.isInfinite(b)) {
throw new ArithmeticException("Can't convert BigInteger to double, number is too big.");
}
return a / b;
}
/**
* Get a identical copy of this fraction
*
* @return copy
*/
private Fraction getCopy() {
return new Fraction(this);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((denominator == null) ? 0 : denominator.hashCode());
result = prime * result + ((numerator == null) ? 0 : numerator.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
Fraction other = (Fraction) obj;
if (denominator == null || other.denominator == null) return false;
if (numerator == null || other.numerator == null) return false;
Fraction thisSimplified = this.simplify();
Fraction otherSimplified = other.simplify();
return thisSimplified.numerator.equals(otherSimplified.numerator) && thisSimplified.denominator.equals(otherSimplified.denominator);
}
/**
* Check if the toString() of this fraction equals to the given string
*
* @param str compared string
* @return equals
*/
public boolean equalsToString(String str) {
return this.toString().equals(str);
}
/**
* Get a string representation of this fraction in the format
* "numerator/denominator", alternatively "numerator" if denominator is
* equal to 1.
*/
@Override
public String toString() {
if (!isFractional()) return numerator.divide(denominator).toString();
return numerator + "/" + denominator;
}
@Override
public Fraction evaluate() {
return this;
}
/**
* Take a factorial of this number, provided it is not fractional.
*
* @return the factorial
*/
public Fraction factorial() {
if (this.isFractional()) {
throw new ArithmeticException("Can't get factorial of a fractional number.");
}
BigInteger val = getBigIntegerValue();
if (val.compareTo(BigInteger.ZERO) < 0) {
throw new ArithmeticException("Can't get factorial of a negative number.");
}
BigInteger result = BigInteger.ONE;
for (BigInteger cnt = BigInteger.ONE; cnt.compareTo(val) <= 0; cnt = cnt.add(BigInteger.ONE)) {
result = result.multiply(cnt);
}
return new Fraction(result, BigInteger.ONE);
}
}

@ -0,0 +1,37 @@
package net.mightypork.rcalc.operations;
import net.mightypork.rcalc.IEvaluable;
import net.mightypork.rcalc.numbers.Fraction;
/**
* Abstract operation with two operands
*
* @author Ondrej Hruska
*/
public abstract class BinaryOperation extends Operation {
/** Left operand */
protected IEvaluable left = null;
/** Right operand */
protected IEvaluable right = null;
/**
* Create a binary operation
*
* @param left left operand
* @param right right operand
*/
public BinaryOperation(IEvaluable left, IEvaluable right) {
this.left = left;
this.right = right;
}
@Override
public abstract Fraction evaluate();
}

@ -0,0 +1,17 @@
package net.mightypork.rcalc.operations;
import net.mightypork.rcalc.IEvaluableToken;
import net.mightypork.rcalc.numbers.Fraction;
/**
* Abstract operation
*
* @author Ondrej Hruska
*/
public abstract class Operation implements IEvaluableToken {
@Override
public abstract Fraction evaluate();
}

@ -0,0 +1,39 @@
package net.mightypork.rcalc.operations;
import net.mightypork.rcalc.IEvaluable;
import net.mightypork.rcalc.numbers.Fraction;
/**
* Addition
*
* @author Ondrej Hruska
*/
public class OperationAdd extends BinaryOperation {
/**
* Create addition
*
* @param left left operand
* @param right right operand
*/
public OperationAdd(IEvaluable left, IEvaluable right) {
super(left, right);
}
@Override
public Fraction evaluate() {
return left.evaluate().add(right.evaluate());
}
@Override
public String toString() {
return "ADD{" + left + "," + right + "}";
}
}

@ -0,0 +1,40 @@
package net.mightypork.rcalc.operations;
import net.mightypork.rcalc.IEvaluable;
import net.mightypork.rcalc.numbers.Fraction;
/**
* Division
*
* @author Ondrej Hruska
*/
public class OperationDivide extends BinaryOperation {
/**
* Create division
*
* @param left left operand
* @param right right operand
*/
public OperationDivide(IEvaluable left, IEvaluable right) {
super(left, right);
}
@Override
public Fraction evaluate() {
return left.evaluate().divide(right.evaluate());
}
@Override
public String toString() {
return "DIV{" + left + "," + right + "}";
}
}

@ -0,0 +1,38 @@
package net.mightypork.rcalc.operations;
import net.mightypork.rcalc.IEvaluable;
import net.mightypork.rcalc.numbers.Fraction;
/**
* Factorial
*
* @author Ondrej Hruska
*/
public class OperationFactorial extends UnaryOperation {
/**
* Create factorial
*
* @param operand operand
*/
public OperationFactorial(IEvaluable operand) {
super(operand);
}
@Override
public Fraction evaluate() {
return operand.evaluate().factorial();
}
@Override
public String toString() {
return "FCT{" + operand + "}";
}
}

@ -0,0 +1,39 @@
package net.mightypork.rcalc.operations;
import net.mightypork.rcalc.IEvaluable;
import net.mightypork.rcalc.numbers.Fraction;
/**
* Modulo
*
* @author Ondrej Hruska
*/
public class OperationModulo extends BinaryOperation {
/**
* Create modulo
*
* @param left left operand
* @param right right operand
*/
public OperationModulo(IEvaluable left, IEvaluable right) {
super(left, right);
}
@Override
public Fraction evaluate() {
return left.evaluate().modulo(right.evaluate());
}
@Override
public String toString() {
return "MOD{" + left + "," + right + "}";
}
}

@ -0,0 +1,40 @@
package net.mightypork.rcalc.operations;
import net.mightypork.rcalc.IEvaluable;
import net.mightypork.rcalc.numbers.Fraction;
/**
* Multiplication
*
* @author Ondrej Hruska
*/
public class OperationMultiply extends BinaryOperation {
/**
* Create multiplication
*
* @param left left operand
* @param right right operand
*/
public OperationMultiply(IEvaluable left, IEvaluable right) {
super(left, right);
}
@Override
public Fraction evaluate() {
return left.evaluate().multiply(right.evaluate());
}
@Override
public String toString() {
return "MUL{" + left + "," + right + "}";
}
}

@ -0,0 +1,40 @@
package net.mightypork.rcalc.operations;
import net.mightypork.rcalc.IEvaluable;
import net.mightypork.rcalc.numbers.Fraction;
/**
* Exponentiation
*
* @author Ondrej Hruska
*/
public class OperationPower extends BinaryOperation {
/**
* Create exponentiation
*
* @param left left operand
* @param right right operand
*/
public OperationPower(IEvaluable left, IEvaluable right) {
super(left, right);
}
@Override
public Fraction evaluate() {
return left.evaluate().power(right.evaluate());
}
@Override
public String toString() {
return "POW{" + left + "," + right + "}";
}
}

@ -0,0 +1,40 @@
package net.mightypork.rcalc.operations;
import net.mightypork.rcalc.IEvaluable;
import net.mightypork.rcalc.numbers.Fraction;
/**
* Subtraction
*
* @author Ondrej Hruska
*/
public class OperationSubtract extends BinaryOperation {
/**
* Create subtraction
*
* @param left left operand
* @param right right operand
*/
public OperationSubtract(IEvaluable left, IEvaluable right) {
super(left, right);
}
@Override
public Fraction evaluate() {
return left.evaluate().subtract(right.evaluate());
}
@Override
public String toString() {
return "SUB{" + left + "," + right + "}";
}
}

@ -0,0 +1,33 @@
package net.mightypork.rcalc.operations;
import net.mightypork.rcalc.IEvaluable;
import net.mightypork.rcalc.numbers.Fraction;
/**
* Abstract unary operator
*
* @author Ondrej Hruska
*/
public abstract class UnaryOperation extends Operation {
/** Operand */
protected IEvaluable operand = null;
/**
* Create unary operation
*
* @param operand operand
*/
public UnaryOperation(IEvaluable operand) {
this.operand = operand;
}
@Override
public abstract Fraction evaluate();
}

@ -0,0 +1,14 @@
package net.mightypork.rcalc.tokens;
import net.mightypork.rcalc.IToken;
/**
* Interface of an operator token
*
* @author Ondrej Hruska
*/
public interface IOperatorToken extends IToken {
}

@ -0,0 +1,23 @@
package net.mightypork.rcalc.tokens;
import net.mightypork.rcalc.TokenList;
import net.mightypork.rcalc.operations.Operation;
/**
* Abstract operation token with two operands
*
* @author Ondrej Hruska
*/
public abstract class TokenBinaryOperator implements IOperatorToken {
/**
* Convert to operation
*
* @param left left operand
* @param right right operand
* @return operation
*/
public abstract Operation toOperation(TokenList left, TokenList right);
}

@ -0,0 +1,29 @@
package net.mightypork.rcalc.tokens;
import net.mightypork.rcalc.TokenList;
import net.mightypork.rcalc.operations.Operation;
import net.mightypork.rcalc.operations.OperationAdd;
/**
* Addition token
*
* @author Ondrej Hruska
*/
public class TokenOperatorAdd extends TokenBinaryOperator {
@Override
public String toString() {
return "+";
}
@Override
public Operation toOperation(TokenList left, TokenList right) {
return new OperationAdd(left.parse(), right.parse());
}
}

@ -0,0 +1,29 @@
package net.mightypork.rcalc.tokens;
import net.mightypork.rcalc.TokenList;
import net.mightypork.rcalc.operations.Operation;
import net.mightypork.rcalc.operations.OperationDivide;
/**
* Division token
*
* @author Ondrej Hruska
*/
public class TokenOperatorDivide extends TokenBinaryOperator {
@Override
public String toString() {
return "/";
}
@Override
public Operation toOperation(TokenList left, TokenList right) {
return new OperationDivide(left.parse(), right.parse());
}
}

@ -0,0 +1,29 @@
package net.mightypork.rcalc.tokens;
import net.mightypork.rcalc.TokenList;
import net.mightypork.rcalc.operations.Operation;
import net.mightypork.rcalc.operations.OperationFactorial;
/**
* Factorial token
*
* @author Ondrej Hruska
*/
public class TokenOperatorFactorial extends TokenUnaryOperatorLeft {
@Override
public String toString() {
return "!";
}
@Override
public Operation toOperation(TokenList argument) {
return new OperationFactorial(argument.parse());
}
}

@ -0,0 +1,29 @@
package net.mightypork.rcalc.tokens;
import net.mightypork.rcalc.TokenList;
import net.mightypork.rcalc.operations.Operation;
import net.mightypork.rcalc.operations.OperationModulo;
/**
* Modulo token
*
* @author Ondrej Hruska
*/
public class TokenOperatorModulo extends TokenBinaryOperator {
@Override
public String toString() {
return "%";
}
@Override
public Operation toOperation(TokenList left, TokenList right) {
return new OperationModulo(left.parse(), right.parse());
}
}

@ -0,0 +1,29 @@
package net.mightypork.rcalc.tokens;
import net.mightypork.rcalc.TokenList;
import net.mightypork.rcalc.operations.Operation;
import net.mightypork.rcalc.operations.OperationMultiply;
/**
* Multiplication token
*
* @author Ondrej Hruska
*/
public class TokenOperatorMultiply extends TokenBinaryOperator {
@Override
public String toString() {
return "*";
}
@Override
public Operation toOperation(TokenList left, TokenList right) {
return new OperationMultiply(left.parse(), right.parse());
}
}

@ -0,0 +1,29 @@
package net.mightypork.rcalc.tokens;
import net.mightypork.rcalc.TokenList;
import net.mightypork.rcalc.operations.Operation;
import net.mightypork.rcalc.operations.OperationPower;
/**
* Exponentiation token
*
* @author Ondrej Hruska
*/
public class TokenOperatorPower extends TokenBinaryOperator {
@Override
public String toString() {
return "^";
}
@Override
public Operation toOperation(TokenList left, TokenList right) {
return new OperationPower(left.parse(), right.parse());
}
}

@ -0,0 +1,29 @@
package net.mightypork.rcalc.tokens;
import net.mightypork.rcalc.TokenList;
import net.mightypork.rcalc.operations.Operation;
import net.mightypork.rcalc.operations.OperationSubtract;
/**
* Subtraction token
*
* @author Ondrej Hruska
*/
public class TokenOperatorSubtract extends TokenBinaryOperator {
@Override
public String toString() {
return "-";
}
@Override
public Operation toOperation(TokenList left, TokenList right) {
return new OperationSubtract(left.parse(), right.parse());
}
}

@ -0,0 +1,20 @@
package net.mightypork.rcalc.tokens;
import net.mightypork.rcalc.IToken;
/**
* Left parenthesis token
*
* @author Ondrej Hruska
*/
public class TokenParenthesisLeft implements IToken {
@Override
public String toString() {
return "(";
}
}

@ -0,0 +1,20 @@
package net.mightypork.rcalc.tokens;
import net.mightypork.rcalc.IToken;
/**
* Right parenthesis token
*
* @author Ondrej Hruska
*/
public class TokenParenthesisRight implements IToken {
@Override
public String toString() {
return ")";
}
}

@ -0,0 +1,22 @@
package net.mightypork.rcalc.tokens;
import net.mightypork.rcalc.TokenList;
import net.mightypork.rcalc.operations.Operation;
/**
* Abstract operator token with one operand on left
*
* @author Ondrej Hruska
*/
public abstract class TokenUnaryOperatorLeft implements IOperatorToken {
/**
* Convert to operation
*
* @param operand operand
* @return operation
*/
public abstract Operation toOperation(TokenList operand);
}

@ -0,0 +1,22 @@
package net.mightypork.rcalc.tokens;
import net.mightypork.rcalc.TokenList;
import net.mightypork.rcalc.operations.Operation;
/**
* Abstract operator token with one operand on right
*
* @author Ondrej Hruska
*/
public abstract class TokenUnaryOperatorRight implements IOperatorToken {
/**
* Convert to operation
*
* @param operand operand
* @return operation
*/
public abstract Operation toOperation(TokenList operand);
}

@ -0,0 +1,131 @@
package net.mightypork.semestralka;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Scanner;
import net.mightypork.rcalc.ParseError;
import net.mightypork.rcalc.RCalcSession;
import net.mightypork.rcalc.numbers.Fraction;
/**
* Batch calculator (reads input from a file)
*
* @author Ondrej Hruska
*/
public class CalculatorBatch implements Runnable {
/** Rcalc Session used by the calculator */
private RCalcSession session = new RCalcSession();
/** File to load the expressions from */
private File file;
/** Show results as fractions */
private boolean fractionalMode = true;
/** Flag that this calculator has been called from interactive mode */
private boolean fromInteractive;
//@formatter:off
private static final String introMessage =
"\nRational Calculator - file input mode\n"
+ "--------------------------------------\n"
+ "(c) Ondrej Hruska 2013\n"
+ "\n";
//@formatter:on
/**
* Create a batch calculator
*
* @param fileToLoad file to be executed
*/
public CalculatorBatch(File fileToLoad) {
this.file = fileToLoad;
this.fromInteractive = false;
}
/**
* Create a batch calculator
*
* @param fileToLoad file to be executed
* @param calledFromInteractive called from the interactive calculator
*/
public CalculatorBatch(File fileToLoad, boolean calledFromInteractive) {
this.file = fileToLoad;
this.fromInteractive = calledFromInteractive;
}
/**
* Set symbol table (share variables with other session)
*
* @param symbolTable symbol table
*/
public void setSymbolTable(HashMap<String, Fraction> symbolTable) {
session.setSymbolTable(symbolTable);
}
@Override
public void run() {
if (!fromInteractive) {
System.out.println(introMessage);
}
System.out.println("\nFILE = " + file.getAbsolutePath() + "\n");
// input scanner
Scanner sc;
try {
sc = new Scanner(new FileReader(file));
} catch (IOException e) {
System.out.println("ERROR:\n\tFile not found.");
return;
}
System.out.println("--- File begin ---");
while (sc.hasNextLine()) {
String expr = sc.nextLine().trim();
System.out.println("\n> " + expr);
if (expr.equalsIgnoreCase("fractional") || expr.equalsIgnoreCase("decimal")) {
if (fractionalMode) {
System.out.println("[FRACTIONAL MODE OFF]");
fractionalMode = false;
} else {
System.out.println("[FRACTIONAL MODE ON]");
fractionalMode = true;
}
} else {
try {
Fraction result = session.evaluate(expr);
System.out.println("= " + (fractionalMode ? result.toString() : result.getDoubleValue()));
} catch (ParseError pe) {
System.out.println("PARSE ERROR:\n\t" + pe.getMessage());
System.out.println("\n--- Aborted ---");
return;
} catch (ArithmeticException ae) {
System.out.println("MATH ERROR:\n\t" + ae.getMessage());
System.out.println("\n--- Aborted ---");
return;
}
}
}
System.out.println("\n--- File end ---");
}
}

@ -0,0 +1,192 @@
package net.mightypork.semestralka;
import java.io.File;
import java.util.*;
import java.util.Map.Entry;
import net.mightypork.rcalc.ParseError;
import net.mightypork.rcalc.RCalcSession;
import net.mightypork.rcalc.numbers.Fraction;
/**
* Interactive calculator
*
* @author Ondrej Hruska
*/
public class CalculatorInteractive implements Runnable {
/** Rcalc Session used by the calculator */
private RCalcSession session = new RCalcSession();
/** Show results as fractions */
private boolean fractionalMode = true;
/** Command history */
private ArrayList<String> history = new ArrayList<String>();
//@formatter:off
private static final String introMessage =
"\nRational Calculator - interactive mode\n"
+ "--------------------------------------\n"
+ "(c) Ondrej Hruska 2013\n"
+ "\n"
+ "Type 'help' for more info.\n\n"
+ "Enter expression, 'exit' to quit.";
private static final String helpMessage =
"\n### HELP ###\n\n"
+ "Commands:\n"
+ "\thelp - show this help\n"
+ "\texit - quit the calculator\n"
+ "\tdebug - toggle debug mode (default off)\n"
+ "\tdecimal - toggle decimal/fractional output\n"
+ "\tvars - print a list of variables\n"
+ "\tload filename - execute commands from a file\n"
+ "\t\t(Expressions from the file can use/change variables)\n"
+ "\n"
+ "Mathematical operations, syntax\n"
+ "\tSupported: + - * / % ^ ! ( )\n"
+ "\tMultiplication sign can be omitted where it makes sense.\n"
+ "\tAlong with fractions, decimal-point format works too.\n"
+ "\tUse 'a/b' syntax to express a fraction.\n"
+ "\n"
+ "Variables\n"
+ "\tAssign a variable:\n"
+ "\t> name = expression\n\n"
+ "\tModify a variable:\n"
+ "\t> name += expression\n"
+ "\t> name -= expression\n"
+ "\t> name *= expression\n"
+ "\t> name /= expression\n"
+ "\t> name %= expression\n"
+ "\t> name ^= expression\n\n"
+ "\tYou can also use variable names in expressions.";
//@formatter:on
/**
* Create an interactive calculator
*/
public CalculatorInteractive() {
}
@Override
public void run() {
System.out.println(introMessage);
// input scanner
Scanner sc = new Scanner(System.in);
while (true) {
System.out.print("\n> ");
// wait for input
while (!sc.hasNextLine()) {}
String expr = sc.nextLine().toLowerCase().trim();
if (expr.equals("%")) { // repeat last expression
if (history.size() > 0) {
expr = history.get(history.size() - 1);
System.out.println("| " + expr);
} else {
System.out.println("No history available.");
continue;
}
}
if (expr.equals("exit")) { // quit
System.out.println("Exit.");
return;
} else if (expr.equals("help")) { // show help
System.out.println(helpMessage);
} else if (expr.equals("debug")) { // toggle debug mode
if (session.isDebug()) {
System.out.println("[DEBUG OFF]");
session.setDebug(false);
} else {
System.out.println("[DEBUG ON]");
session.setDebug(true);
}
} else if (expr.equals("decimal")) { // toggle decimal/fractional output mode
if (fractionalMode) {
System.out.println("[FRACTIONAL MODE OFF]");
fractionalMode = false;
} else {
System.out.println("[FRACTIONAL MODE ON]");
fractionalMode = true;
}
} else if (expr.startsWith("load ")) { // load and execute expressions from file
expr = expr.replace("load ", "");
CalculatorBatch cb = new CalculatorBatch(new File(expr), true);
cb.setSymbolTable(session.getSymbolTable());
cb.run();
} else if (expr.equals("vars")) { // list variables
HashMap<String, Fraction> vars = (session.getSymbolTable());
Map<String, Fraction> varsSorted = sortByKeys(vars);
System.out.println("\nVariables\n");
for (Entry<String, Fraction> entry : varsSorted.entrySet()) {
System.out.println("\t" + entry.getKey() + " = " + entry.getValue());
}
} else { // evaluate an expression
try {
Fraction result = session.evaluate(expr);
if (result == null) {
continue;
}
history.add(expr);
System.out.println("= " + (fractionalMode ? result.toString() : result.getDoubleValue()));
} catch (ParseError pe) {
System.out.println("PARSE ERROR:\n\t" + pe.getMessage());
} catch (ArithmeticException ae) {
System.out.println("MATH ERROR:\n\t" + ae.getMessage());
}
}
}
}
/**
* Sort a map by keys, maintaining key-value pairs.<br>
* Source: StackOverflow.com
*
* @param map map to be sorted
* @return linked hash map with sorted entries
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
private static <K extends Comparable, V> Map<K, V> sortByKeys(Map<K, V> map) {
List<K> keys = new LinkedList<K>(map.keySet());
Collections.sort(keys);
Map<K, V> sortedMap = new LinkedHashMap<K, V>();
for (K key : keys) {
sortedMap.put(key, map.get(key));
}
return sortedMap;
}
}

@ -0,0 +1,36 @@
package net.mightypork.semestralka;
import java.io.File;
/**
* Application main class
*
* @author Ondrej Hruska
*/
public class Main {
/**
* Program entry point
*
* @param args command line arguemnts
*/
public static void main(String[] args) {
if (args.length == 0 || (args.length == 1 && args[0].equals("-i"))) {
Runnable task = new CalculatorInteractive();
task.run();
} else if (args.length == 1) {
Runnable task = new CalculatorBatch(new File(args[0]));
task.run();
}
}
}

@ -0,0 +1,56 @@
package net.mightypork.semestralka;
import net.mightypork.rcalc.RCalc;
/**
* RCalc unit tests
*
* @author Ondrej Hruska
*/
public class Tests {
/**
* Run the tests
*/
public static void run() {
RCalc rc = new RCalc();
rc.setDebug(true);
//@formatter:off
String[] testCases = new String[] {
// Parser unit test
"57**3 + -7^2 + (-7)^2", // power
"(13*7)(99^2) * 4(12/7) + (17-8/2)13 + (4/3)*5", // implicit multiplication
"(1-1)(1--1)(1---1)(1----1)(1--+-+-+++--+-+1)(1--+-+-+-+-----1)(5*-1)", // minus, plus
"-(15/2)+(72*43-2)-(12+1)", // minus with parentheses
"+34-(--8+2*+13)", // + at beginning of scope
// misc
"23%(6/5)",
"53/(5-4-1)",
"100!",
"24/4/3",
"(10+1)(15-3)",
"13(55-3/12)^2",
"(1/2)^-2",
"13^(1/2)", // should fail
"(1/2)*(3/4)",
"(1/2)/(3/4)", // compound fraction
};
//@formatter:on
for (String expr : testCases) {
System.out.println("\n\n###### test case begin ######");
try {
System.out.println("IN: " + expr);
System.out.println("OUT: " + rc.evaluate(expr));
} catch (Exception e) {
System.out.println("ERROR: " + e.getMessage());
}
}
}
}
Loading…
Cancel
Save