diff --git a/res/layout/calc_division_button.xml b/res/layout/calc_division_button.xml index cb6dc93b..bbdcad71 100644 --- a/res/layout/calc_division_button.xml +++ b/res/layout/calc_division_button.xml @@ -8,8 +8,10 @@ \ No newline at end of file diff --git a/res/layout/calc_functions_button.xml b/res/layout/calc_functions_button.xml index 18ddaa8b..1c2846bd 100644 --- a/res/layout/calc_functions_button.xml +++ b/res/layout/calc_functions_button.xml @@ -7,6 +7,7 @@ --> \ No newline at end of file diff --git a/res/layout/calc_subtraction_button.xml b/res/layout/calc_subtraction_button.xml index d27a828b..e30899d2 100644 --- a/res/layout/calc_subtraction_button.xml +++ b/res/layout/calc_subtraction_button.xml @@ -8,7 +8,7 @@ Результат не допустим! Операторы + Возвращает остаток от деления \'x\' на \'y\'. + Суммирует функции \'f(i)\', пробегая по переменной \'i\' от \'from\' до \'to\'. + Возвращает произведение функций \'f(i)\', пробегая по переменной \'i\' от \'from\' до \'to\'. + Возвращает производную порядка \'order\' функции \'f(x)\' по переменной \'x\' и вычисляет её в точке \'x_point\'. + Возвращает интеграл функции \'f(x)\' по переменно \'x\'. + Интегрирует функцию \'f(x)\' по переменной \'x\' от \'a\' до \'b\'. + diff --git a/res/values/strings.xml b/res/values/strings.xml index a741d9cf..302eee32 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -140,12 +140,20 @@ Not-equals function - gives 1 if two arguments are not equals, 0 otherwise. Lesser function - gives 1 if first argument is less than second, 0 otherwise. Greater function - gives 1 if first argument is greater than second, 0 otherwise. - Converts degrees into radians: d - degrees, m - minutes (default = 0), s - seconds (default = 0) - Converts degrees from DMS notation to decimal notation: d - degrees, m - minutes (default = 0), s - seconds (default = 0) + Converts degrees into radians: d - degrees, m - minutes (default = 0), s - seconds (default = 0). + Converts degrees from DMS notation to decimal notation: d - degrees, m - minutes (default = 0), s - seconds (default = 0). Converts radians into degrees. Unable to create empty constant! Current result is not valid! Operators + Modulo operation finds the remainder of division of \'x\' by \'y\'. + Sums functions \'f(i)\', iterating through \'i\' from \'from\' to \'to\'. + Gives product of functions \'f(i)\', iterating through \'i\' from \'from\' to \'to\'. + Gives derivative of order \'order\' of functions \'f(x)\' by \'x\' variable and calculates at point \'x_point\'. + Gives integral of function \'f(x)\' by \'x\' variable. + Integrates function \'f(x)\' by \'x\' variable from \'a\' to \'b\'. + + diff --git a/src/main/java/org/solovyev/android/calculator/CalculatorActivity.java b/src/main/java/org/solovyev/android/calculator/CalculatorActivity.java index 330cb048..760129f7 100644 --- a/src/main/java/org/solovyev/android/calculator/CalculatorActivity.java +++ b/src/main/java/org/solovyev/android/calculator/CalculatorActivity.java @@ -96,6 +96,18 @@ public class CalculatorActivity extends Activity implements FontSizeAdjuster, Sh final OnDragListener historyOnDragListener = new OnDragListenerVibrator(newOnDragListener(new HistoryDragProcessor(this.calculatorModel), dragPreferences), vibrator, preferences); ((DragButton) findViewById(R.id.historyButton)).setOnDragListener(historyOnDragListener); + ((DragButton) findViewById(R.id.subtractionButton)).setOnDragListener(new OnDragListenerVibrator(newOnDragListener(new SimpleOnDragListener.DragProcessor() { + @Override + public boolean processDragEvent(@NotNull DragDirection dragDirection, @NotNull DragButton dragButton, @NotNull Point2d startPoint2d, @NotNull MotionEvent motionEvent) { + if (dragDirection == DragDirection.down) { + operatorsButtonClickHandler(dragButton); + return true; + } + return false; + } + }, dragPreferences), vibrator, preferences)); + + final OnDragListener toPositionOnDragListener = new OnDragListenerVibrator(new SimpleOnDragListener(new CursorDragProcessor(calculatorModel), dragPreferences), vibrator, preferences); ((DragButton) findViewById(R.id.rightButton)).setOnDragListener(toPositionOnDragListener); ((DragButton) findViewById(R.id.leftButton)).setOnDragListener(toPositionOnDragListener); diff --git a/src/main/java/org/solovyev/android/calculator/CalculatorModel.java b/src/main/java/org/solovyev/android/calculator/CalculatorModel.java index 76c68d9d..50091873 100644 --- a/src/main/java/org/solovyev/android/calculator/CalculatorModel.java +++ b/src/main/java/org/solovyev/android/calculator/CalculatorModel.java @@ -269,6 +269,9 @@ public enum CalculatorModel implements CursorControl, HistoryControl extends AndroidMathRegistryImpl { + + @NotNull + private static final Map substitutes = new HashMap(); + static { + substitutes.put("√", "sqrt"); + } + + @NotNull + private static final String FUNCTION_DESCRIPTION_PREFIX = "c_fun_description_"; + + public AndroidFunctionsMathRegistry(@NotNull MathRegistry functionsRegistry) { + super(functionsRegistry, FUNCTION_DESCRIPTION_PREFIX); + } + + @NotNull + @Override + protected Map getSubstitutes() { + return substitutes; + } +} diff --git a/src/main/java/org/solovyev/android/calculator/model/AndroidMathRegistryImpl.java b/src/main/java/org/solovyev/android/calculator/model/AndroidMathRegistryImpl.java index 5a362fc3..3c68ece4 100644 --- a/src/main/java/org/solovyev/android/calculator/model/AndroidMathRegistryImpl.java +++ b/src/main/java/org/solovyev/android/calculator/model/AndroidMathRegistryImpl.java @@ -7,7 +7,6 @@ package org.solovyev.android.calculator.model; import android.content.Context; -import jscl.math.function.Function; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.solovyev.android.calculator.R; @@ -24,32 +23,40 @@ import java.util.Map; * Date: 10/30/11 * Time: 1:03 AM */ -public class AndroidMathRegistryImpl implements AndroidMathRegistry { - - @NotNull - private static final String FUNCTION_DESCRIPTION_PREFIX = "c_fun_description_"; +public abstract class AndroidMathRegistryImpl implements AndroidMathRegistry { @NotNull private final MathRegistry functionsRegistry; - public AndroidMathRegistryImpl(@NotNull MathRegistry functionsRegistry) { + @NotNull + private final String prefix; + + protected AndroidMathRegistryImpl(@NotNull MathRegistry functionsRegistry, @NotNull String prefix) { this.functionsRegistry = functionsRegistry; + this.prefix = prefix; } + @NotNull + protected abstract Map getSubstitutes(); + @Nullable @Override - public String getDescription(@NotNull Context context, @NotNull String functionName) { + public String getDescription(@NotNull Context context, @NotNull String name) { final String result; final Map stringsCache = RClassUtils.getCache(R.string.class); final Integer stringId; - if (!functionName.equals("√")) { - stringId = stringsCache.get(FUNCTION_DESCRIPTION_PREFIX + functionName); + + final Map substitutes = getSubstitutes(); + final String substitute = substitutes.get(name); + if (substitute == null) { + stringId = stringsCache.get(prefix + name); } else { // todo serso: think - stringId = stringsCache.get(FUNCTION_DESCRIPTION_PREFIX + "sqrt"); + stringId = stringsCache.get(prefix + substitute); } + if (stringId != null) { result = context.getString(stringId); } else { diff --git a/src/main/java/org/solovyev/android/calculator/model/AndroidOperatorsMathRegistry.java b/src/main/java/org/solovyev/android/calculator/model/AndroidOperatorsMathRegistry.java new file mode 100644 index 00000000..7de0bfc3 --- /dev/null +++ b/src/main/java/org/solovyev/android/calculator/model/AndroidOperatorsMathRegistry.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + * or visit http://se.solovyev.org + */ + +package org.solovyev.android.calculator.model; + +import org.jetbrains.annotations.NotNull; +import org.solovyev.common.math.MathEntity; +import org.solovyev.common.math.MathRegistry; + +import java.util.HashMap; +import java.util.Map; + +/** + * User: serso + * Date: 11/17/11 + * Time: 11:29 PM + */ +public class AndroidOperatorsMathRegistry extends AndroidMathRegistryImpl { + + @NotNull + private static final Map substitutes = new HashMap(); + static { + substitutes.put("Σ", "sum"); + substitutes.put("∏", "product"); + substitutes.put("∂", "derivative"); + substitutes.put("∫ab", "integral_ab"); + substitutes.put("∫", "integral"); + substitutes.put("Σ", "sum"); + } + + @NotNull + private static final String OPERATOR_DESCRIPTION_PREFIX = "c_op_description_"; + + protected AndroidOperatorsMathRegistry(@NotNull MathRegistry functionsRegistry) { + super(functionsRegistry, OPERATOR_DESCRIPTION_PREFIX); + } + + @NotNull + @Override + protected Map getSubstitutes() { + return substitutes; + } +} diff --git a/src/main/java/org/solovyev/android/calculator/model/CalculatorEngine.java b/src/main/java/org/solovyev/android/calculator/model/CalculatorEngine.java index 625e18ee..4047e4c6 100644 --- a/src/main/java/org/solovyev/android/calculator/model/CalculatorEngine.java +++ b/src/main/java/org/solovyev/android/calculator/model/CalculatorEngine.java @@ -13,24 +13,18 @@ import jscl.math.operator.Operator; import jscl.text.ParseInterruptedException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.solovyev.android.calculator.R; import org.solovyev.android.calculator.jscl.JsclOperation; import org.solovyev.android.msg.AndroidMessage; import org.solovyev.common.NumberMapper; import org.solovyev.common.math.MathRegistry; import org.solovyev.common.msg.MessageRegistry; -import org.solovyev.common.msg.MessageType; -import org.solovyev.common.utils.CollectionsUtils; -import org.solovyev.common.utils.Formatter; import org.solovyev.common.utils.MutableObject; import org.solovyev.common.utils.StringUtils; import java.math.BigDecimal; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; -import java.util.HashSet; import java.util.Locale; -import java.util.Set; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; @@ -70,18 +64,16 @@ public enum CalculatorEngine { private final AndroidVarsRegistry varsRegister = new AndroidVarsRegistryImpl(engine.getConstantsRegistry()); @NotNull - private final AndroidMathRegistry functionsRegistry = new AndroidMathRegistryImpl(engine.getFunctionsRegistry()); + private final AndroidMathRegistry functionsRegistry = new AndroidFunctionsMathRegistry(engine.getFunctionsRegistry()); @NotNull - private final AndroidMathRegistry operatorsRegistry = new AndroidMathRegistryImpl(engine.getOperatorsRegistry()); + private final AndroidMathRegistry operatorsRegistry = new AndroidOperatorsMathRegistry(engine.getOperatorsRegistry()); private final MathRegistry postfixFunctionsRegistry = engine.getPostfixFunctionsRegistry(); - @NotNull - private final static Set tooLongExecutionCache = new HashSet(); - @NotNull private DecimalFormatSymbols decimalGroupSymbols = new DecimalFormatSymbols(Locale.getDefault()); + { decimalGroupSymbols.setDecimalSeparator('.'); decimalGroupSymbols.setGroupingSeparator(GROUPING_SEPARATOR_DEFAULT.charAt(0)); @@ -106,7 +98,7 @@ public enum CalculatorEngine { final DecimalFormat df = new DecimalFormat(); df.setDecimalFormatSymbols(decimalGroupSymbols); df.setGroupingUsed(useGroupingSeparator); - if (round ) { + if (round) { if (isRoundResult()) { df.setMaximumFractionDigits(instance.getPrecision()); return df.format(new BigDecimal(value).setScale(instance.getPrecision(), BigDecimal.ROUND_HALF_UP).doubleValue()); @@ -178,75 +170,70 @@ public enum CalculatorEngine { final JsclOperation finalOperation = operation; final String result; - if (!tooLongExecutionCache.contains(jsclExpression)) { - final MutableObject calculationResult = new MutableObject(null); - final MutableObject exception = new MutableObject(null); - final MutableObject calculationThread = new MutableObject(null); + final MutableObject calculationResult = new MutableObject(null); + final MutableObject exception = new MutableObject(null); + final MutableObject calculationThread = new MutableObject(null); - final CountDownLatch latch = new CountDownLatch(1); + final CountDownLatch latch = new CountDownLatch(1); - new Thread(new Runnable() { - @Override - public void run() { - final Thread thread = Thread.currentThread(); - try { - //Log.d(CalculatorEngine.class.getName(), "Calculation thread started work: " + thread.getName()); - //System.out.println(jsclExpression); - calculationThread.setObject(thread); - calculationResult.setObject(finalOperation.evaluate(jsclExpression)); - } catch (ArithmeticException e) { - //System.out.println(e.getMessage()); - exception.setObject(new ParseException(e.getMessage(), e)); - } catch (jscl.text.ParseException e) { - //System.out.println(e.getMessage()); - exception.setObject(new ParseException(e.getMessage(), e)); - } catch (ParseInterruptedException e) { - //System.out.println(e.getMessage()); - // do nothing - we ourselves interrupt the calculations - } finally { - //Log.d(CalculatorEngine.class.getName(), "Calculation thread ended work: " + thread.getName()); - calculationThread.setObject(null); - latch.countDown(); - } + new Thread(new Runnable() { + @Override + public void run() { + final Thread thread = Thread.currentThread(); + try { + //Log.d(CalculatorEngine.class.getName(), "Calculation thread started work: " + thread.getName()); + //System.out.println(jsclExpression); + calculationThread.setObject(thread); + calculationResult.setObject(finalOperation.evaluate(jsclExpression)); + } catch (ArithmeticException e) { + //System.out.println(e.getMessage()); + exception.setObject(new ParseException(e.getMessage(), e)); + } catch (jscl.text.ParseException e) { + //System.out.println(e.getMessage()); + exception.setObject(new ParseException(e.getMessage(), e)); + } catch (ParseInterruptedException e) { + //System.out.println(e.getMessage()); + // do nothing - we ourselves interrupt the calculations + } finally { + //Log.d(CalculatorEngine.class.getName(), "Calculation thread ended work: " + thread.getName()); + calculationThread.setObject(null); + latch.countDown(); } - }).start(); + } + }).start(); - try { - //Log.d(CalculatorEngine.class.getName(), "Main thread is waiting: " + Thread.currentThread().getName()); - latch.await(timeout, TimeUnit.MILLISECONDS); - //Log.d(CalculatorEngine.class.getName(), "Main thread got up: " + Thread.currentThread().getName()); + try { + //Log.d(CalculatorEngine.class.getName(), "Main thread is waiting: " + Thread.currentThread().getName()); + latch.await(timeout, TimeUnit.MILLISECONDS); + //Log.d(CalculatorEngine.class.getName(), "Main thread got up: " + Thread.currentThread().getName()); - final ParseException evalErrorLocal = exception.getObject(); - final Object calculationResultLocal = calculationResult.getObject(); - final Thread calculationThreadLocal = calculationThread.getObject(); + final ParseException evalErrorLocal = exception.getObject(); + final Object calculationResultLocal = calculationResult.getObject(); + final Thread calculationThreadLocal = calculationThread.getObject(); - if (calculationThreadLocal != null) { - // todo serso: interrupt doesn't stop the thread but it MUST be killed - threadKiller.killThread(calculationThreadLocal); - //calculationThreadLocal.stop(); - } - - if ( evalErrorLocal != null ) { - if ( finalOperation == JsclOperation.numeric && preparedExpression.isExistsUndefinedVar() ) { - return evaluate(JsclOperation.simplify, expression, mr); - } - throw evalErrorLocal; - } - - if ( calculationResultLocal == null ) { - tooLongExecutionCache.add(jsclExpression); - throw new ParseException("Too long calculation for: " + jsclExpression); - } - - } catch (InterruptedException e) { - throw new ParseException(e); + if (calculationThreadLocal != null) { + // todo serso: interrupt doesn't stop the thread but it MUST be killed + threadKiller.killThread(calculationThreadLocal); + //calculationThreadLocal.stop(); } - result = String.valueOf(calculationResult.getObject()).trim(); - } else { - throw new ParseException("Too long calculation for: " + jsclExpression); + if (evalErrorLocal != null) { + if (finalOperation == JsclOperation.numeric && preparedExpression.isExistsUndefinedVar()) { + return evaluate(JsclOperation.simplify, expression, mr); + } + throw evalErrorLocal; + } + + if (calculationResultLocal == null) { + throw new ParseException("Too long calculation for: " + jsclExpression); + } + + } catch (InterruptedException e) { + throw new ParseException(e); } + result = String.valueOf(calculationResult.getObject()).trim(); + return new Result(operation.getFromProcessor().process(result), operation); } } @@ -330,6 +317,7 @@ public enum CalculatorEngine { void setTimeout(int timeout) { this.timeout = timeout; } + // for tests only void setThreadKiller(@NotNull ThreadKiller threadKiller) { this.threadKiller = threadKiller;