diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8ac9c420..1624bd8a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -56,7 +56,11 @@ + android:label="@string/cpp_settings"> + + + + ArrayAdapter makeSimpleSpinnerAdapter(@NonNull Context context) { + return new ArrayAdapter<>(context, R.layout.support_simple_spinner_dropdown_item); + } + public interface ViewProcessor { void process(@Nonnull V view); } diff --git a/app/src/main/java/org/solovyev/android/calculator/Engine.java b/app/src/main/java/org/solovyev/android/calculator/Engine.java index 244881f9..d9d0f779 100644 --- a/app/src/main/java/org/solovyev/android/calculator/Engine.java +++ b/app/src/main/java/org/solovyev/android/calculator/Engine.java @@ -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 groupingSeparator = StringPreference.of("engine.groupingSeparator", String.valueOf(JsclMathEngine.GROUPING_SEPARATOR_DEFAULT)); public static final StringPreference multiplicationSign = StringPreference.of("engine.multiplicationSign", "×"); public static final StringPreference numeralBase = StringPreference.ofTypedValue("engine.numeralBase", "dec", EnumMapper.of(NumeralBase.class)); public static final StringPreference angleUnit = StringPreference.ofTypedValue("engine.angleUnit", "deg", EnumMapper.of(AngleUnit.class)); - public static final Preference version = IntegerPreference.of("engine.version", 1); + public static final Preference version = IntegerPreference.of("engine.version", 2); private static final List 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 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 = StringPreference.ofEnum("engine.output.notation", Notation.dec, Notation.class); + public static final StringPreference separator = StringPreference.ofTypedValue("engine.output.separator", JsclMathEngine.GROUPING_SEPARATOR_DEFAULT, CharacterMapper.INSTANCE); } } } diff --git a/app/src/main/java/org/solovyev/android/calculator/converter/Named.java b/app/src/main/java/org/solovyev/android/calculator/Named.java similarity index 79% rename from app/src/main/java/org/solovyev/android/calculator/converter/Named.java rename to app/src/main/java/org/solovyev/android/calculator/Named.java index c22059ad..bea042f6 100644 --- a/app/src/main/java/org/solovyev/android/calculator/converter/Named.java +++ b/app/src/main/java/org/solovyev/android/calculator/Named.java @@ -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 { +public class Named { @NonNull public final T item; @NonNull @@ -18,12 +18,12 @@ class Named { } @NonNull - static Named create(@NonNull T item, @Nonnull String name) { + public static Named create(@NonNull T item, @Nonnull String name) { return new Named(item, name); } @NonNull - static Named create(@NonNull T item, @StringRes int name, @NonNull Context context) { + public static Named create(@NonNull T item, @StringRes int name, @NonNull Context context) { return create(item, name == 0 ? item.toString() : context.getString(name)); } diff --git a/app/src/main/java/org/solovyev/android/calculator/Preferences.java b/app/src/main/java/org/solovyev/android/calculator/Preferences.java index 0622d50e..a51da55c 100644 --- a/app/src/main/java/org/solovyev/android/calculator/Preferences.java +++ b/app/src/main/java/org/solovyev/android/calculator/Preferences.java @@ -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); } } diff --git a/app/src/main/java/org/solovyev/android/calculator/converter/ConverterFragment.java b/app/src/main/java/org/solovyev/android/calculator/converter/ConverterFragment.java index c69ec60b..275f5173 100644 --- a/app/src/main/java/org/solovyev/android/calculator/converter/ConverterFragment.java +++ b/app/src/main/java/org/solovyev/android/calculator/converter/ConverterFragment.java @@ -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 ArrayAdapter 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> { - @Override - public int compare(Named lhs, Named rhs) { - return lhs.toString().compareTo(rhs.toString()); - } - } - private class KeyboardUser implements FloatingKeyboard.User { @NonNull @Override diff --git a/app/src/main/java/org/solovyev/android/calculator/converter/Convertible.java b/app/src/main/java/org/solovyev/android/calculator/converter/Convertible.java index d02017dc..701fe7b0 100644 --- a/app/src/main/java/org/solovyev/android/calculator/converter/Convertible.java +++ b/app/src/main/java/org/solovyev/android/calculator/converter/Convertible.java @@ -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; diff --git a/app/src/main/java/org/solovyev/android/calculator/converter/ConvertibleDimension.java b/app/src/main/java/org/solovyev/android/calculator/converter/ConvertibleDimension.java index f33b6f1b..f945b4dc 100644 --- a/app/src/main/java/org/solovyev/android/calculator/converter/ConvertibleDimension.java +++ b/app/src/main/java/org/solovyev/android/calculator/converter/ConvertibleDimension.java @@ -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 { diff --git a/app/src/main/java/org/solovyev/android/calculator/converter/NumeralBaseConvertible.java b/app/src/main/java/org/solovyev/android/calculator/converter/NumeralBaseConvertible.java index f211340b..5c5dc63c 100644 --- a/app/src/main/java/org/solovyev/android/calculator/converter/NumeralBaseConvertible.java +++ b/app/src/main/java/org/solovyev/android/calculator/converter/NumeralBaseConvertible.java @@ -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; diff --git a/app/src/main/java/org/solovyev/android/calculator/converter/NumeralBaseDimension.java b/app/src/main/java/org/solovyev/android/calculator/converter/NumeralBaseDimension.java index 1834e257..8368101e 100644 --- a/app/src/main/java/org/solovyev/android/calculator/converter/NumeralBaseDimension.java +++ b/app/src/main/java/org/solovyev/android/calculator/converter/NumeralBaseDimension.java @@ -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; diff --git a/app/src/main/java/org/solovyev/android/calculator/converter/UnitConvertible.java b/app/src/main/java/org/solovyev/android/calculator/converter/UnitConvertible.java index e8b3dded..91aadf3f 100644 --- a/app/src/main/java/org/solovyev/android/calculator/converter/UnitConvertible.java +++ b/app/src/main/java/org/solovyev/android/calculator/converter/UnitConvertible.java @@ -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; diff --git a/app/src/main/java/org/solovyev/android/calculator/converter/UnitDimension.java b/app/src/main/java/org/solovyev/android/calculator/converter/UnitDimension.java index 6dc91974..9fbd4b35 100644 --- a/app/src/main/java/org/solovyev/android/calculator/converter/UnitDimension.java +++ b/app/src/main/java/org/solovyev/android/calculator/converter/UnitDimension.java @@ -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; diff --git a/app/src/main/java/org/solovyev/android/calculator/history/History.java b/app/src/main/java/org/solovyev/android/calculator/history/History.java index e4b90a9f..bb7ef9cf 100644 --- a/app/src/main/java/org/solovyev/android/calculator/history/History.java +++ b/app/src/main/java/org/solovyev/android/calculator/history/History.java @@ -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 result = new LinkedList<>(); - final String groupingSeparator = Preferences.groupingSeparator.getPreference(preferences); + final char separator = Preferences.Output.separator.getPreference(preferences); final List 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 { diff --git a/app/src/main/java/org/solovyev/android/calculator/preferences/NumberFormatExamplesPreference.java b/app/src/main/java/org/solovyev/android/calculator/preferences/NumberFormatExamplesPreference.java new file mode 100644 index 00000000..9eabbbfe --- /dev/null +++ b/app/src/main/java/org/solovyev/android/calculator/preferences/NumberFormatExamplesPreference.java @@ -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; + } +} diff --git a/app/src/main/java/org/solovyev/android/calculator/preferences/PrecisionPreference.java b/app/src/main/java/org/solovyev/android/calculator/preferences/PrecisionPreference.java new file mode 100644 index 00000000..2b62f450 --- /dev/null +++ b/app/src/main/java/org/solovyev/android/calculator/preferences/PrecisionPreference.java @@ -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(); + } + } +} diff --git a/app/src/main/java/org/solovyev/android/calculator/preferences/PreferencesActivity.java b/app/src/main/java/org/solovyev/android/calculator/preferences/PreferencesActivity.java index ca7312cf..930f5313 100644 --- a/app/src/main/java/org/solovyev/android/calculator/preferences/PreferencesActivity.java +++ b/app/src/main/java/org/solovyev/android/calculator/preferences/PreferencesActivity.java @@ -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)); diff --git a/app/src/main/java/org/solovyev/android/calculator/preferences/PreferencesFragment.java b/app/src/main/java/org/solovyev/android/calculator/preferences/PreferencesFragment.java index e18bd7cf..d46c5751 100644 --- a/app/src/main/java/org/solovyev/android/calculator/preferences/PreferencesFragment.java +++ b/app/src/main/java/org/solovyev/android/calculator/preferences/PreferencesFragment.java @@ -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 & PreferenceEntry> void prepareListPreference(@Nonnull final StringPreference p, @Nonnull Class 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(); } diff --git a/app/src/main/java/org/solovyev/android/calculator/text/NaturalComparator.java b/app/src/main/java/org/solovyev/android/calculator/text/NaturalComparator.java new file mode 100644 index 00000000..bf219dfe --- /dev/null +++ b/app/src/main/java/org/solovyev/android/calculator/text/NaturalComparator.java @@ -0,0 +1,12 @@ +package org.solovyev.android.calculator.text; + +import java.util.Comparator; + +public class NaturalComparator implements Comparator { + public static final NaturalComparator INSTANCE = new NaturalComparator(); + + @Override + public int compare(Object lhs, Object rhs) { + return lhs.toString().compareTo(rhs.toString()); + } +} diff --git a/app/src/main/java/org/solovyev/android/calculator/wizard/CalculatorMode.java b/app/src/main/java/org/solovyev/android/calculator/wizard/CalculatorMode.java index 301d6bd3..d1be8929 100644 --- a/app/src/main/java/org/solovyev/android/calculator/wizard/CalculatorMode.java +++ b/app/src/main/java/org/solovyev/android/calculator/wizard/CalculatorMode.java @@ -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(); } diff --git a/app/src/main/java/org/solovyev/android/prefs/CharacterPreference.java b/app/src/main/java/org/solovyev/android/prefs/CharacterPreference.java new file mode 100644 index 00000000..5f6780ab --- /dev/null +++ b/app/src/main/java/org/solovyev/android/prefs/CharacterPreference.java @@ -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 { + 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); + } +} diff --git a/app/src/main/java/org/solovyev/android/prefs/StringPreference.java b/app/src/main/java/org/solovyev/android/prefs/StringPreference.java index 0341ea4d..0c561e72 100644 --- a/app/src/main/java/org/solovyev/android/prefs/StringPreference.java +++ b/app/src/main/java/org/solovyev/android/prefs/StringPreference.java @@ -56,6 +56,11 @@ public final class StringPreference extends AbstractPreference { return new StringPreference(key, mapper.parseValue(defaultValue), mapper); } + @Nonnull + public static StringPreference ofTypedValue(@Nonnull String key, @Nullable T defaultValue, @Nonnull Mapper mapper) { + return new StringPreference(key, defaultValue, mapper); + } + @Nonnull public static StringPreference ofEnum(@Nonnull String key, @Nullable T defaultValue, @Nonnull Class enumType) { return new StringPreference(key, defaultValue, EnumMapper.of(enumType)); diff --git a/app/src/main/java/org/solovyev/android/views/DiscreteSeekBar.java b/app/src/main/java/org/solovyev/android/views/DiscreteSeekBar.java new file mode 100644 index 00000000..088f309e --- /dev/null +++ b/app/src/main/java/org/solovyev/android/views/DiscreteSeekBar.java @@ -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); + } +} diff --git a/app/src/main/java/org/solovyev/common/text/CharacterMapper.java b/app/src/main/java/org/solovyev/common/text/CharacterMapper.java new file mode 100644 index 00000000..a2b79391 --- /dev/null +++ b/app/src/main/java/org/solovyev/common/text/CharacterMapper.java @@ -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 { + + @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); + } +} diff --git a/app/src/main/res/layout/fragment_plot_function_edit.xml b/app/src/main/res/layout/fragment_plot_function_edit.xml index cebfc31a..eecacbf3 100644 --- a/app/src/main/res/layout/fragment_plot_function_edit.xml +++ b/app/src/main/res/layout/fragment_plot_function_edit.xml @@ -59,11 +59,9 @@ @@ -74,11 +72,9 @@ diff --git a/app/src/main/res/layout/preference_precision.xml b/app/src/main/res/layout/preference_precision.xml new file mode 100644 index 00000000..072a035f --- /dev/null +++ b/app/src/main/res/layout/preference_precision.xml @@ -0,0 +1,11 @@ + + + \ No newline at end of file diff --git a/app/src/main/res/values/attrs_dsb.xml b/app/src/main/res/values/attrs_dsb.xml new file mode 100644 index 00000000..1c26d503 --- /dev/null +++ b/app/src/main/res/values/attrs_dsb.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index b908d698..fcca4323 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -319,6 +319,11 @@ gone + + 16sp 14sp \ No newline at end of file diff --git a/app/src/main/res/values/text_non_translatable.xml b/app/src/main/res/values/text_non_translatable.xml index c7026931..ecd1f7b4 100644 --- a/app/src/main/res/values/text_non_translatable.xml +++ b/app/src/main/res/values/text_non_translatable.xml @@ -18,7 +18,6 @@ E %1$s (AMOLED) - 0 1 2 3 @@ -34,6 +33,5 @@ 13 14 15 - 16 \ No newline at end of file diff --git a/app/src/main/res/values/text_strings.xml b/app/src/main/res/values/text_strings.xml index 1c1c5868..446c5885 100644 --- a/app/src/main/res/values/text_strings.xml +++ b/app/src/main/res/values/text_strings.xml @@ -124,4 +124,7 @@ Angles Radix Numeral system + Decimal + Engineering + Scientific diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 0ffdadcc..fdebae46 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -35,6 +35,10 @@ a:key="restart_wizard" a:title="@string/cpp_restart_wizard" /> + + diff --git a/app/src/main/res/xml/preferences_calculations.xml b/app/src/main/res/xml/preferences_calculations.xml index 4aeb0433..3db7afa5 100644 --- a/app/src/main/res/xml/preferences_calculations.xml +++ b/app/src/main/res/xml/preferences_calculations.xml @@ -24,32 +24,6 @@ - - - - - - - - + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/test/java/org/solovyev/android/calculator/Tests.java b/app/src/test/java/org/solovyev/android/calculator/Tests.java index fb8f5108..574534e5 100644 --- a/app/src/test/java/org/solovyev/android/calculator/Tests.java +++ b/app/src/test/java/org/solovyev/android/calculator/Tests.java @@ -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); diff --git a/app/src/test/java/org/solovyev/android/calculator/history/HistoryTest.java b/app/src/test/java/org/solovyev/android/calculator/history/HistoryTest.java index 0e9eef5d..3768d2f4 100644 --- a/app/src/test/java/org/solovyev/android/calculator/history/HistoryTest.java +++ b/app/src/test/java/org/solovyev/android/calculator/history/HistoryTest.java @@ -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"); diff --git a/jscl/src/main/java/jscl/JsclMathEngine.java b/jscl/src/main/java/jscl/JsclMathEngine.java index 17950a19..1ed2adc6 100644 --- a/jscl/src/main/java/jscl/JsclMathEngine.java +++ b/jscl/src/main/java/jscl/JsclMathEngine.java @@ -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; } } diff --git a/jscl/src/main/java/jscl/MathContext.java b/jscl/src/main/java/jscl/MathContext.java index 83e64bb4..ef9d9155 100644 --- a/jscl/src/main/java/jscl/MathContext.java +++ b/jscl/src/main/java/jscl/MathContext.java @@ -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); } diff --git a/jscl/src/main/java/org/solovyev/common/NumberFormatter.java b/jscl/src/main/java/org/solovyev/common/NumberFormatter.java index b875774d..c1dadf8f 100644 --- a/jscl/src/main/java/org/solovyev/common/NumberFormatter.java +++ b/jscl/src/main/java/org/solovyev/common/NumberFormatter.java @@ -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; diff --git a/jscl/src/test/java/jscl/JsclMathEngineTest.java b/jscl/src/test/java/jscl/JsclMathEngineTest.java index ae3cb0c7..d3e9273d 100644 --- a/jscl/src/test/java/jscl/JsclMathEngineTest.java +++ b/jscl/src/test/java/jscl/JsclMathEngineTest.java @@ -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)); diff --git a/jscl/src/test/java/jscl/NumeralBaseTest.java b/jscl/src/test/java/jscl/NumeralBaseTest.java index 055d68b9..e39aae05 100644 --- a/jscl/src/test/java/jscl/NumeralBaseTest.java +++ b/jscl/src/test/java/jscl/NumeralBaseTest.java @@ -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")); diff --git a/jscl/src/test/java/jscl/math/ExpressionTest.java b/jscl/src/test/java/jscl/math/ExpressionTest.java index 8ea51f24..6432eabd 100644 --- a/jscl/src/test/java/jscl/math/ExpressionTest.java +++ b/jscl/src/test/java/jscl/math/ExpressionTest.java @@ -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); } } } diff --git a/jscl/src/test/java/jscl/math/function/CustomFunctionTest.java b/jscl/src/test/java/jscl/math/function/CustomFunctionTest.java index 3d03fc7b..6bd70815 100644 --- a/jscl/src/test/java/jscl/math/function/CustomFunctionTest.java +++ b/jscl/src/test/java/jscl/math/function/CustomFunctionTest.java @@ -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(); diff --git a/jscl/src/test/java/jscl/math/function/RadTest.java b/jscl/src/test/java/jscl/math/function/RadTest.java index 5f9b3719..45e548d8 100644 --- a/jscl/src/test/java/jscl/math/function/RadTest.java +++ b/jscl/src/test/java/jscl/math/function/RadTest.java @@ -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)")); diff --git a/jscl/src/test/java/jscl/math/function/SqrtTest.java b/jscl/src/test/java/jscl/math/function/SqrtTest.java index 0d365858..78634d13 100644 --- a/jscl/src/test/java/jscl/math/function/SqrtTest.java +++ b/jscl/src/test/java/jscl/math/function/SqrtTest.java @@ -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); } diff --git a/jscl/src/test/java/jscl/math/function/trigonometric/CosTest.java b/jscl/src/test/java/jscl/math/function/trigonometric/CosTest.java index 1fa9e052..16631d9d 100644 --- a/jscl/src/test/java/jscl/math/function/trigonometric/CosTest.java +++ b/jscl/src/test/java/jscl/math/function/trigonometric/CosTest.java @@ -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)")); } } diff --git a/jscl/src/test/java/jscl/math/function/trigonometric/SinTest.java b/jscl/src/test/java/jscl/math/function/trigonometric/SinTest.java index f9285248..4599cfe5 100644 --- a/jscl/src/test/java/jscl/math/function/trigonometric/SinTest.java +++ b/jscl/src/test/java/jscl/math/function/trigonometric/SinTest.java @@ -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); } diff --git a/jscl/src/test/java/jscl/math/function/trigonometric/TanTest.java b/jscl/src/test/java/jscl/math/function/trigonometric/TanTest.java index 45f1358f..728cb939 100644 --- a/jscl/src/test/java/jscl/math/function/trigonometric/TanTest.java +++ b/jscl/src/test/java/jscl/math/function/trigonometric/TanTest.java @@ -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); } diff --git a/jscl/src/test/java/jscl/math/numeric/ComplexTest.java b/jscl/src/test/java/jscl/math/numeric/ComplexTest.java index e884b8fd..bf233395 100644 --- a/jscl/src/test/java/jscl/math/numeric/ComplexTest.java +++ b/jscl/src/test/java/jscl/math/numeric/ComplexTest.java @@ -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); diff --git a/jscl/src/test/java/org/solovyev/common/NumberFormatterTest.java b/jscl/src/test/java/org/solovyev/common/NumberFormatterTest.java index e7847319..e9dd8315 100644 --- a/jscl/src/test/java/org/solovyev/common/NumberFormatterTest.java +++ b/jscl/src/test/java/org/solovyev/common/NumberFormatterTest.java @@ -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));