number formatting + fixes
This commit is contained in:
parent
30a79b5025
commit
b70c1ea0d6
@ -35,7 +35,7 @@ public class CalculatorDisplay extends AutoResizeTextView {
|
|||||||
private JsclOperation jsclOperation = JsclOperation.numeric;
|
private JsclOperation jsclOperation = JsclOperation.numeric;
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private final static TextProcessor<TextHighlighter.Result, String> textHighlighter = new TextHighlighter(Color.WHITE, true, CalculatorEngine.instance.getEngine());
|
private final static TextProcessor<TextHighlighter.Result, String> textHighlighter = new TextHighlighter(Color.WHITE, false, CalculatorEngine.instance.getEngine());
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private Generic genericResult;
|
private Generic genericResult;
|
||||||
|
@ -31,7 +31,7 @@ public class CalculatorEditor extends EditText implements SharedPreferences.OnSh
|
|||||||
private boolean highlightText = true;
|
private boolean highlightText = true;
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private final static TextProcessor<TextHighlighter.Result, String> textHighlighter = new TextHighlighter(Color.WHITE, false, CalculatorEngine.instance.getEngine());
|
private final static TextProcessor<TextHighlighter.Result, String> textHighlighter = new TextHighlighter(Color.WHITE, true, CalculatorEngine.instance.getEngine());
|
||||||
|
|
||||||
public CalculatorEditor(Context context) {
|
public CalculatorEditor(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
|
@ -10,8 +10,9 @@ import jscl.MathContext;
|
|||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
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.NumberBuilder;
|
import org.solovyev.android.calculator.model.CalculatorEngine;
|
||||||
import org.solovyev.android.calculator.model.CalculatorParseException;
|
import org.solovyev.android.calculator.model.CalculatorParseException;
|
||||||
|
import org.solovyev.android.calculator.model.NumberBuilder;
|
||||||
import org.solovyev.android.calculator.model.TextProcessor;
|
import org.solovyev.android.calculator.model.TextProcessor;
|
||||||
import org.solovyev.common.utils.MutableObject;
|
import org.solovyev.common.utils.MutableObject;
|
||||||
|
|
||||||
@ -74,11 +75,11 @@ public class TextHighlighter implements TextProcessor<TextHighlighter.Result, St
|
|||||||
private final int colorRed;
|
private final int colorRed;
|
||||||
private final int colorGreen;
|
private final int colorGreen;
|
||||||
private final int colorBlue;
|
private final int colorBlue;
|
||||||
private final boolean simpleFormat;
|
private final boolean allowScientificFormat;
|
||||||
|
|
||||||
public TextHighlighter(int baseColor, boolean simpleFormat, @NotNull MathContext mathContext) {
|
public TextHighlighter(int baseColor, boolean allowScientificFormat, @NotNull MathContext mathContext) {
|
||||||
this.color = baseColor;
|
this.color = baseColor;
|
||||||
this.simpleFormat = simpleFormat;
|
this.allowScientificFormat = allowScientificFormat;
|
||||||
this.mathContext = mathContext;
|
this.mathContext = mathContext;
|
||||||
//this.colorRed = Color.red(baseColor);
|
//this.colorRed = Color.red(baseColor);
|
||||||
this.colorRed = (baseColor >> 16) & 0xFF;
|
this.colorRed = (baseColor >> 16) & 0xFF;
|
||||||
@ -100,7 +101,7 @@ public class TextHighlighter implements TextProcessor<TextHighlighter.Result, St
|
|||||||
|
|
||||||
int numberOffset = 0;
|
int numberOffset = 0;
|
||||||
|
|
||||||
final NumberBuilder numberBuilder = new NumberBuilder(simpleFormat, mathContext.getNumeralBase());
|
final NumberBuilder numberBuilder = new NumberBuilder(allowScientificFormat, CalculatorEngine.instance.getEngine());
|
||||||
for (int i = 0; i < text.length(); i++) {
|
for (int i = 0; i < text.length(); i++) {
|
||||||
MathType.Result mathType = MathType.getType(text, i, numberBuilder.isHexMode());
|
MathType.Result mathType = MathType.getType(text, i, numberBuilder.isHexMode());
|
||||||
|
|
||||||
@ -144,7 +145,7 @@ public class TextHighlighter implements TextProcessor<TextHighlighter.Result, St
|
|||||||
}
|
}
|
||||||
|
|
||||||
final MutableObject<Integer> localNumberOffset = new MutableObject<Integer>(0);
|
final MutableObject<Integer> localNumberOffset = new MutableObject<Integer>(0);
|
||||||
numberBuilder.process(text1, localNumberOffset);
|
numberBuilder.processNumber(text1, localNumberOffset);
|
||||||
numberOffset += localNumberOffset.getObject();
|
numberOffset += localNumberOffset.getObject();
|
||||||
|
|
||||||
if (maxNumberOfOpenGroupSymbols > 0) {
|
if (maxNumberOfOpenGroupSymbols > 0) {
|
||||||
|
@ -21,7 +21,7 @@ import java.util.*;
|
|||||||
|
|
||||||
public enum MathType {
|
public enum MathType {
|
||||||
|
|
||||||
numeral_base(50, true, false) {
|
numeral_base(50, true, false, MathGroupType.number) {
|
||||||
|
|
||||||
private final List<String> tokens = new ArrayList<String>(10);
|
private final List<String> tokens = new ArrayList<String>(10);
|
||||||
{
|
{
|
||||||
@ -40,23 +40,23 @@ public enum MathType {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
dot(200, true, true, ".") {
|
dot(200, true, true, MathGroupType.number, ".") {
|
||||||
@Override
|
@Override
|
||||||
public boolean isNeedMultiplicationSignBefore(@NotNull MathType mathTypeBefore) {
|
public boolean isNeedMultiplicationSignBefore(@NotNull MathType mathTypeBefore) {
|
||||||
return super.isNeedMultiplicationSignBefore(mathTypeBefore) && mathTypeBefore != digit;
|
return super.isNeedMultiplicationSignBefore(mathTypeBefore) && mathTypeBefore != digit;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
grouping_separator(250, false, false, "'", " "){
|
grouping_separator(250, false, false, MathGroupType.number, "'", " "){
|
||||||
@Override
|
@Override
|
||||||
public int processToJscl(@NotNull StringBuilder result, int i, @NotNull String match) throws CalculatorParseException {
|
public int processToJscl(@NotNull StringBuilder result, int i, @NotNull String match) throws CalculatorParseException {
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
power_10(300, false, false, "E"),
|
power_10(300, false, false, MathGroupType.number, "E"),
|
||||||
|
|
||||||
postfix_function(400, false, true) {
|
postfix_function(400, false, true, MathGroupType.function) {
|
||||||
@NotNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public List<String> getTokens() {
|
public List<String> getTokens() {
|
||||||
@ -64,8 +64,8 @@ public enum MathType {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
unary_operation(500, false, false, "-", "="),
|
unary_operation(500, false, false, MathGroupType.operation, "-", "="),
|
||||||
binary_operation(600, false, false, "-", "+", "*", "×", "∙", "/", "^") {
|
binary_operation(600, false, false, MathGroupType.operation, "-", "+", "*", "×", "∙", "/", "^") {
|
||||||
@Override
|
@Override
|
||||||
protected String getSubstituteToJscl(@NotNull String match) {
|
protected String getSubstituteToJscl(@NotNull String match) {
|
||||||
if (match.equals("×") || match.equals("∙")) {
|
if (match.equals("×") || match.equals("∙")) {
|
||||||
@ -76,7 +76,7 @@ public enum MathType {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
open_group_symbol(800, true, false, "[", "(", "{") {
|
open_group_symbol(800, true, false, MathGroupType.other, "[", "(", "{") {
|
||||||
@Override
|
@Override
|
||||||
public boolean isNeedMultiplicationSignBefore(@NotNull MathType mathTypeBefore) {
|
public boolean isNeedMultiplicationSignBefore(@NotNull MathType mathTypeBefore) {
|
||||||
return super.isNeedMultiplicationSignBefore(mathTypeBefore) && mathTypeBefore != function && mathTypeBefore != operator;
|
return super.isNeedMultiplicationSignBefore(mathTypeBefore) && mathTypeBefore != function && mathTypeBefore != operator;
|
||||||
@ -88,7 +88,7 @@ public enum MathType {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
close_group_symbol(900, false, true, "]", ")", "}") {
|
close_group_symbol(900, false, true, MathGroupType.other, "]", ")", "}") {
|
||||||
@Override
|
@Override
|
||||||
public boolean isNeedMultiplicationSignBefore(@NotNull MathType mathTypeBefore) {
|
public boolean isNeedMultiplicationSignBefore(@NotNull MathType mathTypeBefore) {
|
||||||
return false;
|
return false;
|
||||||
@ -100,7 +100,7 @@ public enum MathType {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
function(1000, true, true) {
|
function(1000, true, true, MathGroupType.function) {
|
||||||
@NotNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public List<String> getTokens() {
|
public List<String> getTokens() {
|
||||||
@ -108,7 +108,7 @@ public enum MathType {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
operator(1050, true, true) {
|
operator(1050, true, true, MathGroupType.function) {
|
||||||
@NotNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public List<String> getTokens() {
|
public List<String> getTokens() {
|
||||||
@ -116,7 +116,7 @@ public enum MathType {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
constant(1100, true, true) {
|
constant(1100, true, true, MathGroupType.other) {
|
||||||
@NotNull
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public List<String> getTokens() {
|
public List<String> getTokens() {
|
||||||
@ -129,7 +129,7 @@ public enum MathType {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
digit(1125, true, true) {
|
digit(1125, true, true, MathGroupType.number) {
|
||||||
|
|
||||||
private final List<String> tokens = new ArrayList<String>(16);
|
private final List<String> tokens = new ArrayList<String>(16);
|
||||||
{
|
{
|
||||||
@ -149,9 +149,9 @@ public enum MathType {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
comma(1150, false, false, ","),
|
comma(1150, false, false, MathGroupType.other, ","),
|
||||||
|
|
||||||
text(1200, false, false) {
|
text(1200, false, false, MathGroupType.other) {
|
||||||
@Override
|
@Override
|
||||||
public int processToJscl(@NotNull StringBuilder result, int i, @NotNull String match) {
|
public int processToJscl(@NotNull StringBuilder result, int i, @NotNull String match) {
|
||||||
if (match.length() > 0) {
|
if (match.length() > 0) {
|
||||||
@ -169,6 +169,13 @@ public enum MathType {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public static enum MathGroupType {
|
||||||
|
function,
|
||||||
|
number,
|
||||||
|
operation,
|
||||||
|
other
|
||||||
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private final List<String> tokens;
|
private final List<String> tokens;
|
||||||
|
|
||||||
@ -179,24 +186,35 @@ public enum MathType {
|
|||||||
|
|
||||||
private final boolean needMultiplicationSignAfter;
|
private final boolean needMultiplicationSignAfter;
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private final MathGroupType groupType;
|
||||||
|
|
||||||
MathType(@NotNull Integer priority,
|
MathType(@NotNull Integer priority,
|
||||||
boolean needMultiplicationSignBefore,
|
boolean needMultiplicationSignBefore,
|
||||||
boolean needMultiplicationSignAfter,
|
boolean needMultiplicationSignAfter,
|
||||||
|
@NotNull MathGroupType groupType,
|
||||||
@NotNull String... tokens) {
|
@NotNull String... tokens) {
|
||||||
this(priority, needMultiplicationSignBefore, needMultiplicationSignAfter, CollectionsUtils.asList(tokens));
|
this(priority, needMultiplicationSignBefore, needMultiplicationSignAfter, groupType, CollectionsUtils.asList(tokens));
|
||||||
}
|
}
|
||||||
|
|
||||||
MathType(@NotNull Integer priority,
|
MathType(@NotNull Integer priority,
|
||||||
boolean needMultiplicationSignBefore,
|
boolean needMultiplicationSignBefore,
|
||||||
boolean needMultiplicationSignAfter,
|
boolean needMultiplicationSignAfter,
|
||||||
|
@NotNull MathGroupType groupType,
|
||||||
@NotNull List<String> tokens) {
|
@NotNull List<String> tokens) {
|
||||||
this.priority = priority;
|
this.priority = priority;
|
||||||
this.needMultiplicationSignBefore = needMultiplicationSignBefore;
|
this.needMultiplicationSignBefore = needMultiplicationSignBefore;
|
||||||
this.needMultiplicationSignAfter = needMultiplicationSignAfter;
|
this.needMultiplicationSignAfter = needMultiplicationSignAfter;
|
||||||
|
this.groupType = groupType;
|
||||||
this.tokens = Collections.unmodifiableList(tokens);
|
this.tokens = Collections.unmodifiableList(tokens);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* public static int getPostfixFunctionStart(@NotNull CharSequence s, int position) throws ParseException {
|
@NotNull
|
||||||
|
public MathGroupType getGroupType() {
|
||||||
|
return groupType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* public static int getPostfixFunctionStart(@NotNull CharSequence s, int position) throws ParseException {
|
||||||
assert s.length() > position;
|
assert s.length() > position;
|
||||||
|
|
||||||
int numberOfOpenGroups = 0;
|
int numberOfOpenGroups = 0;
|
||||||
|
@ -169,7 +169,7 @@ public enum CalculatorEngine {
|
|||||||
|
|
||||||
calculationResult.setObject(genericResult);
|
calculationResult.setObject(genericResult);
|
||||||
} catch (AbstractJsclArithmeticException e) {
|
} catch (AbstractJsclArithmeticException e) {
|
||||||
evalException.setObject(new CalculatorEvalException(e, jsclExpression));
|
evalException.setObject(new CalculatorEvalException(e, e, jsclExpression));
|
||||||
} catch (ArithmeticException e) {
|
} catch (ArithmeticException e) {
|
||||||
//System.out.println(e.getMessage());
|
//System.out.println(e.getMessage());
|
||||||
parseException.setObject(new CalculatorParseException(Messages.msg_1, jsclExpression, e.getMessage()));
|
parseException.setObject(new CalculatorParseException(Messages.msg_1, jsclExpression, e.getMessage()));
|
||||||
@ -207,7 +207,8 @@ public enum CalculatorEngine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (parseExceptionObject != null || evalExceptionObject != null) {
|
if (parseExceptionObject != null || evalExceptionObject != null) {
|
||||||
if (finalOperation == JsclOperation.numeric && preparedExpression.isExistsUndefinedVar()) {
|
if (finalOperation == JsclOperation.numeric &&
|
||||||
|
( preparedExpression.isExistsUndefinedVar() || ( evalExceptionObject != null && evalExceptionObject.getCause() instanceof NumeralBaseException)) ) {
|
||||||
return evaluate(JsclOperation.simplify, expression, mr);
|
return evaluate(JsclOperation.simplify, expression, mr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
package org.solovyev.android.calculator.model;
|
package org.solovyev.android.calculator.model;
|
||||||
|
|
||||||
|
import jscl.AbstractJsclArithmeticException;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.solovyev.common.exceptions.SersoException;
|
import org.solovyev.common.exceptions.SersoException;
|
||||||
import org.solovyev.common.msg.Message;
|
import org.solovyev.common.msg.Message;
|
||||||
@ -27,7 +28,8 @@ public class CalculatorEvalException extends SersoException implements Message {
|
|||||||
@NotNull
|
@NotNull
|
||||||
private final String expression;
|
private final String expression;
|
||||||
|
|
||||||
public CalculatorEvalException(@NotNull Message message, String expression) {
|
public CalculatorEvalException(@NotNull Message message, @NotNull Throwable cause, String expression) {
|
||||||
|
super(cause);
|
||||||
this.message = message;
|
this.message = message;
|
||||||
this.expression = expression;
|
this.expression = expression;
|
||||||
}
|
}
|
||||||
|
@ -33,16 +33,16 @@ public class FromJsclSimplifyTextProcessor implements TextProcessor<String, Gene
|
|||||||
public String process(@NotNull String s) {
|
public String process(@NotNull String s) {
|
||||||
final StringBuilder sb = new StringBuilder();
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
final NumberBuilder numberBuilder = new NumberBuilder(true, mathContext.getNumeralBase());
|
final NumberBuilder nb = new NumberBuilder(false, CalculatorEngine.instance.getEngine());
|
||||||
for (int i = 0; i < s.length(); i++) {
|
for (int i = 0; i < s.length(); i++) {
|
||||||
final MathType.Result mathTypeResult = MathType.getType(s, i, numberBuilder.isHexMode());
|
final MathType.Result mathTypeResult = MathType.getType(s, i, nb.isHexMode());
|
||||||
|
|
||||||
numberBuilder.process(sb, mathTypeResult, null);
|
nb.process(sb, mathTypeResult, null);
|
||||||
|
|
||||||
i = mathTypeResult.processFromJscl(sb, i);
|
i = mathTypeResult.processFromJscl(sb, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
numberBuilder.process(sb, null);
|
nb.processNumber(sb, null);
|
||||||
|
|
||||||
return removeMultiplicationSigns(sb.toString());
|
return removeMultiplicationSigns(sb.toString());
|
||||||
}
|
}
|
||||||
|
@ -6,9 +6,10 @@
|
|||||||
|
|
||||||
package org.solovyev.android.calculator.model;
|
package org.solovyev.android.calculator.model;
|
||||||
|
|
||||||
|
import jscl.MathContext;
|
||||||
import jscl.MathEngine;
|
import jscl.MathEngine;
|
||||||
import jscl.NumeralBase;
|
import jscl.NumeralBase;
|
||||||
import jscl.math.numeric.Numeric;
|
import jscl.math.function.IConstant;
|
||||||
import jscl.math.numeric.Real;
|
import jscl.math.numeric.Real;
|
||||||
import jscl.text.*;
|
import jscl.text.*;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
@ -28,59 +29,79 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public class NumberBuilder {
|
public class NumberBuilder {
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private final MathEngine engine;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private StringBuilder numberBuilder = null;
|
private StringBuilder numberBuilder = null;
|
||||||
@Nullable
|
|
||||||
private String number = null;
|
|
||||||
|
|
||||||
private final boolean simpleFormat;
|
private final boolean allowScientificFormat;
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private final NumeralBase defaultNumeralBase;
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private NumeralBase nb;
|
private NumeralBase nb;
|
||||||
|
|
||||||
public NumberBuilder(boolean simpleFormat, @NotNull NumeralBase defaultNumeralBase) {
|
public NumberBuilder(boolean allowScientificFormat, @NotNull MathEngine engine) {
|
||||||
this.simpleFormat = simpleFormat;
|
this.allowScientificFormat = allowScientificFormat;
|
||||||
this.defaultNumeralBase = defaultNumeralBase;
|
this.nb = engine.getNumeralBase();
|
||||||
this.nb = defaultNumeralBase;
|
this.engine = engine;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method replaces number in text according to some rules (e.g. formatting)
|
||||||
|
*
|
||||||
|
* @param text text where number can be replaced
|
||||||
|
* @param mathTypeResult math type result of current token
|
||||||
|
* @param offset offset between new number length and old number length (newNumberLength - oldNumberLength)
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @return new math type result (as one can be changed due to substituting of number with constant)
|
||||||
|
*/
|
||||||
@NotNull
|
@NotNull
|
||||||
public MathType.Result process(@NotNull StringBuilder sb, @NotNull MathType.Result mathTypeResult, @Nullable MutableObject<Integer> numberOffset) {
|
public MathType.Result process(@NotNull StringBuilder text, @NotNull MathType.Result mathTypeResult, @Nullable MutableObject<Integer> offset) {
|
||||||
number = null;
|
|
||||||
|
|
||||||
final MathType.Result possibleResult;
|
final MathType.Result possibleResult;
|
||||||
if ((CollectionsUtils.contains(mathTypeResult.getMathType(), MathType.digit, MathType.numeral_base, MathType.dot, MathType.grouping_separator, MathType.power_10) ||
|
if (canContinue(mathTypeResult)) {
|
||||||
isSignAfterE(mathTypeResult)) && numeralBaseCheck(mathTypeResult) && numeralBaseInTheStart(mathTypeResult.getMathType())) {
|
// let's continue building number
|
||||||
if (numberBuilder == null) {
|
if (numberBuilder == null) {
|
||||||
|
// if new number => create new builder
|
||||||
numberBuilder = new StringBuilder();
|
numberBuilder = new StringBuilder();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mathTypeResult.getMathType() != MathType.numeral_base) {
|
if (mathTypeResult.getMathType() != MathType.numeral_base) {
|
||||||
|
// just add matching string
|
||||||
numberBuilder.append(mathTypeResult.getMatch());
|
numberBuilder.append(mathTypeResult.getMatch());
|
||||||
} else {
|
} else {
|
||||||
|
// set explicitly numeral base (do not include it into number)
|
||||||
nb = NumeralBase.getByPrefix(mathTypeResult.getMatch());
|
nb = NumeralBase.getByPrefix(mathTypeResult.getMatch());
|
||||||
}
|
}
|
||||||
|
|
||||||
possibleResult = null;
|
possibleResult = null;
|
||||||
} else {
|
} else {
|
||||||
possibleResult = process(sb, numberOffset);
|
// process current number (and go to the next one)
|
||||||
|
possibleResult = processNumber(text, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
return possibleResult == null ? mathTypeResult : possibleResult;
|
return possibleResult == null ? mathTypeResult : possibleResult;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method determines if we can continue to process current number
|
||||||
|
* @param mathTypeResult current math type result
|
||||||
|
*
|
||||||
|
* @return true if we can continue of processing of current number, if false - new number should be constructed
|
||||||
|
*/
|
||||||
|
private boolean canContinue(@NotNull MathType.Result mathTypeResult) {
|
||||||
|
return ((mathTypeResult.getMathType().getGroupType() == MathType.MathGroupType.number && numeralBaseCheck(mathTypeResult) && numeralBaseInTheStart(mathTypeResult.getMathType()) || isSignAfterE(mathTypeResult)));
|
||||||
|
}
|
||||||
|
|
||||||
private boolean numeralBaseInTheStart(@NotNull MathType mathType) {
|
private boolean numeralBaseInTheStart(@NotNull MathType mathType) {
|
||||||
return mathType != MathType.numeral_base || numberBuilder == null;
|
return mathType != MathType.numeral_base || numberBuilder == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean numeralBaseCheck( @NotNull MathType.Result mathType ) {
|
private boolean numeralBaseCheck(@NotNull MathType.Result mathType) {
|
||||||
if ( mathType.getMathType() == MathType.digit ) {
|
if (mathType.getMathType() == MathType.digit) {
|
||||||
final Character ch = mathType.getMatch().charAt(0);
|
final Character ch = mathType.getMatch().charAt(0);
|
||||||
if ( NumeralBase.hex.getAcceptableCharacters().contains(ch) && !NumeralBase.dec.getAcceptableCharacters().contains(ch) ) {
|
if (NumeralBase.hex.getAcceptableCharacters().contains(ch) && !NumeralBase.dec.getAcceptableCharacters().contains(ch)) {
|
||||||
if ( nb == NumeralBase.hex ) {
|
if (nb == NumeralBase.hex) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
@ -104,101 +125,92 @@ public class NumberBuilder {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Method replaces number in text according to some rules (e.g. formatting)
|
||||||
|
*
|
||||||
|
* @param text text where number can be replaced
|
||||||
|
* @param offset offset between new number length and old number length (newNumberLength - oldNumberLength)
|
||||||
|
*
|
||||||
|
* @return new math type result (as one can be changed due to substituting of number with constant)
|
||||||
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public MathType.Result process(@NotNull StringBuilder sb, @Nullable MutableObject<Integer> numberOffset) {
|
public MathType.Result processNumber(@NotNull StringBuilder text, @Nullable MutableObject<Integer> offset) {
|
||||||
int numberOfTokens = 0;
|
// total number of trimmed chars
|
||||||
|
int trimmedChars = 0;
|
||||||
|
|
||||||
|
String number = null;
|
||||||
|
|
||||||
|
// save numeral base (as later it might be replaced)
|
||||||
|
final NumeralBase localNb = getNumeralBase();
|
||||||
|
|
||||||
final NumeralBase localNb;
|
|
||||||
if (numberBuilder != null) {
|
if (numberBuilder != null) {
|
||||||
try {
|
try {
|
||||||
number = numberBuilder.toString();
|
number = numberBuilder.toString();
|
||||||
List<String> tokens = new ArrayList<String>();
|
|
||||||
|
// let's get rid of unnecessary characters (grouping separators, + after E)
|
||||||
|
final List<String> tokens = new ArrayList<String>();
|
||||||
tokens.addAll(MathType.grouping_separator.getTokens());
|
tokens.addAll(MathType.grouping_separator.getTokens());
|
||||||
|
// + after E can be omitted: 10+E = 10E (NOTE: - cannot be omitted )
|
||||||
tokens.add("+");
|
tokens.add("+");
|
||||||
for (String groupingSeparator : tokens) {
|
for (String groupingSeparator : tokens) {
|
||||||
String newNumber = number.replace(groupingSeparator, "");
|
final String trimmedNumber = number.replace(groupingSeparator, "");
|
||||||
numberOfTokens += number.length() - newNumber.length();
|
trimmedChars += number.length() - trimmedNumber.length();
|
||||||
number = newNumber;
|
number = trimmedNumber;
|
||||||
}
|
}
|
||||||
|
|
||||||
toDouble(number, getNumeralBase());
|
// check if number still valid
|
||||||
|
toDouble(number, getNumeralBase(), engine);
|
||||||
|
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
|
// number is not valid => stop
|
||||||
number = null;
|
number = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
numberBuilder = null;
|
numberBuilder = null;
|
||||||
localNb = getNumeralBase();
|
|
||||||
nb = defaultNumeralBase;
|
// must set default numeral base (exit numeral base mode)
|
||||||
} else {
|
nb = engine.getNumeralBase();
|
||||||
number = null;
|
|
||||||
localNb = getNumeralBase();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return replaceSystemVars(sb, number, numberOfTokens, numberOffset, localNb, simpleFormat);
|
return replaceNumberInText(text, number, trimmedChars, offset, localNb, allowScientificFormat, engine);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private static MathType.Result replaceSystemVars(StringBuilder sb, String number, int numberOfTokens, @Nullable MutableObject<Integer> numberOffset, @NotNull NumeralBase nb, boolean simpleFormat) {
|
private static MathType.Result replaceNumberInText(@NotNull StringBuilder text,
|
||||||
|
@Nullable String number,
|
||||||
|
int trimmedChars,
|
||||||
|
@Nullable MutableObject<Integer> offset,
|
||||||
|
@NotNull NumeralBase nb,
|
||||||
|
boolean allowScientificFormat,
|
||||||
|
@NotNull final MathEngine engine) {
|
||||||
MathType.Result result = null;
|
MathType.Result result = null;
|
||||||
|
|
||||||
if (number != null) {
|
if (number != null) {
|
||||||
final String finalNumber = number;
|
final String finalNumber = number;
|
||||||
final Var var = CollectionsUtils.find(CalculatorEngine.instance.getVarsRegister().getSystemEntities(), new Finder<Var>() {
|
|
||||||
|
// detect if current number is precisely equals to constant in constants' registry (NOTE: ONLY FOR SYSTEM CONSTANTS)
|
||||||
|
final IConstant constant = CollectionsUtils.find(engine.getConstantsRegistry().getSystemEntities(), new Finder<IConstant>() {
|
||||||
@Override
|
@Override
|
||||||
public boolean isFound(@Nullable Var var) {
|
public boolean isFound(@Nullable IConstant constant) {
|
||||||
return var != null && finalNumber.equals(var.getValue());
|
return constant != null && finalNumber.equals(constant.getValue());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (var != null) {
|
// in any case remove old number from text
|
||||||
sb.delete(sb.length() - number.length() - numberOfTokens, sb.length());
|
final int oldNumberLength = number.length() + trimmedChars;
|
||||||
sb.append(var.getName());
|
text.delete(text.length() - oldNumberLength, text.length());
|
||||||
result = new MathType.Result(MathType.constant, var.getName());
|
|
||||||
} else {
|
|
||||||
sb.delete(sb.length() - number.length() - numberOfTokens, sb.length());
|
|
||||||
|
|
||||||
final String formattedNumber;
|
if (constant != null) {
|
||||||
|
// let's change number with constant from registry
|
||||||
if (!simpleFormat) {
|
text.append(constant.getName());
|
||||||
int indexOfDot = number.indexOf('.');
|
result = new MathType.Result(MathType.constant, constant.getName());
|
||||||
|
|
||||||
if (indexOfDot < 0) {
|
|
||||||
int indexOfE;
|
|
||||||
if (nb == NumeralBase.hex) {
|
|
||||||
indexOfE = -1;
|
|
||||||
} else {
|
} else {
|
||||||
indexOfE = number.indexOf('E');
|
final String newNumber = formatNumber(number, nb, allowScientificFormat, engine);
|
||||||
|
if (offset != null) {
|
||||||
|
// register offset between old number and new number
|
||||||
|
offset.setObject(newNumber.length() - oldNumberLength);
|
||||||
}
|
}
|
||||||
if (indexOfE < 0) {
|
text.append(newNumber);
|
||||||
formattedNumber = toString(toDouble(number, nb), nb);
|
|
||||||
} else {
|
|
||||||
final String part;
|
|
||||||
if (indexOfDot != 0) {
|
|
||||||
part = toString(toDouble(number.substring(0, indexOfE), nb), nb);
|
|
||||||
} else {
|
|
||||||
part = "";
|
|
||||||
}
|
|
||||||
formattedNumber = part + number.substring(indexOfE);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
final String integerPart;
|
|
||||||
if (indexOfDot != 0) {
|
|
||||||
integerPart = toString(toDouble(number.substring(0, indexOfDot), nb), nb);
|
|
||||||
} else {
|
|
||||||
integerPart = "";
|
|
||||||
}
|
|
||||||
formattedNumber = integerPart + number.substring(indexOfDot);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
formattedNumber = toString(toDouble(number, nb), nb);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (numberOffset != null) {
|
|
||||||
numberOffset.setObject(formattedNumber.length() - number.length() - numberOfTokens);
|
|
||||||
}
|
|
||||||
sb.append(formattedNumber);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -206,40 +218,78 @@ public class NumberBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private static String toString(@NotNull Double value, @NotNull NumeralBase nb) {
|
private static String formatNumber(@NotNull String number, @NotNull NumeralBase nb, boolean allowScientificFormat, @NotNull MathEngine engine) {
|
||||||
return CalculatorEngine.instance.getEngine().format(value, nb);
|
String result;
|
||||||
|
|
||||||
|
if (allowScientificFormat) {
|
||||||
|
int indexOfDot = number.indexOf('.');
|
||||||
|
|
||||||
|
if (indexOfDot < 0) {
|
||||||
|
int indexOfE;
|
||||||
|
if (nb == NumeralBase.hex) {
|
||||||
|
indexOfE = -1;
|
||||||
|
} else {
|
||||||
|
indexOfE = number.indexOf(MathType.POWER_10);
|
||||||
|
}
|
||||||
|
if (indexOfE < 0) {
|
||||||
|
result = toString(number, nb, engine);
|
||||||
|
} else {
|
||||||
|
final String part;
|
||||||
|
if (indexOfDot != 0) {
|
||||||
|
part = toString(number.substring(0, indexOfE), nb, engine);
|
||||||
|
} else {
|
||||||
|
part = "";
|
||||||
|
}
|
||||||
|
result = part + number.substring(indexOfE);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
final String integerPart;
|
||||||
|
if (indexOfDot != 0) {
|
||||||
|
integerPart = toString(number.substring(0, indexOfDot), nb, engine);
|
||||||
|
} else {
|
||||||
|
integerPart = "";
|
||||||
|
}
|
||||||
|
result = integerPart + number.substring(indexOfDot);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result = toString(number, nb, engine);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private static String toString(@NotNull String value, @NotNull NumeralBase nb, @NotNull MathContext mathContext) {
|
||||||
|
return mathContext.format(toDouble(value, nb, mathContext), nb);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isHexMode() {
|
public boolean isHexMode() {
|
||||||
return nb == NumeralBase.hex || ( nb == null && defaultNumeralBase == NumeralBase.hex);
|
return nb == NumeralBase.hex || (nb == null && engine.getNumeralBase() == NumeralBase.hex);
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private NumeralBase getNumeralBase(){
|
private NumeralBase getNumeralBase() {
|
||||||
return nb == null ? defaultNumeralBase : nb;
|
return nb == null ? engine.getNumeralBase() : nb;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
private static Double toDouble(@NotNull String s, @NotNull NumeralBase nb) throws NumberFormatException{
|
private static Double toDouble(@NotNull String s, @NotNull NumeralBase nb, @NotNull final MathContext mc) throws NumberFormatException {
|
||||||
|
final NumeralBase defaultNb = mc.getNumeralBase();
|
||||||
final MathEngine me = CalculatorEngine.instance.getEngine();
|
|
||||||
|
|
||||||
final NumeralBase defaultNb = me.getNumeralBase();
|
|
||||||
try {
|
try {
|
||||||
me.setNumeralBase(nb);
|
mc.setNumeralBase(nb);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return JsclIntegerParser.parser.parse(Parser.Parameters.newInstance(s, new MutableInt(0), me), null).content().doubleValue();
|
return JsclIntegerParser.parser.parse(Parser.Parameters.newInstance(s, new MutableInt(0), mc), null).content().doubleValue();
|
||||||
} catch (ParseException e) {
|
} catch (ParseException e) {
|
||||||
try {
|
try {
|
||||||
return ((Real) DoubleParser.parser.parse(Parser.Parameters.newInstance(s, new MutableInt(0), me), null).content()).doubleValue();
|
return ((Real) DoubleParser.parser.parse(Parser.Parameters.newInstance(s, new MutableInt(0), mc), null).content()).doubleValue();
|
||||||
} catch (ParseException e1) {
|
} catch (ParseException e1) {
|
||||||
throw new NumberFormatException();
|
throw new NumberFormatException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
me.setNumeralBase(defaultNb);
|
mc.setNumeralBase(defaultNb);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
|
|
||||||
package org.solovyev.android.calculator.model;
|
package org.solovyev.android.calculator.model;
|
||||||
|
|
||||||
import jscl.JsclMathEngine;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.solovyev.android.calculator.StartsWithFinder;
|
import org.solovyev.android.calculator.StartsWithFinder;
|
||||||
import org.solovyev.android.calculator.jscl.JsclOperation;
|
import org.solovyev.android.calculator.jscl.JsclOperation;
|
||||||
@ -41,7 +40,7 @@ class ToJsclTextProcessor implements TextProcessor<PreparedExpression, String> {
|
|||||||
|
|
||||||
final StringBuilder sb = new StringBuilder(s);
|
final StringBuilder sb = new StringBuilder(s);
|
||||||
|
|
||||||
final NumberBuilder nb = new NumberBuilder(true, CalculatorEngine.instance.getEngine().getNumeralBase());
|
final NumberBuilder nb = new NumberBuilder(false, CalculatorEngine.instance.getEngine());
|
||||||
for (int i = 0; i < s.length(); i++) {
|
for (int i = 0; i < s.length(); i++) {
|
||||||
if (s.charAt(i) == ' ') continue;
|
if (s.charAt(i) == ' ') continue;
|
||||||
startsWithFinder.setI(i);
|
startsWithFinder.setI(i);
|
||||||
|
@ -26,7 +26,7 @@ public class TextHighlighterTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testProcess() throws Exception {
|
public void testProcess() throws Exception {
|
||||||
TextProcessor<?, String> textHighlighter = new TextHighlighter(0, true, JsclMathEngine.instance);
|
TextProcessor<?, String> textHighlighter = new TextHighlighter(0, false, JsclMathEngine.instance);
|
||||||
|
|
||||||
final Random random = new Random(new Date().getTime());
|
final Random random = new Random(new Date().getTime());
|
||||||
for (int i = 0; i < 1000; i++) {
|
for (int i = 0; i < 1000; i++) {
|
||||||
@ -48,7 +48,7 @@ public class TextHighlighterTest {
|
|||||||
Assert.assertEquals("1 000 000", textHighlighter.process("1000000").toString());
|
Assert.assertEquals("1 000 000", textHighlighter.process("1000000").toString());
|
||||||
Assert.assertEquals("1 000 000", textHighlighter.process("1000000").toString());
|
Assert.assertEquals("1 000 000", textHighlighter.process("1000000").toString());
|
||||||
|
|
||||||
textHighlighter = new TextHighlighter(0, false, JsclMathEngine.instance);
|
textHighlighter = new TextHighlighter(0, true, JsclMathEngine.instance);
|
||||||
Assert.assertEquals("0.1E3", textHighlighter.process("0.1E3").toString());
|
Assert.assertEquals("0.1E3", textHighlighter.process("0.1E3").toString());
|
||||||
Assert.assertEquals("1E3", textHighlighter.process("1E3").toString());
|
Assert.assertEquals("1E3", textHighlighter.process("1E3").toString());
|
||||||
Assert.assertEquals("2<b>0x:</b>", textHighlighter.process("20x:").toString());
|
Assert.assertEquals("2<b>0x:</b>", textHighlighter.process("20x:").toString());
|
||||||
@ -60,7 +60,7 @@ public class TextHighlighterTest {
|
|||||||
Assert.assertEquals("-1 000 000E3", textHighlighter.process("-1000000E3").toString());
|
Assert.assertEquals("-1 000 000E3", textHighlighter.process("-1000000E3").toString());
|
||||||
Assert.assertEquals("-1 000 000E-3", textHighlighter.process("-1000000E-3").toString());
|
Assert.assertEquals("-1 000 000E-3", textHighlighter.process("-1000000E-3").toString());
|
||||||
Assert.assertEquals("-1 000 000E-30000", textHighlighter.process("-1000000E-30000").toString());
|
Assert.assertEquals("-1 000 000E-30000", textHighlighter.process("-1000000E-30000").toString());
|
||||||
textHighlighter = new TextHighlighter(0, true, JsclMathEngine.instance);
|
textHighlighter = new TextHighlighter(0, false, JsclMathEngine.instance);
|
||||||
|
|
||||||
textHighlighter.process("cannot calculate 3^10^10 !!!\n" +
|
textHighlighter.process("cannot calculate 3^10^10 !!!\n" +
|
||||||
" unable to enter 0. FIXED\n" +
|
" unable to enter 0. FIXED\n" +
|
||||||
|
@ -183,6 +183,18 @@ public class CalculatorEngineTest {
|
|||||||
Assert.assertEquals("100", cm.evaluate(JsclOperation.numeric, "0.1E3").getResult());
|
Assert.assertEquals("100", cm.evaluate(JsclOperation.numeric, "0.1E3").getResult());
|
||||||
Assert.assertEquals("3.957", cm.evaluate(JsclOperation.numeric, "ln(8)lg(8)+ln(8)").getResult());
|
Assert.assertEquals("3.957", cm.evaluate(JsclOperation.numeric, "ln(8)lg(8)+ln(8)").getResult());
|
||||||
|
|
||||||
|
Assert.assertEquals("0.933", cm.evaluate(JsclOperation.numeric, "0x:E/0x:F").getResult());
|
||||||
|
|
||||||
|
try {
|
||||||
|
cm.getEngine().setNumeralBase(NumeralBase.hex);
|
||||||
|
Assert.assertEquals("E/F", cm.evaluate(JsclOperation.numeric, "0x:E/0x:F").getResult());
|
||||||
|
Assert.assertEquals("E/F", cm.evaluate(JsclOperation.simplify, "0x:E/0x:F").getResult());
|
||||||
|
Assert.assertEquals("E/F", cm.evaluate(JsclOperation.numeric, "E/F").getResult());
|
||||||
|
Assert.assertEquals("E/F", cm.evaluate(JsclOperation.simplify, "E/F").getResult());
|
||||||
|
} finally {
|
||||||
|
cm.getEngine().setNumeralBase(NumeralBase.dec);
|
||||||
|
}
|
||||||
|
|
||||||
Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "((((((0))))))").getResult());
|
Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "((((((0))))))").getResult());
|
||||||
Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((0))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))").getResult());
|
Assert.assertEquals("0", cm.evaluate(JsclOperation.numeric, "((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((0))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))))").getResult());
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user