Skip to content

Commit

Permalink
function evaluation refactored, simple tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Muusssi committed Jul 24, 2015
1 parent af6f220 commit baa417e
Show file tree
Hide file tree
Showing 6 changed files with 287 additions and 154 deletions.
1 change: 1 addition & 0 deletions UnitCalc/.classpath
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
<classpathentry kind="output" path="bin"/>
</classpath>
60 changes: 60 additions & 0 deletions UnitCalc/src/tests/CalculatorCalculateTests.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package tests;

import static org.junit.Assert.*;

import java.awt.TextArea;
import java.math.BigDecimal;

import javax.swing.JTextArea;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import unitCalc.Calculator;

public class CalculatorCalculateTests {

@Before
public void setUp() throws Exception {
Calculator.initCalculator();
}

@After
public void tearDown() throws Exception {

}

void assertCalculation(String calculation, String answer) {
Calculator.inform(calculation);
assertEquals(calculation+" should equal "+answer, new BigDecimal(answer).toPlainString(), Calculator.calculate(calculation).value.stripTrailingZeros().toPlainString());
}

@Test
public void simple_arithmetic_should_work() {
assertCalculation("4+5", "9");
assertCalculation("4-5", "-1");
assertCalculation("4*5", "20");
assertCalculation("4/5", "0.8");


assertCalculation("2-(1+1)", "0");
assertCalculation("2-1+1", "2");
assertCalculation("1+2*2", "5");
assertCalculation("2*2+1", "5");
assertCalculation("(2*2+1)", "5");
}

@Test
public void basic_functions_shuold_work() {
assertCalculation("sin(0)", "0");
assertCalculation("sin(pi)", "0");
assertCalculation("sin(0.5*pi)", "1");
assertCalculation("sin(-0.5*pi)", "-1");

assertCalculation("cos(0)", "1");
assertCalculation("cos(pi)", "-1");
//assertCalculation("cos(0.5*pi)", "0"); TODO needs fixing
}

}
33 changes: 25 additions & 8 deletions UnitCalc/src/unitCalc/Calculator.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,14 @@ public class Calculator {
static Variable e;
static Measure unitlesMeasure;

public static void inform(String info) {
public static String inform(String info) {
if (resultArea == null) {
System.out.println(info);
}
else {
resultArea.append(info+"\n");
}
return null;
}

public static void setResultArea(JTextArea area) {
Expand All @@ -45,6 +46,7 @@ public static void showError(int index) {

public static Variable calculate(String calculation) {
lastCalculation = calculation;
Variable ans = null;
if (calculation.equals("const")) {
Variable.listConstants();
return null;
Expand Down Expand Up @@ -92,7 +94,7 @@ else if (Function.functionMap.containsKey(lexing.get(0).id)) {
String varName = lexing.poll().id;
lexing.poll();
LinkedList<Variable> postFix = toPostFix(lexing);
Variable ans = evaluate(postFix);
ans = evaluate(postFix);
if (ans != null) {
ans = new Variable(ans.value, varName, ans.siBase);
ans.show();
Expand Down Expand Up @@ -129,9 +131,8 @@ else if (lexingLenth > 2 && lexing.get(lexingLenth-2).type == CalcToken.TokenTyp
}
*/

Variable ans = evaluate(postFix);
ans = evaluate(postFix);
if (ans != null) {
ans = new Variable(ans.value, "ans", ans.siBase);
if (genTranslate) {
ans.show(null);
}
Expand All @@ -141,10 +142,11 @@ else if (translateTo != null) {
else {
ans.show();
}
ans = new Variable(ans.value, "ans", ans.siBase);
}
}
Calculator.inform("-----------------------");
return null;
return ans;
}

/** Evaluates an expression which should be in postfix notation */
Expand Down Expand Up @@ -261,7 +263,21 @@ else if (Function.functionMap.containsKey(tok.id)) {
}
else {
if (Unit.unitMap.containsKey(tok.id)) {
inform("Unable to set unit: '"+tok.id+"'");
if (previousTok != null) {
if (Variable.varMap.containsKey(previousTok.id)) {
inform("Error: Unable to set unit '"+tok.id+"' because "+previousTok.id+" already has a unit.");
return null;
}
else {
inform("Error: Unable to set unit '"+tok.id+"' because "+previousTok.id+" already has a unit: "
+Variable.varMap.get(previousTok.id).measure.baseUnit.abr);
return null;
}
}
else {
inform("Error: Unable to set unit "+tok.id);
return null;
}
}
else {
inform("Unknown identifier: '"+tok.id+"'");
Expand Down Expand Up @@ -823,11 +839,12 @@ public static void initCalculator() {
unit.addAlternativeAbr("pi");
unitlesMeasure.addUnit("radians", "rad", new BigDecimal("1"));


Function.initFunctionMap();
}

public static void main(String[] args) {
Calculator.initCalculator();
Function.initFunctionMap();

// the main loop
Scanner reader = new Scanner(System.in);
Expand All @@ -838,7 +855,7 @@ public static void main(String[] args) {
calculation = reader.nextLine();
answer = calculate(calculation);
if (answer != null) {
answer.show();
//answer.show();
}
}

Expand Down
142 changes: 29 additions & 113 deletions UnitCalc/src/unitCalc/Function.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,116 +12,38 @@ public class Function {
public String explanation;
public int argNum;

public FunctionEvaluators.FunctionEvaluator functionEvaluator;

public Function(String name, String identifier, int argNum, String explanation) {

public Function(String name, String identifier, int argNum, String explanation, FunctionEvaluators.FunctionEvaluator functionEvaluator) {
this.name = name;
this.identifier = identifier;
this.explanation = explanation;
this.argNum = argNum;
functionMap.put(this.identifier, this);
this.functionEvaluator = functionEvaluator;
}




public Variable evaluate(Variable[] arguments) {
// TODO functions: abs(), cbrt(), ...

// Trigonometric functions
if (this.identifier.equals("sin")) {
if (arguments[0].value.remainder(Calculator.pi.value).compareTo(BigDecimal.ZERO) == 0) {
return new Variable(BigDecimal.ZERO);
}
else {
return new Variable(BigDecimal.valueOf(Math.sin(arguments[0].value.doubleValue())));
}
}
else if (this.identifier.equals("asin")) {
if ((arguments[0].value.compareTo(BigDecimal.ONE)) > 0 || (arguments[0].value.compareTo(new BigDecimal("-1")) < 0)) {
Calculator.inform("Math error: arc sin is only defined for values between -1 and 1");
return null;
}
else if (arguments[0].value.compareTo(BigDecimal.ONE) == 0) {
return new Variable(new BigDecimal("0.5").multiply(Calculator.pi.value));
}
else if (arguments[0].value.compareTo(new BigDecimal("-1")) == 0) {
return new Variable(new BigDecimal("-0.5").multiply(Calculator.pi.value));
}
else if (arguments[0].value.compareTo(BigDecimal.ZERO) == 0) {
return new Variable(BigDecimal.ZERO);
}
return new Variable(BigDecimal.valueOf(Math.asin(arguments[0].value.doubleValue())));
}
else if (this.identifier.equals("cos")) {
return new Variable(BigDecimal.valueOf(Math.cos(arguments[0].value.doubleValue())));
}
else if (this.identifier.equals("acos")) {
if ((arguments[0].value.compareTo(BigDecimal.ONE)) > 0 || (arguments[0].value.compareTo(new BigDecimal("-1")) < 0)) {
Calculator.inform("Math error: arc cos is only defined for values between -1 and 1");
return null;
}
return new Variable(BigDecimal.valueOf(Math.acos(arguments[0].value.doubleValue())));
if (this.functionEvaluator == null) {
Calculator.inform("Error: Function '"+this.name+"' has not been implemented yet. But surely in next release.");
return null;
}
else if (this.identifier.equals("tan")) {
if (arguments[0].value.remainder(Calculator.pi.value.multiply(new BigDecimal("0.5"))).compareTo(BigDecimal.ZERO) == 0) {
if (arguments[0].value.remainder(Calculator.pi.value).compareTo(BigDecimal.ZERO) == 0) {
return new Variable(BigDecimal.ZERO);
}
else {
Calculator.inform("Math error: tan not defined at ±¹/2");
else {
for (int i=0; i<arguments.length; i++) {
if (!arguments[i].isUnitless()) {
Calculator.inform("Error: Function '"+this.name+"' only accepts unitless variables.");
Calculator.inform("One argument has unit: "+arguments[i].measure.baseUnit.abr);
return null;
}
}
else {
return new Variable(BigDecimal.valueOf(Math.tan(arguments[0].value.doubleValue())));
}
return this.functionEvaluator.evaluate(arguments);
}
else if (this.identifier.equals("atan")) {
return new Variable(BigDecimal.valueOf(Math.atan(arguments[0].value.doubleValue())));
}

//Exponents
else if (this.identifier.equals("exp")) {
if (arguments[0].value.remainder(BigDecimal.ONE).compareTo(BigDecimal.ZERO) == 0) {
return new Variable(Calculator.e.value.pow(arguments[0].value.intValue()));
}
return new Variable(BigDecimal.valueOf(Math.exp(arguments[0].value.doubleValue())));
}
else if (this.identifier.equals("ln")) {
if (arguments[0].value.compareTo(BigDecimal.ZERO) <= 0) {
Calculator.inform("Math error - Logartihms are only defined for values greater than zero.");
return null;
}
return new Variable(BigDecimal.valueOf(Math.log(arguments[0].value.doubleValue())));
}
else if (this.identifier.equals("lg")) {
if (arguments[0].value.compareTo(BigDecimal.ZERO) <= 0) {
Calculator.inform("Math error - Logartihms are only defined for values greater than zero.");
return null;
}
return new Variable(BigDecimal.valueOf(Math.log10(arguments[0].value.doubleValue())));
}

else if (this.identifier.equals("sqrt")) {
if (arguments[0].value.compareTo(BigDecimal.ZERO) < 0) {
Calculator.inform("Math error - Square root is only supported for non-negative values.");
return null;
}
return new Variable(BigDecimal.valueOf(Math.sqrt(arguments[0].value.doubleValue())));
}

else if (this.identifier.equals("cbrt")) {
return new Variable(BigDecimal.valueOf(Math.cbrt(arguments[0].value.doubleValue())));
}

else {
Calculator.inform("Error: Unimplemented function");
return null;
}

}

public static Variable flg(Variable arg) {
return null;
}

public static Variable factorial(Variable arg) {
if (!arg.isUnitless()) {
Expand All @@ -143,29 +65,23 @@ public static Variable factorial(Variable arg) {
return new Variable(ans);
}


public static Variable fsin(Variable arg) {
// Exact from table
// or Using expansions until error<1e-100
//while ()
return null;
}





public static void initFunctionMap() {
new Function("sin", "sin", 1, "");
new Function("arc sin", "asin", 1, "");
new Function("cos", "cos", 1, "");
new Function("arc cos", "acos", 1, "");
new Function("tan", "tan", 1, "");
new Function("arc tan", "atan", 1, "");
new Function("exponent", "exp", 1, "");
new Function("natural logarithm", "ln", 1, "");
new Function("logarithm with base 10", "lg", 1, "");
new Function("square root", "sqrt", 1, "");
new Function("cubic root", "cbrt", 1, "");
//new Function("date meaning time since 1970-01-01 for comparing dates", "date", 3, "");
FunctionEvaluators funcEvaluators = new FunctionEvaluators();
new Function("sin", "sin", 1, "", funcEvaluators.new SinEvaluator());
new Function("arc sin", "asin", 1, "", funcEvaluators.new AsinEvaluator());
new Function("cos", "cos", 1, "", funcEvaluators.new CosEvaluator());
new Function("arc cos", "acos", 1, "", funcEvaluators.new AcosEvaluator());
new Function("tan", "tan", 1, "", funcEvaluators.new TanEvaluator());
new Function("arc tan", "atan", 1, "", funcEvaluators.new AtanEvaluator());
new Function("exponent", "exp", 1, "", funcEvaluators.new ExpEvaluator());
new Function("natural logarithm", "ln", 1, "", funcEvaluators.new LnEvaluator());
new Function("logarithm with base 10", "lg", 1, "", funcEvaluators.new LgEvaluator());
new Function("square root", "sqrt", 1, "", funcEvaluators.new SqrtEvaluator());
new Function("cubic root", "cbrt", 1, "", funcEvaluators.new CbrtEvaluator());
new Function("date", "date", 3, "date returns time since 1970-01-01 for comparing dates", null);
}
}
Loading

0 comments on commit baa417e

Please sign in to comment.