Merge branch 'dev-number-format'
Conflicts: jscl/src/main/java/jscl/JsclMathEngine.java
This commit is contained in:
commit
0aef4e16d0
@ -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"
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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));
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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));
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
@ -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"/>
|
||||
|
||||
|
11
app/src/main/res/layout/preference_precision.xml
Normal file
11
app/src/main/res/layout/preference_precision.xml
Normal 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" />
|
8
app/src/main/res/values/attrs_dsb.xml
Normal file
8
app/src/main/res/values/attrs_dsb.xml
Normal 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>
|
@ -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>
|
@ -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>
|
@ -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>
|
||||
|
@ -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" />
|
||||
|
@ -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"
|
||||
|
49
app/src/main/res/xml/preferences_number_format.xml
Normal file
49
app/src/main/res/xml/preferences_number_format.xml
Normal 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>
|
@ -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);
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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"));
|
||||
|
@ -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("1°").numeric().toString());
|
||||
assertEquals("0.0349065850398866", Expression.valueOf("2°").numeric().toString());
|
||||
assertEquals("0.0523598775598299", Expression.valueOf("3°").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("5°").numeric().toString());
|
||||
assertEquals("0.017453292519943", Expression.valueOf("1°").numeric().toString());
|
||||
assertEquals("0.034906585039887", Expression.valueOf("2°").numeric().toString());
|
||||
assertEquals("0.05235987755983", Expression.valueOf("3°").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("5°").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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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)"));
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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)"));
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
|
Loading…
Reference in New Issue
Block a user