rounding fix + color display + refactor

This commit is contained in:
serso 2011-10-21 00:29:55 +04:00
parent 2f385c83eb
commit f5d0fa9d3b
10 changed files with 300 additions and 231 deletions

View File

@ -6,8 +6,14 @@
package org.solovyev.android.calculator; package org.solovyev.android.calculator;
import android.content.Context; import android.content.Context;
import android.graphics.Color;
import android.text.Html;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log;
import android.widget.TextView; import android.widget.TextView;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.model.ParseException;
import org.solovyev.android.calculator.model.TextProcessor;
/** /**
* User: serso * User: serso
@ -18,6 +24,9 @@ public class CalculatorDisplay extends TextView {
private boolean valid = true; private boolean valid = true;
@NotNull
private final static TextProcessor<String> textHighlighter = new TextHighlighter(Color.WHITE);
public CalculatorDisplay(Context context) { public CalculatorDisplay(Context context) {
super(context); super(context);
} }
@ -44,4 +53,22 @@ public class CalculatorDisplay extends TextView {
setValid(true); setValid(true);
} }
public synchronized void redraw() {
if (isValid()) {
String text = getText().toString();
Log.d(this.getClass().getName(), text);
try {
text = textHighlighter.process(text);
} catch (ParseException e) {
Log.e(this.getClass().getName(), e.getMessage(), e);
}
Log.d(this.getClass().getName(), text);
super.setText(Html.fromHtml(text), BufferType.EDITABLE);
}
}
} }

View File

@ -171,19 +171,20 @@ public enum CalculatorModel implements CursorControl, HistoryControl<CalculatorH
} }
private void evaluate(@Nullable final String expression) { private void evaluate(@Nullable final String expression) {
final CalculatorDisplay localDisplay = display;
if (!StringUtils.isEmpty(expression)) { if (!StringUtils.isEmpty(expression)) {
try { try {
Log.d(CalculatorModel.class.getName(), "Trying to evaluate: " + expression /*+ StringUtils.fromStackTrace(Thread.currentThread().getStackTrace())*/); Log.d(CalculatorModel.class.getName(), "Trying to evaluate: " + expression /*+ StringUtils.fromStackTrace(Thread.currentThread().getStackTrace())*/);
localDisplay.setText(calculatorEngine.evaluate(JsclOperation.numeric, expression)); display.setText(calculatorEngine.evaluate(JsclOperation.numeric, expression));
} catch (EvalError e) { } catch (EvalError e) {
handleEvaluationException(expression, localDisplay, e); handleEvaluationException(expression, display, e);
} catch (ParseException e) { } catch (ParseException e) {
handleEvaluationException(expression, localDisplay, e); handleEvaluationException(expression, display, e);
} }
} else { } else {
localDisplay.setText(""); this.display.setText("");
} }
this.display.redraw();
} }
private void handleEvaluationException(@NotNull String expression, @NotNull CalculatorDisplay localDisplay, @NotNull Exception e) { private void handleEvaluationException(@NotNull String expression, @NotNull CalculatorDisplay localDisplay, @NotNull Exception e) {
@ -212,15 +213,19 @@ public enum CalculatorModel implements CursorControl, HistoryControl<CalculatorH
final MathType.Result mathType = MathType.getType(text, 0); final MathType.Result mathType = MathType.getType(text, 0);
int cursorPositionOffset = 0; int cursorPositionOffset = 0;
final StringBuilder textToBeInserted = new StringBuilder(text); final StringBuilder textToBeInserted = new StringBuilder(text);
switch (mathType.getMathType()) { switch (mathType.getMathType()) {
case function: case function:
textToBeInserted.append("()"); textToBeInserted.append("()");
cursorPositionOffset = -1; cursorPositionOffset = -1;
break; break;
case group_symbols: }
if (cursorPositionOffset == 0) {
if ( MathType.openGroupSymbols.contains(text) ) {
cursorPositionOffset = -1; cursorPositionOffset = -1;
break; }
} }
editor.getText().insert(editor.getSelectionStart(), textToBeInserted.toString()); editor.getText().insert(editor.getSelectionStart(), textToBeInserted.toString());
@ -256,6 +261,7 @@ public enum CalculatorModel implements CursorControl, HistoryControl<CalculatorH
setValuesFromHistory(this.display, editorHistoryState.getDisplayState()); setValuesFromHistory(this.display, editorHistoryState.getDisplayState());
editor.redraw(); editor.redraw();
display.redraw();
} }
} }

View File

@ -97,15 +97,15 @@ public class TextHighlighter implements TextProcessor<String> {
for (; i < s.length(); i++) { for (; i < s.length(); i++) {
char ch = s.charAt(i); char ch = s.charAt(i);
if (MathType.openGroupSymbols.contains(ch)) { if (MathType.open_group_symbol.getTokens().contains(String.valueOf(ch))) {
result.append(ch); result.append(ch);
result.append("</font>"); result.append("</font>");
i = processBracketGroup(result, s, i + 1, numberOfOpenings + 1, maxNumberOfGroups); i = processBracketGroup(result, s, i + 1, numberOfOpenings + 1, maxNumberOfGroups);
result.append("<font color=\"").append(getColor(maxNumberOfGroups, numberOfOpenings)).append("\">"); result.append("<font color=\"").append(getColor(maxNumberOfGroups, numberOfOpenings)).append("\">");
if (i < s.length() && MathType.closeGroupSymbols.contains(s.charAt(i))) { if (i < s.length() && MathType.close_group_symbol.getTokens().contains(String.valueOf(s.charAt(i)))) {
result.append(s.charAt(i)); result.append(s.charAt(i));
} }
} else if (MathType.closeGroupSymbols.contains(ch)) { } else if (MathType.close_group_symbol.getTokens().contains(String.valueOf(ch))) {
break; break;
} else { } else {
result.append(ch); result.append(ch);

View File

@ -7,12 +7,14 @@
package org.solovyev.android.calculator.jscl; package org.solovyev.android.calculator.jscl;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.math.MathType; import org.solovyev.android.calculator.math.MathType;
import org.solovyev.android.calculator.model.CalculatorEngine; import org.solovyev.android.calculator.model.CalculatorEngine;
import org.solovyev.android.calculator.model.ParseException; import org.solovyev.android.calculator.model.ParseException;
import org.solovyev.android.calculator.model.TextProcessor; import org.solovyev.android.calculator.model.TextProcessor;
import org.solovyev.common.utils.MathUtils;
import org.solovyev.util.math.Complex; import java.math.BigDecimal;
import java.text.DecimalFormat;
/** /**
* User: serso * User: serso
@ -25,11 +27,12 @@ class FromJsclNumericTextProcessor implements TextProcessor<String> {
@Override @Override
public String process(@NotNull String result) throws ParseException { public String process(@NotNull String result) throws ParseException {
try { try {
final Double roundedValue = round(result); final Double doubleValue = Double.valueOf(result);
if ( roundedValue.isInfinite() ) {
result = MathType.INFINITY; if (doubleValue.isInfinite()) {
result = MathType.INFINITY;
} else { } else {
result = String.valueOf(roundedValue); result = format(doubleValue);
} }
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
result = result.replace(MathType.INFINITY_JSCL, MathType.INFINITY); result = result.replace(MathType.INFINITY_JSCL, MathType.INFINITY);
@ -49,6 +52,21 @@ class FromJsclNumericTextProcessor implements TextProcessor<String> {
return result; return result;
} }
private String format(@NotNull String value) {
return format(Double.valueOf(value));
}
private String format(@NotNull Double value) {
if (!value.isInfinite() && !value.isNaN()) {
final DecimalFormat df = new DecimalFormat();
df.setGroupingUsed(false);
df.setMaximumFractionDigits(CalculatorEngine.instance.getPrecision());
return df.format(new BigDecimal(value).setScale(CalculatorEngine.instance.getPrecision(), BigDecimal.ROUND_HALF_UP).doubleValue());
} else {
return String.valueOf(value);
}
}
protected String createResultForComplexNumber(@NotNull final String s) { protected String createResultForComplexNumber(@NotNull final String s) {
final Complex complex = new Complex(); final Complex complex = new Complex();
@ -56,13 +74,13 @@ class FromJsclNumericTextProcessor implements TextProcessor<String> {
// may be it's just complex number // may be it's just complex number
int plusIndex = s.lastIndexOf("+"); int plusIndex = s.lastIndexOf("+");
if (plusIndex >= 0) { if (plusIndex >= 0) {
complex.setReal(round(s.substring(0, plusIndex))); complex.setReal(format(s.substring(0, plusIndex)));
result += complex.getReal(); result += complex.getReal();
result += "+"; result += "+";
} else { } else {
plusIndex = s.lastIndexOf("-"); plusIndex = s.lastIndexOf("-");
if (plusIndex >= 0) { if (plusIndex >= 0) {
complex.setReal(round(s.substring(0, plusIndex))); complex.setReal(format(s.substring(0, plusIndex)));
result += complex.getReal(); result += complex.getReal();
result += "-"; result += "-";
} }
@ -71,7 +89,7 @@ class FromJsclNumericTextProcessor implements TextProcessor<String> {
int multiplyIndex = s.indexOf("*"); int multiplyIndex = s.indexOf("*");
if (multiplyIndex >= 0) { if (multiplyIndex >= 0) {
complex.setImaginary(round(s.substring(plusIndex >= 0 ? plusIndex + 1 : 0, multiplyIndex))); complex.setImaginary(format(s.substring(plusIndex >= 0 ? plusIndex + 1 : 0, multiplyIndex)));
result += complex.getImaginary(); result += complex.getImaginary();
} }
@ -81,8 +99,31 @@ class FromJsclNumericTextProcessor implements TextProcessor<String> {
return result; return result;
} }
private Double round(@NotNull String result) { private class Complex {
final Double dResult = Double.valueOf(result);
return MathUtils.round(dResult, CalculatorEngine.instance.getNumberOfFractionDigits()); @Nullable
private String real;
@Nullable
private String imaginary;
@Nullable
public String getReal() {
return real;
}
public void setReal(@Nullable String real) {
this.real = real;
}
@Nullable
public String getImaginary() {
return imaginary;
}
public void setImaginary(@Nullable String imaginary) {
this.imaginary = imaginary;
}
} }
} }

View File

@ -49,8 +49,8 @@ public class Functions {
allPrefix = functions; allPrefix = functions;
} }
public final static Character FACT = '!'; public final static String FACT = "!";
public final static Character DEGREE = '°'; public final static String DEGREE = "°";
public static final List<Character> allPostfix = Arrays.asList(FACT, DEGREE); public static final List<String> allPostfix = Arrays.asList(FACT, DEGREE);
} }

View File

@ -9,60 +9,119 @@ import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.CharacterAtPositionFinder; import org.solovyev.android.calculator.CharacterAtPositionFinder;
import org.solovyev.android.calculator.StartsWithFinder; import org.solovyev.android.calculator.StartsWithFinder;
import org.solovyev.android.calculator.model.CalculatorEngine; import org.solovyev.android.calculator.model.CalculatorEngine;
import org.solovyev.common.utils.CollectionsUtils;
import org.solovyev.common.utils.Finder; import org.solovyev.common.utils.Finder;
import java.util.Arrays; import java.util.*;
import java.util.List;
import static org.solovyev.common.utils.CollectionsUtils.get; import static org.solovyev.common.utils.CollectionsUtils.get;
public enum MathType { public enum MathType {
digit, digit(100, true, true, "0", "1", "2", "3", "4", "5", "6", "7", "8", "9") {
dot, @Override
power_10, public boolean isNeedMultiplicationSignBefore(@NotNull MathType mathTypeBefore) {
constant, return super.isNeedMultiplicationSignBefore(mathTypeBefore) && mathTypeBefore != digit && mathTypeBefore != dot;
function, }
postfix_function, },
unary_operation,
binary_operation, dot(200, true, true, "."){
group_symbols, @Override
open_group_symbol, public boolean isNeedMultiplicationSignBefore(@NotNull MathType mathTypeBefore) {
close_group_symbol, return super.isNeedMultiplicationSignBefore(mathTypeBefore) && mathTypeBefore != digit;
text; }
},
power_10(300, true, false, "E"),
postfix_function(400, true, false, Functions.allPostfix),
unary_operation(500, false, false, "-", "=", "!"),
binary_operation(600, false, false, "-", "+", "*", "×", "", "/", "^"),
open_group_symbol(800, true, false, "[", "(", "{") {
@Override
public boolean isNeedMultiplicationSignBefore(@NotNull MathType mathTypeBefore) {
return super.isNeedMultiplicationSignBefore(mathTypeBefore) && mathTypeBefore != function;
}
},
close_group_symbol(900, false, true, "]", ")", "}") {
@Override
public boolean isNeedMultiplicationSignBefore(@NotNull MathType mathTypeBefore) {
return false;
}
},
function(1000, true, true, Functions.allPrefix),
constant(1100, true, true) {
@NotNull
@Override
public List<String> getTokens() {
return CalculatorEngine.instance.getVarsRegister().getVarNames();
}
},
text(1200, false, false);
@NotNull
private final List<String> tokens;
@NotNull
private final Integer priority;
private final boolean needMultiplicationSignBefore;
private final boolean needMultiplicationSignAfter;
MathType(@NotNull Integer priority,
boolean needMultiplicationSignBefore,
boolean needMultiplicationSignAfter,
@NotNull String... tokens) {
this(priority, needMultiplicationSignBefore, needMultiplicationSignAfter, CollectionsUtils.asList(tokens));
}
MathType(@NotNull Integer priority,
boolean needMultiplicationSignBefore,
boolean needMultiplicationSignAfter,
@NotNull List<String> tokens) {
this.priority = priority;
this.needMultiplicationSignBefore = needMultiplicationSignBefore;
this.needMultiplicationSignAfter = needMultiplicationSignAfter;
this.tokens = Collections.unmodifiableList(tokens);
}
@NotNull
public List<String> getTokens() {
return tokens;
}
private boolean isNeedMultiplicationSignBefore() {
return needMultiplicationSignBefore;
}
private boolean isNeedMultiplicationSignAfter() {
return needMultiplicationSignAfter;
}
public boolean isNeedMultiplicationSignBefore(@NotNull MathType mathTypeBefore) {
return needMultiplicationSignBefore && mathTypeBefore.isNeedMultiplicationSignAfter();
}
public static final List<String> openGroupSymbols = Arrays.asList("[]", "()", "{}");
public final static Character POWER_10 = 'E'; public final static Character POWER_10 = 'E';
public final static String POWER_10_JSCL = "10^"; public final static String POWER_10_JSCL = "10^";
public static final String IMAGINARY_NUMBER = "i"; public static final String IMAGINARY_NUMBER = "i";
public static final String IMAGINARY_NUMBER_JSCL = "sqrt(-1)"; public static final String IMAGINARY_NUMBER_JSCL = "sqrt(-1)";
public static final String PI = "π"; public static final String PI = "π";
public static final String E = "e"; public static final String E = "e";
public final static String NAN = "NaN"; public final static String NAN = "NaN";
public final static String INFINITY = ""; public final static String INFINITY = "";
public final static String INFINITY_JSCL = "Infinity"; public final static String INFINITY_JSCL = "Infinity";
public static final List<String> constants = Arrays.asList(E, PI, IMAGINARY_NUMBER, NAN, INFINITY); public static final List<String> constants = Arrays.asList(E, PI, IMAGINARY_NUMBER, NAN, INFINITY);
public static final List<String> digits = Arrays.asList("0", "1", "2", "3", "4", "5", "6", "7", "8", "9");
public static final List<Character> dots = Arrays.asList('.');
public static final List<Character> unaryOperations = Arrays.asList('-', '=', '!');
public static final List<Character> binaryOperations = Arrays.asList('-', '+', '*', '×', '∙', '/', '^');
public static final List<String> prefixFunctions = Functions.allPrefix;
public static final List<Character> postfixFunctions = Functions.allPostfix;
public static final List<String> groupSymbols = Arrays.asList("[]", "()", "{}");
public static final List<Character> openGroupSymbols = Arrays.asList('[', '(', '{');
public static final List<Character> closeGroupSymbols = Arrays.asList(']', ')', '}');
/** /**
* Method determines mathematical entity type for text substring starting from ith index * Method determines mathematical entity type for text substring starting from ith index
* *
@ -80,67 +139,39 @@ public enum MathType {
return new Result(MathType.text, text); return new Result(MathType.text, text);
} }
final StartsWithFinder stringStartWithFinder = new StartsWithFinder(text, i); final StartsWithFinder startsWithFinder = new StartsWithFinder(text, i);
final CharacterAtPositionFinder characterStartWithFinder = new CharacterAtPositionFinder(text, i);
String foundString = get(digits, stringStartWithFinder); for (MathType mathType : getMathTypesByPriority()) {
if (foundString != null) { final String s = get(mathType.getTokens(), startsWithFinder);
return new Result(MathType.digit, foundString); if ( s != null ) {
} return new Result(mathType, s);
}
Character foundCharacter = get(dots, characterStartWithFinder);
if (foundCharacter != null) {
return new Result(dot, String.valueOf(foundCharacter));
}
foundCharacter = characterStartWithFinder.isFound(POWER_10) ? POWER_10 : null;
if (foundCharacter != null) {
return new Result(power_10, String.valueOf(foundCharacter));
}
foundCharacter = get(postfixFunctions, characterStartWithFinder);
if (foundCharacter != null) {
return new Result(postfix_function, String.valueOf(foundCharacter));
}
foundCharacter = get(unaryOperations, characterStartWithFinder);
if (foundCharacter != null) {
return new Result(unary_operation, String.valueOf(foundCharacter));
}
foundCharacter = get(binaryOperations, characterStartWithFinder);
if (foundCharacter != null) {
return new Result(binary_operation, String.valueOf(foundCharacter));
}
foundString = get(groupSymbols, stringStartWithFinder);
if (foundString != null) {
return new Result(MathType.group_symbols, foundString);
}
foundCharacter = get(openGroupSymbols, characterStartWithFinder);
if (foundCharacter != null) {
return new Result(open_group_symbol, String.valueOf(foundCharacter));
}
foundCharacter = get(closeGroupSymbols, characterStartWithFinder);
if (foundCharacter != null) {
return new Result(close_group_symbol, String.valueOf(foundCharacter));
}
foundString = get(prefixFunctions, stringStartWithFinder);
if (foundString != null) {
return new Result(MathType.function, foundString);
}
foundString = get(CalculatorEngine.instance.getVarsRegister().getVarNames(), stringStartWithFinder);
if (foundString != null) {
return new Result(MathType.constant, foundString);
} }
return new Result(MathType.text, text.substring(i)); return new Result(MathType.text, text.substring(i));
} }
private static List<MathType> mathTypesByPriority;
@NotNull
private static List<MathType> getMathTypesByPriority() {
if ( mathTypesByPriority == null ) {
final List<MathType> result = CollectionsUtils.asList(MathType.values());
Collections.sort(result, new Comparator<MathType>() {
@Override
public int compare(MathType l, MathType r) {
return l.priority.compareTo(r.priority);
}
});
mathTypesByPriority = result;
}
return mathTypesByPriority;
}
public static class Result { public static class Result {
@NotNull @NotNull
@ -149,7 +180,7 @@ public enum MathType {
@NotNull @NotNull
private final String match; private final String match;
public Result(@NotNull MathType mathType, @NotNull String match){ public Result(@NotNull MathType mathType, @NotNull String match) {
this.mathType = mathType; this.mathType = mathType;
this.match = match; this.match = match;

View File

@ -39,7 +39,7 @@ public enum CalculatorEngine {
@NotNull @NotNull
private final Object lock = new Object(); private final Object lock = new Object();
private int numberOfFractionDigits = 5; private int precision = 5;
@NotNull @NotNull
public final TextProcessor<PreparedExpression> preprocessor = new ToJsclTextProcessor(); public final TextProcessor<PreparedExpression> preprocessor = new ToJsclTextProcessor();
@ -85,12 +85,12 @@ public enum CalculatorEngine {
} }
} }
public int getNumberOfFractionDigits() { public int getPrecision() {
return numberOfFractionDigits; return precision;
} }
public void setNumberOfFractionDigits(int numberOfFractionDigits) { public void setPrecision(int precision) {
this.numberOfFractionDigits = numberOfFractionDigits; this.precision = precision;
} }
public void init(@Nullable Context context, @Nullable SharedPreferences preferences) throws EvalError { public void init(@Nullable Context context, @Nullable SharedPreferences preferences) throws EvalError {
@ -105,7 +105,7 @@ public enum CalculatorEngine {
if (preferences != null) { if (preferences != null) {
final NumberMapper<Integer> integerNumberMapper = new NumberMapper<Integer>(Integer.class); final NumberMapper<Integer> integerNumberMapper = new NumberMapper<Integer>(Integer.class);
//noinspection ConstantConditions //noinspection ConstantConditions
this.setNumberOfFractionDigits(integerNumberMapper.parseValue(preferences.getString(RESULT_PRECISION_P_KEY, RESULT_PRECISION_DEFAULT))); this.setPrecision(integerNumberMapper.parseValue(preferences.getString(RESULT_PRECISION_P_KEY, RESULT_PRECISION_DEFAULT)));
} }
varsRegister.init(context, preferences); varsRegister.init(context, preferences);

View File

@ -50,7 +50,7 @@ class ToJsclTextProcessor implements TextProcessor<PreparedExpression> {
// NOTE: fix for jscl for EMPTY functions processing (see tests) // NOTE: fix for jscl for EMPTY functions processing (see tests)
startsWithFinder.setI(i + 1); startsWithFinder.setI(i + 1);
if ( i < s.length() && CollectionsUtils.get(MathType.groupSymbols, startsWithFinder) != null) { if ( i < s.length() && CollectionsUtils.get(MathType.openGroupSymbols, startsWithFinder) != null) {
throw new ParseException("Empty function: " + mathTypeResult.getMatch()); throw new ParseException("Empty function: " + mathTypeResult.getMatch());
/*i += 2; /*i += 2;
sb.append("(" + SPECIAL_STRING + ")"); sb.append("(" + SPECIAL_STRING + ")");
@ -78,7 +78,7 @@ class ToJsclTextProcessor implements TextProcessor<PreparedExpression> {
startsWithFinder.setI(i); startsWithFinder.setI(i);
int offset = 0; int offset = 0;
String functionName = CollectionsUtils.get(MathType.prefixFunctions, startsWithFinder); String functionName = CollectionsUtils.get(MathType.function.getTokens(), startsWithFinder);
if (functionName == null) { if (functionName == null) {
String varName = CollectionsUtils.get(CalculatorEngine.instance.getVarsRegister().getVarNames(), startsWithFinder); String varName = CollectionsUtils.get(CalculatorEngine.instance.getVarsRegister().getVarNames(), startsWithFinder);
if (varName != null) { if (varName != null) {
@ -114,7 +114,7 @@ class ToJsclTextProcessor implements TextProcessor<PreparedExpression> {
for (Var var : CalculatorEngine.instance.getVarsRegister().getVars()) { for (Var var : CalculatorEngine.instance.getVarsRegister().getVars()) {
if (!var.isSystem()) { if (!var.isSystem()) {
if (s.startsWith(var.getName(), i)) { if (s.startsWith(var.getName(), i)) {
if (CollectionsUtils.get(MathType.prefixFunctions, startsWithFinder) == null) { if (CollectionsUtils.get(MathType.function.getTokens(), startsWithFinder) == null) {
} }
} }
} }
@ -149,7 +149,7 @@ class ToJsclTextProcessor implements TextProcessor<PreparedExpression> {
if (i > 0) { if (i > 0) {
final EndsWithFinder endsWithFinder = new EndsWithFinder(s); final EndsWithFinder endsWithFinder = new EndsWithFinder(s);
endsWithFinder.setI(i + 1); endsWithFinder.setI(i + 1);
if (!CollectionsUtils.contains(MathType.prefixFunctions, FilterType.included, endsWithFinder)) { if (!CollectionsUtils.contains(MathType.function.getTokens(), FilterType.included, endsWithFinder)) {
MathType type = MathType.getType(s, i).getMathType(); MathType type = MathType.getType(s, i).getMathType();
if (type != MathType.constant) { if (type != MathType.constant) {
return true; return true;
@ -204,36 +204,16 @@ class ToJsclTextProcessor implements TextProcessor<PreparedExpression> {
@NotNull String s, @NotNull String s,
int i, int i,
@Nullable MathType.Result mathTypeBeforeResult) { @Nullable MathType.Result mathTypeBeforeResult) {
MathType.Result result = MathType.getType(s, i); final MathType.Result result = MathType.getType(s, i);
if (i > 0) { if (i > 0) {
final MathType mathType = result.getMathType(); final MathType current = result.getMathType();
assert mathTypeBeforeResult != null; assert mathTypeBeforeResult != null;
final MathType mathTypeBefore = mathTypeBeforeResult.getMathType(); final MathType before = mathTypeBeforeResult.getMathType();
if (mathTypeBefore == MathType.constant || (mathTypeBefore != MathType.binary_operation && if (current.isNeedMultiplicationSignBefore(before)) {
mathTypeBefore != MathType.unary_operation && sb.append("*");
mathTypeBefore != MathType.power_10 &&
mathTypeBefore != MathType.function &&
mathTypeBefore != MathType.open_group_symbol)) {
if (mathType == MathType.constant) {
sb.append("*");
} else if (mathType == MathType.power_10) {
sb.append("*");
} else if (mathType == MathType.open_group_symbol && mathTypeBefore != null) {
sb.append("*");
} else if (mathType == MathType.digit && ((mathTypeBefore != MathType.digit && mathTypeBefore != MathType.dot) || mathTypeBefore == MathType.constant)) {
sb.append("*");
} else {
for (String function : MathType.prefixFunctions) {
if (s.startsWith(function, i)) {
sb.append("*");
break;
}
}
}
} }
} }

View File

@ -9,7 +9,6 @@ import bsh.EvalError;
import org.junit.Assert; import org.junit.Assert;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.simpleframework.xml.Attribute;
import org.solovyev.android.calculator.jscl.JsclOperation; import org.solovyev.android.calculator.jscl.JsclOperation;
/** /**
@ -28,27 +27,52 @@ public class CalculatorEngineTest {
public void testEvaluate() throws Exception { public void testEvaluate() throws Exception {
final CalculatorEngine cm = CalculatorEngine.instance; final CalculatorEngine cm = CalculatorEngine.instance;
Assert.assertEquals("4.0", cm.evaluate(JsclOperation.numeric, "2+2")); Assert.assertEquals("4", cm.evaluate(JsclOperation.numeric, "2+2"));
Assert.assertEquals("-0.7568", cm.evaluate(JsclOperation.numeric, "sin(4)")); Assert.assertEquals("-0.757", cm.evaluate(JsclOperation.numeric, "sin(4)"));
Assert.assertEquals("0.5236", cm.evaluate(JsclOperation.numeric, "asin(0.5)")); Assert.assertEquals("0.524", cm.evaluate(JsclOperation.numeric, "asin(0.5)"));
Assert.assertEquals("-0.39626", cm.evaluate(JsclOperation.numeric, "sin(4)asin(0.5)")); Assert.assertEquals("-0.396", cm.evaluate(JsclOperation.numeric, "sin(4)asin(0.5)"));
Assert.assertEquals("-0.5604", cm.evaluate(JsclOperation.numeric, "sin(4)asin(0.5)sqrt(2)")); Assert.assertEquals("-0.56", cm.evaluate(JsclOperation.numeric, "sin(4)asin(0.5)sqrt(2)"));
Assert.assertEquals("-0.5604", cm.evaluate(JsclOperation.numeric, "sin(4)asin(0.5)√(2)")); Assert.assertEquals("-0.56", cm.evaluate(JsclOperation.numeric, "sin(4)asin(0.5)√(2)"));
Assert.assertEquals("7.38906", cm.evaluate(JsclOperation.numeric, "e^2")); Assert.assertEquals("7.389", cm.evaluate(JsclOperation.numeric, "e^2"));
Assert.assertEquals("7.38906", cm.evaluate(JsclOperation.numeric, "exp(1)^2")); Assert.assertEquals("7.389", cm.evaluate(JsclOperation.numeric, "exp(1)^2"));
Assert.assertEquals("7.38906", cm.evaluate(JsclOperation.numeric, "exp(2)")); Assert.assertEquals("7.389", cm.evaluate(JsclOperation.numeric, "exp(2)"));
Assert.assertEquals("2.0+i", cm.evaluate(JsclOperation.numeric, "2*1+sqrt(-1)")); Assert.assertEquals("2+i", cm.evaluate(JsclOperation.numeric, "2*1+sqrt(-1)"));
Assert.assertEquals("0.92054+3.14159i", cm.evaluate(JsclOperation.numeric, "ln(5cosh(38π√(2cos(2))))")); Assert.assertEquals("0.921+3.142i", cm.evaluate(JsclOperation.numeric, "ln(5cosh(38π√(2cos(2))))"));
Assert.assertEquals("7.38906i", cm.evaluate(JsclOperation.numeric, "iexp(2)")); Assert.assertEquals("7.389i", cm.evaluate(JsclOperation.numeric, "iexp(2)"));
Assert.assertEquals("2.0+7.38906i", cm.evaluate(JsclOperation.numeric, "2+iexp(2)")); Assert.assertEquals("2+7.389i", cm.evaluate(JsclOperation.numeric, "2+iexp(2)"));
Assert.assertEquals("2.0+7.38906i", cm.evaluate(JsclOperation.numeric, "2+√(-1)exp(2)")); Assert.assertEquals("2+7.389i", cm.evaluate(JsclOperation.numeric, "2+√(-1)exp(2)"));
Assert.assertEquals("2.0-2.5i", cm.evaluate(JsclOperation.numeric, "2-2.5i")); Assert.assertEquals("2-2.5i", cm.evaluate(JsclOperation.numeric, "2-2.5i"));
Assert.assertEquals("-2.0-2.5i", cm.evaluate(JsclOperation.numeric, "-2-2.5i")); Assert.assertEquals("-2-2.5i", cm.evaluate(JsclOperation.numeric, "-2-2.5i"));
Assert.assertEquals("-2.0+2.5i", cm.evaluate(JsclOperation.numeric, "-2+2.5i")); Assert.assertEquals("-2+2.5i", cm.evaluate(JsclOperation.numeric, "-2+2.5i"));
Assert.assertEquals("-2.0+2.1i", cm.evaluate(JsclOperation.numeric, "-2+2.1i")); Assert.assertEquals("-2+2.1i", cm.evaluate(JsclOperation.numeric, "-2+2.1i"));
Assert.assertEquals("-3.41007+3.41007i", cm.evaluate(JsclOperation.numeric, "(5tan(2i)+2i)/(1-i)")); Assert.assertEquals("-3.41+3.41i", cm.evaluate(JsclOperation.numeric, "(5tan(2i)+2i)/(1-i)"));
Assert.assertEquals("-0.1-0.2i", cm.evaluate(JsclOperation.numeric, "(1-i)/(2+6i)")); Assert.assertEquals("-0.1-0.2i", cm.evaluate(JsclOperation.numeric, "(1-i)/(2+6i)"));
CalculatorEngine.instance.getVarsRegister().addVar(null, new Var.Builder("si", 5d));
Assert.assertEquals("-0.959", cm.evaluate(JsclOperation.numeric, "sin(5)"));
Assert.assertEquals("-4.795", cm.evaluate(JsclOperation.numeric, "sin(5)si"));
Assert.assertEquals("-23.973", cm.evaluate(JsclOperation.numeric, "sisin(5)si"));
Assert.assertEquals("-23.973", cm.evaluate(JsclOperation.numeric, "si*sin(5)si"));
Assert.assertEquals("-3.309", cm.evaluate(JsclOperation.numeric, "sisin(5si)si"));
CalculatorEngine.instance.getVarsRegister().addVar(null, new Var.Builder("s", 1d));
Assert.assertEquals("5", cm.evaluate(JsclOperation.numeric, "si"));
CalculatorEngine.instance.getVarsRegister().addVar(null, new Var.Builder("k", 3.5d));
CalculatorEngine.instance.getVarsRegister().addVar(null, new Var.Builder("k1", 4d));
Assert.assertEquals("4", cm.evaluate(JsclOperation.numeric, "k11"));
CalculatorEngine.instance.getVarsRegister().addVar(null, new Var.Builder("t", (String)null));
Assert.assertEquals("11×t", cm.evaluate(JsclOperation.numeric, "t11"));
Assert.assertEquals("11×e×t", cm.evaluate(JsclOperation.numeric, "t11e"));
Assert.assertEquals("11×Infinity×t", cm.evaluate(JsclOperation.numeric, "t11∞"));
Assert.assertEquals("-t+t^3", cm.evaluate(JsclOperation.numeric, "t(t-1)(t+1)"));
}
@Test
public void testEmptyFunction() throws Exception {
final CalculatorEngine cm = CalculatorEngine.instance;
try { try {
cm.evaluate(JsclOperation.numeric, "cos(cos(cos(cos(acos(acos(acos(acos(acos(acos(acos(acos(cos(cos(cos(cos(cosh(acos(cos(cos(cos(cos(cos(acos(acos(acos(acos(acos(acos(acos(acos(cos(cos(cos(cos(cosh(acos(cos())))))))))))))))))))))))))))))))))))))"); cm.evaluate(JsclOperation.numeric, "cos(cos(cos(cos(acos(acos(acos(acos(acos(acos(acos(acos(cos(cos(cos(cos(cosh(acos(cos(cos(cos(cos(cos(acos(acos(acos(acos(acos(acos(acos(acos(cos(cos(cos(cos(cosh(acos(cos())))))))))))))))))))))))))))))))))))))");
Assert.fail(); Assert.fail();
@ -60,78 +84,28 @@ public class CalculatorEngineTest {
Assert.fail(); Assert.fail();
} catch (ParseException e){ } catch (ParseException e){
} }
Assert.assertEquals("0.73909", cm.evaluate(JsclOperation.numeric, "cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(1))))))))))))))))))))))))))))))))))))")); Assert.assertEquals("0.739", cm.evaluate(JsclOperation.numeric, "cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(cos(1))))))))))))))))))))))))))))))))))))"));
CalculatorEngine.instance.getVarsRegister().addVar(null, new Var.Builder("si", 5d)); CalculatorEngine.instance.getVarsRegister().addVar(null, new Var.Builder("si", 5d));
Assert.assertEquals("5.0", cm.evaluate(JsclOperation.numeric, "si")); Assert.assertEquals("5", cm.evaluate(JsclOperation.numeric, "si"));
try { try {
cm.evaluate(JsclOperation.numeric, "sin"); cm.evaluate(JsclOperation.numeric, "sin");
Assert.fail(); Assert.fail();
} catch (EvalError e) { } catch (EvalError e) {
} }
Assert.assertEquals("-0.95892", cm.evaluate(JsclOperation.numeric, "sin(5)"));
Assert.assertEquals("-4.79462", cm.evaluate(JsclOperation.numeric, "sin(5)si"));
Assert.assertEquals("-23.97311", cm.evaluate(JsclOperation.numeric, "sisin(5)si"));
Assert.assertEquals("-23.97311", cm.evaluate(JsclOperation.numeric, "si*sin(5)si"));
Assert.assertEquals("-3.30879", cm.evaluate(JsclOperation.numeric, "sisin(5si)si"));
CalculatorEngine.instance.getVarsRegister().addVar(null, new Var.Builder("s", 1d));
Assert.assertEquals("5.0", cm.evaluate(JsclOperation.numeric, "si"));
CalculatorEngine.instance.getVarsRegister().addVar(null, new Var.Builder("k", 3.5d));
CalculatorEngine.instance.getVarsRegister().addVar(null, new Var.Builder("k1", 4d));
Assert.assertEquals("4.0", cm.evaluate(JsclOperation.numeric, "k11"));
CalculatorEngine.instance.getVarsRegister().addVar(null, new Var.Builder("t", (String)null));
Assert.assertEquals("11*t", cm.evaluate(JsclOperation.numeric, "t11"));
Assert.assertEquals("11*2.718281828459045*t", cm.evaluate(JsclOperation.numeric, "t11e"));
Assert.assertEquals("11*Infinity*t", cm.evaluate(JsclOperation.numeric, "t11∞"));
Assert.assertEquals("-t+t^3", cm.evaluate(JsclOperation.numeric, "t(t-1)(t+1)"));
} }
public interface TestInterface { @Test
Integer getField(); public void testRounding() throws Exception {
} final CalculatorEngine cm = CalculatorEngine.instance;
public class TestClass implements TestInterface{ cm.setPrecision(2);
Assert.assertEquals("12345678.9", cm.evaluate(JsclOperation.numeric, "1.23456789E7"));
cm.setPrecision(10);
Assert.assertEquals("12345678.9", cm.evaluate(JsclOperation.numeric, "1.23456789E7"));
Assert.assertEquals("123456789", cm.evaluate(JsclOperation.numeric, "1.234567890E8"));
Assert.assertEquals("1234567890.1", cm.evaluate(JsclOperation.numeric, "1.2345678901E9"));
@Attribute(required = true)
private Integer field;
public TestClass() {
}
public TestClass(Integer field) {
this.field = field;
}
public Integer getField() {
return field;
}
public void setField(Integer field) {
this.field = field;
}
}
public class NewTestClass implements TestInterface{
@Attribute
private Integer field;
public NewTestClass() {
}
public NewTestClass(Integer field) {
this.field = field;
}
public Integer getField() {
return field;
}
public void setField(Integer field) {
this.field = field;
}
} }
} }

View File

@ -9,7 +9,6 @@ package org.solovyev.android.calculator.model;
import org.junit.Assert; import org.junit.Assert;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.junit.Test; import org.junit.Test;
import org.solovyev.android.calculator.jscl.JsclOperation;
/** /**
* User: serso * User: serso
@ -27,6 +26,17 @@ public class ToJsclTextProcessorTest {
public void testProcess() throws Exception { public void testProcess() throws Exception {
final ToJsclTextProcessor preprocessor = new ToJsclTextProcessor(); final ToJsclTextProcessor preprocessor = new ToJsclTextProcessor();
Assert.assertEquals( "", preprocessor.process("").toString());
Assert.assertEquals( "()", preprocessor.process("[]").toString());
Assert.assertEquals( "()*()", preprocessor.process("[][]").toString());
Assert.assertEquals( "()*(1)", preprocessor.process("[][1]").toString());
Assert.assertEquals( "(0)*(1)", preprocessor.process("[0][1]").toString());
Assert.assertEquals( "(0)*(1*10^)", preprocessor.process("[0][1E]").toString());
Assert.assertEquals( "(0)*(1*10^1)", preprocessor.process("[0][1E1]").toString());
Assert.assertEquals( "(0)*(1*10^-1)", preprocessor.process("[0][1E-1]").toString());
Assert.assertEquals( "(0)*(1.*10^-1)", preprocessor.process("[0][1.E-1]").toString());
Assert.assertEquals( "(0)*(2*10^-1)", preprocessor.process("[0][2*E-1]").toString());
Assert.assertEquals( "(0)*log(1)*(2*10^-1)", preprocessor.process("[0]ln(1)[2*E-1]").toString());
Assert.assertEquals( "sin(4)*asin(0.5)*sqrt(2)", preprocessor.process("sin(4)asin(0.5)sqrt(2)").toString()); Assert.assertEquals( "sin(4)*asin(0.5)*sqrt(2)", preprocessor.process("sin(4)asin(0.5)sqrt(2)").toString());
Assert.assertEquals( "sin(4)*cos(5)", preprocessor.process("sin(4)cos(5)").toString()); Assert.assertEquals( "sin(4)*cos(5)", preprocessor.process("sin(4)cos(5)").toString());
Assert.assertEquals( "3.141592653589793*sin(4)*3.141592653589793*cos(sqrt(5))", preprocessor.process("πsin(4)πcos(√(5))").toString()); Assert.assertEquals( "3.141592653589793*sin(4)*3.141592653589793*cos(sqrt(5))", preprocessor.process("πsin(4)πcos(√(5))").toString());