Merge branch 'dev-number-format'

Conflicts:
	jscl/src/main/java/jscl/JsclMathEngine.java
This commit is contained in:
serso 2016-05-04 23:45:03 +02:00
commit 0aef4e16d0
48 changed files with 936 additions and 286 deletions

View File

@ -56,7 +56,11 @@
<activity
android:name=".preferences.PreferencesActivity"
android:label="@string/cpp_settings" />
android:label="@string/cpp_settings">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
</intent-filter>
</activity>
<activity
android:name=".preferences.PreferencesActivity$Dialog"

View File

@ -25,7 +25,11 @@ package org.solovyev.android.calculator;
import android.app.Activity;
import android.app.Application;
import android.app.Dialog;
import android.content.*;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.net.Uri;
@ -48,14 +52,17 @@ import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.ArrayAdapter;
import org.solovyev.android.Check;
import org.solovyev.android.calculator.floating.FloatingCalculatorService;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public final class App {
public static final String TAG = "C++";
@ -276,6 +283,11 @@ public final class App {
processViewsOfType0(view, viewClass, viewProcessor);
}
@Nonnull
public static <T> ArrayAdapter<T> makeSimpleSpinnerAdapter(@NonNull Context context) {
return new ArrayAdapter<>(context, R.layout.support_simple_spinner_dropdown_item);
}
public interface ViewProcessor<V> {
void process(@Nonnull V view);
}

View File

@ -22,7 +22,10 @@
package org.solovyev.android.calculator;
import android.content.Context;
import android.content.SharedPreferences;
import android.support.annotation.NonNull;
import android.support.annotation.StringRes;
import android.text.TextUtils;
import com.squareup.otto.Bus;
import jscl.AngleUnit;
@ -32,14 +35,15 @@ import jscl.NumeralBase;
import jscl.math.operator.Operator;
import jscl.text.Identifier;
import jscl.text.Parser;
import midpcalc.Real;
import org.solovyev.android.Check;
import org.solovyev.android.calculator.functions.FunctionsRegistry;
import org.solovyev.android.calculator.operators.OperatorsRegistry;
import org.solovyev.android.calculator.operators.PostfixFunctionsRegistry;
import org.solovyev.android.prefs.BooleanPreference;
import org.solovyev.android.prefs.IntegerPreference;
import org.solovyev.android.prefs.Preference;
import org.solovyev.android.prefs.StringPreference;
import org.solovyev.android.calculator.preferences.PreferenceEntry;
import org.solovyev.android.prefs.*;
import org.solovyev.common.text.CharacterMapper;
import org.solovyev.common.NumberFormatter;
import org.solovyev.common.text.EnumMapper;
import org.solovyev.common.text.NumberMapper;
@ -86,15 +90,8 @@ public class Engine implements SharedPreferences.OnSharedPreferenceChangeListene
public Engine(@Nonnull JsclMathEngine mathEngine) {
this.mathEngine = mathEngine;
this.mathEngine.setRoundResult(true);
this.mathEngine.setUseGroupingSeparator(true);
}
private static void migratePreference(@Nonnull SharedPreferences preferences, @Nonnull BooleanPreference preference, @Nonnull String oldKey, @Nonnull SharedPreferences.Editor editor) {
if (!preferences.contains(oldKey)) {
return;
}
editor.putBoolean(preference.getKey(), preferences.getBoolean(oldKey, false));
this.mathEngine.setPrecision(5);
this.mathEngine.setGroupingSeparator(JsclMathEngine.GROUPING_SEPARATOR_DEFAULT);
}
private static void migratePreference(@Nonnull SharedPreferences preferences, @Nonnull StringPreference<?> preference, @Nonnull String oldKey, @Nonnull SharedPreferences.Editor editor) {
@ -168,13 +165,33 @@ public class Engine implements SharedPreferences.OnSharedPreferenceChangeListene
}
final SharedPreferences.Editor editor = preferences.edit();
if (oldVersion == 0) {
migratePreference(preferences, Preferences.groupingSeparator, "org.solovyev.android.calculator.CalculatorActivity_calc_grouping_separator", editor);
migratePreference(preferences, Preferences.Output.separator, "org.solovyev.android.calculator.CalculatorActivity_calc_grouping_separator", editor);
migratePreference(preferences, Preferences.multiplicationSign, "org.solovyev.android.calculator.CalculatorActivity_calc_multiplication_sign", editor);
migratePreference(preferences, Preferences.numeralBase, "org.solovyev.android.calculator.CalculatorActivity_numeral_bases", editor);
migratePreference(preferences, Preferences.angleUnit, "org.solovyev.android.calculator.CalculatorActivity_angle_units", editor);
migratePreference(preferences, Preferences.Output.precision, "org.solovyev.android.calculator.CalculatorModel_result_precision", editor);
migratePreference(preferences, Preferences.Output.scientificNotation, "calculation.output.science_notation", editor);
migratePreference(preferences, Preferences.Output.round, "org.solovyev.android.calculator.CalculatorModel_round_result", editor);
if (preferences.contains("engine.output.science_notation")) {
final boolean scientific = preferences.getBoolean("engine.output.science_notation", false);
Preferences.Output.notation.putPreference(editor, scientific ? Notation.sci : Notation.dec);
}
if (preferences.contains("org.solovyev.android.calculator.CalculatorModel_round_result")) {
final boolean round = preferences.getBoolean("org.solovyev.android.calculator.CalculatorModel_round_result", true);
if (!round) {
Preferences.Output.precision.putPreference(editor, NumberFormatter.MAX_PRECISION);
}
}
} else if (oldVersion == 1) {
migratePreference(preferences, Preferences.Output.separator, "engine.groupingSeparator", editor);
if (preferences.contains("engine.output.scientificNotation")) {
final boolean scientific = preferences.getBoolean("engine.output.scientificNotation", false);
Preferences.Output.notation.putPreference(editor, scientific ? Notation.sci : Notation.dec);
}
if (preferences.contains("engine.output.round")) {
final boolean round = preferences.getBoolean("engine.output.round", true);
if (!round) {
Preferences.Output.precision.putPreference(editor, NumberFormatter.MAX_PRECISION);
}
}
}
Preferences.version.putDefault(editor);
editor.apply();
@ -202,16 +219,9 @@ public class Engine implements SharedPreferences.OnSharedPreferenceChangeListene
setMultiplicationSign(Preferences.multiplicationSign.getPreference(preferences));
mathEngine.setPrecision(Preferences.Output.precision.getPreference(preferences));
mathEngine.setScienceNotation(Preferences.Output.scientificNotation.getPreference(preferences));
mathEngine.setRoundResult(Preferences.Output.round.getPreference(preferences));
mathEngine.setNotation(Preferences.Output.notation.getPreference(preferences).id);
mathEngine.setGroupingSeparator(Preferences.Output.separator.getPreference(preferences));
final String groupingSeparator = Preferences.groupingSeparator.getPreference(preferences);
if (TextUtils.isEmpty(groupingSeparator)) {
mathEngine.setUseGroupingSeparator(false);
} else {
mathEngine.setUseGroupingSeparator(true);
mathEngine.setGroupingSeparator(groupingSeparator.charAt(0));
}
bus.post(ChangedEvent.INSTANCE);
}
@ -231,28 +241,54 @@ public class Engine implements SharedPreferences.OnSharedPreferenceChangeListene
}
}
public enum Notation implements PreferenceEntry {
dec(Real.NumberFormat.FSE_NONE, R.string.cpp_number_format_dec),
eng(Real.NumberFormat.FSE_ENG, R.string.cpp_number_format_eng),
sci(Real.NumberFormat.FSE_SCI, R.string.cpp_number_format_sci);
public final int id;
@StringRes
public final int name;
Notation(int id, @StringRes int name) {
this.id = id;
this.name = name;
}
@NonNull
@Override
public CharSequence getName(@NonNull Context context) {
return context.getString(name);
}
@NonNull
@Override
public CharSequence getId() {
return name();
}
}
public static class ChangedEvent {
static final ChangedEvent INSTANCE = new ChangedEvent();
private ChangedEvent() {
}
}
public static class Preferences {
public static final StringPreference<String> groupingSeparator = StringPreference.of("engine.groupingSeparator", String.valueOf(JsclMathEngine.GROUPING_SEPARATOR_DEFAULT));
public static final StringPreference<String> multiplicationSign = StringPreference.of("engine.multiplicationSign", "×");
public static final StringPreference<NumeralBase> numeralBase = StringPreference.ofTypedValue("engine.numeralBase", "dec", EnumMapper.of(NumeralBase.class));
public static final StringPreference<AngleUnit> angleUnit = StringPreference.ofTypedValue("engine.angleUnit", "deg", EnumMapper.of(AngleUnit.class));
public static final Preference<Integer> version = IntegerPreference.of("engine.version", 1);
public static final Preference<Integer> version = IntegerPreference.of("engine.version", 2);
private static final List<String> preferenceKeys = new ArrayList<>();
static {
preferenceKeys.add(groupingSeparator.getKey());
preferenceKeys.add(multiplicationSign.getKey());
preferenceKeys.add(numeralBase.getKey());
preferenceKeys.add(angleUnit.getKey());
preferenceKeys.add(Output.precision.getKey());
preferenceKeys.add(Output.scientificNotation.getKey());
preferenceKeys.add(Output.round.getKey());
preferenceKeys.add(Output.notation.getKey());
preferenceKeys.add(Output.separator.getKey());
}
@Nonnull
@ -262,8 +298,8 @@ public class Engine implements SharedPreferences.OnSharedPreferenceChangeListene
public static class Output {
public static final StringPreference<Integer> precision = StringPreference.ofTypedValue("engine.output.precision", "5", NumberMapper.of(Integer.class));
public static final BooleanPreference scientificNotation = BooleanPreference.of("engine.output.scientificNotation", false);
public static final BooleanPreference round = BooleanPreference.of("engine.output.round", true);
public static final StringPreference<Notation> notation = StringPreference.ofEnum("engine.output.notation", Notation.dec, Notation.class);
public static final StringPreference<Character> separator = StringPreference.ofTypedValue("engine.output.separator", JsclMathEngine.GROUPING_SEPARATOR_DEFAULT, CharacterMapper.INSTANCE);
}
}
}

View File

@ -1,4 +1,4 @@
package org.solovyev.android.calculator.converter;
package org.solovyev.android.calculator;
import android.content.Context;
import android.support.annotation.NonNull;
@ -6,7 +6,7 @@ import android.support.annotation.StringRes;
import javax.annotation.Nonnull;
class Named<T> {
public class Named<T> {
@NonNull
public final T item;
@NonNull
@ -18,12 +18,12 @@ class Named<T> {
}
@NonNull
static <T> Named<T> create(@NonNull T item, @Nonnull String name) {
public static <T> Named<T> create(@NonNull T item, @Nonnull String name) {
return new Named<T>(item, name);
}
@NonNull
static <T> Named<T> create(@NonNull T item, @StringRes int name, @NonNull Context context) {
public static <T> Named<T> create(@NonNull T item, @StringRes int name, @NonNull Context context) {
return create(item, name == 0 ? item.toString() : context.getString(name));
}

View File

@ -53,6 +53,8 @@ import java.util.EnumMap;
import java.util.Locale;
import java.util.Map;
import jscl.JsclMathEngine;
import static org.solovyev.android.prefs.IntegerPreference.DEF_VALUE;
public final class Preferences {
@ -108,19 +110,19 @@ public final class Preferences {
}
private static void setInitialDefaultValues(@Nonnull Application application, @Nonnull SharedPreferences preferences, @Nonnull SharedPreferences.Editor editor) {
if (!Engine.Preferences.groupingSeparator.isSet(preferences)) {
if (!Engine.Preferences.Output.separator.isSet(preferences)) {
final Locale locale = Locale.getDefault();
if (locale != null) {
final DecimalFormatSymbols decimalFormatSymbols = new DecimalFormatSymbols(locale);
int index = MathType.grouping_separator.getTokens().indexOf(String.valueOf(decimalFormatSymbols.getGroupingSeparator()));
final String groupingSeparator;
final int index = MathType.grouping_separator.getTokens().indexOf(String.valueOf(decimalFormatSymbols.getGroupingSeparator()));
final char separator;
if (index >= 0) {
groupingSeparator = MathType.grouping_separator.getTokens().get(index);
separator = MathType.grouping_separator.getTokens().get(index).charAt(0);
} else {
groupingSeparator = " ";
separator = JsclMathEngine.GROUPING_SEPARATOR_DEFAULT;
}
Engine.Preferences.groupingSeparator.putPreference(editor, groupingSeparator);
Engine.Preferences.Output.separator.putPreference(editor, separator);
}
}

View File

@ -21,21 +21,39 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.*;
import butterknife.Bind;
import butterknife.ButterKnife;
import org.solovyev.android.calculator.*;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import org.solovyev.android.calculator.App;
import org.solovyev.android.calculator.AppComponent;
import org.solovyev.android.calculator.AppModule;
import org.solovyev.android.calculator.BaseDialogFragment;
import org.solovyev.android.calculator.Clipboard;
import org.solovyev.android.calculator.Editor;
import org.solovyev.android.calculator.Keyboard;
import org.solovyev.android.calculator.Named;
import org.solovyev.android.calculator.R;
import org.solovyev.android.calculator.keyboard.FloatingKeyboard;
import org.solovyev.android.calculator.keyboard.FloatingKeyboardWindow;
import org.solovyev.android.calculator.keyboard.FloatingNumberKeyboard;
import org.solovyev.android.calculator.math.MathUtils;
import org.solovyev.android.calculator.text.NaturalComparator;
import org.solovyev.android.calculator.view.EditTextCompat;
import javax.annotation.Nonnull;
import javax.inject.Inject;
import java.util.Comparator;
import static org.solovyev.android.calculator.UiPreferences.Converter.*;
import butterknife.Bind;
import butterknife.ButterKnife;
import static org.solovyev.android.calculator.UiPreferences.Converter.lastDimension;
import static org.solovyev.android.calculator.UiPreferences.Converter.lastUnitsFrom;
import static org.solovyev.android.calculator.UiPreferences.Converter.lastUnitsTo;
public class ConverterFragment extends BaseDialogFragment
implements AdapterView.OnItemSelectedListener, View.OnFocusChangeListener, TextView.OnEditorActionListener, View.OnClickListener, TextWatcher {
@ -44,7 +62,6 @@ public class ConverterFragment extends BaseDialogFragment
private static final String STATE_SELECTION_FROM = "selection.from";
private static final String STATE_SELECTION_TO = "selection.to";
private static final String EXTRA_VALUE = "value";
private static final NamedItemComparator COMPARATOR = new NamedItemComparator();
public static final int NONE = -1;
@NonNull
@ -96,11 +113,6 @@ public class ConverterFragment extends BaseDialogFragment
App.showDialog(fragment, "converter", activity.getSupportFragmentManager());
}
@Nonnull
private static <T> ArrayAdapter<T> makeAdapter(@NonNull Context context) {
return new ArrayAdapter<>(context, R.layout.support_simple_spinner_dropdown_item);
}
@NonNull
@Override
public AlertDialog onCreateDialog(@Nullable Bundle savedInstanceState) {
@ -130,13 +142,13 @@ public class ConverterFragment extends BaseDialogFragment
final View view = inflater.inflate(R.layout.cpp_unit_converter, null);
ButterKnife.bind(this, view);
dimensionsAdapter = makeAdapter(context);
dimensionsAdapter = App.makeSimpleSpinnerAdapter(context);
for (ConvertibleDimension dimension : UnitDimension.values()) {
dimensionsAdapter.add(dimension.named(context));
}
dimensionsAdapter.add(NumeralBaseDimension.get().named(context));
adapterFrom = makeAdapter(context);
adapterTo = makeAdapter(context);
adapterFrom = App.makeSimpleSpinnerAdapter(context);
adapterTo = App.makeSimpleSpinnerAdapter(context);
dimensionsSpinner.setAdapter(dimensionsAdapter);
spinnerFrom.setAdapter(adapterFrom);
@ -235,7 +247,7 @@ public class ConverterFragment extends BaseDialogFragment
for (Convertible unit : dimension.getUnits()) {
adapterFrom.add(unit.named(getActivity()));
}
adapterFrom.sort(COMPARATOR);
adapterFrom.sort(NaturalComparator.INSTANCE);
adapterFrom.setNotifyOnChange(true);
adapterFrom.notifyDataSetChanged();
if (pendingFromSelection != NONE) {
@ -261,7 +273,7 @@ public class ConverterFragment extends BaseDialogFragment
adapterTo.add(unit.named(getActivity()));
}
}
adapterTo.sort(COMPARATOR);
adapterTo.sort(NaturalComparator.INSTANCE);
adapterTo.setNotifyOnChange(true);
adapterTo.notifyDataSetChanged();
if (selectedUnit != null && !except.equals(selectedUnit)) {
@ -431,13 +443,6 @@ public class ConverterFragment extends BaseDialogFragment
super.dismiss();
}
private static class NamedItemComparator implements Comparator<Named<Convertible>> {
@Override
public int compare(Named<Convertible> lhs, Named<Convertible> rhs) {
return lhs.toString().compareTo(rhs.toString());
}
}
private class KeyboardUser implements FloatingKeyboard.User {
@NonNull
@Override

View File

@ -3,6 +3,8 @@ package org.solovyev.android.calculator.converter;
import android.content.Context;
import android.support.annotation.NonNull;
import org.solovyev.android.calculator.Named;
interface Convertible {
@NonNull
String convert(@NonNull Convertible to, @NonNull String value) throws NumberFormatException;

View File

@ -3,6 +3,8 @@ package org.solovyev.android.calculator.converter;
import android.content.Context;
import android.support.annotation.NonNull;
import org.solovyev.android.calculator.Named;
import java.util.List;
public interface ConvertibleDimension {

View File

@ -2,6 +2,9 @@ package org.solovyev.android.calculator.converter;
import android.content.Context;
import android.support.annotation.NonNull;
import org.solovyev.android.calculator.Named;
import jscl.JsclMathEngine;
import jscl.NumeralBase;
import midpcalc.Real;

View File

@ -3,6 +3,8 @@ package org.solovyev.android.calculator.converter;
import android.content.Context;
import android.support.annotation.NonNull;
import jscl.NumeralBase;
import org.solovyev.android.calculator.Named;
import org.solovyev.android.calculator.R;
import java.util.ArrayList;

View File

@ -2,6 +2,9 @@ package org.solovyev.android.calculator.converter;
import android.content.Context;
import android.support.annotation.NonNull;
import org.solovyev.android.calculator.Named;
import jscl.JsclMathEngine;
import jscl.NumeralBase;

View File

@ -4,6 +4,8 @@ import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import org.solovyev.android.calculator.Named;
import org.solovyev.android.calculator.R;
import javax.measure.unit.Dimension;

View File

@ -27,22 +27,26 @@ import android.content.SharedPreferences;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import com.google.common.base.Strings;
import com.squareup.otto.Bus;
import com.squareup.otto.Subscribe;
import org.json.JSONArray;
import org.json.JSONException;
import org.solovyev.android.Check;
import org.solovyev.android.calculator.*;
import org.solovyev.android.calculator.AppModule;
import org.solovyev.android.calculator.Calculator;
import org.solovyev.android.calculator.Display;
import org.solovyev.android.calculator.DisplayState;
import org.solovyev.android.calculator.Editor;
import org.solovyev.android.calculator.EditorState;
import org.solovyev.android.calculator.Engine.Preferences;
import org.solovyev.android.calculator.ErrorReporter;
import org.solovyev.android.calculator.Runnables;
import org.solovyev.android.calculator.json.Json;
import org.solovyev.android.io.FileSystem;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
@ -51,6 +55,12 @@ import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Executor;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import static android.text.TextUtils.isEmpty;
@Singleton
@ -115,15 +125,15 @@ public class History {
private static boolean isIntermediate(@Nonnull String olderText,
@Nonnull String newerText,
@NonNull String groupingSeparator) {
char separator) {
if (TextUtils.isEmpty(olderText)) {
return true;
}
if (TextUtils.isEmpty(newerText)) {
return false;
}
olderText = trimGroupingSeparators(olderText, groupingSeparator);
newerText = trimGroupingSeparators(newerText, groupingSeparator);
olderText = trimGroupingSeparators(olderText, separator);
newerText = trimGroupingSeparators(newerText, separator);
final int diff = newerText.length() - olderText.length();
if (diff >= 1) {
@ -138,11 +148,10 @@ public class History {
}
@NonNull
static String trimGroupingSeparators(@NonNull String text, @NonNull String groupingSeparator) {
if (TextUtils.isEmpty(groupingSeparator)) {
static String trimGroupingSeparators(@NonNull String text, char separator) {
if (separator == 0) {
return text;
}
Check.isTrue(groupingSeparator.length() == 1);
final StringBuilder sb = new StringBuilder(text.length());
for (int i = 0; i < text.length(); i++) {
if (i == 0 || i == text.length() - 1) {
@ -150,7 +159,7 @@ public class History {
sb.append(text.charAt(i));
continue;
}
if (Character.isDigit(text.charAt(i - 1)) && text.charAt(i) == groupingSeparator.charAt(0) && Character.isDigit(text.charAt(i + 1))) {
if (Character.isDigit(text.charAt(i - 1)) && text.charAt(i) == separator && Character.isDigit(text.charAt(i + 1))) {
// grouping separator => skip
continue;
}
@ -302,7 +311,7 @@ public class History {
final List<HistoryState> result = new LinkedList<>();
final String groupingSeparator = Preferences.groupingSeparator.getPreference(preferences);
final char separator = Preferences.Output.separator.getPreference(preferences);
final List<HistoryState> states = recent.asList();
final int statesCount = states.size();
@ -312,7 +321,7 @@ public class History {
final HistoryState newerState = states.get(i);
final String olderText = olderState.editor.getTextString();
final String newerText = newerState.editor.getTextString();
if (streak >= MAX_INTERMEDIATE_STREAK || !isIntermediate(olderText, newerText, groupingSeparator)) {
if (streak >= MAX_INTERMEDIATE_STREAK || !isIntermediate(olderText, newerText, separator)) {
result.add(0, olderState);
streak = 0;
} else {

View File

@ -0,0 +1,59 @@
package org.solovyev.android.calculator.preferences;
import android.content.Context;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import jscl.JsclMathEngine;
import org.solovyev.android.material.preferences.Preference;
@SuppressWarnings("unused")
public class NumberFormatExamplesPreference extends Preference {
public NumberFormatExamplesPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public NumberFormatExamplesPreference(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public NumberFormatExamplesPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public NumberFormatExamplesPreference(Context context) {
super(context);
}
public void update(JsclMathEngine engine) {
final StringBuilder examples = new StringBuilder();
examples.append(" 1/3 = ").append(engine.format(1d / 3)).append("\n");
examples.append(" √2 = ").append(engine.format(Math.sqrt(2d))).append("\n");
examples.append("\n");
examples.append(" 1000 = ").append(engine.format(1000d)).append("\n");
examples.append(" 1000000 = ").append(engine.format(1000000d)).append("\n");
examples.append(" 11^10 = ").append(engine.format(Math.pow(11d, 10))).append("\n");
examples.append(" 10^24 = ").append(engine.format(Math.pow(10d, 24))).append("\n");
examples.append("\n");
examples.append(" 0.001 = ").append(engine.format(0.001d)).append("\n");
examples.append("0.000001 = ").append(engine.format(0.000001d)).append("\n");
examples.append(" 11^10 = ").append(engine.format(Math.pow(11d, -10))).append("\n");
examples.append(" 10^24 = ").append(engine.format(Math.pow(10d, -24)));
setSummary(examples);
}
@Override
protected View onCreateView(ViewGroup parent) {
final View view = super.onCreateView(parent);
final View summary = view.findViewById(android.R.id.summary);
if (summary instanceof TextView) {
final TextView textView = (TextView) summary;
textView.setMaxLines(12);
textView.setLines(12);
textView.setTypeface(Typeface.MONOSPACE);
}
return view;
}
}

View File

@ -0,0 +1,72 @@
package org.solovyev.android.calculator.preferences;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Build;
import android.preference.DialogPreference;
import android.util.AttributeSet;
import android.view.View;
import butterknife.Bind;
import butterknife.ButterKnife;
import org.solovyev.android.calculator.Engine;
import org.solovyev.android.calculator.R;
import org.solovyev.android.views.DiscreteSeekBar;
import static org.solovyev.common.NumberFormatter.MAX_PRECISION;
import static org.solovyev.common.NumberFormatter.MIN_PRECISION;
@SuppressWarnings("unused")
public class PrecisionPreference extends DialogPreference {
@Bind(R.id.precision_seekbar)
DiscreteSeekBar seekBar;
{
setPersistent(false);
setDialogLayoutResource(R.layout.preference_precision);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public PrecisionPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public PrecisionPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public PrecisionPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public PrecisionPreference(Context context) {
super(context);
}
@Override
protected void onBindDialogView(View view) {
super.onBindDialogView(view);
ButterKnife.bind(this, view);
final SharedPreferences preferences = getSharedPreferences();
seekBar.setMax(MAX_PRECISION - 1);
final int precision = Math.max(MIN_PRECISION, Math.min(MAX_PRECISION, Engine.Preferences.Output.precision.getPreference(preferences)));
seekBar.setCurrentTick(precision - 1);
}
@Override
protected void onDialogClosed(boolean save) {
super.onDialogClosed(save);
if (!save) {
return;
}
final int precision = seekBar.getCurrentTick() + 1;
if (callChangeListener(precision)) {
final SharedPreferences.Editor editor = getSharedPreferences().edit();
Engine.Preferences.Output.precision.putPreference(editor, precision);
editor.apply();
}
}
}

View File

@ -35,6 +35,7 @@ public class PreferencesActivity extends BaseActivity implements SharedPreferenc
static {
preferenceDefs.append(R.xml.preferences, new PrefDef("screen-main", R.string.cpp_settings));
preferenceDefs.append(R.xml.preferences_number_format, new PrefDef("screen-number-format", R.string.c_prefs_calculations_category));
preferenceDefs.append(R.xml.preferences_calculations, new PrefDef("screen-calculations", R.string.c_prefs_calculations_category));
preferenceDefs.append(R.xml.preferences_appearance, new PrefDef("screen-appearance", R.string.c_prefs_appearance_category));
preferenceDefs.append(R.xml.preferences_other, new PrefDef("screen-other", R.string.c_prefs_other_category));

View File

@ -11,6 +11,9 @@ import android.support.v4.app.FragmentActivity;
import android.util.SparseArray;
import android.view.View;
import android.widget.ListView;
import com.squareup.otto.Bus;
import com.squareup.otto.Subscribe;
import jscl.JsclMathEngine;
import org.solovyev.android.calculator.AdView;
import org.solovyev.android.calculator.Engine;
import org.solovyev.android.calculator.Preferences;
@ -22,7 +25,9 @@ import org.solovyev.android.checkout.BillingRequests;
import org.solovyev.android.checkout.Checkout;
import org.solovyev.android.checkout.ProductTypes;
import org.solovyev.android.checkout.RequestListener;
import org.solovyev.android.prefs.StringPreference;
import org.solovyev.android.wizard.Wizards;
import org.solovyev.common.text.CharacterMapper;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@ -47,11 +52,15 @@ public class PreferencesFragment extends org.solovyev.android.material.preferenc
Languages languages;
@Inject
Wizards wizards;
@Inject
JsclMathEngine engine;
@Inject
Bus bus;
@Nonnull
public static PreferencesFragment create(int preferencesResId, int layoutResId) {
public static PreferencesFragment create(int preferences, int layout) {
final PreferencesFragment fragment = new PreferencesFragment();
fragment.setArguments(createArguments(preferencesResId, layoutResId, NO_THEME));
fragment.setArguments(createArguments(preferences, layout, NO_THEME));
return fragment;
}
@ -61,6 +70,7 @@ public class PreferencesFragment extends org.solovyev.android.material.preferenc
cast(this).getComponent().inject(this);
preferences.registerOnSharedPreferenceChangeListener(this);
bus.register(this);
}
private void setPreferenceIntent(int xml, @Nonnull PreferencesActivity.PrefDef def) {
@ -107,6 +117,11 @@ public class PreferencesFragment extends org.solovyev.android.material.preferenc
}
});
}
} else if (preference == R.xml.preferences_number_format) {
prepareListPreference(Engine.Preferences.Output.notation, Engine.Notation.class);
preparePrecisionPreference();
prepareSeparatorPreference();
prepareNumberFormatExamplesPreference();
}
prepareLanguagePreference(preference);
@ -133,8 +148,73 @@ public class PreferencesFragment extends org.solovyev.android.material.preferenc
});
}
});
}
onSharedPreferenceChanged(preferences, Engine.Preferences.Output.round.getKey());
private void prepareNumberFormatExamplesPreference() {
final NumberFormatExamplesPreference preference = (NumberFormatExamplesPreference) preferenceManager.findPreference("numberFormat.examples");
if (preference == null) {
return;
}
preference.update(engine);
}
private void prepareSeparatorPreference() {
final ListPreference preference = (ListPreference) preferenceManager.findPreference(Engine.Preferences.Output.separator.getKey());
preference.setSummary(separatorName(Engine.Preferences.Output.separator.getPreference(preferences)));
preference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference p, Object newValue) {
preference.setSummary(separatorName(CharacterMapper.INSTANCE.parseValue(String.valueOf(newValue))));
return true;
}
});
}
private int separatorName(char separator) {
switch (separator) {
case '\'':
return R.string.p_grouping_separator_apostrophe;
case ' ':
return R.string.p_grouping_separator_space;
case 0:
return R.string.p_grouping_separator_no;
}
return R.string.p_grouping_separator_no;
}
private void preparePrecisionPreference() {
final PrecisionPreference preference = (PrecisionPreference) preferenceManager.findPreference(Engine.Preferences.Output.precision.getKey());
preference.setSummary(String.valueOf(Engine.Preferences.Output.precision.getPreference(preferences)));
preference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference p, Object newValue) {
preference.setSummary(String.valueOf(newValue));
return true;
}
});
}
private <E extends Enum<E> & PreferenceEntry> void prepareListPreference(@Nonnull final StringPreference<E> p, @Nonnull Class<E> type) {
final ListPreference preference = (ListPreference) preferenceManager.findPreference(p.getKey());
if (preference == null) {
return;
}
final E[] entries = type.getEnumConstants();
final FragmentActivity activity = getActivity();
populate(preference, entries);
preference.setSummary(p.getPreference(preferences).getName(activity));
preference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference p, Object newValue) {
for (E entry : entries) {
if (entry.getId().equals(newValue)) {
preference.setSummary(entry.getName(activity));
break;
}
}
return true;
}
});
}
private void prepareLayoutPreference(int preference) {
@ -219,12 +299,11 @@ public class PreferencesFragment extends org.solovyev.android.material.preferenc
@Override
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
if (Engine.Preferences.Output.round.getKey().equals(key)) {
final Preference preference = findPreference(Engine.Preferences.Output.precision.getKey());
if (preference != null) {
preference.setEnabled(preferences.getBoolean(key, Engine.Preferences.Output.round.getDefaultValue()));
}
}
}
@Subscribe
public void onEngineChanged(Engine.ChangedEvent e) {
prepareNumberFormatExamplesPreference();
}
@Override
@ -253,6 +332,7 @@ public class PreferencesFragment extends org.solovyev.android.material.preferenc
@Override
public void onDestroy() {
bus.unregister(this);
preferences.unregisterOnSharedPreferenceChangeListener(this);
super.onDestroy();
}

View File

@ -0,0 +1,12 @@
package org.solovyev.android.calculator.text;
import java.util.Comparator;
public class NaturalComparator implements Comparator<Object> {
public static final NaturalComparator INSTANCE = new NaturalComparator();
@Override
public int compare(Object lhs, Object rhs) {
return lhs.toString().compareTo(rhs.toString());
}
}

View File

@ -23,12 +23,15 @@
package org.solovyev.android.calculator.wizard;
import android.content.SharedPreferences;
import jscl.AngleUnit;
import org.solovyev.android.calculator.Engine;
import org.solovyev.android.calculator.Preferences;
import org.solovyev.common.NumberFormatter;
import javax.annotation.Nonnull;
import jscl.AngleUnit;
enum CalculatorMode {
simple() {
@ -38,8 +41,8 @@ enum CalculatorMode {
Preferences.Gui.mode.putPreference(editor, Preferences.Gui.Mode.simple);
Engine.Preferences.angleUnit.putPreference(editor, AngleUnit.deg);
Engine.Preferences.Output.scientificNotation.putPreference(editor, false);
Engine.Preferences.Output.round.putPreference(editor, true);
Engine.Preferences.Output.notation.putPreference(editor, Engine.Notation.dec);
Engine.Preferences.Output.precision.putPreference(editor, 5);
editor.apply();
}
@ -52,8 +55,8 @@ enum CalculatorMode {
Preferences.Gui.mode.putPreference(editor, Preferences.Gui.Mode.engineer);
Engine.Preferences.angleUnit.putPreference(editor, AngleUnit.rad);
Engine.Preferences.Output.scientificNotation.putPreference(editor, true);
Engine.Preferences.Output.round.putPreference(editor, false);
Engine.Preferences.Output.notation.putPreference(editor, Engine.Notation.eng);
Engine.Preferences.Output.precision.putPreference(editor, NumberFormatter.MAX_PRECISION);
editor.apply();
}

View File

@ -0,0 +1,27 @@
package org.solovyev.android.prefs;
import android.content.SharedPreferences;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class CharacterPreference extends AbstractPreference<Character> {
private CharacterPreference(@Nonnull String key, @Nullable Character defaultValue) {
super(key, defaultValue);
}
public static CharacterPreference of(@Nonnull String key, @Nullable Character defaultValue) {
return new CharacterPreference(key, defaultValue);
}
@Nullable
@Override
protected Character getPersistedValue(@Nonnull SharedPreferences preferences) {
return (char) preferences.getInt(getKey(), 0);
}
@Override
protected void putPersistedValue(@Nonnull SharedPreferences.Editor editor, @Nonnull Character value) {
editor.putInt(getKey(), value);
}
}

View File

@ -56,6 +56,11 @@ public final class StringPreference<T> extends AbstractPreference<T> {
return new StringPreference<T>(key, mapper.parseValue(defaultValue), mapper);
}
@Nonnull
public static <T> StringPreference<T> ofTypedValue(@Nonnull String key, @Nullable T defaultValue, @Nonnull Mapper<T> mapper) {
return new StringPreference<T>(key, defaultValue, mapper);
}
@Nonnull
public static <T extends Enum> StringPreference<T> ofEnum(@Nonnull String key, @Nullable T defaultValue, @Nonnull Class<T> enumType) {
return new StringPreference<T>(key, defaultValue, EnumMapper.of(enumType));

View File

@ -0,0 +1,228 @@
package org.solovyev.android.views;
import android.animation.ObjectAnimator;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Build;
import android.util.AttributeSet;
import android.view.animation.DecelerateInterpolator;
import android.widget.SeekBar;
import org.solovyev.android.Check;
import org.solovyev.android.calculator.R;
/**
* SeekBar for discrete values with a label displayed underneath the active tick
*/
public class DiscreteSeekBar extends SeekBar {
// Duration of how quick the SeekBar thumb should snap to its destination value
private static final int THUMB_SNAP_DURATION_TIME = 100;
private final Paint mPaint = new Paint();
private ObjectAnimator mObjectAnimator;
private OnChangeListener mOnChangeListener;
private int mCurrentTick = 0;
private CharSequence[] mTickLabels;
private ColorStateList mLabelColor;
public DiscreteSeekBar(Context context) {
super(context);
init(context, null, 0);
}
public DiscreteSeekBar(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs, 0);
}
public DiscreteSeekBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs, defStyle);
}
private void init(Context context, AttributeSet attrs, int defStyle) {
final TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.DiscreteSeekBar, defStyle, 0);
mTickLabels = a.getTextArray(R.styleable.DiscreteSeekBar_values);
final int labelsSize = a.getDimensionPixelSize(R.styleable.DiscreteSeekBar_labelsSize, 0);
final ColorStateList labelsColor = a.getColorStateList(R.styleable.DiscreteSeekBar_labelsColor);
a.recycle();
Check.isNotNull(mTickLabels);
Check.isTrue(mTickLabels.length > 0);
Check.isTrue(labelsSize > 0);
mPaint.setStyle(Paint.Style.FILL);
mPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
mPaint.setTextSize(labelsSize);
if (labelsColor != null) {
setLabelColor(labelsColor);
} else {
mPaint.setColor(Color.BLACK);
}
// Extend the bottom padding to include tick label height (including descent in order to not
// clip glyphs that extends below the baseline).
Paint.FontMetricsInt fi = mPaint.getFontMetricsInt();
setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(),
getPaddingBottom() + labelsSize + fi.descent);
super.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
cancelAnimator();
}
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
private void cancelAnimator() {
if (mObjectAnimator != null) {
mObjectAnimator.cancel();
}
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
mCurrentTick = getClosestTick(seekBar.getProgress());
final int endProgress = getProgressForTick(mCurrentTick);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
startAnimator(seekBar, endProgress);
} else {
seekBar.setProgress(endProgress);
}
if (mOnChangeListener != null) {
mOnChangeListener.onValueChanged(mCurrentTick);
}
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
private void startAnimator(SeekBar seekBar, int endProgress) {
mObjectAnimator = ObjectAnimator.ofInt(
seekBar, "progress", seekBar.getProgress(), endProgress);
mObjectAnimator.setInterpolator(new DecelerateInterpolator());
mObjectAnimator.setDuration(THUMB_SNAP_DURATION_TIME);
mObjectAnimator.start();
}
});
}
private int getClosestTick(int progress) {
float normalizedValue = (float) progress / getMax();
return Math.round(normalizedValue * getMaxTick());
}
private int getProgressForTick(int tick) {
return (getMax() / getMaxTick()) * tick;
}
@Override
public void setOnSeekBarChangeListener(OnSeekBarChangeListener seekBarChangeListener) {
// It doesn't make sense to expose the interface for listening to intermediate changes.
Check.isTrue(false);
}
/**
* Get the largest tick value the SeekBar can represent
*
* @return maximum tick value
*/
public int getMaxTick() {
return mTickLabels.length - 1;
}
/**
* Set listener for observing value changes
*
* @param onChangeListener listener that should receive updates
*/
public void setOnChangeListener(OnChangeListener onChangeListener) {
mOnChangeListener = onChangeListener;
}
/**
* Set tick value
*
* @param tick tick value in range [0, maxTick]
*/
public void setCurrentTick(int tick) {
Check.isTrue(tick >= 0);
Check.isTrue(tick <= getMaxTick());
mCurrentTick = tick;
setProgress(getProgressForTick(mCurrentTick));
}
public int getCurrentTick() {
return mCurrentTick;
}
public void setLabelColor(int color) {
mLabelColor = ColorStateList.valueOf(color);
updateLabelColor();
}
public void setLabelColor(ColorStateList colors) {
mLabelColor = colors;
updateLabelColor();
}
private void updateLabelColor() {
int color = mLabelColor.getColorForState(getDrawableState(), Color.BLACK);
if (color != mPaint.getColor()) {
mPaint.setColor(color);
invalidate();
}
}
@Override
protected void drawableStateChanged() {
super.drawableStateChanged();
updateLabelColor();
}
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
final float sliderWidth = getWidth() - getPaddingRight() - getPaddingLeft();
final float sliderStepSize = sliderWidth / getMaxTick();
int closestTick = getClosestTick(getProgress());
String text = mTickLabels[closestTick].toString();
final float startOffset = getPaddingLeft();
final float tickLabelWidth = mPaint.measureText(text);
final float tickPos = sliderStepSize * closestTick;
final float labelOffset;
// First step description text should be anchored with its left edge just
// below the slider start tick. The last step description should be anchored
// to the right just under the end tick. Tick labels in between are centered below
// each tick.
if (closestTick == 0) {
labelOffset = startOffset;
} else if (closestTick == getMaxTick()) {
labelOffset = startOffset + sliderWidth - tickLabelWidth;
} else {
labelOffset = startOffset + tickPos - tickLabelWidth / 2;
}
// Text position is drawn from bottom left, with bottom at the font baseline. We need to
// offset by the descent to cover e.g 'g' that extends below the baseline.
final Paint.FontMetricsInt m = mPaint.getFontMetricsInt();
final int lowestPosForFullGlyphCoverage = getHeight() - m.descent;
canvas.drawText(text, labelOffset, lowestPosForFullGlyphCoverage, mPaint);
}
/**
* Listener for observing tick changes
*/
public interface OnChangeListener {
void onValueChanged(int selectedTick);
}
}

View File

@ -0,0 +1,27 @@
package org.solovyev.common.text;
import android.text.TextUtils;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class CharacterMapper implements Mapper<Character> {
@Nonnull
public static final CharacterMapper INSTANCE = new CharacterMapper();
private CharacterMapper() {
}
@Nonnull
@Override
public String formatValue(@Nullable Character value) throws IllegalArgumentException {
return value == null || value == 0 ? "" : String.valueOf(value);
}
@Nonnull
@Override
public Character parseValue(@Nullable String value) throws IllegalArgumentException {
return TextUtils.isEmpty(value) ? 0 : value.charAt(0);
}
}

View File

@ -59,11 +59,9 @@
<TextView
android:id="@+id/fn_linewidth_label"
style="@style/TextAppearance.AppCompat.Caption"
style="@style/CppLabel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="4dp"
android:paddingLeft="4dp"
android:text="@string/cpp_plot_function_line_width"
tools:ignore="RtlSymmetry"/>
@ -74,11 +72,9 @@
<TextView
android:id="@+id/fn_color_label"
style="@style/TextAppearance.AppCompat.Caption"
style="@style/CppLabel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="4dp"
android:paddingLeft="4dp"
android:text="@string/cpp_plot_function_line_color"
tools:ignore="RtlSymmetry"/>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<org.solovyev.android.views.DiscreteSeekBar android:id="@+id/precision_seekbar"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/cpp_dialog_spacing"
app:labelsColor="?android:attr/textColorSecondary"
app:labelsSize="12sp"
app:values="@array/cpp_prefs_precisions" />

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="DiscreteSeekBar">
<attr name="values" format="reference"/>
<attr name="labelsSize" format="dimension|reference"/>
<attr name="labelsColor" format="color|reference"/>
</declare-styleable>
</resources>

View File

@ -319,6 +319,11 @@
<item name="android:visibility">gone</item>
</style>
<style name="CppLabel" parent="TextAppearance.AppCompat.Caption">
<item name="android:paddingLeft">4dp</item>
<item name="android:paddingRight">4dp</item>
</style>
<dimen name="list_item_text_size">16sp</dimen>
<dimen name="list_item_text_size_small">14sp</dimen>
</resources>

View File

@ -18,7 +18,6 @@
<string name="cpp_exponent" translatable="false">E</string>
<string name="cpp_theme_black" translatable="false">%1$s (AMOLED)</string>
<string-array name="cpp_prefs_precisions" translatable="false">
<item>0</item>
<item>1</item>
<item>2</item>
<item>3</item>
@ -34,6 +33,5 @@
<item>13</item>
<item>14</item>
<item>15</item>
<item>16</item>
</string-array>
</resources>

View File

@ -124,4 +124,7 @@
<string name="cpp_angles">Angles</string>
<string name="cpp_radix">Radix</string>
<string name="cpp_numeral_system">Numeral system</string>
<string name="cpp_number_format_dec">Decimal</string>
<string name="cpp_number_format_eng">Engineering</string>
<string name="cpp_number_format_sci">Scientific</string>
</resources>

View File

@ -35,6 +35,10 @@
a:key="restart_wizard"
a:title="@string/cpp_restart_wizard" />
<Preference
a:key="screen-number-format"
a:title="Number format" />
<Preference
a:key="screen-calculations"
a:title="@string/c_prefs_calculations_category" />

View File

@ -24,32 +24,6 @@
<PreferenceScreen xmlns:a="http://schemas.android.com/apk/res/android">
<android.preference.CheckBoxPreference
a:defaultValue="true"
a:key="engine.output.round"
a:summary="@string/c_calc_round_result_summary"
a:title="@string/c_calc_round_result_title" />
<ListPreference
a:entries="@array/cpp_prefs_precisions"
a:entryValues="@array/cpp_prefs_precisions"
a:key="engine.output.precision"
a:summary="@string/c_calc_result_precision_summary"
a:title="@string/p_calc_result_precision_title" />
<android.preference.CheckBoxPreference
a:defaultValue="false"
a:key="engine.output.scientificNotation"
a:summary="@string/c_calc_science_notation_summary"
a:title="@string/c_calc_science_notation_title" />
<ListPreference
a:entries="@array/p_grouping_separator_names"
a:entryValues="@array/p_grouping_separator_values"
a:key="engine.groupingSeparator"
a:summary="@string/c_calc_grouping_separator_summary"
a:title="@string/c_calc_grouping_separator" />
<ListPreference
a:entries="@array/p_angle_units_names"
a:entryValues="@array/p_angle_units"

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright 2016 serso aka se.solovyev
~
~ Licensed under the Apache License, Version 2.0 (the "License");
~ you may not use this file except in compliance with the License.
~ You may obtain a copy of the License at
~
~ http://www.apache.org/licenses/LICENSE-2.0
~
~ Unless required by applicable law or agreed to in writing, software
~ distributed under the License is distributed on an "AS IS" BASIS,
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
~ See the License for the specific language governing permissions and
~ limitations under the License.
~
~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~ Contact details
~
~ Email: se.solovyev@gmail.com
~ Site: http://se.solovyev.org
-->
<PreferenceScreen xmlns:a="http://schemas.android.com/apk/res/android"
xmlns:m="http://schemas.android.com/apk/res-auto">
<org.solovyev.android.material.preferences.ListPreference
a:key="engine.output.notation"
a:title="Notation"
m:materialColor="@color/material_text_selector" />
<org.solovyev.android.calculator.preferences.PrecisionPreference
a:key="engine.output.precision"
a:title="@string/p_calc_result_precision_title" />
<org.solovyev.android.material.preferences.ListPreference
a:entries="@array/p_grouping_separator_names"
a:entryValues="@array/p_grouping_separator_values"
a:key="engine.output.separator"
a:title="@string/c_calc_grouping_separator"
m:materialColor="@color/material_text_selector" />
<org.solovyev.android.calculator.preferences.NumberFormatExamplesPreference
a:key="numberFormat.examples"
a:selectable="false"
a:title="Examples" />
</PreferenceScreen>

View File

@ -23,7 +23,6 @@ public class Tests {
@NonNull
public static Engine makeEngine() {
final JsclMathEngine mathEngine = JsclMathEngine.getInstance();
mathEngine.setUseGroupingSeparator(true);
mathEngine.setGroupingSeparator(' ');
final Engine engine = new Engine(mathEngine);
engine.postfixFunctionsRegistry = new PostfixFunctionsRegistry(mathEngine);

View File

@ -25,7 +25,9 @@ package org.solovyev.android.calculator.history;
import android.content.SharedPreferences;
import android.os.Handler;
import android.os.Looper;
import com.squareup.otto.Bus;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@ -35,19 +37,33 @@ import org.robolectric.RobolectricGradleTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.solovyev.android.CalculatorTestRunner;
import org.solovyev.android.calculator.*;
import org.solovyev.android.calculator.BuildConfig;
import org.solovyev.android.calculator.Display;
import org.solovyev.android.calculator.DisplayState;
import org.solovyev.android.calculator.Editor;
import org.solovyev.android.calculator.EditorState;
import org.solovyev.android.calculator.Engine;
import org.solovyev.android.calculator.ErrorReporter;
import org.solovyev.android.calculator.json.Json;
import org.solovyev.android.io.FileSystem;
import javax.annotation.Nonnull;
import java.io.File;
import java.util.List;
import static org.junit.Assert.*;
import javax.annotation.Nonnull;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.*;
import static org.solovyev.android.calculator.Engine.Preferences.groupingSeparator;
import static org.mockito.Mockito.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.solovyev.android.calculator.Tests.sameThreadExecutor;
import static org.solovyev.android.calculator.jscl.JsclOperation.numeric;
@ -106,8 +122,8 @@ public class HistoryTest {
@Test
public void testRecentHistoryShouldTakeIntoAccountGroupingSeparator() throws Exception {
when(history.preferences.contains(eq(groupingSeparator.getKey()))).thenReturn(true);
when(history.preferences.getString(eq(groupingSeparator.getKey()), anyString())).thenReturn(" ");
when(history.preferences.contains(eq(Engine.Preferences.Output.separator.getKey()))).thenReturn(true);
when(history.preferences.getString(eq(Engine.Preferences.Output.separator.getKey()), anyString())).thenReturn(" ");
addState("1");
addState("12");
addState("123");
@ -119,7 +135,7 @@ public class HistoryTest {
assertEquals("12 345", states.get(0).editor.getTextString());
history.clearRecent();
when(history.preferences.getString(eq(groupingSeparator.getKey()), anyString())).thenReturn("'");
when(history.preferences.getString(eq(Engine.Preferences.Output.separator.getKey()), anyString())).thenReturn("'");
addState("1");
addState("12");
addState("123");

View File

@ -2,6 +2,13 @@ package jscl;
import jscl.math.Expression;
import jscl.math.Generic;
import jscl.math.NotIntegerException;
import jscl.math.function.Constants;
import jscl.math.function.ConstantsRegistry;
import jscl.math.function.Function;
import jscl.math.function.FunctionsRegistry;
import jscl.math.function.IConstant;
import jscl.math.function.PostfixFunctionsRegistry;
import jscl.math.function.*;
import jscl.math.operator.Operator;
import jscl.math.operator.Percent;
@ -13,6 +20,9 @@ import org.solovyev.common.math.MathRegistry;
import org.solovyev.common.msg.MessageRegistry;
import org.solovyev.common.msg.Messages;
import static midpcalc.Real.NumberFormat.FSE_ENG;
import static midpcalc.Real.NumberFormat.FSE_NONE;
import static midpcalc.Real.NumberFormat.FSE_SCI;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.math.BigInteger;
@ -36,11 +46,9 @@ public class JsclMathEngine implements MathEngine {
return new NumberFormatter();
}
};
private char groupingSeparator = GROUPING_SEPARATOR_DEFAULT;
private boolean roundResult = false;
private int numberFormat = FSE_NONE;
private int precision = 5;
private boolean useGroupingSeparator = false;
private char groupingSeparator = NumberFormatter.NO_GROUPING;
private int notation = FSE_NONE;
private int precision = NumberFormatter.MAX_PRECISION;
@Nonnull
private AngleUnit angleUnits = DEFAULT_ANGLE_UNITS;
@Nonnull
@ -161,9 +169,9 @@ public class JsclMathEngine implements MathEngine {
private NumberFormatter prepareNumberFormatter(@Nonnull NumeralBase nb) {
final NumberFormatter nf = numberFormatter.get();
nf.setGroupingSeparator(useGroupingSeparator ? getGroupingSeparatorChar(nb) : NumberFormatter.NO_GROUPING);
nf.setPrecision(roundResult ? precision : NumberFormatter.NO_ROUNDING);
switch (numberFormat) {
nf.setGroupingSeparator(hasGroupingSeparator() ? getGroupingSeparator(nb) : NumberFormatter.NO_GROUPING);
nf.setPrecision(precision);
switch (notation) {
case FSE_ENG:
nf.useEngineeringFormat(NumberFormatter.DEFAULT_MAGNITUDE);
break;
@ -247,7 +255,7 @@ public class JsclMathEngine implements MathEngine {
@Nonnull
@Override
public String format(@Nonnull String value, @Nonnull NumeralBase nb) {
if (!useGroupingSeparator) {
if (!hasGroupingSeparator()) {
return value;
}
final int dot = value.indexOf('.');
@ -265,8 +273,8 @@ public class JsclMathEngine implements MathEngine {
@Nonnull
public String insertSeparators(@Nonnull String value, @Nonnull NumeralBase nb) {
final String separator = getGroupingSeparator(nb);
final StringBuilder result = new StringBuilder(value.length() + nb.getGroupingSize() * separator.length());
final char separator = getGroupingSeparator(nb);
final StringBuilder result = new StringBuilder(value.length() + nb.getGroupingSize());
for (int i = value.length() - 1; i >= 0; i--) {
result.append(value.charAt(i));
if (i != 0 && (value.length() - i) % nb.getGroupingSize() == 0) {
@ -276,43 +284,30 @@ public class JsclMathEngine implements MathEngine {
return result.reverse().toString();
}
@Nonnull
private String getGroupingSeparator(@Nonnull NumeralBase nb) {
return nb == NumeralBase.dec ? String.valueOf(groupingSeparator) : " ";
private boolean hasGroupingSeparator() {
return groupingSeparator != NumberFormatter.NO_GROUPING;
}
private char getGroupingSeparatorChar(@Nonnull NumeralBase nb) {
private char getGroupingSeparator(@Nonnull NumeralBase nb) {
return nb == NumeralBase.dec ? groupingSeparator : ' ';
}
public void setRoundResult(boolean roundResult) {
this.roundResult = roundResult;
}
public void setPrecision(int precision) {
this.precision = precision;
}
public void setUseGroupingSeparator(boolean useGroupingSeparator) {
this.useGroupingSeparator = useGroupingSeparator;
}
public void setScienceNotation(boolean scienceNotation) {
setNumberFormat(scienceNotation ? FSE_SCI : FSE_NONE);
}
public void setNumberFormat(int numberFormat) {
if (numberFormat != FSE_SCI && numberFormat != FSE_ENG && numberFormat != FSE_NONE) {
throw new IllegalArgumentException("Unsupported format: " + numberFormat);
public void setNotation(int notation) {
if (notation != FSE_SCI && notation != FSE_ENG && notation != FSE_NONE) {
throw new IllegalArgumentException("Unsupported notation: " + notation);
}
this.numberFormat = numberFormat;
this.notation = notation;
}
public char getGroupingSeparator() {
return this.groupingSeparator;
}
public void setGroupingSeparator(char groupingSeparator) {
this.groupingSeparator = groupingSeparator;
public void setGroupingSeparator(char separator) {
this.groupingSeparator = separator;
}
}

View File

@ -1,12 +1,14 @@
package jscl;
import org.solovyev.common.math.MathRegistry;
import java.math.BigInteger;
import javax.annotation.Nonnull;
import jscl.math.function.Function;
import jscl.math.function.IConstant;
import jscl.math.operator.Operator;
import org.solovyev.common.math.MathRegistry;
import javax.annotation.Nonnull;
import java.math.BigInteger;
public interface MathContext {
@ -36,13 +38,9 @@ public interface MathContext {
void setNumeralBase(@Nonnull NumeralBase numeralBase);
void setRoundResult(boolean roundResult);
void setPrecision(int precision);
void setUseGroupingSeparator(boolean useGroupingSeparator);
void setGroupingSeparator(char groupingSeparator);
void setGroupingSeparator(char separator);
@Nonnull
String format(double value);
@ -55,5 +53,5 @@ public interface MathContext {
@Nonnull
String format(@Nonnull String value, @Nonnull NumeralBase nb);
void setScienceNotation(boolean scienceNotation);
void setNotation(int notation);
}

View File

@ -11,10 +11,11 @@ import static midpcalc.Real.NumberFormat.*;
public class NumberFormatter {
public static final int NO_GROUPING = 0;
public static final char NO_GROUPING = 0;
public static final int NO_ROUNDING = -1;
public static final int DEFAULT_MAGNITUDE = 5;
public static final int MAX_PRECISION = 16;
public static final int MIN_PRECISION = 1;
public static final int MAX_PRECISION = 15;
private final Real.NumberFormat numberFormat = new Real.NumberFormat();
private final Real real = new Real();
@ -39,7 +40,7 @@ public class NumberFormatter {
}
public void setPrecision(int precision) {
this.precision = precision;
this.precision = Math.max(MIN_PRECISION, Math.min(precision, MAX_PRECISION));
}
public void setGroupingSeparator(char groupingSeparator) {
@ -94,7 +95,6 @@ public class NumberFormatter {
final BigInteger absValue = value.abs();
final boolean simpleFormat = useSimpleFormat(radix, absValue);
final int effectivePrecision = precision == NO_ROUNDING ? MAX_PRECISION : precision;
if (simpleFormat) {
numberFormat.fse = FSE_FIX;
} else if (format == FSE_NONE) {
@ -105,7 +105,7 @@ public class NumberFormatter {
numberFormat.fse = format;
}
numberFormat.thousand = groupingSeparator;
numberFormat.precision = effectivePrecision;
numberFormat.precision = Math.max(0, Math.min(precision, MAX_PRECISION));
numberFormat.base = radix;
numberFormat.maxwidth = simpleFormat ? 100 : 30;

View File

@ -1,8 +1,12 @@
package jscl;
import midpcalc.Real;
import org.junit.Before;
import org.junit.Test;
import org.solovyev.common.NumberFormatter;
import midpcalc.Real;
import midpcalc.Real;
import static org.junit.Assert.assertEquals;
@ -23,7 +27,7 @@ public class JsclMathEngineTest {
@Test
public void testFormat() throws Exception {
try {
me.setUseGroupingSeparator(true);
me.setGroupingSeparator(' ');
assertEquals("1", me.format(1d, NumeralBase.bin));
assertEquals("10", me.format(2d, NumeralBase.bin));
assertEquals("11", me.format(3d, NumeralBase.bin));
@ -38,7 +42,6 @@ public class JsclMathEngineTest {
assertEquals("1 0100", me.format(20d, NumeralBase.bin));
assertEquals("1 1111", me.format(31d, NumeralBase.bin));
me.setRoundResult(true);
me.setPrecision(10);
assertEquals("111 1111 0011 0110", me.format(32566d, NumeralBase.bin));
@ -56,11 +59,11 @@ public class JsclMathEngineTest {
assertEquals("D 25 0F 77 0A.6F7319", me.format(56456345354.43534534523459999d, NumeralBase.hex));
assertEquals("3 E7.4CCCCCCCCD", me.format(999.3d, NumeralBase.hex));
me.setRoundResult(false);
me.setPrecision(NumberFormatter.MAX_PRECISION);
assertEquals("6.CCDA6A054226DB6E-19", me.format(0.00000000000000000000009d, NumeralBase.hex));
assertEquals("A.E15D766ED03E2BEE-20", me.format(0.000000000000000000000009d, NumeralBase.hex));
} finally {
me.setUseGroupingSeparator(false);
me.setGroupingSeparator(NumberFormatter.NO_GROUPING);
}
assertEquals("1", me.format(1d, NumeralBase.bin));
@ -87,13 +90,12 @@ public class JsclMathEngineTest {
@Test
public void testPiComputation() throws Exception {
assertEquals("-1+0.0000000000000001*i", me.evaluate("exp(√(-1)*Π)"));
assertEquals("-1+0*i", me.evaluate("exp(√(-1)*Π)"));
}
@Test
public void testBinShouldAlwaysUseSpaceAsGroupingSeparator() throws Exception {
me.setGroupingSeparator('\'');
me.setUseGroupingSeparator(true);
assertEquals("100 0000 0000", me.format(1024d, NumeralBase.bin));
}
@ -101,15 +103,13 @@ public class JsclMathEngineTest {
@Test
public void testHexShouldAlwaysUseSpaceAsGroupingSeparator() throws Exception {
me.setGroupingSeparator('\'');
me.setUseGroupingSeparator(true);
assertEquals("4 00", me.format(1024d, NumeralBase.hex));
}
@Test
public void testEngineeringNotationWithRounding() throws Exception {
me.setNumberFormat(Real.NumberFormat.FSE_ENG);
me.setRoundResult(true);
me.setNotation(Real.NumberFormat.FSE_ENG);
me.setPrecision(5);
assertEquals("10E6", me.format(10000000d));
@ -164,8 +164,8 @@ public class JsclMathEngineTest {
@Test
public void testEngineeringNotationWithoutRounding() throws Exception {
me.setNumberFormat(Real.NumberFormat.FSE_ENG);
me.setRoundResult(false);
me.setNotation(Real.NumberFormat.FSE_ENG);
me.setPrecision(NumberFormatter.MAX_PRECISION);
assertEquals("10E6", me.format(10000000d));
assertEquals("99E6", me.format(99000000d));

View File

@ -77,7 +77,7 @@ public class NumeralBaseTest {
assertEquals("11111110", me.evaluate("111001+11000101"));
assertEquals("1101100100101111", me.evaluate("11011001001011110/10"));
assertEquals("1001000011001010", me.evaluate("11011001001011110/11"));
assertEquals("0.1010101010101011", me.evaluate("10/11"));
assertEquals("0.101010101010101", me.evaluate("10/11"));
me.setNumeralBase(NumeralBase.hex);
assertEquals("637B", me.evaluate("56CE+CAD"));

View File

@ -8,7 +8,9 @@ import jscl.math.function.Constant;
import jscl.math.function.ExtendedConstant;
import jscl.math.function.IConstant;
import jscl.text.ParseException;
import midpcalc.Real;
import org.junit.Test;
import org.solovyev.common.NumberFormatter;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@ -139,7 +141,7 @@ public class ExpressionTest {
@Test
public void testExpressions() throws Exception {
assertEquals("3", Expression.valueOf("3").numeric().toString());
assertEquals("0.6931471805599453", Expression.valueOf("ln(2)").numeric().toString());
assertEquals("0.693147180559945", Expression.valueOf("ln(2)").numeric().toString());
assertEquals("1", Expression.valueOf("lg(10)").numeric().toString());
assertEquals("0", Expression.valueOf("eq(0, 1)").numeric().toString());
assertEquals("1", Expression.valueOf("eq(1, 1)").numeric().toString());
@ -162,7 +164,7 @@ public class ExpressionTest {
final AngleUnit angleUnits = me.getAngleUnits();
try {
me.setAngleUnits(AngleUnit.rad);
assertEquals("-0.9055783620066238", Expression.valueOf("sin(4!)").numeric().toString());
assertEquals("-0.905578362006624", Expression.valueOf("sin(4!)").numeric().toString());
} finally {
me.setAngleUnits(angleUnits);
}
@ -174,7 +176,7 @@ public class ExpressionTest {
} catch (NotIntegerException e) {
}
assertEquals("0.5235987755982988", Expression.valueOf("3.141592653589793/3!").numeric().toString());
assertEquals("0.523598775598299", Expression.valueOf("3.141592653589793/3!").numeric().toString());
try {
assertEquals("3.141592653589793/3.141592653589793!", Expression.valueOf("3.141592653589793/3.141592653589793!").numeric().toString());
fail();
@ -206,14 +208,14 @@ public class ExpressionTest {
final AngleUnit defaultAngleUnits = me.getAngleUnits();
try {
me.setAngleUnits(AngleUnit.rad);
assertEquals("0.0174532925199433", Expression.valueOf("").numeric().toString());
assertEquals("0.0349065850398866", Expression.valueOf("").numeric().toString());
assertEquals("0.0523598775598299", Expression.valueOf("").numeric().toString());
assertEquals("0.2617993877991495", Expression.valueOf("3°*5").numeric().toString());
assertEquals("0.0027415567780804", Expression.valueOf("3°^2").numeric().toString());
assertEquals("0.0109662271123215", Expression.valueOf("3!°^2").numeric().toString());
assertEquals("0.0009138522593601", Expression.valueOf("3°°").numeric().toString());
assertEquals("0.0872664625997165", Expression.valueOf("").numeric().toString());
assertEquals("0.017453292519943", Expression.valueOf("").numeric().toString());
assertEquals("0.034906585039887", Expression.valueOf("").numeric().toString());
assertEquals("0.05235987755983", Expression.valueOf("").numeric().toString());
assertEquals("0.261799387799149", Expression.valueOf("3°*5").numeric().toString());
assertEquals("0.00274155677808", Expression.valueOf("3°^2").numeric().toString());
assertEquals("0.010966227112322", Expression.valueOf("3!°^2").numeric().toString());
assertEquals("0.00091385225936", Expression.valueOf("3°°").numeric().toString());
assertEquals("0.087266462599716", Expression.valueOf("").numeric().toString());
assertEquals("2.05235987755983", Expression.valueOf("2+3°").numeric().toString());
} finally {
me.setAngleUnits(defaultAngleUnits);
@ -371,7 +373,7 @@ public class ExpressionTest {
try {
me.setAngleUnits(AngleUnit.rad);
assertEquals("0.6931471805599453+Π*i", Expression.valueOf("ln(-2)").numeric().toString());
assertEquals("0.693147180559945+Π*i", Expression.valueOf("ln(-2)").numeric().toString());
} finally {
me.setAngleUnits(AngleUnit.deg);
}
@ -381,7 +383,7 @@ public class ExpressionTest {
assertEquals("sin(n!)", Expression.valueOf("sin(n!)").expand().toString());
assertEquals("sin(n°)", Expression.valueOf("sin(n°)").expand().toString());
assertEquals("sin(30°)", Expression.valueOf("sin(30°)").expand().toString());
assertEquals("0.4999999999999999", Expression.valueOf("sin(30°)").expand().numeric().toString());
assertEquals("0.5", Expression.valueOf("sin(30°)").expand().numeric().toString());
assertEquals("sin(2!)", Expression.valueOf("sin(2!)").expand().toString());
assertEquals("12", Expression.valueOf("3*(3+1)").expand().toString());
@ -402,7 +404,7 @@ public class ExpressionTest {
} catch (ParseException e) {
}
assertEquals("0.4999999999999999", Expression.valueOf("sin(30°)").numeric().toString());
assertEquals("0.5", Expression.valueOf("sin(30°)").numeric().toString());
assertEquals("π", Expression.valueOf("√(π)^2").simplify().toString());
assertEquals("π", Expression.valueOf("√(π^2)").simplify().toString());
assertEquals("π^2", Expression.valueOf("√(π^2*π^2)").simplify().toString());
@ -414,7 +416,7 @@ public class ExpressionTest {
// in deg mode π=180 and factorial of 180 is calculating
assertEquals("0", Expression.valueOf("Π/Π!").numeric().toString());
assertEquals("0.0000000000000001*i", Expression.valueOf("exp((Π*i))+1").numeric().toString());
assertEquals("0*i", Expression.valueOf("exp((Π*i))+1").numeric().toString());
assertEquals("20*x^3", Expression.valueOf("∂(5*x^4, x)").expand().simplify().toString());
assertEquals("25*x", Expression.valueOf("5*x*5").expand().simplify().toString());
assertEquals("20*x", Expression.valueOf("5*x*4").expand().simplify().toString());
@ -436,10 +438,10 @@ public class ExpressionTest {
try {
me.setNumeralBase(NumeralBase.hex);
assertEquals("0.EEEEEEEEEEEEEC88", me.evaluate("0x:E/0x:F"));
assertEquals("0.EEEEEEEEEEEEEC9", me.evaluate("0x:E/0x:F"));
assertEquals("E/F", me.simplify("0x:E/0x:F"));
assertEquals("0.EEEEEEEEEEEEEC88", me.evaluate("E/F"));
assertEquals("0.EEEEEEEEEEEEEC9", me.evaluate("E/F"));
assertEquals("E/F", me.simplify("E/F"));
} finally {
@ -466,9 +468,9 @@ public class ExpressionTest {
assertEquals("-1.471127674303735", me.evaluate("atan(-10)"));
assertEquals("-1.10714871779409", me.evaluate("atan(-2)"));
assertEquals("-0.7853981633974483", me.evaluate("atan(-1)"));
assertEquals("-0.785398163397448", me.evaluate("atan(-1)"));
assertEquals("0", me.evaluate("atan(0)"));
assertEquals("0.7853981633974483", me.evaluate("atan(1)"));
assertEquals("0.785398163397448", me.evaluate("atan(1)"));
assertEquals("1.10714871779409", me.evaluate("atan(2)"));
assertEquals("1.471127674303735", me.evaluate("atan(10)"));
@ -481,9 +483,9 @@ public class ExpressionTest {
assertEquals("1.570796326794897", me.evaluate("acot(0)"));
assertEquals("2.677945044588987", me.evaluate("acot(-2)"));
assertEquals("2.356194490192345", me.evaluate("acot(-1)"));
assertEquals("0.7853981633974483", me.evaluate("acot(1)"));
assertEquals("0.4636476090008062", me.evaluate("acot(2)"));
assertEquals("0.0996686524911619", me.evaluate("acot(10)"));
assertEquals("0.785398163397448", me.evaluate("acot(1)"));
assertEquals("0.463647609000806", me.evaluate("acot(2)"));
assertEquals("0.099668652491162", me.evaluate("acot(10)"));
assertEquals("Π", me.evaluate("π"));
assertEquals("Π", me.evaluate("3.14159265358979323846"));
@ -632,12 +634,12 @@ public class ExpressionTest {
try {
mathEngine.setAngleUnits(AngleUnit.rad);
testSinEqualsToSinh(mathEngine, 0d);
testSinEqualsToSinh(mathEngine, 1d, "0.8414709848078965");
testSinEqualsToSinh(mathEngine, 3d, "0.1411200080598672");
testSinEqualsToSinh(mathEngine, 1d, "0.841470984807897");
testSinEqualsToSinh(mathEngine, 3d, "0.141120008059867");
testSinEqualsToSinh(mathEngine, 6d);
testSinEqualsToSinh(mathEngine, -1d, "-0.8414709848078965");
testSinEqualsToSinh(mathEngine, -3.3d, "0.1577456941432482");
testSinEqualsToSinh(mathEngine, -232.2d, "0.2742948637368958");
testSinEqualsToSinh(mathEngine, -1d, "-0.841470984807897");
testSinEqualsToSinh(mathEngine, -3.3d, "0.157745694143248");
testSinEqualsToSinh(mathEngine, -232.2d, "0.274294863736896");
} finally {
mathEngine.setAngleUnits(defaultAngleUnits);
}
@ -645,12 +647,12 @@ public class ExpressionTest {
try {
mathEngine.setAngleUnits(AngleUnit.deg);
testSinEqualsToSinh(mathEngine, 0d);
testSinEqualsToSinh(mathEngine, 1d, "0.0174524064372835");
testSinEqualsToSinh(mathEngine, 3d, "0.0523359562429438");
testSinEqualsToSinh(mathEngine, 6d, "0.1045284632676535");
testSinEqualsToSinh(mathEngine, -1d, "-0.0174524064372835");
testSinEqualsToSinh(mathEngine, -3.3d, "-0.0575640269595673");
testSinEqualsToSinh(mathEngine, -232.2d, "0.7901550123756904");
testSinEqualsToSinh(mathEngine, 1d, "0.017452406437284");
testSinEqualsToSinh(mathEngine, 3d, "0.052335956242944");
testSinEqualsToSinh(mathEngine, 6d, "0.104528463267653");
testSinEqualsToSinh(mathEngine, -1d, "-0.017452406437284");
testSinEqualsToSinh(mathEngine, -3.3d, "-0.057564026959567");
testSinEqualsToSinh(mathEngine, -232.2d, "0.79015501237569");
assertEquals("Π/2", mathEngine.simplify("Π/2"));
} finally {
mathEngine.setAngleUnits(defaultAngleUnits);
@ -726,7 +728,7 @@ public class ExpressionTest {
final AngleUnit defaultAngleUnits = JsclMathEngine.getInstance().getAngleUnits();
try {
JsclMathEngine.getInstance().setAngleUnits(AngleUnit.rad);
assertEquals("-0.9092974268256817", Expression.valueOf("∂(cos(t),t,2)").numeric().toString());
assertEquals("-0.909297426825682", Expression.valueOf("∂(cos(t),t,2)").numeric().toString());
assertEquals("∂(cos(t), t, 2, 1)", Expression.valueOf("∂(cos(t),t,2)").simplify().toString());
assertEquals("-2.234741690198506", Expression.valueOf("∂(t*cos(t),t,2)").numeric().toString());
assertEquals("-4.469483380397012", Expression.valueOf("2*∂(t*cos(t),t,2)").numeric().toString());
@ -754,7 +756,7 @@ public class ExpressionTest {
assertEquals(Expression.valueOf("3").numeric().toString(), Expression.valueOf("Σ(n°,n,1,2)").expand().numeric().toString());
assertEquals("200", Expression.valueOf("Σ(n°/n°,n,1,200)").expand().numeric().toString());
assertEquals("-sin(1)-sin(2)", Expression.valueOf("Σ(∂(cos(t),t,n),n,1,2)").expand().toString());
assertEquals("-0.0523519031397845", Expression.valueOf("Σ(∂(cos(t),t,n),n,1,2)").expand().numeric().toString());
assertEquals("-0.052351903139784", Expression.valueOf("Σ(∂(cos(t),t,n),n,1,2)").expand().numeric().toString());
}
@Test
@ -786,54 +788,49 @@ public class ExpressionTest {
public void testFormat() throws Exception {
final JsclMathEngine me = JsclMathEngine.getInstance();
try {
me.setUseGroupingSeparator(true);
me.setGroupingSeparator(' ');
assertEquals("123 456.7891011", Expression.valueOf("123456.7891011").numeric().toString());
assertEquals("123 456.7891011", Expression.valueOf("123456.7891011").simplify().toString());
assertEquals("123 456.7891011123", Expression.valueOf("123456.7891011123123123123123").simplify().toString());
assertEquals("0.000001222", Expression.valueOf("1222/(10^9)").numeric().toString());
assertEquals("12 345", JsclInteger.valueOf(12345L).toString());
me.setScienceNotation(true);
me.setNotation(Real.NumberFormat.FSE_SCI);
assertEquals("0", Expression.valueOf("0.0").simplify().toString());
assertEquals("1", Expression.valueOf("1.0").simplify().toString());
assertEquals("100", Expression.valueOf("100.0").simplify().toString());
me.setScienceNotation(false);
me.setRoundResult(true);
me.setNotation(Real.NumberFormat.FSE_NONE);
me.setPrecision(5);
assertEquals("0", Expression.valueOf("1222/(10^9)").numeric().toString());
me.setScienceNotation(true);
me.setRoundResult(true);
me.setNotation(Real.NumberFormat.FSE_SCI);
me.setPrecision(5);
assertEquals("1.222E-6", Expression.valueOf("1222/(10^9)").numeric().toString());
me.setRoundResult(true);
me.setPrecision(10);
assertEquals("1.222E-6", Expression.valueOf("1222/(10^9)").numeric().toString());
me.setRoundResult(false);
me.setPrecision(NumberFormatter.MAX_PRECISION);
assertEquals("1.222E-6", Expression.valueOf("1222/(10^9)").numeric().toString());
me.setScienceNotation(false);
assertEquals("0.3333333333333333", Expression.valueOf("1/3").numeric().toString());
me.setNotation(Real.NumberFormat.FSE_NONE);
assertEquals("0.333333333333333", Expression.valueOf("1/3").numeric().toString());
me.setScienceNotation(true);
assertEquals("0.3333333333333333", Expression.valueOf("1/3").numeric().toString());
me.setNotation(Real.NumberFormat.FSE_SCI);
assertEquals("0.333333333333333", Expression.valueOf("1/3").numeric().toString());
me.setRoundResult(true);
me.setPrecision(10);
assertEquals("0.3333333333", Expression.valueOf("1/3").numeric().toString());
me.setScienceNotation(false);
me.setRoundResult(true);
me.setNotation(Real.NumberFormat.FSE_NONE);
me.setPrecision(10);
assertEquals("0.3333333333", Expression.valueOf("1/3").numeric().toString());
} finally {
me.setUseGroupingSeparator(false);
me.setScienceNotation(false);
me.setRoundResult(false);
me.setGroupingSeparator(NumberFormatter.NO_GROUPING);
me.setNotation(Real.NumberFormat.FSE_NONE);
me.setPrecision(NumberFormatter.MAX_PRECISION);
}
}
}

View File

@ -37,7 +37,7 @@ public class CustomFunctionTest {
assertEquals("", Expression.valueOf("log(1, 10)").numeric().toString());
assertEquals("3.321928094887363", Expression.valueOf("log(2, 10)").numeric().toString());
assertEquals("1.430676558073393", Expression.valueOf("log(5, 10)").numeric().toString());
assertEquals("0.9602525677891275", Expression.valueOf("log(11, 10)").numeric().toString());
assertEquals("0.960252567789128", Expression.valueOf("log(11, 10)").numeric().toString());
assertEquals("1/b*1/ln(a)", Expression.valueOf("∂(log(a, b), b)").expand().toString());
assertEquals("-1/a*(1/ln(a))^2*ln(b)", Expression.valueOf("∂(log(a, b), a)").expand().toString());
@ -133,10 +133,10 @@ public class CustomFunctionTest {
final CustomFunction.Builder jBuilder4 = new CustomFunction.Builder("testFunction5", asList("a", "b"), "testFunction2(a, b/2, 2, 1) - testFunction(a, b!, 4!, 1)");
mathEngine.getFunctionsRegistry().addOrUpdate(jBuilder4.create());
assertEquals("0.4996954135095478", Expression.valueOf("testFunction5(2, 3)").numeric().toString());
assertEquals("0.4996954135095478", Expression.valueOf("testFunction5(2, 3)").numeric().toString());
assertEquals("0.4996954135095478", Expression.valueOf("testFunction5(2*1, 3)").numeric().toString());
assertEquals("-0.0000000000000001", Expression.valueOf("testFunction5(2*1, 2^2-1+e^0)").numeric().toString());
assertEquals("0.499695413509548", Expression.valueOf("testFunction5(2, 3)").numeric().toString());
assertEquals("0.499695413509548", Expression.valueOf("testFunction5(2, 3)").numeric().toString());
assertEquals("0.499695413509548", Expression.valueOf("testFunction5(2*1, 3)").numeric().toString());
assertEquals("0", Expression.valueOf("testFunction5(2*1, 2^2-1+e^0)").numeric().toString());
try {
Expression.valueOf("testFunction5(2, 3.5)").numeric();

View File

@ -10,10 +10,10 @@ public class RadTest {
public void testRad() throws Exception {
final JsclMathEngine mathEngine = new JsclMathEngine();
assertEquals("0.0349065850398866", mathEngine.evaluate("rad(2)"));
assertEquals("0.0349065850398866", mathEngine.evaluate("rad(1+1)"));
assertEquals("-0.0349065850398866", mathEngine.evaluate("rad(-2)"));
assertEquals("-0.0349065850398866", mathEngine.evaluate("rad(-1-1)"));
assertEquals("0.034906585039887", mathEngine.evaluate("rad(2)"));
assertEquals("0.034906585039887", mathEngine.evaluate("rad(1+1)"));
assertEquals("-0.034906585039887", mathEngine.evaluate("rad(-2)"));
assertEquals("-0.034906585039887", mathEngine.evaluate("rad(-1-1)"));
assertEquals("π", mathEngine.evaluate("rad(180)"));
assertEquals(String.valueOf(-Math.PI), mathEngine.evaluate("rad(-180)"));

View File

@ -13,13 +13,13 @@ public class SqrtTest {
final JsclMathEngine me = JsclMathEngine.getInstance();
final AngleUnit defaultAngleUnits = me.getAngleUnits();
assertEquals("0.9999060498015505+0.0137073546047075*i", me.evaluate("√(√(-1))"));
assertEquals("0.9984971498638638+0.0548036651487895*i", me.evaluate("√(√(-1))^4"));
assertEquals("0.999906049801551+0.013707354604707*i", me.evaluate("√(√(-1))"));
assertEquals("0.998497149863864+0.05480366514879*i", me.evaluate("√(√(-1))^4"));
try {
me.setAngleUnits(AngleUnit.rad);
assertEquals("0.7071067811865476+0.7071067811865475*i", me.evaluate("√(√(-1))"));
assertEquals("-1+0.0000000000000003*i", me.evaluate("√(√(-1))^4"));
assertEquals("0.707106781186548+0.707106781186548*i", me.evaluate("√(√(-1))"));
assertEquals("-1+0*i", me.evaluate("√(√(-1))^4"));
} finally {
me.setAngleUnits(defaultAngleUnits);
}

View File

@ -20,9 +20,9 @@ public class CosTest {
me.getConstantsRegistry().addOrUpdate(t.create());
Assert.assertEquals("-sin(t)", me.simplify("∂(cos(t),t,t,1)"));
Assert.assertEquals("∂(cos(t), t, t, 1°)", me.simplify("∂(cos(t),t,t,1°)"));
Assert.assertEquals("-0.1736481776669303", me.evaluate("∂(cos(t),t,t,1)"));
Assert.assertEquals("-0.17364817766693", me.evaluate("∂(cos(t),t,t,1)"));
Assert.assertEquals("∂(cos(t), t, t, 1°)", me.evaluate("∂(cos(t),t,t,1°)"));
Assert.assertEquals("-0.1736481776669303", me.evaluate("∂(cos(t),t,t,2-1)"));
Assert.assertEquals("-0.1736481776669303", me.evaluate("∂(cos(t),t,t,2^5-31)"));
Assert.assertEquals("-0.17364817766693", me.evaluate("∂(cos(t),t,t,2-1)"));
Assert.assertEquals("-0.17364817766693", me.evaluate("∂(cos(t),t,t,2^5-31)"));
}
}

View File

@ -26,9 +26,9 @@ public class SinTest {
try {
me.setAngleUnits(AngleUnit.rad);
Assert.assertEquals("0.5403023058681398", me.evaluate("cos(1)"));
Assert.assertEquals("0.3623577544766736", me.evaluate("cos(1.2)"));
Assert.assertEquals("0.1779445513914661", me.evaluate("∫ab(sin(x), x, 1, 1.2)"));
Assert.assertEquals("0.54030230586814", me.evaluate("cos(1)"));
Assert.assertEquals("0.362357754476674", me.evaluate("cos(1.2)"));
Assert.assertEquals("0.177944551391466", me.evaluate("∫ab(sin(x), x, 1, 1.2)"));
} finally {
me.setAngleUnits(AngleUnit.deg);
}
@ -38,7 +38,7 @@ public class SinTest {
try {
me.setAngleUnits(AngleUnit.rad);
Assert.assertEquals("0.1339745962155613", me.evaluate("∫ab(sin(x), x, 0, 30°)"));
Assert.assertEquals("0.133974596215561", me.evaluate("∫ab(sin(x), x, 0, 30°)"));
} finally {
me.setAngleUnits(AngleUnit.deg);
}

View File

@ -29,9 +29,9 @@ public class TanTest {
me.setAngleUnits(AngleUnit.rad);
assertEquals("-2*ln(2)-ln(cos(x))", me.simplify("∫(tan(x), x)"));
assertEquals("-(2*ln(2)+ln(cos(x*π)))/π", me.simplify("∫(tan(π*x), x)"));
assertEquals("-0.0153088314659858", me.evaluate("ln(cos(10*π/180))"));
assertEquals("-0.1438410362258904", me.evaluate("ln(cos(30*π/180))"));
assertEquals("0.1285322047599047", me.evaluate("∫ab(tan(x), x, 10*π/180, 30*π/180)"));
assertEquals("-0.015308831465986", me.evaluate("ln(cos(10*π/180))"));
assertEquals("-0.14384103622589", me.evaluate("ln(cos(30*π/180))"));
assertEquals("0.128532204759905", me.evaluate("∫ab(tan(x), x, 10*π/180, 30*π/180)"));
} finally {
me.setAngleUnits(AngleUnit.deg);
}

View File

@ -11,8 +11,8 @@ public class ComplexTest {
@Test
public void testSmallImag() throws Exception {
assertEquals("1+0.0000000000000001*i", Complex.valueOf(1, 0.0000000000000001).toString());
assertEquals("1-0.0000000000000001*i", Complex.valueOf(1, -0.0000000000000001).toString());
assertEquals("1+0.000000000000001*i", Complex.valueOf(1, 0.000000000000001).toString());
assertEquals("1-0.000000000000001*i", Complex.valueOf(1, -0.000000000000001).toString());
}
@Test
@ -23,7 +23,7 @@ public class ComplexTest {
assertEquals("1.175201193643801*i", Expression.valueOf("sin(i)").numeric().toString());
assertEquals("11013.2328747034*i", Expression.valueOf("sin(10*i)").numeric().toString());
assertEquals("11013.23292010332", Expression.valueOf("cos(10*i)").numeric().toString());
assertEquals("0.4621171572600097*i", Expression.valueOf("tan(i/2)").numeric().toString());
assertEquals("0.46211715726001*i", Expression.valueOf("tan(i/2)").numeric().toString());
assertEquals("-2.163953413738653*i", Expression.valueOf("cot(i/2)").numeric().toString());
} finally {
JsclMathEngine.getInstance().setAngleUnits(JsclMathEngine.DEFAULT_ANGLE_UNITS);

View File

@ -36,11 +36,11 @@ public class NumberFormatterTest {
numberFormatter.setPrecision(NO_ROUNDING);
assertEquals("1", numberFormatter.format(1d));
assertEquals("0.3333333333333333", numberFormatter.format(1d / 3));
assertEquals("0.333333333333333", numberFormatter.format(1d / 3));
assertEquals("3.333333333333333E-19", numberFormatter.format(pow(10, -18) / 3));
assertEquals("1.23456789E18", numberFormatter.format(123456789 * pow(10, 10)));
assertEquals("1E-16", numberFormatter.format(pow(10, -16)));
assertEquals("5.9999999999999949E18", numberFormatter.format(5999999999999994999d));
assertEquals("5.999999999999995E18", numberFormatter.format(5999999999999994999d));
testScientificFormat();
}
@ -68,10 +68,10 @@ public class NumberFormatterTest {
assertEquals("1", numberFormatter.format(1d));
assertEquals("0.000001", numberFormatter.format(pow(10, -6)));
assertEquals("0.3333333333333333", numberFormatter.format(1d / 3));
assertEquals("0.333333333333333", numberFormatter.format(1d / 3));
assertEquals("3.333333333333333E-19", numberFormatter.format(pow(10, -18) / 3));
assertEquals("1234567890000000000", numberFormatter.format(123456789 * pow(10, 10)));
assertEquals("0.0000000000000001", numberFormatter.format(pow(10, -16)));
assertEquals("1E-16", numberFormatter.format(pow(10, -16)));
assertEquals("1E-17", numberFormatter.format(pow(10, -17)));
assertEquals("1E-18", numberFormatter.format(pow(10, -18)));
assertEquals("1.5E-18", numberFormatter.format(1.5 * pow(10, -18)));
@ -102,6 +102,7 @@ public class NumberFormatterTest {
// testing simple format with and without rounding
private void testSimpleFormat() {
assertEquals("0.00001", numberFormatter.format(pow(10, -5)));
assertEquals("0.01", numberFormatter.format(3.11 - 3.1));
assertEquals("100", numberFormatter.format(pow(10, 2)));
assertEquals("1", numberFormatter.format(BigInteger.ONE));