Import translations. DO NOT MERGE
am: a7c62ac9fd -s ours
Change-Id: Ib441d6f424da131e8b977d01241069bf7c4b95ea
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index b01e2c8..a7675b1 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -28,9 +28,14 @@
<activity
android:name=".Calculator"
android:label="@string/app_name"
+ android:launchMode="singleTask"
android:theme="@style/Theme.Calculator">
+ <layout
+ android:minHeight="220dp"
+ android:minWidth="230dp" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
+
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.APP_CALCULATOR" />
</intent-filter>
@@ -38,9 +43,9 @@
<activity
android:name=".Licenses"
+ android:launchMode="singleTop"
android:parentActivityName=".Calculator"
android:theme="@style/Theme.Licenses" />
</application>
-
</manifest>
diff --git a/res/layout/activity_calculator_land.xml b/res/layout/activity_calculator_land.xml
index 1fe12db..5dd2c20 100644
--- a/res/layout/activity_calculator_land.xml
+++ b/res/layout/activity_calculator_land.xml
@@ -32,7 +32,7 @@
android:layout_weight="1">
<include layout="@layout/pad_numeric" />
- <include layout="@layout/pad_operator_two_col" />
+ <include layout="@layout/pad_operator" />
<include layout="@layout/pad_advanced" />
</LinearLayout>
diff --git a/res/layout/activity_calculator_port.xml b/res/layout/activity_calculator_port.xml
index 0cb5dc7..4cafa94 100644
--- a/res/layout/activity_calculator_port.xml
+++ b/res/layout/activity_calculator_port.xml
@@ -21,10 +21,7 @@
android:layout_height="match_parent"
android:orientation="vertical">
- <include
- layout="@layout/display"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ <include layout="@layout/display"/>
<com.android.calculator2.CalculatorPadViewPager
android:id="@+id/pad_pager"
@@ -38,7 +35,7 @@
android:layout_height="match_parent">
<include layout="@layout/pad_numeric" />
- <include layout="@layout/pad_operator_one_col" />
+ <include layout="@layout/pad_operator" />
</LinearLayout>
diff --git a/res/layout/activity_calculator_tablet_port.xml b/res/layout/activity_calculator_tablet_port.xml
index b56450e..9eab3af 100644
--- a/res/layout/activity_calculator_tablet_port.xml
+++ b/res/layout/activity_calculator_tablet_port.xml
@@ -21,17 +21,17 @@
android:layout_height="match_parent"
android:orientation="vertical">
- <include
- layout="@layout/display"
+ <include layout="@layout/display" />
+
+ <include layout="@layout/pad_advanced" />
+
+ <LinearLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content" />
+ android:layout_height="0dip"
+ android:layout_weight="500">
- <include layout="@layout/pad_advanced_tablet_port" />
-
- <LinearLayout style="@style/PadLinearLayoutStyle">
-
- <include layout="@layout/pad_numeric" />
- <include layout="@layout/pad_operator_two_col" />
+ <include layout="@layout/pad_numeric"/>
+ <include layout="@layout/pad_operator" />
</LinearLayout>
diff --git a/res/layout/display.xml b/res/layout/display.xml
deleted file mode 100644
index 638f0d2..0000000
--- a/res/layout/display.xml
+++ /dev/null
@@ -1,64 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-
-<RelativeLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/display"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:background="@color/display_background_color"
- android:elevation="4dip">
-
- <Toolbar
- android:id="@+id/toolbar"
- android:layout_width="match_parent"
- android:layout_height="?android:attr/actionBarSize">
-
- <TextView
- android:id="@+id/mode"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:fontFamily="sans-serif-medium"
- android:textAllCaps="true"
- android:textSize="16sp" />
-
- </Toolbar>
-
- <com.android.calculator2.CalculatorText
- android:id="@+id/formula"
- style="@style/DisplayTextStyle.Formula"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@id/toolbar"
- android:ellipsize="none"
- android:longClickable="true"
- android:singleLine="true"
- android:scrollHorizontally="true"
- android:textColor="@color/display_formula_text_color"
- android:textIsSelectable="false" />
-
- <com.android.calculator2.CalculatorResult
- android:id="@+id/result"
- style="@style/DisplayTextStyle.Result"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_below="@id/formula"
- android:bufferType="spannable"
- android:singleLine="true"
- android:textColor="@color/display_result_text_color" />
-
-</RelativeLayout>
diff --git a/res/layout/display_one_line.xml b/res/layout/display_one_line.xml
new file mode 100644
index 0000000..c016b15
--- /dev/null
+++ b/res/layout/display_one_line.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<com.android.calculator2.CalculatorDisplay
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/display"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@color/display_background_color"
+ android:clipChildren="false"
+ android:elevation="4dip"
+ android:orientation="vertical">
+
+ <include layout="@layout/toolbar" />
+
+ <FrameLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <com.android.calculator2.CalculatorScrollView
+ android:id="@+id/formula_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom"
+ android:overScrollMode="never"
+ android:scrollbars="none">
+
+ <com.android.calculator2.CalculatorText
+ android:id="@+id/formula"
+ style="@style/DisplayTextStyle.Formula"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_gravity="bottom|end"
+ android:ellipsize="none"
+ android:gravity="bottom|end"
+ android:longClickable="true"
+ android:singleLine="true"
+ android:textColor="@color/display_formula_text_color"
+ android:textIsSelectable="false" />
+
+ </com.android.calculator2.CalculatorScrollView>
+
+ <com.android.calculator2.CalculatorResult
+ android:id="@+id/result"
+ style="@style/DisplayTextStyle.Result"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="bottom"
+ android:bufferType="spannable"
+ android:singleLine="true"
+ android:textColor="@color/display_result_text_color"
+ android:visibility="invisible" />
+
+ </FrameLayout>
+
+</com.android.calculator2.CalculatorDisplay>
diff --git a/res/layout/display_two_line.xml b/res/layout/display_two_line.xml
new file mode 100644
index 0000000..3735a85
--- /dev/null
+++ b/res/layout/display_two_line.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<com.android.calculator2.CalculatorDisplay
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/display"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@color/display_background_color"
+ android:elevation="4dip"
+ android:orientation="vertical">
+
+ <include layout="@layout/toolbar" />
+
+ <com.android.calculator2.CalculatorScrollView
+ android:id="@+id/formula_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:overScrollMode="never"
+ android:scrollbars="none">
+
+ <com.android.calculator2.CalculatorText
+ android:id="@+id/formula"
+ style="@style/DisplayTextStyle.Formula"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_gravity="bottom|end"
+ android:ellipsize="none"
+ android:longClickable="true"
+ android:singleLine="true"
+ android:textColor="@color/display_formula_text_color"
+ android:textIsSelectable="false" />
+
+ </com.android.calculator2.CalculatorScrollView>
+
+ <com.android.calculator2.CalculatorResult
+ android:id="@+id/result"
+ style="@style/DisplayTextStyle.Result"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:bufferType="spannable"
+ android:singleLine="true"
+ android:textColor="@color/display_result_text_color" />
+
+</com.android.calculator2.CalculatorDisplay>
diff --git a/res/layout/pad_advanced.xml b/res/layout/pad_advanced_3x5.xml
similarity index 100%
rename from res/layout/pad_advanced.xml
rename to res/layout/pad_advanced_3x5.xml
diff --git a/res/layout/pad_advanced_4x4.xml b/res/layout/pad_advanced_4x4.xml
new file mode 100644
index 0000000..ddbee47
--- /dev/null
+++ b/res/layout/pad_advanced_4x4.xml
@@ -0,0 +1,207 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<GridLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/pad_advanced"
+ style="@style/PadLayoutStyle.Advanced"
+ android:rowCount="4"
+ android:columnCount="4"
+ android:background="@color/pad_advanced_background_color">
+
+ <Button
+ android:id="@+id/toggle_inv"
+ style="@style/PadButtonStyle.Advanced.Text"
+ android:layout_row="0"
+ android:layout_column="0"
+ android:background="@drawable/pad_button_inverse_background"
+ android:contentDescription="@string/desc_inv_off"
+ android:text="@string/inv" />
+
+ <Button
+ android:id="@+id/toggle_mode"
+ style="@style/PadButtonStyle.Advanced.Text"
+ android:layout_row="0"
+ android:layout_column="1"
+ android:contentDescription="@string/desc_switch_deg"
+ android:text="@string/mode_deg" />
+
+ <Button
+ android:id="@+id/op_pct"
+ style="@style/PadButtonStyle.Advanced"
+ android:layout_row="0"
+ android:layout_column="2"
+ android:contentDescription="@string/desc_op_pct"
+ android:text="@string/op_pct" />
+
+ <Button
+ android:id="@+id/fun_sin"
+ style="@style/PadButtonStyle.Advanced"
+ android:layout_row="1"
+ android:layout_column="0"
+ android:contentDescription="@string/desc_fun_sin"
+ android:text="@string/fun_sin" />
+
+ <Button
+ android:id="@+id/fun_arcsin"
+ style="@style/PadButtonStyle.Advanced"
+ android:layout_row="1"
+ android:layout_column="0"
+ android:contentDescription="@string/desc_fun_arcsin"
+ android:fontFamily="sans-serif-medium"
+ android:text="@string/fun_arcsin"
+ android:visibility="gone" />
+
+ <Button
+ android:id="@+id/fun_cos"
+ style="@style/PadButtonStyle.Advanced"
+ android:layout_row="1"
+ android:layout_column="1"
+ android:contentDescription="@string/desc_fun_cos"
+ android:text="@string/fun_cos" />
+
+ <Button
+ android:id="@+id/fun_arccos"
+ style="@style/PadButtonStyle.Advanced"
+ android:layout_row="1"
+ android:layout_column="1"
+ android:contentDescription="@string/desc_fun_arccos"
+ android:fontFamily="sans-serif-medium"
+ android:text="@string/fun_arccos"
+ android:visibility="gone" />
+
+ <Button
+ android:id="@+id/fun_tan"
+ style="@style/PadButtonStyle.Advanced"
+ android:layout_row="1"
+ android:layout_column="2"
+ android:contentDescription="@string/desc_fun_tan"
+ android:text="@string/fun_tan" />
+
+ <Button
+ android:id="@+id/fun_arctan"
+ style="@style/PadButtonStyle.Advanced"
+ android:layout_row="1"
+ android:layout_column="2"
+ android:contentDescription="@string/desc_fun_arctan"
+ android:fontFamily="sans-serif-medium"
+ android:text="@string/fun_arctan"
+ android:visibility="gone" />
+
+ <Button
+ android:id="@+id/const_pi"
+ style="@style/PadButtonStyle.Advanced"
+ android:layout_row="1"
+ android:layout_column="3"
+ android:contentDescription="@string/desc_const_pi"
+ android:text="@string/const_pi" />
+
+ <Button
+ android:id="@+id/fun_ln"
+ style="@style/PadButtonStyle.Advanced"
+ android:layout_row="2"
+ android:layout_column="0"
+ android:contentDescription="@string/desc_fun_ln"
+ android:text="@string/fun_ln" />
+
+ <Button
+ android:id="@+id/fun_exp"
+ style="@style/PadButtonStyle.Advanced"
+ android:layout_row="2"
+ android:layout_column="0"
+ android:contentDescription="@string/desc_fun_exp"
+ android:fontFamily="sans-serif-medium"
+ android:text="@string/fun_exp"
+ android:visibility="gone" />
+
+ <Button
+ android:id="@+id/fun_log"
+ style="@style/PadButtonStyle.Advanced"
+ android:layout_row="2"
+ android:layout_column="1"
+ android:contentDescription="@string/desc_fun_log"
+ android:text="@string/fun_log" />
+
+ <Button
+ android:id="@+id/fun_10pow"
+ style="@style/PadButtonStyle.Advanced"
+ android:layout_row="2"
+ android:layout_column="1"
+ android:contentDescription="@string/desc_fun_10pow"
+ android:fontFamily="sans-serif-medium"
+ android:text="@string/fun_10pow"
+ android:visibility="gone" />
+
+ <Button
+ android:id="@+id/op_fact"
+ style="@style/PadButtonStyle.Advanced"
+ android:layout_row="2"
+ android:layout_column="2"
+ android:contentDescription="@string/desc_op_fact"
+ android:text="@string/op_fact" />
+
+ <Button
+ android:id="@+id/const_e"
+ style="@style/PadButtonStyle.Advanced"
+ android:layout_row="2"
+ android:layout_column="3"
+ android:contentDescription="@string/desc_const_e"
+ android:text="@string/const_e" />
+
+ <Button
+ android:id="@+id/lparen"
+ style="@style/PadButtonStyle.Advanced"
+ android:layout_row="3"
+ android:layout_column="0"
+ android:contentDescription="@string/desc_lparen"
+ android:text="@string/lparen" />
+
+ <Button
+ android:id="@+id/rparen"
+ style="@style/PadButtonStyle.Advanced"
+ android:layout_row="3"
+ android:layout_column="1"
+ android:contentDescription="@string/desc_rparen"
+ android:text="@string/rparen" />
+
+ <Button
+ android:id="@+id/op_sqrt"
+ style="@style/PadButtonStyle.Advanced"
+ android:layout_row="3"
+ android:layout_column="2"
+ android:contentDescription="@string/desc_op_sqrt"
+ android:text="@string/op_sqrt" />
+
+ <Button
+ android:id="@+id/op_sqr"
+ style="@style/PadButtonStyle.Advanced"
+ android:layout_row="3"
+ android:layout_column="2"
+ android:contentDescription="@string/desc_op_sqr"
+ android:fontFamily="sans-serif-medium"
+ android:text="@string/op_sqr"
+ android:visibility="gone" />
+
+ <Button
+ android:id="@+id/op_pow"
+ style="@style/PadButtonStyle.Advanced"
+ android:layout_row="3"
+ android:layout_column="3"
+ android:contentDescription="@string/desc_op_pow"
+ android:text="@string/op_pow" />
+
+</GridLayout>
\ No newline at end of file
diff --git a/res/layout/pad_advanced_tablet_port.xml b/res/layout/pad_advanced_5x3.xml
similarity index 100%
rename from res/layout/pad_advanced_tablet_port.xml
rename to res/layout/pad_advanced_5x3.xml
diff --git a/res/layout/toolbar.xml b/res/layout/toolbar.xml
new file mode 100644
index 0000000..56f05b6
--- /dev/null
+++ b/res/layout/toolbar.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<Toolbar
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?android:attr/actionBarSize"
+ android:layout_gravity="top">
+
+ <TextView
+ android:id="@+id/mode"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:fontFamily="sans-serif-medium"
+ android:textAllCaps="true"
+ android:textSize="16sp" />
+
+</Toolbar>
\ No newline at end of file
diff --git a/res/mipmap-hdpi/ic_launcher_calculator.png b/res/mipmap-hdpi/ic_launcher_calculator.png
old mode 100755
new mode 100644
index 7db1239..e50e87e
--- a/res/mipmap-hdpi/ic_launcher_calculator.png
+++ b/res/mipmap-hdpi/ic_launcher_calculator.png
Binary files differ
diff --git a/res/mipmap-mdpi/ic_launcher_calculator.png b/res/mipmap-mdpi/ic_launcher_calculator.png
old mode 100755
new mode 100644
index bce23c3..2fcdc67
--- a/res/mipmap-mdpi/ic_launcher_calculator.png
+++ b/res/mipmap-mdpi/ic_launcher_calculator.png
Binary files differ
diff --git a/res/mipmap-xhdpi/ic_launcher_calculator.png b/res/mipmap-xhdpi/ic_launcher_calculator.png
old mode 100755
new mode 100644
index ad86415..67993fc
--- a/res/mipmap-xhdpi/ic_launcher_calculator.png
+++ b/res/mipmap-xhdpi/ic_launcher_calculator.png
Binary files differ
diff --git a/res/mipmap-xxhdpi/ic_launcher_calculator.png b/res/mipmap-xxhdpi/ic_launcher_calculator.png
old mode 100755
new mode 100644
index d799a8f..71387a9
--- a/res/mipmap-xxhdpi/ic_launcher_calculator.png
+++ b/res/mipmap-xxhdpi/ic_launcher_calculator.png
Binary files differ
diff --git a/res/mipmap-xxxhdpi/ic_launcher_calculator.png b/res/mipmap-xxxhdpi/ic_launcher_calculator.png
old mode 100755
new mode 100644
index 6f3111e..9d61dc4
--- a/res/mipmap-xxxhdpi/ic_launcher_calculator.png
+++ b/res/mipmap-xxxhdpi/ic_launcher_calculator.png
Binary files differ
diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml
index 96c27d4..5edcae9 100644
--- a/res/values-af/strings.xml
+++ b/res/values-af/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"vee uit"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"wys omgekeerde funksies"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"versteek omgekeerde funksies"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Geen formule nie"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Getalle en basiese handelinge"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Gevorderde handelinge"</string>
<string name="error_nan" msgid="5674077944929888710">"Nie getal nie"</string>
<string name="error_syntax" msgid="4786987111228645602">"Swak uitdrukking"</string>
<string name="error_aborted" msgid="3402238176316342537">"Gestaak"</string>
diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml
index 0d84edf..14dbd5f 100644
--- a/res/values-am/strings.xml
+++ b/res/values-am/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"ሰርዝ"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"የተገላቢጦሽ ተግባሮችን አሳይ"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"የተገላቢጦሽ ተግባሮችን ደብቅ"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"ምንም ቀመር የለም"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"ቁጥሮች እና መሠረታዊ ክወናዎች"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"የላቁ ክወናዎች"</string>
<string name="error_nan" msgid="5674077944929888710">"ቁጥር አይደለም"</string>
<string name="error_syntax" msgid="4786987111228645602">"መጥፎ የሒሳብ ሐረግ"</string>
<string name="error_aborted" msgid="3402238176316342537">"ተቋርጧል"</string>
diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml
index 412d9ec..3085943 100644
--- a/res/values-ar/strings.xml
+++ b/res/values-ar/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"حذف"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"عرض الدالات العكسية"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"إخفاء الدالات العكسية"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"ليست هناك صيغة"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"الأرقام والعمليات الأساسية"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"العمليات المتقدمة"</string>
<string name="error_nan" msgid="5674077944929888710">"ليس رقمًا"</string>
<string name="error_syntax" msgid="4786987111228645602">"صيغة سيئة"</string>
<string name="error_aborted" msgid="3402238176316342537">"تم الإلغاء"</string>
diff --git a/res/values-az-rAZ/strings.xml b/res/values-az-rAZ/strings.xml
index 09f7cb1..acd95fb 100644
--- a/res/values-az-rAZ/strings.xml
+++ b/res/values-az-rAZ/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"silin"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"əks funksiyaları göstərin"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"əks funksiyaları gizlədin"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Düstur yoxdur"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Nömrələr və əsas əməliyyatlar"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Qabaqcıl əməliyyatlar"</string>
<string name="error_nan" msgid="5674077944929888710">"Rəqəm deyil"</string>
<string name="error_syntax" msgid="4786987111228645602">"Yalnış ifadə"</string>
<string name="error_aborted" msgid="3402238176316342537">"Ləğv edilib"</string>
diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml
index 1869d48..24ad323 100644
--- a/res/values-b+sr+Latn/strings.xml
+++ b/res/values-b+sr+Latn/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"izbriši"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"prikaži inverzne funkcije"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"sakrij inverzne funkcije"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Nema formule"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Brojevi i osnovne operacije"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Napredne operacije"</string>
<string name="error_nan" msgid="5674077944929888710">"Nije broj"</string>
<string name="error_syntax" msgid="4786987111228645602">"Neispravan izraz"</string>
<string name="error_aborted" msgid="3402238176316342537">"Otkazano je"</string>
diff --git a/res/values-be-rBY/strings.xml b/res/values-be-rBY/strings.xml
index 79ca42f..b5e63b8 100644
--- a/res/values-be-rBY/strings.xml
+++ b/res/values-be-rBY/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"выдаліць"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"паказаць адваротныя функцыі"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"схаваць адваротныя функцыі"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Няма формулы"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Лічбы і асноўныя аперацыі"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Дадатковыя аперацыі"</string>
<string name="error_nan" msgid="5674077944929888710">"Не з\'яўляецца лікам"</string>
<string name="error_syntax" msgid="4786987111228645602">"Няправільны выраз"</string>
<string name="error_aborted" msgid="3402238176316342537">"Спынена"</string>
diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml
index f442b04..5f8222f 100644
--- a/res/values-bg/strings.xml
+++ b/res/values-bg/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"изтриване"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"показване на обратните функции"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"скриване на обратните функции"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Няма формула"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Цифри и основни действия"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Разширени действия"</string>
<string name="error_nan" msgid="5674077944929888710">"Не е число"</string>
<string name="error_syntax" msgid="4786987111228645602">"Неправилен израз"</string>
<string name="error_aborted" msgid="3402238176316342537">"Прекратено"</string>
diff --git a/res/values-bn-rBD/strings.xml b/res/values-bn-rBD/strings.xml
index 2742857..b6ed7c8 100644
--- a/res/values-bn-rBD/strings.xml
+++ b/res/values-bn-rBD/strings.xml
@@ -27,7 +27,7 @@
<string name="desc_const_e" msgid="1889187337970539507">"ইউলার সংখ্যা"</string>
<string name="desc_const_pi" msgid="5430918714009441655">"পাই"</string>
<string name="desc_dec_point" msgid="5725254504360445023">"পয়েন্ট"</string>
- <string name="desc_lparen" msgid="8688758170211924916">"বাঁ লঘুবন্ধনী"</string>
+ <string name="desc_lparen" msgid="8688758170211924916">"বাম লঘুবন্ধনী"</string>
<string name="desc_rparen" msgid="7920608385146151731">"ডান লঘুবন্ধনী"</string>
<string name="desc_fun_cos" msgid="3787913784504974731">"কোসাইন"</string>
<string name="desc_fun_ln" msgid="2505119732546227166">"স্বাভাবিক লগারিদম"</string>
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"মুছুন"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"বিপরীত ক্রিয়াকলাপ দেখান"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"বিপরীত ক্রিয়াকলাপ লুকান"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"কোনো সূত্র নেই"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"নম্বর এবং মৌলিক ক্রিয়াকলাপ"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"উন্নত ক্রিয়াকলাপ"</string>
<string name="error_nan" msgid="5674077944929888710">"একটি সংখ্যা নয়"</string>
<string name="error_syntax" msgid="4786987111228645602">"খারাপ এক্সপ্রেশন"</string>
<string name="error_aborted" msgid="3402238176316342537">"বাতিল করা হয়েছে"</string>
diff --git a/res/values-bs-rBA/strings.xml b/res/values-bs-rBA/strings.xml
index 7759882..4dcd00a 100644
--- a/res/values-bs-rBA/strings.xml
+++ b/res/values-bs-rBA/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"izbriši"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"prikaži inverzne funkcije"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"sakrij inverzne funkcije"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Nema formule"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Brojevi i osnovne operacije"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Napredne operacije"</string>
<string name="error_nan" msgid="5674077944929888710">"Nije broj"</string>
<string name="error_syntax" msgid="4786987111228645602">"Nepravilan izraz"</string>
<string name="error_aborted" msgid="3402238176316342537">"Prekinuto"</string>
diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml
index 1800849..557e57e 100644
--- a/res/values-ca/strings.xml
+++ b/res/values-ca/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"suprimeix"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"mostra les funcions inverses"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"amaga les funcions inverses"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"No hi ha cap fórmula"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Números i operacions bàsiques"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Operacions avançades"</string>
<string name="error_nan" msgid="5674077944929888710">"No és una xifra"</string>
<string name="error_syntax" msgid="4786987111228645602">"Expr. incorrecta"</string>
<string name="error_aborted" msgid="3402238176316342537">"Anul·lat"</string>
diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml
index 7c49941..a61fda4 100644
--- a/res/values-cs/strings.xml
+++ b/res/values-cs/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"smazat"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"zobrazit inverzní funkce"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"skrýt inverzní funkce"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Žádný vzorec"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Čísla a základní operace"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Pokročilé operace"</string>
<string name="error_nan" msgid="5674077944929888710">"Není číslo"</string>
<string name="error_syntax" msgid="4786987111228645602">"Chybný výraz"</string>
<string name="error_aborted" msgid="3402238176316342537">"Zrušeno"</string>
diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml
index bac1079..a75d929 100644
--- a/res/values-da/strings.xml
+++ b/res/values-da/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"slet"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"vis inversfunktioner"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"skjul inversfunktioner"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Ingen formel"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Tal og grundlæggende handlinger"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Avancerede handlinger"</string>
<string name="error_nan" msgid="5674077944929888710">"Ikke et tal"</string>
<string name="error_syntax" msgid="4786987111228645602">"Ugyldigt udtryk"</string>
<string name="error_aborted" msgid="3402238176316342537">"Afbrudt"</string>
diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml
index 7441e17..673c5cd 100644
--- a/res/values-de/strings.xml
+++ b/res/values-de/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"Löschen"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"Umkehrfunktionen anzeigen"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"Umkehrfunktionen ausblenden"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Keine Formel"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Zahlen und grundlegende Vorgänge"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Erweiterte Vorgänge"</string>
<string name="error_nan" msgid="5674077944929888710">"Keine Zahl"</string>
<string name="error_syntax" msgid="4786987111228645602">"Ausdrucksfehler"</string>
<string name="error_aborted" msgid="3402238176316342537">"Abgebrochen"</string>
diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml
index ea025ab..77903dc 100644
--- a/res/values-el/strings.xml
+++ b/res/values-el/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"διαγραφή"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"εμφάνιση αντίστροφων λειτουργιών"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"απόκρυψη αντίστροφων λειτουργιών"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Δεν υπάρχει τύπος"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Αριθμοί και βασικές λειτουργίες"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Προηγμένες λειτουργίες"</string>
<string name="error_nan" msgid="5674077944929888710">"Όχι αριθμός"</string>
<string name="error_syntax" msgid="4786987111228645602">"Λάθος έκφραση"</string>
<string name="error_aborted" msgid="3402238176316342537">"Ακυρώθηκε"</string>
diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml
index 15fd704..a93046c 100644
--- a/res/values-en-rAU/strings.xml
+++ b/res/values-en-rAU/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"Delete"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"show inverse functions"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"hide inverse functions"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"No formula"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Numbers and basic operations"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Advanced operations"</string>
<string name="error_nan" msgid="5674077944929888710">"Not a number"</string>
<string name="error_syntax" msgid="4786987111228645602">"Bad expression"</string>
<string name="error_aborted" msgid="3402238176316342537">"Aborted"</string>
diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml
index 15fd704..a93046c 100644
--- a/res/values-en-rGB/strings.xml
+++ b/res/values-en-rGB/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"Delete"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"show inverse functions"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"hide inverse functions"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"No formula"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Numbers and basic operations"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Advanced operations"</string>
<string name="error_nan" msgid="5674077944929888710">"Not a number"</string>
<string name="error_syntax" msgid="4786987111228645602">"Bad expression"</string>
<string name="error_aborted" msgid="3402238176316342537">"Aborted"</string>
diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml
index 15fd704..a93046c 100644
--- a/res/values-en-rIN/strings.xml
+++ b/res/values-en-rIN/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"Delete"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"show inverse functions"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"hide inverse functions"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"No formula"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Numbers and basic operations"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Advanced operations"</string>
<string name="error_nan" msgid="5674077944929888710">"Not a number"</string>
<string name="error_syntax" msgid="4786987111228645602">"Bad expression"</string>
<string name="error_aborted" msgid="3402238176316342537">"Aborted"</string>
diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml
index e13eb43..33a5f82 100644
--- a/res/values-es-rUS/strings.xml
+++ b/res/values-es-rUS/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"eliminar"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"mostrar funciones inversas"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"ocultar funciones inversas"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Sin fórmula"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Números y operaciones básicas"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Operaciones avanzadas"</string>
<string name="error_nan" msgid="5674077944929888710">"No es número."</string>
<string name="error_syntax" msgid="4786987111228645602">"Expresión incorrecta"</string>
<string name="error_aborted" msgid="3402238176316342537">"Interrumpida"</string>
diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml
index c8ba9d8..ad9713f 100644
--- a/res/values-es/strings.xml
+++ b/res/values-es/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"eliminar"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"muestra las funciones inversas"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"oculta las funciones inversas"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Sin fórmula"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Números y operaciones básicas"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Operaciones avanzadas"</string>
<string name="error_nan" msgid="5674077944929888710">"No es un número"</string>
<string name="error_syntax" msgid="4786987111228645602">"Expresión incorrecta"</string>
<string name="error_aborted" msgid="3402238176316342537">"Cancelado"</string>
diff --git a/res/values-et-rEE/strings.xml b/res/values-et-rEE/strings.xml
index 347d749..be62df1 100644
--- a/res/values-et-rEE/strings.xml
+++ b/res/values-et-rEE/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"kustutamine"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"pöördfunktsioonide kuvamine"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"pöördfunktsioonide peitmine"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Valem puudub"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Numbrid ja lihtsad toimingud"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Täpsemad toimingud"</string>
<string name="error_nan" msgid="5674077944929888710">"Pole number"</string>
<string name="error_syntax" msgid="4786987111228645602">"Vale avaldis"</string>
<string name="error_aborted" msgid="3402238176316342537">"Katkestatud"</string>
diff --git a/res/values-eu-rES/strings.xml b/res/values-eu-rES/strings.xml
index 0f9000e..f1696bc 100644
--- a/res/values-eu-rES/strings.xml
+++ b/res/values-eu-rES/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"ezabatu"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"erakutsi alderantzizko funtzioak"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"ezkutatu alderantzizko funtzioak"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Ez dago formularik"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Zenbakiak eta oinarrizko eragiketak"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Eragiketa aurreratuak"</string>
<string name="error_nan" msgid="5674077944929888710">"Ez da zenbakia"</string>
<string name="error_syntax" msgid="4786987111228645602">"Sintaxi-errorea"</string>
<string name="error_aborted" msgid="3402238176316342537">"Utzi egin da"</string>
diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml
index 2c3be75..ed4e20e 100644
--- a/res/values-fa/strings.xml
+++ b/res/values-fa/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"حذف"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"نمایش توابع معکوس"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"پنهان کردن توابع معکوس"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"فرمولی وجود ندارد"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"عملیات عددی و پایه"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"عملیات پیشرفته"</string>
<string name="error_nan" msgid="5674077944929888710">"پاسخ عددی ندارد"</string>
<string name="error_syntax" msgid="4786987111228645602">"عبارت نادرست"</string>
<string name="error_aborted" msgid="3402238176316342537">"لغو شد"</string>
diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml
index 78daa5a..f270ba8 100644
--- a/res/values-fi/strings.xml
+++ b/res/values-fi/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"poista"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"näytä käänteisfunktiot"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"piilota käänteisfunktiot"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Ei kaavaa"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Numerot ja peruslaskutoimitukset"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Lisätoiminnot"</string>
<string name="error_nan" msgid="5674077944929888710">"Ei numero"</string>
<string name="error_syntax" msgid="4786987111228645602">"Virhe ilmaisussa"</string>
<string name="error_aborted" msgid="3402238176316342537">"Keskeytettiin."</string>
diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml
index e349bb5..03583ad 100644
--- a/res/values-fr-rCA/strings.xml
+++ b/res/values-fr-rCA/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"supprimer"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"afficher les fonctions inverses"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"masquer les fonctions inverses"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Aucune formule"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Chiffres et opérations de base"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Opérations avancées"</string>
<string name="error_nan" msgid="5674077944929888710">"Pas un nombre"</string>
<string name="error_syntax" msgid="4786987111228645602">"Express. incorr."</string>
<string name="error_aborted" msgid="3402238176316342537">"Annulée"</string>
diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml
index a7f558e..d78451b 100644
--- a/res/values-fr/strings.xml
+++ b/res/values-fr/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"supprimer"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"afficher les fonctions inverses"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"masquer les fonctions inverses"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Aucune formule"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Nombres et opérations de base"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Opérations avancées"</string>
<string name="error_nan" msgid="5674077944929888710">"Pas un nombre"</string>
<string name="error_syntax" msgid="4786987111228645602">"Express. incorr."</string>
<string name="error_aborted" msgid="3402238176316342537">"Annulée"</string>
diff --git a/res/values-gl-rES/strings.xml b/res/values-gl-rES/strings.xml
index 3a5ed43..bbe3517 100644
--- a/res/values-gl-rES/strings.xml
+++ b/res/values-gl-rES/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"eliminar"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"mostrar funcións inversas"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"ocultar funcións inversas"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Sen fórmula"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Números e operacións básicas"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Operacións avanzadas"</string>
<string name="error_nan" msgid="5674077944929888710">"Non é número"</string>
<string name="error_syntax" msgid="4786987111228645602">"Expresión incorrecta"</string>
<string name="error_aborted" msgid="3402238176316342537">"Abortada"</string>
diff --git a/res/values-gu-rIN/strings.xml b/res/values-gu-rIN/strings.xml
index 487e6b6..8fd8d3b 100644
--- a/res/values-gu-rIN/strings.xml
+++ b/res/values-gu-rIN/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"કાઢી નાખો"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"વ્યુત્ક્રમ કાર્ય બતાવો"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"વ્યુત્ક્રમ કાર્ય છુપાવો"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"કોઇ ફોર્મૂલા નથી"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"નંબરો અને મૂળભૂત ઓપરેશન્સ"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"વિગતવાર ઓપરેશન્સ"</string>
<string name="error_nan" msgid="5674077944929888710">"કોઈ સંખ્યા નથી"</string>
<string name="error_syntax" msgid="4786987111228645602">"ખોટી પદાવલિ"</string>
<string name="error_aborted" msgid="3402238176316342537">"રદ કરેલ"</string>
diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml
index 6c23e92..cd1aaf3 100644
--- a/res/values-hi/strings.xml
+++ b/res/values-hi/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"हटाएं"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"प्रतिलोम फ़ंक्शन दिखाएं"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"प्रतिलोम फ़ंक्शन छिपाएं"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"कोई फ़ार्मूला नहीं"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"संख्याएं और मूल संचालन"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"उन्नत संचालन"</string>
<string name="error_nan" msgid="5674077944929888710">"संख्या नहीं है"</string>
<string name="error_syntax" msgid="4786987111228645602">"खराब व्यंजक"</string>
<string name="error_aborted" msgid="3402238176316342537">"निरस्त किया गया"</string>
diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml
index aa485a3..177dbde 100644
--- a/res/values-hr/strings.xml
+++ b/res/values-hr/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"izbriši"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"prikazuje inverzne funkcije"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"sakriva inverzne funkcije"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Nema formule"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Brojevi i osnovne operacije"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Napredne operacije"</string>
<string name="error_nan" msgid="5674077944929888710">"Nije broj"</string>
<string name="error_syntax" msgid="4786987111228645602">"Neispravan izraz"</string>
<string name="error_aborted" msgid="3402238176316342537">"Prekinuto"</string>
diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml
index 607a7f1..722d826 100644
--- a/res/values-hu/strings.xml
+++ b/res/values-hu/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"törlés"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"inverz függvények megjelenítése"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"inverz függvények elrejtése"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Nincs képlet"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Számok és alapműveletek"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Speciális műveletek"</string>
<string name="error_nan" msgid="5674077944929888710">"Nem egy szám"</string>
<string name="error_syntax" msgid="4786987111228645602">"Hibás kifejezés"</string>
<string name="error_aborted" msgid="3402238176316342537">"Megszakítva"</string>
diff --git a/res/values-hy-rAM/strings.xml b/res/values-hy-rAM/strings.xml
index 909c4aa..dfb51f4 100644
--- a/res/values-hy-rAM/strings.xml
+++ b/res/values-hy-rAM/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"ջնջել"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"ցուցադրել հակադարձ գործառույթները"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"թաքցնել հակադարձ գործառույթները"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Բանաձև չկա"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Թվեր և հիմնական գործողություններ"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Լրացուցիչ գործողություններ"</string>
<string name="error_nan" msgid="5674077944929888710">"Թիվ չէ"</string>
<string name="error_syntax" msgid="4786987111228645602">"Սխալ է"</string>
<string name="error_aborted" msgid="3402238176316342537">"Ընդհատված"</string>
diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml
index 036cf3d..c1008ad 100644
--- a/res/values-in/strings.xml
+++ b/res/values-in/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"hapus"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"tampilkan fungsi balikan"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"semunyikan fungsi balikan"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Tidak ada formula"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Angka dan operasi dasar"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Operasi lanjutan"</string>
<string name="error_nan" msgid="5674077944929888710">"Bukan angka"</string>
<string name="error_syntax" msgid="4786987111228645602">"Pernyataan salah"</string>
<string name="error_aborted" msgid="3402238176316342537">"Dibatalkan"</string>
diff --git a/res/values-is-rIS/strings.xml b/res/values-is-rIS/strings.xml
index 10978b7..89c8174 100644
--- a/res/values-is-rIS/strings.xml
+++ b/res/values-is-rIS/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"eyða"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"sýna andhverf föll"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"fela andhverf föll"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Engin formúla"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Tölur og einfaldar aðgerðir"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Ítarlegri aðgerðir"</string>
<string name="error_nan" msgid="5674077944929888710">"Ekki tala"</string>
<string name="error_syntax" msgid="4786987111228645602">"Röng segð"</string>
<string name="error_aborted" msgid="3402238176316342537">"Hætt við"</string>
diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml
index bf4d880..1d2e8e4 100644
--- a/res/values-it/strings.xml
+++ b/res/values-it/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"elimina"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"mostra funzioni inverse"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"nascondi funzioni inverse"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Nessuna formula"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Numeri e operazioni basilari"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Operazioni avanzate"</string>
<string name="error_nan" msgid="5674077944929888710">"Non è un numero"</string>
<string name="error_syntax" msgid="4786987111228645602">"Espressione errata"</string>
<string name="error_aborted" msgid="3402238176316342537">"Interrotta"</string>
diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml
index 6d24ffc..35d81af 100644
--- a/res/values-iw/strings.xml
+++ b/res/values-iw/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"מחק"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"הצג פונקציות אינברס"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"הסתר פונקציות אינברס"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"אין נוסחה"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"מספרים ופעולות בסיסיות"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"פעולות מתקדמות"</string>
<string name="error_nan" msgid="5674077944929888710">"אינו מספר"</string>
<string name="error_syntax" msgid="4786987111228645602">"ביטוי שגוי"</string>
<string name="error_aborted" msgid="3402238176316342537">"בוטל"</string>
diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml
index 45a62c1..26d017f 100644
--- a/res/values-ja/strings.xml
+++ b/res/values-ja/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"削除"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"逆関数を表示する"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"逆関数を表示しない"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"数式がありません"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"数字と基本的な演算"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"高度な演算"</string>
<string name="error_nan" msgid="5674077944929888710">"数値以外です"</string>
<string name="error_syntax" msgid="4786987111228645602">"式が無効です"</string>
<string name="error_aborted" msgid="3402238176316342537">"中止しました"</string>
diff --git a/res/values-ka-rGE/strings.xml b/res/values-ka-rGE/strings.xml
index ca9e157..aec5fb1 100644
--- a/res/values-ka-rGE/strings.xml
+++ b/res/values-ka-rGE/strings.xml
@@ -57,8 +57,11 @@
<string name="desc_del" msgid="7879515781929311432">"წაშლა"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"შებრუნებული ფუნქციების ჩვენება"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"შებრუნებული ფუნქციების დამალვა"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"ფორმულა არ არის"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"რიცხვები და ძირითადი ოპერაციები"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"დამატებითი ოპერაციები"</string>
<string name="error_nan" msgid="5674077944929888710">"არ არის რიცხვი"</string>
- <string name="error_syntax" msgid="4786987111228645602">"არასწორი გამოსახულება"</string>
+ <string name="error_syntax" msgid="4786987111228645602">"არასწორი ფრაზა"</string>
<string name="error_aborted" msgid="3402238176316342537">"შეწყვეტილია"</string>
<string name="error_overflow" msgid="7800547394563434764">"განუსაზღვრელი?"</string>
<string name="error_zero_divide" msgid="458040988686661801">"0-ზე გაყოფა შეუძლებელია"</string>
diff --git a/res/values-kk-rKZ/strings.xml b/res/values-kk-rKZ/strings.xml
index f7d0662..5d2185e 100644
--- a/res/values-kk-rKZ/strings.xml
+++ b/res/values-kk-rKZ/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"жою"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"кері функцияларды көрсетеді"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"кері функцияларды жасырады"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Формуласы жоқ"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Сандар және негізгі операциялар"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Күрделі операциялар"</string>
<string name="error_nan" msgid="5674077944929888710">"Сан емес"</string>
<string name="error_syntax" msgid="4786987111228645602">"Қате өрнек"</string>
<string name="error_aborted" msgid="3402238176316342537">"Тоқтатылды"</string>
diff --git a/res/values-km-rKH/strings.xml b/res/values-km-rKH/strings.xml
index 4b6ec9e..5b0b725 100644
--- a/res/values-km-rKH/strings.xml
+++ b/res/values-km-rKH/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"លុប"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"បង្ហាញមុខងារច្រាស"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"លាក់មុខងារច្រាស"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"គ្មានរូបមន្ត"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"លេខ និងប្រតិបត្តិការមូលដ្ឋាន"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"ប្រតិបត្តិការកម្រិតខ្ពស់"</string>
<string name="error_nan" msgid="5674077944929888710">"មិនមែនជាលេខទេ"</string>
<string name="error_syntax" msgid="4786987111228645602">"កន្សោមមិនត្រឹមត្រូវ"</string>
<string name="error_aborted" msgid="3402238176316342537">"បោះបង់"</string>
diff --git a/res/values-kn-rIN/strings.xml b/res/values-kn-rIN/strings.xml
index c8b9aab..75debd8 100644
--- a/res/values-kn-rIN/strings.xml
+++ b/res/values-kn-rIN/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"ಅಳಿಸು"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"ವಿಲೋಮ ಫಂಕ್ಷನ್ಗಳನ್ನು ತೋರಿಸು"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"ವಿಲೋಮ ಫಂಕ್ಷನ್ಗಳನ್ನು ಮರೆಮಾಡು"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"ಯಾವುದೇ ಫಾರ್ಮುಲಾ ಇಲ್ಲ"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"ಸಂಖ್ಯೆಗಳು ಮತ್ತು ಮೂಲ ಕಾರ್ಯಾಚರಣೆಗಳು"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"ಸುಧಾರಿತ ಕಾರ್ಯಾಚರಣೆಗಳು"</string>
<string name="error_nan" msgid="5674077944929888710">"ಸಂಖ್ಯೆಯಲ್ಲ"</string>
<string name="error_syntax" msgid="4786987111228645602">"ತಪ್ಪು ಪದ"</string>
<string name="error_aborted" msgid="3402238176316342537">"ರದ್ದು ಮಾಡಲಾಗಿದೆ"</string>
@@ -69,7 +72,7 @@
<string name="dismiss" msgid="7872443888515066216">"ವಜಾಗೊಳಿಸು"</string>
<string name="exact" msgid="2597237880041789948">"(ನಿಖರ)"</string>
<string name="approximate" msgid="7117143366610670836">"(±1 ಕೊನೆಯ ಅಂಕಿಯಲ್ಲಿ)"</string>
- <string name="menu_leading" msgid="2338520833272667740">"ಮೊದಲ ಅಂಕೆ ಜೊತೆ ಉತ್ತರಿಸಿ"</string>
- <string name="menu_fraction" msgid="1247477377840252234">"ಭಾಗದಂತೆ ಉತ್ತರಿಸಿ"</string>
+ <string name="menu_leading" msgid="2338520833272667740">"ಮೊದಲ ಅಂಕೆ ಜೊತೆ ಉತ್ತರಿಸು"</string>
+ <string name="menu_fraction" msgid="1247477377840252234">"ಭಾಗದಂತೆ ಉತ್ತರಿಸು"</string>
<string name="menu_licenses" msgid="1890541368064108592">"ಮುಕ್ತ ಮೂಲ ಪರವಾನಗಿಗಳು"</string>
</resources>
diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml
index e968fb8..15eeb0a 100644
--- a/res/values-ko/strings.xml
+++ b/res/values-ko/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"삭제"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"역함수 표시"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"역함수 숨기기"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"수식 없음"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"숫자 및 기본 작업"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"고급 작업"</string>
<string name="error_nan" msgid="5674077944929888710">"숫자가 아님"</string>
<string name="error_syntax" msgid="4786987111228645602">"잘못된 수식"</string>
<string name="error_aborted" msgid="3402238176316342537">"중단됨"</string>
diff --git a/res/values-ky-rKG/strings.xml b/res/values-ky-rKG/strings.xml
index fdd6823..b6112c7 100644
--- a/res/values-ky-rKG/strings.xml
+++ b/res/values-ky-rKG/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"жок кылуу"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"карама-каршы функцияларды көрсөтүү"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"карама-каршы функцияларды жашыруу"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Формула жок"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Сандар жана негизги операциялар"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Өркүндөтүлгөн операциялар"</string>
<string name="error_nan" msgid="5674077944929888710">"Сан эмес"</string>
<string name="error_syntax" msgid="4786987111228645602">"Начар туюнтма"</string>
<string name="error_aborted" msgid="3402238176316342537">"Үзгүлтккө учурды"</string>
diff --git a/res/values-lo-rLA/strings.xml b/res/values-lo-rLA/strings.xml
index 55ff0a5..eb8b553 100644
--- a/res/values-lo-rLA/strings.xml
+++ b/res/values-lo-rLA/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"ລຶບ"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"ສະແດງຟັງຄ໌ຊັນທີ່ຈຸ່ມລົງ"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"ເຊື່ອງຟັງຄ໌ຊັນທີ່ຈຸ່ມລົງ"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"ບໍ່ມີສູດຄຳນວນ"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"ຄຳສັ່ງພື້ນຖານ ແລະ ຕົວເລກ"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"ຄຳສັ່ງຂັ້ນສູງ"</string>
<string name="error_nan" msgid="5674077944929888710">"ບໍ່ແມ່ນໂຕເລກ"</string>
<string name="error_syntax" msgid="4786987111228645602">"ສຳນວນບໍ່ດີ"</string>
<string name="error_aborted" msgid="3402238176316342537">"ລົບລ້າງ"</string>
diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml
index 2faf0a0..1d87167 100644
--- a/res/values-lt/strings.xml
+++ b/res/values-lt/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"ištrinti"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"rodyti atvirkštines funkcijas"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"slėpti atvirkštines funkcijas"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Nėra formulės"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Skaičiai ir pagrindiniai veiksmai"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Išplėstiniai veiksmai"</string>
<string name="error_nan" msgid="5674077944929888710">"Ne skaičius"</string>
<string name="error_syntax" msgid="4786987111228645602">"Blogas reiškinys"</string>
<string name="error_aborted" msgid="3402238176316342537">"Nutraukta"</string>
diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml
index 91b447e..1ac91da 100644
--- a/res/values-lv/strings.xml
+++ b/res/values-lv/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"dzēst"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"rādīt inversās funkcijas"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"slēpt inversās funkcijas"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Nav formulas"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Skaitļi un pamatdarbības"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Papildu darbības"</string>
<string name="error_nan" msgid="5674077944929888710">"Nav skaitlis"</string>
<string name="error_syntax" msgid="4786987111228645602">"Kļūd. izteiksme"</string>
<string name="error_aborted" msgid="3402238176316342537">"Pārtraukts"</string>
diff --git a/res/values-mk-rMK/strings.xml b/res/values-mk-rMK/strings.xml
index f7af89f..7ad6da7 100644
--- a/res/values-mk-rMK/strings.xml
+++ b/res/values-mk-rMK/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"избриши"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"прикажи инверзни функции"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"сокриј инверзни функции"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Нема формула"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Броеви и основни операции"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Напредни операции"</string>
<string name="error_nan" msgid="5674077944929888710">"Не е број"</string>
<string name="error_syntax" msgid="4786987111228645602">"Погрешен израз"</string>
<string name="error_aborted" msgid="3402238176316342537">"Прекинато"</string>
diff --git a/res/values-ml-rIN/strings.xml b/res/values-ml-rIN/strings.xml
index e21e42b..45078ff 100644
--- a/res/values-ml-rIN/strings.xml
+++ b/res/values-ml-rIN/strings.xml
@@ -17,7 +17,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="1756503303878374382">"കാൽക്കുലേറ്റർ"</string>
+ <string name="app_name" msgid="1756503303878374382">"കാൽക്കുലേ."</string>
<string name="dec_point" msgid="8920102493070918054">"."</string>
<string name="mode_deg" msgid="1146123354434332479">"ഡിഗ്രി"</string>
<string name="mode_rad" msgid="1434228830085760996">"rad"</string>
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"ഇല്ലാതാക്കുക"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"വിപരീത പ്രവർത്തനങ്ങൾ കാണിക്കുക"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"വിപരീത പ്രവർത്തനങ്ങൾ മറയ്ക്കുക"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"ഫോർമുലയില്ല"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"നമ്പറുകളും അടിസ്ഥാന പ്രവർത്തനങ്ങളും"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"വിപുലമായ പ്രവർത്തനങ്ങൾ"</string>
<string name="error_nan" msgid="5674077944929888710">"സംഖ്യയല്ല"</string>
<string name="error_syntax" msgid="4786987111228645602">"മോശം എക്സ്പ്രഷൻ"</string>
<string name="error_aborted" msgid="3402238176316342537">"പരാജയപ്പെട്ടു"</string>
diff --git a/res/values-mn-rMN/strings.xml b/res/values-mn-rMN/strings.xml
index 536c5af..cb69f6e 100644
--- a/res/values-mn-rMN/strings.xml
+++ b/res/values-mn-rMN/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"устгах"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"урвуу фүнкцыг харуулах"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"урвуу функцийг нуух"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Томъёо байхгүй"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Тоо, үндсэн үйл ажиллагаа"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Сайжруулсан үйл ажиллагаа"</string>
<string name="error_nan" msgid="5674077944929888710">"Тоо биш байна"</string>
<string name="error_syntax" msgid="4786987111228645602">"Буруу илэрхийлэл"</string>
<string name="error_aborted" msgid="3402238176316342537">"Зогсоосон"</string>
diff --git a/res/values-mr-rIN/strings.xml b/res/values-mr-rIN/strings.xml
index e3f10b8..810baec 100644
--- a/res/values-mr-rIN/strings.xml
+++ b/res/values-mr-rIN/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"हटवा"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"व्यस्त कार्ये दर्शवा"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"व्यस्त कार्ये लपवा"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"सूत्र नाही"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"संख्या आणि मूलभूत ऑपरेशन"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"प्रगत ऑपरेशन"</string>
<string name="error_nan" msgid="5674077944929888710">"संख्या नाही"</string>
<string name="error_syntax" msgid="4786987111228645602">"खराब पदावली"</string>
<string name="error_aborted" msgid="3402238176316342537">"विलोपन केले"</string>
diff --git a/res/values-ms-rMY/strings.xml b/res/values-ms-rMY/strings.xml
index bec9757..4674f25 100644
--- a/res/values-ms-rMY/strings.xml
+++ b/res/values-ms-rMY/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"padam"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"tunjukkan fungsi songsang"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"sembunyikan fungsi songsang"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Tiada formula"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Nombor dan pengendalian asas"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Pengendalian lanjutan"</string>
<string name="error_nan" msgid="5674077944929888710">"Bukan nombor"</string>
<string name="error_syntax" msgid="4786987111228645602">"Ungkapan salah"</string>
<string name="error_aborted" msgid="3402238176316342537">"Dihenti paksa"</string>
diff --git a/res/values-my-rMM/strings.xml b/res/values-my-rMM/strings.xml
index fb45bb8..2b35837 100644
--- a/res/values-my-rMM/strings.xml
+++ b/res/values-my-rMM/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"ဖျက်ရန်"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"ပြောင်းပြန်ဆောင်ရွက်ချက်များ ပြပါ"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"ပြောင်းပြန်ဆောင်ရွက်ချက်များ ဖျောက်ထားရန်"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"ဖော်မြူလာမရှိပါ"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"နံပါတ်နှင့် အခြေခံလုပ်ငန်းများ"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"အဆင့်မြင့် လုပ်ငန်းများ"</string>
<string name="error_nan" msgid="5674077944929888710">"ဂဏန်း မဟုတ်"</string>
<string name="error_syntax" msgid="4786987111228645602">"ဖော်ပြချက်အမှား"</string>
<string name="error_aborted" msgid="3402238176316342537">"ဖျက်လိုက်ပြီ"</string>
diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml
index fab30cd..bd2dc79 100644
--- a/res/values-nb/strings.xml
+++ b/res/values-nb/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"slett"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"vis omvendte funksjoner"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"skjul omvendte funksjoner"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Ingen formel"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Tall og grunnleggende operasjoner"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Avanserte operasjoner"</string>
<string name="error_nan" msgid="5674077944929888710">"Ikke et tall"</string>
<string name="error_syntax" msgid="4786987111228645602">"Ugyldig uttrykk"</string>
<string name="error_aborted" msgid="3402238176316342537">"Avbrutt"</string>
diff --git a/res/values-ne-rNP/strings.xml b/res/values-ne-rNP/strings.xml
index fc96071..6528f13 100644
--- a/res/values-ne-rNP/strings.xml
+++ b/res/values-ne-rNP/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"मेटाउनुहोस्"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"व्युत्क्रम प्रकार्यहरु देखाउनुहोस्"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"व्युत्क्रम प्रकार्यहरु लुकाउनुहोस्"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"कुनै सूत्र छैन"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"संख्या र आधारभूत कारबाहीहरू"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"उन्नत कारबाहीहरू"</string>
<string name="error_nan" msgid="5674077944929888710">"एक नम्बर होइन"</string>
<string name="error_syntax" msgid="4786987111228645602">"खराब अभिव्यक्ति"</string>
<string name="error_aborted" msgid="3402238176316342537">"रद्द गरियो"</string>
diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml
index 183d4d6..aeac46f 100644
--- a/res/values-nl/strings.xml
+++ b/res/values-nl/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"verwijderen"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"inverse functies weergeven"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"inverse functies verbergen"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Geen formule"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Cijfers en basisbewerkingen"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Geavanceerde bewerkingen"</string>
<string name="error_nan" msgid="5674077944929888710">"Is geen getal"</string>
<string name="error_syntax" msgid="4786987111228645602">"Ongeldige invoer"</string>
<string name="error_aborted" msgid="3402238176316342537">"Afgebroken"</string>
diff --git a/res/values-pa-rIN/strings.xml b/res/values-pa-rIN/strings.xml
index 30f249c..22bf90b 100644
--- a/res/values-pa-rIN/strings.xml
+++ b/res/values-pa-rIN/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"ਮਿਟਾਓ"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"ਉਲਟੇ ਫੰਕਸ਼ਨ ਦਿਖਾਓ"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"ਉਲਟੇ ਫੰਕਸ਼ਨ ਲੁਕਾਓ"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"ਕੋਈ ਫ਼ਾਰਮੂਲਾ ਨਹੀਂ"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"ਨੰਬਰ ਅਤੇ ਮੂਲ ਆਪਰੇਸ਼ਨ"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"ਉੱਨਤ ਆਪਰੇਸ਼ਨ"</string>
<string name="error_nan" msgid="5674077944929888710">"ਇੱਕ ਸੰਖਿਆ ਨਹੀਂ"</string>
<string name="error_syntax" msgid="4786987111228645602">"ਗਲਤ ਸਮੀਕਰਨ"</string>
<string name="error_aborted" msgid="3402238176316342537">"ਬਰਖ਼ਾਸਤ ਕੀਤਾ ਗਿਆ"</string>
diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml
index 2e372a4..4b1e736 100644
--- a/res/values-pl/strings.xml
+++ b/res/values-pl/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"usuń"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"pokaż funkcje odwrotne"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"ukryj funkcje odwrotne"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Brak formuły"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Liczby i operacje podstawowe"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Operacje zaawansowane"</string>
<string name="error_nan" msgid="5674077944929888710">"To nie liczba"</string>
<string name="error_syntax" msgid="4786987111228645602">"Błędne wyrażenie"</string>
<string name="error_aborted" msgid="3402238176316342537">"Przerwano"</string>
diff --git a/res/values-pt-rBR/strings.xml b/res/values-pt-rBR/strings.xml
deleted file mode 100644
index 0a04f55..0000000
--- a/res/values-pt-rBR/strings.xml
+++ /dev/null
@@ -1,75 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright (C) 2015 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-
-<resources xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
- <string name="app_name" msgid="1756503303878374382">"Calculadora"</string>
- <string name="dec_point" msgid="8920102493070918054">","</string>
- <string name="mode_deg" msgid="1146123354434332479">"graus"</string>
- <string name="mode_rad" msgid="1434228830085760996">"rad"</string>
- <string name="clr" msgid="6730945431543327337">"limp"</string>
- <string name="cleared" msgid="3952521190281880880">"apagados"</string>
- <string name="del" msgid="5878069000864178910">"del"</string>
- <string name="desc_const_e" msgid="1889187337970539507">"Número de Euler"</string>
- <string name="desc_const_pi" msgid="5430918714009441655">"pi"</string>
- <string name="desc_dec_point" msgid="5725254504360445023">"ponto"</string>
- <string name="desc_lparen" msgid="8688758170211924916">"parêntese esquerdo"</string>
- <string name="desc_rparen" msgid="7920608385146151731">"parêntese direito"</string>
- <string name="desc_fun_cos" msgid="3787913784504974731">"cosseno"</string>
- <string name="desc_fun_ln" msgid="2505119732546227166">"logaritmo natural"</string>
- <string name="desc_fun_log" msgid="4202230639814060062">"logaritmo"</string>
- <string name="desc_fun_sin" msgid="7425661271929838876">"seno"</string>
- <string name="desc_fun_tan" msgid="3545496562069310036">"tangente"</string>
- <string name="desc_fun_arccos" msgid="7583077865497939123">"cosseno inverso"</string>
- <string name="desc_fun_arcsin" msgid="8592309261123302915">"seno inverso"</string>
- <string name="desc_fun_arctan" msgid="6891388393360447455">"tangente inversa"</string>
- <string name="desc_fun_10pow" msgid="3972646467174520812">"dez elevado a"</string>
- <string name="desc_fun_exp" msgid="657106837530588390">"função exponencial"</string>
- <string name="desc_op_sqr" msgid="693577095029219627">"ao quadrado"</string>
- <string name="desc_op_add" msgid="5539599782598050813">"mais"</string>
- <string name="desc_op_div" msgid="3032399266115257498">"dividir"</string>
- <string name="desc_op_fact" msgid="6142780103294179702">"fatorial"</string>
- <string name="desc_op_mul" msgid="6882122010553947015">"multiplicar"</string>
- <string name="desc_op_pct" msgid="2466179646911499434">"porcentagem"</string>
- <string name="desc_op_pow" msgid="6010144442344099030">"potência"</string>
- <string name="desc_op_sqrt" msgid="6882241473601721767">"raiz quadrada"</string>
- <string name="desc_op_sub" msgid="2744940875059679129">"menos"</string>
- <string name="desc_mode_deg" msgid="4129831241246511710">"modo grau"</string>
- <string name="desc_mode_rad" msgid="730135521908627673">"modo radiano"</string>
- <string name="desc_switch_deg" msgid="6675211986921592374">"alterar para graus"</string>
- <string name="desc_switch_rad" msgid="2845875847488919914">"alterar para radianos"</string>
- <string name="desc_eq" msgid="3349320880874699285">"igual"</string>
- <string name="desc_clr" msgid="737502124268890797">"limpar"</string>
- <string name="desc_del" msgid="7879515781929311432">"excluir"</string>
- <string name="desc_inv_off" msgid="3672218250519616068">"mostrar funções inversas"</string>
- <string name="desc_inv_on" msgid="2515675590767677178">"ocultar funções inversas"</string>
- <string name="error_nan" msgid="5674077944929888710">"Não é número"</string>
- <string name="error_syntax" msgid="4786987111228645602">"Expressão ruim"</string>
- <string name="error_aborted" msgid="3402238176316342537">"Cancelada"</string>
- <string name="error_overflow" msgid="7800547394563434764">"Infinito?"</string>
- <string name="error_zero_divide" msgid="458040988686661801">"Impos. dividir por 0"</string>
- <string name="text_copied_toast" msgid="2104264466812485425">"Texto copiado"</string>
- <string name="cancelled" msgid="1618889609742874632">"Cálculo cancelado"</string>
- <string name="timeout" msgid="802690170415591535">"Tempo limite atingido. O valor pode ser infinito ou indefinido."</string>
- <string name="ok_remove_timeout" msgid="8344529153919268011">"Usar tempos limite mais longos"</string>
- <string name="dismiss" msgid="7872443888515066216">"Dispensar"</string>
- <string name="exact" msgid="2597237880041789948">"(exato)"</string>
- <string name="approximate" msgid="7117143366610670836">"(±1 no último dígito)"</string>
- <string name="menu_leading" msgid="2338520833272667740">"Resposta c/ dígitos iniciais"</string>
- <string name="menu_fraction" msgid="1247477377840252234">"Responder como fração"</string>
- <string name="menu_licenses" msgid="1890541368064108592">"Lic. cód. aberto"</string>
-</resources>
diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml
index 85fb586..36930b2 100644
--- a/res/values-pt-rPT/strings.xml
+++ b/res/values-pt-rPT/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"eliminar"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"mostrar funções inversas"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"ocultar funções inversas"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Sem fórmula"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Números e operações básicas"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Operações avançadas"</string>
<string name="error_nan" msgid="5674077944929888710">"Não é um núm."</string>
<string name="error_syntax" msgid="4786987111228645602">"Expr. incorreta"</string>
<string name="error_aborted" msgid="3402238176316342537">"Interrompida"</string>
diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml
index 0a04f55..35dbf09 100644
--- a/res/values-pt/strings.xml
+++ b/res/values-pt/strings.xml
@@ -18,7 +18,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
<string name="app_name" msgid="1756503303878374382">"Calculadora"</string>
- <string name="dec_point" msgid="8920102493070918054">","</string>
+ <string name="dec_point" msgid="8920102493070918054">"."</string>
<string name="mode_deg" msgid="1146123354434332479">"graus"</string>
<string name="mode_rad" msgid="1434228830085760996">"rad"</string>
<string name="clr" msgid="6730945431543327337">"limp"</string>
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"excluir"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"mostrar funções inversas"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"ocultar funções inversas"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Nenhuma fórmula"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Números e operações básicas"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Operações avançadas"</string>
<string name="error_nan" msgid="5674077944929888710">"Não é número"</string>
<string name="error_syntax" msgid="4786987111228645602">"Expressão ruim"</string>
<string name="error_aborted" msgid="3402238176316342537">"Cancelada"</string>
diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml
index 9e3027b..1bcd940 100644
--- a/res/values-ro/strings.xml
+++ b/res/values-ro/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"șterge"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"afișează inversele funcțiilor"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"ascunde inversele funcțiilor"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Nicio formulă"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Numere și operații de bază"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Operații avansate"</string>
<string name="error_nan" msgid="5674077944929888710">"Nu este număr"</string>
<string name="error_syntax" msgid="4786987111228645602">"Expresie greșită"</string>
<string name="error_aborted" msgid="3402238176316342537">"Anulat"</string>
diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml
index 8116280..f7ad547 100644
--- a/res/values-ru/strings.xml
+++ b/res/values-ru/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"удалить"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"показать обратные функции"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"скрыть обратные функции"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Нет формулы"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Числа и основные операции"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Сложные операции"</string>
<string name="error_nan" msgid="5674077944929888710">"Не число"</string>
<string name="error_syntax" msgid="4786987111228645602">"Неправ. выраж-е"</string>
<string name="error_aborted" msgid="3402238176316342537">"Отменено"</string>
diff --git a/res/values-si-rLK/strings.xml b/res/values-si-rLK/strings.xml
index 081c007..3e5843b 100644
--- a/res/values-si-rLK/strings.xml
+++ b/res/values-si-rLK/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"මකන්න"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"ප්රතිලෝම ශ්රිත පෙන්වන්න"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"ප්රතිලෝම ශ්රිත සඟවන්න"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"සූත්රයක් නැත"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"අංක සහ මූලික මෙහෙයුම්"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"උසස් මෙහෙයුම්"</string>
<string name="error_nan" msgid="5674077944929888710">"අංකයක් නොවේ"</string>
<string name="error_syntax" msgid="4786987111228645602">"වැරදි ප්රකාශනයක්"</string>
<string name="error_aborted" msgid="3402238176316342537">"නතර කෙරිණි"</string>
diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml
index 4b56384..8d65412 100644
--- a/res/values-sk/strings.xml
+++ b/res/values-sk/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"odstrániť"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"zobraziť inverzné funkcie"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"skryť inverzné funkcie"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Žiadny vzorec"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Čísla a základné operácie"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Pokročilé operácie"</string>
<string name="error_nan" msgid="5674077944929888710">"Nie je číslo"</string>
<string name="error_syntax" msgid="4786987111228645602">"Chybný výraz"</string>
<string name="error_aborted" msgid="3402238176316342537">"Prerušené"</string>
diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml
index 187dd9a..2741715 100644
--- a/res/values-sl/strings.xml
+++ b/res/values-sl/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"izbriši"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"prikaz inverznih funkcij"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"skritje inverznih funkcij"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Ni formule"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Številke in osnovni postopki"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Napredni postopki"</string>
<string name="error_nan" msgid="5674077944929888710">"Ni število"</string>
<string name="error_syntax" msgid="4786987111228645602">"Napačen izraz"</string>
<string name="error_aborted" msgid="3402238176316342537">"Preklicano"</string>
diff --git a/res/values-sq-rAL/strings.xml b/res/values-sq-rAL/strings.xml
index 76833ee..2fac45d 100644
--- a/res/values-sq-rAL/strings.xml
+++ b/res/values-sq-rAL/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"fshi"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"shfaq funksionet e kundërta"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"fshih funksionet e kundërta"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Nuk ka formulë"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Numrat dhe veprimet bazë"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Veprimet e përparuara"</string>
<string name="error_nan" msgid="5674077944929888710">"S\'është numër"</string>
<string name="error_syntax" msgid="4786987111228645602">"Shprehje e pasaktë"</string>
<string name="error_aborted" msgid="3402238176316342537">"U ndërpre"</string>
diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml
index 15fe0aa..f05ae7b 100644
--- a/res/values-sr/strings.xml
+++ b/res/values-sr/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"избриши"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"прикажи инверзне функције"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"сакриј инверзне функције"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Нема формуле"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Бројеви и основне операције"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Напредне операције"</string>
<string name="error_nan" msgid="5674077944929888710">"Није број"</string>
<string name="error_syntax" msgid="4786987111228645602">"Неисправан израз"</string>
<string name="error_aborted" msgid="3402238176316342537">"Отказано је"</string>
diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml
index d4c35fe..7d0369e 100644
--- a/res/values-sv/strings.xml
+++ b/res/values-sv/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"radera"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"visa inverterade funktioner"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"dölj inverterade funktioner"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Ingen formel"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Siffror och grundläggande åtgärder"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Avancerade åtgärder"</string>
<string name="error_nan" msgid="5674077944929888710">"Inte ett tal"</string>
<string name="error_syntax" msgid="4786987111228645602">"Felaktigt uttryck"</string>
<string name="error_aborted" msgid="3402238176316342537">"Avbruten"</string>
diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml
index 2225162..c624652 100644
--- a/res/values-sw/strings.xml
+++ b/res/values-sw/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"futa"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"Onyesha chaguo za kukokotoa za kinyume"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"ficha chaguo za kukokotoa za kinyume"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Hakuna fomula"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Nambari na utendaji wa msingi"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Utendaji wa kina"</string>
<string name="error_nan" msgid="5674077944929888710">"Si nambari"</string>
<string name="error_syntax" msgid="4786987111228645602">"Hesabu yenye hitilafu"</string>
<string name="error_aborted" msgid="3402238176316342537">"Imeghairiwa"</string>
diff --git a/res/values-sw400dp-land/styles.xml b/res/values-sw400dp-land/styles.xml
deleted file mode 100644
index e37e8a0..0000000
--- a/res/values-sw400dp-land/styles.xml
+++ /dev/null
@@ -1,61 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-
-<!-- Styles for landscape phone (e.g. Nexus 4/5). -->
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
-
- <style name="DisplayTextStyle.Formula">
- <item name="android:paddingTop">2dip</item>
- <item name="android:paddingBottom">10dip</item>
- <item name="android:paddingStart">36dip</item>
- <item name="android:paddingEnd">36dip</item>
- <item name="android:textSize">32sp</item>
- </style>
-
- <style name="DisplayTextStyle.Result">
- <item name="android:paddingTop">12dip</item>
- <item name="android:paddingBottom">18dip</item>
- <item name="android:paddingStart">36dip</item>
- <item name="android:paddingEnd">36dip</item>
- <item name="android:textSize">32sp</item>
- </style>
-
- <style name="PadButtonStyle.Advanced">
- <item name="android:background">@drawable/pad_button_advanced_background</item>
- <item name="android:textColor">@color/pad_button_advanced_text_color</item>
- <item name="android:textSize">17sp</item>
- </style>
-
- <style name="PadButtonStyle.Advanced.Text">
- <item name="android:textAllCaps">true</item>
- <item name="android:textSize">15sp</item>
- </style>
-
- <style name="PadButtonStyle.Numeric">
- <item name="android:textSize">27sp</item>
- </style>
-
- <style name="PadButtonStyle.Operator">
- <item name="android:textSize">24sp</item>
- </style>
-
- <style name="PadButtonStyle.Operator.Text">
- <item name="android:textAllCaps">true</item>
- <item name="android:textSize">15sp</item>
- </style>
-
-</resources>
diff --git a/res/values-sw400dp-port/styles.xml b/res/values-sw400dp-port/styles.xml
deleted file mode 100644
index c715d52..0000000
--- a/res/values-sw400dp-port/styles.xml
+++ /dev/null
@@ -1,63 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-
-<!-- Styles for portrait phone (e.g. Nexus 4/5). -->
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
-
- <style name="DisplayTextStyle.Formula">
- <item name="android:paddingTop">16dip</item>
- <item name="android:paddingBottom">28dip</item>
- <item name="android:paddingStart">16dip</item>
- <item name="android:paddingEnd">16dip</item>
- <item name="minTextSize">42sp</item>
- <item name="maxTextSize">74sp</item>
- <item name="stepTextSize">8sp</item>
- </style>
-
- <style name="DisplayTextStyle.Result">
- <item name="android:paddingTop">16dip</item>
- <item name="android:paddingBottom">42dip</item>
- <item name="android:paddingStart">16dip</item>
- <item name="android:paddingEnd">16dip</item>
- <item name="android:textSize">42sp</item>
- </style>
-
- <style name="PadButtonStyle.Advanced">
- <item name="android:background">@drawable/pad_button_advanced_background</item>
- <item name="android:textColor">@color/pad_button_advanced_text_color</item>
- <item name="android:textSize">23sp</item>
- </style>
-
- <style name="PadButtonStyle.Advanced.Text">
- <item name="android:textAllCaps">true</item>
- <item name="android:textSize">20sp</item>
- </style>
-
- <style name="PadButtonStyle.Numeric">
- <item name="android:textSize">36sp</item>
- </style>
-
- <style name="PadButtonStyle.Operator">
- <item name="android:textSize">27sp</item>
- </style>
-
- <style name="PadButtonStyle.Operator.Text">
- <item name="android:textAllCaps">true</item>
- <item name="android:textSize">20sp</item>
- </style>
-
-</resources>
diff --git a/res/values-sw600dp-land/styles.xml b/res/values-sw600dp-land/styles.xml
deleted file mode 100644
index d25fe6b..0000000
--- a/res/values-sw600dp-land/styles.xml
+++ /dev/null
@@ -1,92 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-
-<!-- Styles for landscape 600dip-wide tablet (e.g. Nexus 7). -->
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
-
- <style name="DisplayTextStyle.Formula">
- <item name="android:paddingTop">4dip</item>
- <item name="android:paddingBottom">16dip</item>
- <item name="android:paddingStart">44dip</item>
- <item name="android:paddingEnd">44dip</item>
- <item name="android:textSize">42sp</item>
- </style>
-
- <style name="DisplayTextStyle.Result">
- <item name="android:paddingTop">18dip</item>
- <item name="android:paddingBottom">30dip</item>
- <item name="android:paddingStart">44dip</item>
- <item name="android:paddingEnd">44dip</item>
- <item name="android:textSize">42sp</item>
- </style>
-
- <style name="PadButtonStyle.Advanced">
- <item name="android:background">@drawable/pad_button_advanced_background</item>
- <item name="android:textColor">@color/pad_button_advanced_text_color</item>
- <item name="android:textSize">27sp</item>
- </style>
-
- <style name="PadButtonStyle.Advanced.Text">
- <item name="android:textAllCaps">true</item>
- <item name="android:textSize">24sp</item>
- </style>
-
- <style name="PadButtonStyle.Numeric">
- <item name="android:textSize">36sp</item>
- </style>
-
- <style name="PadButtonStyle.Numeric.Equals">
- <item name="android:visibility">gone</item>
- </style>
-
- <style name="PadButtonStyle.Operator">
- <item name="android:textSize">36sp</item>
- </style>
-
- <style name="PadButtonStyle.Operator.Text">
- <item name="android:textAllCaps">true</item>
- <item name="android:textSize">24sp</item>
- </style>
-
- <style name="PadLayoutStyle.Advanced">
- <item name="android:layout_width">0dip</item>
- <item name="android:layout_weight">500</item>
- <item name="android:paddingTop">10dip</item>
- <item name="android:paddingBottom">10dip</item>
- <item name="android:paddingStart">18dip</item>
- <item name="android:paddingEnd">18dip</item>
- </style>
-
- <style name="PadLayoutStyle.Numeric">
- <item name="android:layout_width">0dip</item>
- <item name="android:layout_weight">500</item>
- <item name="android:paddingTop">10dip</item>
- <item name="android:paddingBottom">10dip</item>
- <item name="android:paddingStart">18dip</item>
- <item name="android:paddingEnd">18dip</item>
- </style>
-
- <style name="PadLayoutStyle.Operator">
- <item name="android:layout_width">0dip</item>
- <item name="android:layout_weight">280</item>
- <item name="android:paddingTop">10dip</item>
- <item name="android:paddingBottom">10dip</item>
- <item name="android:paddingStart">18dip</item>
- <item name="android:paddingEnd">18dip</item>
- </style>
-
-</resources>
diff --git a/res/values-sw768dp-land/styles.xml b/res/values-sw768dp-land/styles.xml
deleted file mode 100644
index 162e5bc..0000000
--- a/res/values-sw768dp-land/styles.xml
+++ /dev/null
@@ -1,63 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-
-<!-- Styles for landscape 800dip-wide tablet (e.g. Nexus 10). -->
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
-
- <style name="DisplayTextStyle.Formula">
- <item name="android:paddingTop">16dip</item>
- <item name="android:paddingBottom">32dip</item>
- <item name="android:paddingStart">44dip</item>
- <item name="android:paddingEnd">44dip</item>
- <item name="minTextSize">56sp</item>
- <item name="maxTextSize">72sp</item>
- <item name="stepTextSize">8sp</item>
- </style>
-
- <style name="DisplayTextStyle.Result">
- <item name="android:paddingTop">20dip</item>
- <item name="android:paddingBottom">48dip</item>
- <item name="android:paddingStart">44dip</item>
- <item name="android:paddingEnd">44dip</item>
- <item name="android:textSize">56sp</item>
- </style>
-
- <style name="PadButtonStyle.Advanced">
- <item name="android:background">@drawable/pad_button_advanced_background</item>
- <item name="android:textColor">@color/pad_button_advanced_text_color</item>
- <item name="android:textSize">30sp</item>
- </style>
-
- <style name="PadButtonStyle.Advanced.Text">
- <item name="android:textAllCaps">true</item>
- <item name="android:textSize">26sp</item>
- </style>
-
- <style name="PadButtonStyle.Numeric">
- <item name="android:textSize">36sp</item>
- </style>
-
- <style name="PadButtonStyle.Operator">
- <item name="android:textSize">36sp</item>
- </style>
-
- <style name="PadButtonStyle.Operator.Text">
- <item name="android:textAllCaps">true</item>
- <item name="android:textSize">26sp</item>
- </style>
-
-</resources>
diff --git a/res/values-sw768dp-port/styles.xml b/res/values-sw768dp-port/styles.xml
deleted file mode 100644
index c9bceba..0000000
--- a/res/values-sw768dp-port/styles.xml
+++ /dev/null
@@ -1,63 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-
-<!-- Styles for portrait 800dip-wide tablet (e.g. Nexus 10). -->
-<resources xmlns:android="http://schemas.android.com/apk/res/android">
-
- <style name="DisplayTextStyle.Formula">
- <item name="android:paddingTop">24dip</item>
- <item name="android:paddingBottom">32dip</item>
- <item name="android:paddingStart">44dip</item>
- <item name="android:paddingEnd">44dip</item>
- <item name="minTextSize">56sp</item>
- <item name="maxTextSize">80sp</item>
- <item name="stepTextSize">8sp</item>
- </style>
-
- <style name="DisplayTextStyle.Result">
- <item name="android:paddingTop">24dip</item>
- <item name="android:paddingBottom">56dip</item>
- <item name="android:paddingStart">44dip</item>
- <item name="android:paddingEnd">44dip</item>
- <item name="android:textSize">56sp</item>
- </style>
-
- <style name="PadButtonStyle.Advanced">
- <item name="android:background">@drawable/pad_button_advanced_background</item>
- <item name="android:textColor">@color/pad_button_advanced_text_color</item>
- <item name="android:textSize">32sp</item>
- </style>
-
- <style name="PadButtonStyle.Advanced.Text">
- <item name="android:textAllCaps">true</item>s
- <item name="android:textSize">28sp</item>
- </style>
-
- <style name="PadButtonStyle.Numeric">
- <item name="android:textSize">38sp</item>
- </style>
-
- <style name="PadButtonStyle.Operator">
- <item name="android:textSize">38sp</item>
- </style>
-
- <style name="PadButtonStyle.Operator.Text">
- <item name="android:textAllCaps">true</item>
- <item name="android:textSize">28sp</item>
- </style>
-
-</resources>
diff --git a/res/values-sw800dp-port/layout.xml b/res/values-sw800dp-port/layout.xml
deleted file mode 100644
index 46d71cb..0000000
--- a/res/values-sw800dp-port/layout.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- -->
-
-<resources>
- <item name="activity_calculator" type="layout">@layout/activity_calculator_tablet_port</item>
-</resources>
\ No newline at end of file
diff --git a/res/values-ta-rIN/strings.xml b/res/values-ta-rIN/strings.xml
index 2e044e2..e386cb5 100644
--- a/res/values-ta-rIN/strings.xml
+++ b/res/values-ta-rIN/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"நீக்கு"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"நேர்மாறு சார்புகளைக் காட்டு"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"நேர்மாறு சார்புகளை மறை"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"சூத்திரம் இல்லை"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"எண்களும் அடிப்படை செயல்பாடுகளும்"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"மேம்பட்ட செயல்பாடுகள்"</string>
<string name="error_nan" msgid="5674077944929888710">"எண் அல்ல"</string>
<string name="error_syntax" msgid="4786987111228645602">"தவறான கோவை"</string>
<string name="error_aborted" msgid="3402238176316342537">"நிறுத்தப்பட்டது"</string>
diff --git a/res/values-te-rIN/strings.xml b/res/values-te-rIN/strings.xml
index 73c5096..82f22b0 100644
--- a/res/values-te-rIN/strings.xml
+++ b/res/values-te-rIN/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"తొలగించు"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"విలోమ ఫంక్షన్లను చూపుతుంది"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"విలోమ ఫంక్షన్లను దాస్తుంది"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"సూత్రం లేదు"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"సంఖ్యలు మరియు ప్రాథమిక చర్యలు"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"అధునాతన చర్యలు"</string>
<string name="error_nan" msgid="5674077944929888710">"సంఖ్య కాదు"</string>
<string name="error_syntax" msgid="4786987111228645602">"తప్పు వ్యక్తీకరణ"</string>
<string name="error_aborted" msgid="3402238176316342537">"రద్దు చేసారు"</string>
diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml
index 28641af..40b84ff 100644
--- a/res/values-th/strings.xml
+++ b/res/values-th/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"ลบ"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"แสดงฟังก์ชันผกผัน"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"ซ่อนฟังก์ชันผกผัน"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"ไม่มีสูตร"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"แป้นตัวเลขและการคำนวนขั้นต้น"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"การใช้งานขั้นสูง"</string>
<string name="error_nan" msgid="5674077944929888710">"ไม่ใช่ตัวเลข"</string>
<string name="error_syntax" msgid="4786987111228645602">"นิพจน์ไม่ถูกต้อง"</string>
<string name="error_aborted" msgid="3402238176316342537">"ล้มเลิกแล้ว"</string>
diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml
index 8cb141a..a3659c8 100644
--- a/res/values-tl/strings.xml
+++ b/res/values-tl/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"i-delete"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"ipakita ang mga inverse function"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"itago ang mga inverse function"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Walang formula"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Mga numero at basic na pagpapatakbo"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Mga advanced na pagpapatakbo"</string>
<string name="error_nan" msgid="5674077944929888710">"Hindi numero"</string>
<string name="error_syntax" msgid="4786987111228645602">"Bad expression"</string>
<string name="error_aborted" msgid="3402238176316342537">"Itinigil"</string>
diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml
index 996a2df..d50ed11 100644
--- a/res/values-tr/strings.xml
+++ b/res/values-tr/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"sil"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"ters fonksiyonları göster"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"ters fonksiyonları gizle"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Formül yok"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Sayılar ve temel işlemler"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Gelişmiş işlemler"</string>
<string name="error_nan" msgid="5674077944929888710">"Sayı değil"</string>
<string name="error_syntax" msgid="4786987111228645602">"Hatalı ifade"</string>
<string name="error_aborted" msgid="3402238176316342537">"İptal edildi"</string>
diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml
index c98ede1..4a5fa70 100755
--- a/res/values-uk/strings.xml
+++ b/res/values-uk/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"видалити"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"показати обернені функції"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"сховати обернені функції"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Немає формули"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Числа й основні операції"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Додаткові операції"</string>
<string name="error_nan" msgid="5674077944929888710">"Не число"</string>
<string name="error_syntax" msgid="4786987111228645602">"Невірний вираз"</string>
<string name="error_aborted" msgid="3402238176316342537">"Скасовано"</string>
diff --git a/res/values-ur-rPK/strings.xml b/res/values-ur-rPK/strings.xml
index ff7119a..198ed61 100644
--- a/res/values-ur-rPK/strings.xml
+++ b/res/values-ur-rPK/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"حذف کریں"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"معکوس فنکشنز دکھائیں"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"معکوس فنکشنز چھپائیں"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"کوئی فارمولہ نہیں"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"اعداد اور بنیادی کاروائیاں"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"اعلی کاروائیاں"</string>
<string name="error_nan" msgid="5674077944929888710">"نمبر نہیں ہے"</string>
<string name="error_syntax" msgid="4786987111228645602">"غلط ایکسپریشن"</string>
<string name="error_aborted" msgid="3402238176316342537">"ساقط کر دیا گیا"</string>
diff --git a/res/values-uz-rUZ/strings.xml b/res/values-uz-rUZ/strings.xml
index 24ed74d..4d45843 100644
--- a/res/values-uz-rUZ/strings.xml
+++ b/res/values-uz-rUZ/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"o‘chirish"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"teskari funksiyalarni ko‘rsatish"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"teskari funksiyalarni berkitish"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Formula yo‘q"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Sonlar va asosiy amallar"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Qo‘shimcha amallar"</string>
<string name="error_nan" msgid="5674077944929888710">"Haqiqiy son emas"</string>
<string name="error_syntax" msgid="4786987111228645602">"Noto‘g‘ri ifoda"</string>
<string name="error_aborted" msgid="3402238176316342537">"Bekor qilindi"</string>
diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml
index a12368d..940b3d5 100644
--- a/res/values-vi/strings.xml
+++ b/res/values-vi/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"gạch bỏ"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"hiển thị hàm nghịch"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"ẩn hàm nghịch"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Không có công thức"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Số và phép tính cơ bản"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Phép tính nâng cao"</string>
<string name="error_nan" msgid="5674077944929888710">"Không phải số"</string>
<string name="error_syntax" msgid="4786987111228645602">"Biểu thức không hợp lệ"</string>
<string name="error_aborted" msgid="3402238176316342537">"Đã hủy bỏ"</string>
diff --git a/res/values-port/layout.xml b/res/values-w230dp-h220dp/layout.xml
similarity index 71%
copy from res/values-port/layout.xml
copy to res/values-w230dp-h220dp/layout.xml
index 99ea0b8..0e698d5 100644
--- a/res/values-port/layout.xml
+++ b/res/values-w230dp-h220dp/layout.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2014 The Android Open Source Project
+ Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -17,4 +17,7 @@
<resources>
<item name="activity_calculator" type="layout">@layout/activity_calculator_port</item>
-</resources>
\ No newline at end of file
+ <item name="display" type="layout">@layout/display_one_line</item>
+ <item name="pad_operator" type="layout">@layout/pad_operator_one_col</item>
+ <item name="pad_advanced" type="layout">@layout/pad_advanced_4x4</item>
+</resources>
diff --git a/res/values-w230dp-h220dp/styles.xml b/res/values-w230dp-h220dp/styles.xml
new file mode 100644
index 0000000..88a52ce
--- /dev/null
+++ b/res/values-w230dp-h220dp/styles.xml
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <style name="DisplayTextStyle.Formula">
+ <item name="android:paddingTop">0dip</item>
+ <item name="android:paddingBottom">0dip</item>
+ <item name="android:paddingStart">16dip</item>
+ <item name="android:paddingEnd">16dip</item>
+ <item name="android:gravity">bottom</item>
+ <item name="minTextSize">28dip</item>
+ <item name="maxTextSize">28dip</item>
+ <item name="stepTextSize">8dip</item>
+ </style>
+
+ <style name="DisplayTextStyle.Result">
+ <item name="android:paddingTop">0dip</item>
+ <item name="android:paddingBottom">0dip</item>
+ <item name="android:paddingStart">16dip</item>
+ <item name="android:paddingEnd">16dip</item>
+ <item name="android:gravity">bottom</item>
+ <item name="android:textSize">28dip</item>
+ </style>
+
+ <style name="PadButtonStyle.Advanced">
+ <item name="android:background">@drawable/pad_button_advanced_background</item>
+ <item name="android:textColor">@color/pad_button_advanced_text_color</item>
+ <item name="android:textSize">14dip</item>
+ </style>
+
+ <style name="PadButtonStyle.Advanced.Text">
+ <item name="android:textAllCaps">true</item>
+ <item name="android:textSize">12dip</item>
+ </style>
+
+ <style name="PadButtonStyle.Numeric">
+ <item name="android:textSize">16dip</item>
+ </style>
+
+ <style name="PadButtonStyle.Numeric.Equals">
+ <item name="android:visibility">visible</item>
+ </style>
+
+ <style name="PadButtonStyle.Operator">
+ <item name="android:textSize">14dip</item>
+ </style>
+
+ <style name="PadButtonStyle.Operator.Text">
+ <item name="android:textAllCaps">true</item>
+ <item name="android:textSize">12dip</item>
+ </style>
+
+ <style name="PadLayoutStyle.Advanced">
+ <item name="android:elevation">4dip</item>
+ <item name="android:paddingTop">2dip</item>
+ <item name="android:paddingBottom">8dip</item>
+ <item name="android:paddingStart">18dip</item>
+ <item name="android:paddingEnd">18dip</item>
+ </style>
+
+ <style name="PadLayoutStyle.Numeric">
+ <item name="android:layout_width">0dip</item>
+ <item name="android:layout_weight">7</item>
+ <item name="android:paddingTop">2dip</item>
+ <item name="android:paddingBottom">8dip</item>
+ <item name="android:paddingStart">8dip</item>
+ <item name="android:paddingEnd">8dip</item>
+ </style>
+
+ <style name="PadLayoutStyle.Operator">
+ <item name="android:layout_width">0dip</item>
+ <item name="android:layout_weight">3</item>
+ <item name="android:paddingTop">2dip</item>
+ <item name="android:paddingBottom">8dip</item>
+ <item name="android:paddingStart">4dip</item>
+ <item name="android:paddingEnd">28dip</item>
+ </style>
+
+</resources>
diff --git a/res/values-port/layout.xml b/res/values-w230dp-h275dp/layout.xml
similarity index 71%
copy from res/values-port/layout.xml
copy to res/values-w230dp-h275dp/layout.xml
index 99ea0b8..f9a2d97 100644
--- a/res/values-port/layout.xml
+++ b/res/values-w230dp-h275dp/layout.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2014 The Android Open Source Project
+ Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -17,4 +17,7 @@
<resources>
<item name="activity_calculator" type="layout">@layout/activity_calculator_port</item>
-</resources>
\ No newline at end of file
+ <item name="display" type="layout">@layout/display_two_line</item>
+ <item name="pad_operator" type="layout">@layout/pad_operator_one_col</item>
+ <item name="pad_advanced" type="layout">@layout/pad_advanced_4x4</item>
+</resources>
diff --git a/res/values-w230dp-h275dp/styles.xml b/res/values-w230dp-h275dp/styles.xml
new file mode 100644
index 0000000..2f715a3
--- /dev/null
+++ b/res/values-w230dp-h275dp/styles.xml
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <style name="DisplayTextStyle.Formula">
+ <item name="android:paddingTop">0dip</item>
+ <item name="android:paddingBottom">0dip</item>
+ <item name="android:paddingStart">16dip</item>
+ <item name="android:paddingEnd">16dip</item>
+ <item name="android:gravity">bottom</item>
+ <item name="minTextSize">28dip</item>
+ <item name="maxTextSize">28dip</item>
+ <item name="stepTextSize">8dip</item>
+ </style>
+
+ <style name="DisplayTextStyle.Result">
+ <item name="android:paddingTop">0dip</item>
+ <item name="android:paddingBottom">0dip</item>
+ <item name="android:paddingStart">16dip</item>
+ <item name="android:paddingEnd">16dip</item>
+ <item name="android:gravity">bottom</item>
+ <item name="android:textSize">28dip</item>
+ </style>
+
+ <style name="PadButtonStyle.Advanced">
+ <item name="android:background">@drawable/pad_button_advanced_background</item>
+ <item name="android:textColor">@color/pad_button_advanced_text_color</item>
+ <item name="android:textSize">16dip</item>
+ </style>
+
+ <style name="PadButtonStyle.Advanced.Text">
+ <item name="android:textAllCaps">true</item>
+ <item name="android:textSize">14dip</item>
+ </style>
+
+ <style name="PadButtonStyle.Numeric">
+ <item name="android:textSize">18dip</item>
+ </style>
+
+ <style name="PadButtonStyle.Numeric.Equals">
+ <item name="android:visibility">visible</item>
+ </style>
+
+ <style name="PadButtonStyle.Operator">
+ <item name="android:textSize">16dip</item>
+ </style>
+
+ <style name="PadButtonStyle.Operator.Text">
+ <item name="android:textAllCaps">true</item>
+ <item name="android:textSize">14dip</item>
+ </style>
+
+ <style name="PadLayoutStyle.Advanced">
+ <item name="android:elevation">4dip</item>
+ <item name="android:paddingTop">2dip</item>
+ <item name="android:paddingBottom">8dip</item>
+ <item name="android:paddingStart">18dip</item>
+ <item name="android:paddingEnd">18dip</item>
+ </style>
+
+ <style name="PadLayoutStyle.Numeric">
+ <item name="android:layout_width">0dip</item>
+ <item name="android:layout_weight">7</item>
+ <item name="android:paddingTop">2dip</item>
+ <item name="android:paddingBottom">8dip</item>
+ <item name="android:paddingStart">8dip</item>
+ <item name="android:paddingEnd">8dip</item>
+ </style>
+
+ <style name="PadLayoutStyle.Operator">
+ <item name="android:layout_width">0dip</item>
+ <item name="android:layout_weight">3</item>
+ <item name="android:paddingTop">2dip</item>
+ <item name="android:paddingBottom">8dip</item>
+ <item name="android:paddingStart">4dip</item>
+ <item name="android:paddingEnd">28dip</item>
+ </style>
+
+</resources>
diff --git a/res/values-port/layout.xml b/res/values-w230dp-h375dp/layout.xml
similarity index 71%
copy from res/values-port/layout.xml
copy to res/values-w230dp-h375dp/layout.xml
index 99ea0b8..f9a2d97 100644
--- a/res/values-port/layout.xml
+++ b/res/values-w230dp-h375dp/layout.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2014 The Android Open Source Project
+ Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -17,4 +17,7 @@
<resources>
<item name="activity_calculator" type="layout">@layout/activity_calculator_port</item>
-</resources>
\ No newline at end of file
+ <item name="display" type="layout">@layout/display_two_line</item>
+ <item name="pad_operator" type="layout">@layout/pad_operator_one_col</item>
+ <item name="pad_advanced" type="layout">@layout/pad_advanced_4x4</item>
+</resources>
diff --git a/res/values-port/styles.xml b/res/values-w230dp-h375dp/styles.xml
similarity index 61%
copy from res/values-port/styles.xml
copy to res/values-w230dp-h375dp/styles.xml
index a6d2013..4aa32d8 100644
--- a/res/values-port/styles.xml
+++ b/res/values-w230dp-h375dp/styles.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2014 The Android Open Source Project
+ Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -15,40 +15,39 @@
limitations under the License.
-->
-<!-- Styles for portrait phone (e.g. Nexus 4/5). -->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<style name="DisplayTextStyle.Formula">
- <item name="android:paddingTop">8dip</item>
- <item name="android:paddingBottom">24dip</item>
+ <item name="android:paddingTop">2dip</item>
+ <item name="android:paddingBottom">10dip</item>
<item name="android:paddingStart">16dip</item>
<item name="android:paddingEnd">16dip</item>
- <item name="minTextSize">32sp</item>
- <item name="maxTextSize">56sp</item>
- <item name="stepTextSize">8sp</item>
+ <item name="minTextSize">32dip</item>
+ <item name="maxTextSize">32dip</item>
+ <item name="stepTextSize">8dip</item>
</style>
<style name="DisplayTextStyle.Result">
- <item name="android:paddingTop">10dip</item>
- <item name="android:paddingBottom">32dip</item>
+ <item name="android:paddingTop">12dip</item>
+ <item name="android:paddingBottom">18dip</item>
<item name="android:paddingStart">16dip</item>
<item name="android:paddingEnd">16dip</item>
- <item name="android:textSize">32sp</item>
+ <item name="android:textSize">32dip</item>
</style>
<style name="PadButtonStyle.Advanced">
<item name="android:background">@drawable/pad_button_advanced_background</item>
<item name="android:textColor">@color/pad_button_advanced_text_color</item>
- <item name="android:textSize">20sp</item>
+ <item name="android:textSize">17dip</item>
</style>
<style name="PadButtonStyle.Advanced.Text">
<item name="android:textAllCaps">true</item>
- <item name="android:textSize">17sp</item>
+ <item name="android:textSize">15dip</item>
</style>
<style name="PadButtonStyle.Numeric">
- <item name="android:textSize">32sp</item>
+ <item name="android:textSize">27dip</item>
</style>
<style name="PadButtonStyle.Numeric.Equals">
@@ -56,36 +55,36 @@
</style>
<style name="PadButtonStyle.Operator">
- <item name="android:textSize">23sp</item>
+ <item name="android:textSize">24dip</item>
</style>
<style name="PadButtonStyle.Operator.Text">
<item name="android:textAllCaps">true</item>
- <item name="android:textSize">15sp</item>
+ <item name="android:textSize">15dip</item>
</style>
<style name="PadLayoutStyle.Advanced">
<item name="android:elevation">4dip</item>
- <item name="android:paddingTop">12dip</item>
- <item name="android:paddingBottom">20dip</item>
- <item name="android:paddingStart">20dip</item>
- <item name="android:paddingEnd">20dip</item>
+ <item name="android:paddingTop">2dip</item>
+ <item name="android:paddingBottom">8dip</item>
+ <item name="android:paddingStart">18dip</item>
+ <item name="android:paddingEnd">18dip</item>
</style>
<style name="PadLayoutStyle.Numeric">
<item name="android:layout_width">0dip</item>
- <item name="android:layout_weight">264</item>
- <item name="android:paddingTop">12dip</item>
- <item name="android:paddingBottom">20dip</item>
- <item name="android:paddingStart">12dip</item>
- <item name="android:paddingEnd">12dip</item>
+ <item name="android:layout_weight">7</item>
+ <item name="android:paddingTop">2dip</item>
+ <item name="android:paddingBottom">8dip</item>
+ <item name="android:paddingStart">8dip</item>
+ <item name="android:paddingEnd">8dip</item>
</style>
<style name="PadLayoutStyle.Operator">
<item name="android:layout_width">0dip</item>
- <item name="android:layout_weight">96</item>
- <item name="android:paddingTop">8dip</item>
- <item name="android:paddingBottom">24dip</item>
+ <item name="android:layout_weight">3</item>
+ <item name="android:paddingTop">2dip</item>
+ <item name="android:paddingBottom">8dip</item>
<item name="android:paddingStart">4dip</item>
<item name="android:paddingEnd">28dip</item>
</style>
diff --git a/res/values-port/layout.xml b/res/values-w230dp-h475dp-port/layout.xml
similarity index 71%
copy from res/values-port/layout.xml
copy to res/values-w230dp-h475dp-port/layout.xml
index 99ea0b8..9f7bd6b 100644
--- a/res/values-port/layout.xml
+++ b/res/values-w230dp-h475dp-port/layout.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2014 The Android Open Source Project
+ Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -17,4 +17,7 @@
<resources>
<item name="activity_calculator" type="layout">@layout/activity_calculator_port</item>
-</resources>
\ No newline at end of file
+ <item name="display" type="layout">@layout/display_two_line</item>
+ <item name="pad_operator" type="layout">@layout/pad_operator_one_col</item>
+ <item name="pad_advanced" type="layout">@layout/pad_advanced_3x5</item>
+</resources>
diff --git a/res/values-port/styles.xml b/res/values-w230dp-h475dp-port/styles.xml
similarity index 70%
copy from res/values-port/styles.xml
copy to res/values-w230dp-h475dp-port/styles.xml
index a6d2013..050de1e 100644
--- a/res/values-port/styles.xml
+++ b/res/values-w230dp-h475dp-port/styles.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2014 The Android Open Source Project
+ Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -15,40 +15,39 @@
limitations under the License.
-->
-<!-- Styles for portrait phone (e.g. Nexus 4/5). -->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<style name="DisplayTextStyle.Formula">
<item name="android:paddingTop">8dip</item>
- <item name="android:paddingBottom">24dip</item>
+ <item name="android:paddingBottom">18dip</item>
<item name="android:paddingStart">16dip</item>
<item name="android:paddingEnd">16dip</item>
- <item name="minTextSize">32sp</item>
- <item name="maxTextSize">56sp</item>
- <item name="stepTextSize">8sp</item>
+ <item name="minTextSize">32dip</item>
+ <item name="maxTextSize">56dip</item>
+ <item name="stepTextSize">8dip</item>
</style>
<style name="DisplayTextStyle.Result">
- <item name="android:paddingTop">10dip</item>
- <item name="android:paddingBottom">32dip</item>
+ <item name="android:paddingTop">18dip</item>
+ <item name="android:paddingBottom">36dip</item>
<item name="android:paddingStart">16dip</item>
<item name="android:paddingEnd">16dip</item>
- <item name="android:textSize">32sp</item>
+ <item name="android:textSize">32dip</item>
</style>
<style name="PadButtonStyle.Advanced">
<item name="android:background">@drawable/pad_button_advanced_background</item>
<item name="android:textColor">@color/pad_button_advanced_text_color</item>
- <item name="android:textSize">20sp</item>
+ <item name="android:textSize">20dip</item>
</style>
<style name="PadButtonStyle.Advanced.Text">
<item name="android:textAllCaps">true</item>
- <item name="android:textSize">17sp</item>
+ <item name="android:textSize">17dip</item>
</style>
<style name="PadButtonStyle.Numeric">
- <item name="android:textSize">32sp</item>
+ <item name="android:textSize">32dip</item>
</style>
<style name="PadButtonStyle.Numeric.Equals">
@@ -56,34 +55,34 @@
</style>
<style name="PadButtonStyle.Operator">
- <item name="android:textSize">23sp</item>
+ <item name="android:textSize">23dip</item>
</style>
<style name="PadButtonStyle.Operator.Text">
<item name="android:textAllCaps">true</item>
- <item name="android:textSize">15sp</item>
+ <item name="android:textSize">17dip</item>
</style>
<style name="PadLayoutStyle.Advanced">
<item name="android:elevation">4dip</item>
<item name="android:paddingTop">12dip</item>
<item name="android:paddingBottom">20dip</item>
- <item name="android:paddingStart">20dip</item>
- <item name="android:paddingEnd">20dip</item>
+ <item name="android:paddingStart">18dip</item>
+ <item name="android:paddingEnd">18dip</item>
</style>
<style name="PadLayoutStyle.Numeric">
<item name="android:layout_width">0dip</item>
- <item name="android:layout_weight">264</item>
+ <item name="android:layout_weight">7</item>
<item name="android:paddingTop">12dip</item>
<item name="android:paddingBottom">20dip</item>
- <item name="android:paddingStart">12dip</item>
- <item name="android:paddingEnd">12dip</item>
+ <item name="android:paddingStart">8dip</item>
+ <item name="android:paddingEnd">8dip</item>
</style>
<style name="PadLayoutStyle.Operator">
<item name="android:layout_width">0dip</item>
- <item name="android:layout_weight">96</item>
+ <item name="android:layout_weight">3</item>
<item name="android:paddingTop">8dip</item>
<item name="android:paddingBottom">24dip</item>
<item name="android:paddingStart">4dip</item>
diff --git a/res/values-port/layout.xml b/res/values-w375dp-h220dp/layout.xml
similarity index 71%
rename from res/values-port/layout.xml
rename to res/values-w375dp-h220dp/layout.xml
index 99ea0b8..ac4cde8 100644
--- a/res/values-port/layout.xml
+++ b/res/values-w375dp-h220dp/layout.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2014 The Android Open Source Project
+ Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -17,4 +17,7 @@
<resources>
<item name="activity_calculator" type="layout">@layout/activity_calculator_port</item>
-</resources>
\ No newline at end of file
+ <item name="display" type="layout">@layout/display_one_line</item>
+ <item name="pad_operator" type="layout">@layout/pad_operator_two_col</item>
+ <item name="pad_advanced" type="layout">@layout/pad_advanced_4x4</item>
+</resources>
diff --git a/res/values-port/styles.xml b/res/values-w375dp-h220dp/styles.xml
similarity index 61%
copy from res/values-port/styles.xml
copy to res/values-w375dp-h220dp/styles.xml
index a6d2013..3c6fe85 100644
--- a/res/values-port/styles.xml
+++ b/res/values-w375dp-h220dp/styles.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2014 The Android Open Source Project
+ Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -15,77 +15,78 @@
limitations under the License.
-->
-<!-- Styles for portrait phone (e.g. Nexus 4/5). -->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<style name="DisplayTextStyle.Formula">
- <item name="android:paddingTop">8dip</item>
- <item name="android:paddingBottom">24dip</item>
+ <item name="android:paddingTop">0dip</item>
+ <item name="android:paddingBottom">0dip</item>
<item name="android:paddingStart">16dip</item>
<item name="android:paddingEnd">16dip</item>
- <item name="minTextSize">32sp</item>
- <item name="maxTextSize">56sp</item>
- <item name="stepTextSize">8sp</item>
+ <item name="android:gravity">bottom</item>
+ <item name="minTextSize">28dip</item>
+ <item name="maxTextSize">28dip</item>
+ <item name="stepTextSize">8dip</item>
</style>
<style name="DisplayTextStyle.Result">
- <item name="android:paddingTop">10dip</item>
- <item name="android:paddingBottom">32dip</item>
+ <item name="android:paddingTop">0dip</item>
+ <item name="android:paddingBottom">0dip</item>
<item name="android:paddingStart">16dip</item>
<item name="android:paddingEnd">16dip</item>
- <item name="android:textSize">32sp</item>
+ <item name="android:gravity">bottom</item>
+ <item name="android:textSize">28dip</item>
</style>
<style name="PadButtonStyle.Advanced">
<item name="android:background">@drawable/pad_button_advanced_background</item>
<item name="android:textColor">@color/pad_button_advanced_text_color</item>
- <item name="android:textSize">20sp</item>
+ <item name="android:textSize">14dip</item>
</style>
<style name="PadButtonStyle.Advanced.Text">
<item name="android:textAllCaps">true</item>
- <item name="android:textSize">17sp</item>
+ <item name="android:textSize">12dip</item>
</style>
<style name="PadButtonStyle.Numeric">
- <item name="android:textSize">32sp</item>
+ <item name="android:textSize">16dip</item>
</style>
<style name="PadButtonStyle.Numeric.Equals">
- <item name="android:visibility">visible</item>
+ <item name="android:visibility">gone</item>
</style>
<style name="PadButtonStyle.Operator">
- <item name="android:textSize">23sp</item>
+ <item name="android:textSize">14dip</item>
</style>
<style name="PadButtonStyle.Operator.Text">
<item name="android:textAllCaps">true</item>
- <item name="android:textSize">15sp</item>
+ <item name="android:textSize">12dip</item>
</style>
<style name="PadLayoutStyle.Advanced">
<item name="android:elevation">4dip</item>
- <item name="android:paddingTop">12dip</item>
- <item name="android:paddingBottom">20dip</item>
+ <item name="android:paddingTop">2dip</item>
+ <item name="android:paddingBottom">8dip</item>
<item name="android:paddingStart">20dip</item>
<item name="android:paddingEnd">20dip</item>
</style>
<style name="PadLayoutStyle.Numeric">
<item name="android:layout_width">0dip</item>
- <item name="android:layout_weight">264</item>
- <item name="android:paddingTop">12dip</item>
- <item name="android:paddingBottom">20dip</item>
- <item name="android:paddingStart">12dip</item>
- <item name="android:paddingEnd">12dip</item>
+ <item name="android:layout_weight">5</item>
+ <item name="android:paddingTop">2dip</item>
+ <item name="android:paddingBottom">8dip</item>
+ <item name="android:paddingStart">8dip</item>
+ <item name="android:paddingEnd">8dip</item>
</style>
<style name="PadLayoutStyle.Operator">
<item name="android:layout_width">0dip</item>
- <item name="android:layout_weight">96</item>
- <item name="android:paddingTop">8dip</item>
- <item name="android:paddingBottom">24dip</item>
+ <item name="android:layout_weight">3</item>
+ <item name="android:paddingTop">2dip</item>
+ <item name="android:paddingBottom">8dip</item>
<item name="android:paddingStart">4dip</item>
<item name="android:paddingEnd">28dip</item>
</style>
diff --git a/res/values-port/layout.xml b/res/values-w375dp-h275dp/layout.xml
similarity index 71%
copy from res/values-port/layout.xml
copy to res/values-w375dp-h275dp/layout.xml
index 99ea0b8..3890667 100644
--- a/res/values-port/layout.xml
+++ b/res/values-w375dp-h275dp/layout.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2014 The Android Open Source Project
+ Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -17,4 +17,7 @@
<resources>
<item name="activity_calculator" type="layout">@layout/activity_calculator_port</item>
-</resources>
\ No newline at end of file
+ <item name="display" type="layout">@layout/display_two_line</item>
+ <item name="pad_operator" type="layout">@layout/pad_operator_two_col</item>
+ <item name="pad_advanced" type="layout">@layout/pad_advanced_4x4</item>
+</resources>
diff --git a/res/values-port/styles.xml b/res/values-w375dp-h275dp/styles.xml
similarity index 61%
copy from res/values-port/styles.xml
copy to res/values-w375dp-h275dp/styles.xml
index a6d2013..c628645 100644
--- a/res/values-port/styles.xml
+++ b/res/values-w375dp-h275dp/styles.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2014 The Android Open Source Project
+ Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -15,77 +15,78 @@
limitations under the License.
-->
-<!-- Styles for portrait phone (e.g. Nexus 4/5). -->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<style name="DisplayTextStyle.Formula">
- <item name="android:paddingTop">8dip</item>
- <item name="android:paddingBottom">24dip</item>
+ <item name="android:paddingTop">0dip</item>
+ <item name="android:paddingBottom">0dip</item>
<item name="android:paddingStart">16dip</item>
<item name="android:paddingEnd">16dip</item>
- <item name="minTextSize">32sp</item>
- <item name="maxTextSize">56sp</item>
- <item name="stepTextSize">8sp</item>
+ <item name="android:gravity">bottom</item>
+ <item name="minTextSize">28dip</item>
+ <item name="maxTextSize">28dip</item>
+ <item name="stepTextSize">8dip</item>
</style>
<style name="DisplayTextStyle.Result">
- <item name="android:paddingTop">10dip</item>
- <item name="android:paddingBottom">32dip</item>
+ <item name="android:paddingTop">0dip</item>
+ <item name="android:paddingBottom">0dip</item>
<item name="android:paddingStart">16dip</item>
<item name="android:paddingEnd">16dip</item>
- <item name="android:textSize">32sp</item>
+ <item name="android:gravity">bottom</item>
+ <item name="android:textSize">28dip</item>
</style>
<style name="PadButtonStyle.Advanced">
<item name="android:background">@drawable/pad_button_advanced_background</item>
<item name="android:textColor">@color/pad_button_advanced_text_color</item>
- <item name="android:textSize">20sp</item>
+ <item name="android:textSize">17dip</item>
</style>
<style name="PadButtonStyle.Advanced.Text">
<item name="android:textAllCaps">true</item>
- <item name="android:textSize">17sp</item>
+ <item name="android:textSize">14dip</item>
</style>
<style name="PadButtonStyle.Numeric">
- <item name="android:textSize">32sp</item>
+ <item name="android:textSize">20dip</item>
</style>
<style name="PadButtonStyle.Numeric.Equals">
- <item name="android:visibility">visible</item>
+ <item name="android:visibility">gone</item>
</style>
<style name="PadButtonStyle.Operator">
- <item name="android:textSize">23sp</item>
+ <item name="android:textSize">17dip</item>
</style>
<style name="PadButtonStyle.Operator.Text">
<item name="android:textAllCaps">true</item>
- <item name="android:textSize">15sp</item>
+ <item name="android:textSize">14dip</item>
</style>
<style name="PadLayoutStyle.Advanced">
<item name="android:elevation">4dip</item>
- <item name="android:paddingTop">12dip</item>
- <item name="android:paddingBottom">20dip</item>
+ <item name="android:paddingTop">2dip</item>
+ <item name="android:paddingBottom">8dip</item>
<item name="android:paddingStart">20dip</item>
<item name="android:paddingEnd">20dip</item>
</style>
<style name="PadLayoutStyle.Numeric">
<item name="android:layout_width">0dip</item>
- <item name="android:layout_weight">264</item>
- <item name="android:paddingTop">12dip</item>
- <item name="android:paddingBottom">20dip</item>
- <item name="android:paddingStart">12dip</item>
- <item name="android:paddingEnd">12dip</item>
+ <item name="android:layout_weight">5</item>
+ <item name="android:paddingTop">2dip</item>
+ <item name="android:paddingBottom">8dip</item>
+ <item name="android:paddingStart">8dip</item>
+ <item name="android:paddingEnd">8dip</item>
</style>
<style name="PadLayoutStyle.Operator">
<item name="android:layout_width">0dip</item>
- <item name="android:layout_weight">96</item>
- <item name="android:paddingTop">8dip</item>
- <item name="android:paddingBottom">24dip</item>
+ <item name="android:layout_weight">3</item>
+ <item name="android:paddingTop">2dip</item>
+ <item name="android:paddingBottom">8dip</item>
<item name="android:paddingStart">4dip</item>
<item name="android:paddingEnd">28dip</item>
</style>
diff --git a/res/values-port/layout.xml b/res/values-w375dp-h375dp/layout.xml
similarity index 71%
copy from res/values-port/layout.xml
copy to res/values-w375dp-h375dp/layout.xml
index 99ea0b8..3890667 100644
--- a/res/values-port/layout.xml
+++ b/res/values-w375dp-h375dp/layout.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2014 The Android Open Source Project
+ Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -17,4 +17,7 @@
<resources>
<item name="activity_calculator" type="layout">@layout/activity_calculator_port</item>
-</resources>
\ No newline at end of file
+ <item name="display" type="layout">@layout/display_two_line</item>
+ <item name="pad_operator" type="layout">@layout/pad_operator_two_col</item>
+ <item name="pad_advanced" type="layout">@layout/pad_advanced_4x4</item>
+</resources>
diff --git a/res/values-port/styles.xml b/res/values-w375dp-h375dp/styles.xml
similarity index 66%
copy from res/values-port/styles.xml
copy to res/values-w375dp-h375dp/styles.xml
index a6d2013..14e96ca 100644
--- a/res/values-port/styles.xml
+++ b/res/values-w375dp-h375dp/styles.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2014 The Android Open Source Project
+ Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -15,77 +15,76 @@
limitations under the License.
-->
-<!-- Styles for portrait phone (e.g. Nexus 4/5). -->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<style name="DisplayTextStyle.Formula">
- <item name="android:paddingTop">8dip</item>
- <item name="android:paddingBottom">24dip</item>
+ <item name="android:paddingTop">2dip</item>
+ <item name="android:paddingBottom">10dip</item>
<item name="android:paddingStart">16dip</item>
<item name="android:paddingEnd">16dip</item>
- <item name="minTextSize">32sp</item>
- <item name="maxTextSize">56sp</item>
- <item name="stepTextSize">8sp</item>
+ <item name="minTextSize">32dip</item>
+ <item name="maxTextSize">32dip</item>
+ <item name="stepTextSize">8dip</item>
</style>
<style name="DisplayTextStyle.Result">
- <item name="android:paddingTop">10dip</item>
- <item name="android:paddingBottom">32dip</item>
+ <item name="android:paddingTop">12dip</item>
+ <item name="android:paddingBottom">18dip</item>
<item name="android:paddingStart">16dip</item>
<item name="android:paddingEnd">16dip</item>
- <item name="android:textSize">32sp</item>
+ <item name="android:textSize">32dip</item>
</style>
<style name="PadButtonStyle.Advanced">
<item name="android:background">@drawable/pad_button_advanced_background</item>
<item name="android:textColor">@color/pad_button_advanced_text_color</item>
- <item name="android:textSize">20sp</item>
+ <item name="android:textSize">17dip</item>
</style>
<style name="PadButtonStyle.Advanced.Text">
<item name="android:textAllCaps">true</item>
- <item name="android:textSize">17sp</item>
+ <item name="android:textSize">15dip</item>
</style>
<style name="PadButtonStyle.Numeric">
- <item name="android:textSize">32sp</item>
+ <item name="android:textSize">27dip</item>
</style>
<style name="PadButtonStyle.Numeric.Equals">
- <item name="android:visibility">visible</item>
+ <item name="android:visibility">gone</item>
</style>
<style name="PadButtonStyle.Operator">
- <item name="android:textSize">23sp</item>
+ <item name="android:textSize">24dip</item>
</style>
<style name="PadButtonStyle.Operator.Text">
<item name="android:textAllCaps">true</item>
- <item name="android:textSize">15sp</item>
+ <item name="android:textSize">15dip</item>
</style>
<style name="PadLayoutStyle.Advanced">
<item name="android:elevation">4dip</item>
- <item name="android:paddingTop">12dip</item>
- <item name="android:paddingBottom">20dip</item>
+ <item name="android:paddingTop">2dip</item>
+ <item name="android:paddingBottom">8dip</item>
<item name="android:paddingStart">20dip</item>
<item name="android:paddingEnd">20dip</item>
</style>
<style name="PadLayoutStyle.Numeric">
<item name="android:layout_width">0dip</item>
- <item name="android:layout_weight">264</item>
- <item name="android:paddingTop">12dip</item>
- <item name="android:paddingBottom">20dip</item>
- <item name="android:paddingStart">12dip</item>
- <item name="android:paddingEnd">12dip</item>
+ <item name="android:layout_weight">5</item>
+ <item name="android:paddingTop">8dip</item>
+ <item name="android:paddingBottom">8dip</item>
+ <item name="android:paddingStart">8dip</item>
+ <item name="android:paddingEnd">8dip</item>
</style>
<style name="PadLayoutStyle.Operator">
<item name="android:layout_width">0dip</item>
- <item name="android:layout_weight">96</item>
+ <item name="android:layout_weight">3</item>
<item name="android:paddingTop">8dip</item>
- <item name="android:paddingBottom">24dip</item>
+ <item name="android:paddingBottom">8dip</item>
<item name="android:paddingStart">4dip</item>
<item name="android:paddingEnd">28dip</item>
</style>
diff --git a/res/values-port/layout.xml b/res/values-w375dp-h500dp-port/layout.xml
similarity index 71%
copy from res/values-port/layout.xml
copy to res/values-w375dp-h500dp-port/layout.xml
index 99ea0b8..9f7bd6b 100644
--- a/res/values-port/layout.xml
+++ b/res/values-w375dp-h500dp-port/layout.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2014 The Android Open Source Project
+ Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -17,4 +17,7 @@
<resources>
<item name="activity_calculator" type="layout">@layout/activity_calculator_port</item>
-</resources>
\ No newline at end of file
+ <item name="display" type="layout">@layout/display_two_line</item>
+ <item name="pad_operator" type="layout">@layout/pad_operator_one_col</item>
+ <item name="pad_advanced" type="layout">@layout/pad_advanced_3x5</item>
+</resources>
diff --git a/res/values-port/styles.xml b/res/values-w375dp-h500dp-port/styles.xml
similarity index 75%
rename from res/values-port/styles.xml
rename to res/values-w375dp-h500dp-port/styles.xml
index a6d2013..066aa8e 100644
--- a/res/values-port/styles.xml
+++ b/res/values-w375dp-h500dp-port/styles.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2014 The Android Open Source Project
+ Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -15,40 +15,39 @@
limitations under the License.
-->
-<!-- Styles for portrait phone (e.g. Nexus 4/5). -->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<style name="DisplayTextStyle.Formula">
- <item name="android:paddingTop">8dip</item>
- <item name="android:paddingBottom">24dip</item>
+ <item name="android:paddingTop">16dip</item>
+ <item name="android:paddingBottom">28dip</item>
<item name="android:paddingStart">16dip</item>
<item name="android:paddingEnd">16dip</item>
- <item name="minTextSize">32sp</item>
- <item name="maxTextSize">56sp</item>
- <item name="stepTextSize">8sp</item>
+ <item name="minTextSize">42dip</item>
+ <item name="maxTextSize">74dip</item>
+ <item name="stepTextSize">8dip</item>
</style>
<style name="DisplayTextStyle.Result">
- <item name="android:paddingTop">10dip</item>
- <item name="android:paddingBottom">32dip</item>
+ <item name="android:paddingTop">16dip</item>
+ <item name="android:paddingBottom">42dip</item>
<item name="android:paddingStart">16dip</item>
<item name="android:paddingEnd">16dip</item>
- <item name="android:textSize">32sp</item>
+ <item name="android:textSize">42dip</item>
</style>
<style name="PadButtonStyle.Advanced">
<item name="android:background">@drawable/pad_button_advanced_background</item>
<item name="android:textColor">@color/pad_button_advanced_text_color</item>
- <item name="android:textSize">20sp</item>
+ <item name="android:textSize">23dip</item>
</style>
<style name="PadButtonStyle.Advanced.Text">
<item name="android:textAllCaps">true</item>
- <item name="android:textSize">17sp</item>
+ <item name="android:textSize">20dip</item>
</style>
<style name="PadButtonStyle.Numeric">
- <item name="android:textSize">32sp</item>
+ <item name="android:textSize">36dip</item>
</style>
<style name="PadButtonStyle.Numeric.Equals">
@@ -56,12 +55,12 @@
</style>
<style name="PadButtonStyle.Operator">
- <item name="android:textSize">23sp</item>
+ <item name="android:textSize">27dip</item>
</style>
<style name="PadButtonStyle.Operator.Text">
<item name="android:textAllCaps">true</item>
- <item name="android:textSize">15sp</item>
+ <item name="android:textSize">20dip</item>
</style>
<style name="PadLayoutStyle.Advanced">
@@ -74,7 +73,7 @@
<style name="PadLayoutStyle.Numeric">
<item name="android:layout_width">0dip</item>
- <item name="android:layout_weight">264</item>
+ <item name="android:layout_weight">7</item>
<item name="android:paddingTop">12dip</item>
<item name="android:paddingBottom">20dip</item>
<item name="android:paddingStart">12dip</item>
@@ -83,7 +82,7 @@
<style name="PadLayoutStyle.Operator">
<item name="android:layout_width">0dip</item>
- <item name="android:layout_weight">96</item>
+ <item name="android:layout_weight">3</item>
<item name="android:paddingTop">8dip</item>
<item name="android:paddingBottom">24dip</item>
<item name="android:paddingStart">4dip</item>
diff --git a/res/values-sw600dp-port/layout.xml b/res/values-w375dp-h768dp-port/layout.xml
similarity index 71%
rename from res/values-sw600dp-port/layout.xml
rename to res/values-w375dp-h768dp-port/layout.xml
index 46d71cb..98db0fe 100644
--- a/res/values-sw600dp-port/layout.xml
+++ b/res/values-w375dp-h768dp-port/layout.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2014 The Android Open Source Project
+ Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -17,4 +17,7 @@
<resources>
<item name="activity_calculator" type="layout">@layout/activity_calculator_tablet_port</item>
-</resources>
\ No newline at end of file
+ <item name="display" type="layout">@layout/display_two_line</item>
+ <item name="pad_advanced" type="layout">@layout/pad_advanced_5x3</item>
+ <item name="pad_operator" type="layout">@layout/pad_operator_two_col</item>
+</resources>
diff --git a/res/values-sw600dp-port/styles.xml b/res/values-w375dp-h768dp-port/styles.xml
similarity index 78%
copy from res/values-sw600dp-port/styles.xml
copy to res/values-w375dp-h768dp-port/styles.xml
index f439521..ec91033 100644
--- a/res/values-sw600dp-port/styles.xml
+++ b/res/values-w375dp-h768dp-port/styles.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2014 The Android Open Source Project
+ Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -15,8 +15,6 @@
limitations under the License.
-->
-
-<!-- Styles for portrait 600dip-wide tablet (e.g. Nexus 7). -->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<style name="DisplayTextStyle.Formula">
@@ -24,9 +22,9 @@
<item name="android:paddingBottom">32dip</item>
<item name="android:paddingStart">44dip</item>
<item name="android:paddingEnd">44dip</item>
- <item name="minTextSize">48sp</item>
- <item name="maxTextSize">72sp</item>
- <item name="stepTextSize">8sp</item>
+ <item name="minTextSize">48dip</item>
+ <item name="maxTextSize">72dip</item>
+ <item name="stepTextSize">8dip</item>
</style>
<style name="DisplayTextStyle.Result">
@@ -34,22 +32,22 @@
<item name="android:paddingBottom">48dip</item>
<item name="android:paddingStart">44dip</item>
<item name="android:paddingEnd">44dip</item>
- <item name="android:textSize">48sp</item>
+ <item name="android:textSize">48dip</item>
</style>
<style name="PadButtonStyle.Advanced">
<item name="android:background">@drawable/pad_button_advanced_background</item>
<item name="android:textColor">@color/pad_button_advanced_text_color</item>
- <item name="android:textSize">27sp</item>
+ <item name="android:textSize">27dip</item>
</style>
<style name="PadButtonStyle.Advanced.Text">
<item name="android:textAllCaps">true</item>
- <item name="android:textSize">24sp</item>
+ <item name="android:textSize">24dip</item>
</style>
<style name="PadButtonStyle.Numeric">
- <item name="android:textSize">36sp</item>
+ <item name="android:textSize">36dip</item>
</style>
<style name="PadButtonStyle.Numeric.Equals">
@@ -57,12 +55,12 @@
</style>
<style name="PadButtonStyle.Operator">
- <item name="android:textSize">36sp</item>
+ <item name="android:textSize">36dip</item>
</style>
<style name="PadButtonStyle.Operator.Text">
<item name="android:textAllCaps">true</item>
- <item name="android:textSize">24sp</item>
+ <item name="android:textSize">24dip</item>
</style>
<style name="PadLayoutStyle.Advanced">
@@ -88,14 +86,8 @@
<item name="android:layout_weight">264</item>
<item name="android:paddingTop">8dip</item>
<item name="android:paddingBottom">8dip</item>
- <item name="android:paddingStart">8dip</item>
+ <item name="android:paddingStart">4dip</item>
<item name="android:paddingEnd">8dip</item>
</style>
- <style name="PadLinearLayoutStyle">
- <item name="android:layout_width">match_parent</item>
- <item name="android:layout_height">0dip</item>
- <item name="android:layout_weight">500</item>
- </style>
-
</resources>
diff --git a/res/values-land/layout.xml b/res/values-w520dp-h220dp-land/layout.xml
similarity index 71%
rename from res/values-land/layout.xml
rename to res/values-w520dp-h220dp-land/layout.xml
index 8bc268c..f4d3c4f 100644
--- a/res/values-land/layout.xml
+++ b/res/values-w520dp-h220dp-land/layout.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2014 The Android Open Source Project
+ Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -17,4 +17,7 @@
<resources>
<item name="activity_calculator" type="layout">@layout/activity_calculator_land</item>
-</resources>
\ No newline at end of file
+ <item name="display" type="layout">@layout/display_one_line</item>
+ <item name="pad_operator" type="layout">@layout/pad_operator_two_col</item>
+ <item name="pad_advanced" type="layout">@layout/pad_advanced_4x4</item>
+</resources>
diff --git a/res/values-land/styles.xml b/res/values-w520dp-h220dp-land/styles.xml
similarity index 63%
rename from res/values-land/styles.xml
rename to res/values-w520dp-h220dp-land/styles.xml
index 3730145..e90e530 100644
--- a/res/values-land/styles.xml
+++ b/res/values-w520dp-h220dp-land/styles.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2014 The Android Open Source Project
+ Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -15,38 +15,41 @@
limitations under the License.
-->
-<!-- Styles for landscape phone (e.g. Nexus 4/5). -->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<style name="DisplayTextStyle.Formula">
- <item name="android:paddingTop">2dip</item>
- <item name="android:paddingBottom">6dip</item>
+ <item name="android:paddingTop">0dip</item>
+ <item name="android:paddingBottom">0dip</item>
<item name="android:paddingStart">36dip</item>
<item name="android:paddingEnd">36dip</item>
- <item name="android:textSize">24sp</item>
+ <item name="android:gravity">bottom</item>
+ <item name="minTextSize">28dip</item>
+ <item name="maxTextSize">28dip</item>
+ <item name="stepTextSize">8dip</item>
</style>
<style name="DisplayTextStyle.Result">
- <item name="android:paddingTop">8dip</item>
- <item name="android:paddingBottom">12dip</item>
+ <item name="android:paddingTop">0dip</item>
+ <item name="android:paddingBottom">0dip</item>
<item name="android:paddingStart">36dip</item>
<item name="android:paddingEnd">36dip</item>
- <item name="android:textSize">24sp</item>
+ <item name="android:gravity">bottom</item>
+ <item name="android:textSize">28dip</item>
</style>
<style name="PadButtonStyle.Advanced">
<item name="android:background">@drawable/pad_button_advanced_background</item>
<item name="android:textColor">@color/pad_button_advanced_text_color</item>
- <item name="android:textSize">15sp</item>
+ <item name="android:textSize">14dip</item>
</style>
<style name="PadButtonStyle.Advanced.Text">
<item name="android:textAllCaps">true</item>
- <item name="android:textSize">13sp</item>
+ <item name="android:textSize">12dip</item>
</style>
<style name="PadButtonStyle.Numeric">
- <item name="android:textSize">23sp</item>
+ <item name="android:textSize">16dip</item>
</style>
<style name="PadButtonStyle.Numeric.Equals">
@@ -54,39 +57,39 @@
</style>
<style name="PadButtonStyle.Operator">
- <item name="android:textSize">20sp</item>
+ <item name="android:textSize">14dip</item>
</style>
<style name="PadButtonStyle.Operator.Text">
<item name="android:textAllCaps">true</item>
- <item name="android:textSize">13sp</item>
+ <item name="android:textSize">12dip</item>
</style>
<style name="PadLayoutStyle.Advanced">
<item name="android:layout_width">0dip</item>
- <item name="android:layout_weight">208</item>
- <item name="android:paddingTop">4dip</item>
- <item name="android:paddingBottom">4dip</item>
+ <item name="android:layout_weight">4</item>
+ <item name="android:paddingTop">2dip</item>
+ <item name="android:paddingBottom">8dip</item>
<item name="android:paddingStart">8dip</item>
<item name="android:paddingEnd">8dip</item>
</style>
<style name="PadLayoutStyle.Numeric">
<item name="android:layout_width">0dip</item>
- <item name="android:layout_weight">240</item>
- <item name="android:paddingTop">4dip</item>
- <item name="android:paddingBottom">4dip</item>
+ <item name="android:layout_weight">3</item>
+ <item name="android:paddingTop">2dip</item>
+ <item name="android:paddingBottom">8dip</item>
<item name="android:paddingStart">8dip</item>
<item name="android:paddingEnd">8dip</item>
</style>
<style name="PadLayoutStyle.Operator">
<item name="android:layout_width">0dip</item>
- <item name="android:layout_weight">144</item>
- <item name="android:paddingTop">4dip</item>
- <item name="android:paddingBottom">4dip</item>
- <item name="android:paddingStart">12dip</item>
- <item name="android:paddingEnd">12dip</item>
+ <item name="android:layout_weight">2</item>
+ <item name="android:paddingTop">2dip</item>
+ <item name="android:paddingBottom">8dip</item>
+ <item name="android:paddingStart">8dip</item>
+ <item name="android:paddingEnd">8dip</item>
</style>
</resources>
diff --git a/res/values-land/layout.xml b/res/values-w520dp-h275dp-land/layout.xml
similarity index 71%
copy from res/values-land/layout.xml
copy to res/values-w520dp-h275dp-land/layout.xml
index 8bc268c..15b5b1b 100644
--- a/res/values-land/layout.xml
+++ b/res/values-w520dp-h275dp-land/layout.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2014 The Android Open Source Project
+ Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -17,4 +17,7 @@
<resources>
<item name="activity_calculator" type="layout">@layout/activity_calculator_land</item>
-</resources>
\ No newline at end of file
+ <item name="display" type="layout">@layout/display_two_line</item>
+ <item name="pad_advanced" type="layout">@layout/pad_advanced_3x5</item>
+ <item name="pad_operator" type="layout">@layout/pad_operator_two_col</item>
+</resources>
diff --git a/res/values-land/styles.xml b/res/values-w520dp-h275dp-land/styles.xml
similarity index 63%
copy from res/values-land/styles.xml
copy to res/values-w520dp-h275dp-land/styles.xml
index 3730145..9d66a9b 100644
--- a/res/values-land/styles.xml
+++ b/res/values-w520dp-h275dp-land/styles.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2014 The Android Open Source Project
+ Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -15,38 +15,39 @@
limitations under the License.
-->
-<!-- Styles for landscape phone (e.g. Nexus 4/5). -->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<style name="DisplayTextStyle.Formula">
<item name="android:paddingTop">2dip</item>
- <item name="android:paddingBottom">6dip</item>
+ <item name="android:paddingBottom">8dip</item>
<item name="android:paddingStart">36dip</item>
<item name="android:paddingEnd">36dip</item>
- <item name="android:textSize">24sp</item>
+ <item name="minTextSize">28dip</item>
+ <item name="maxTextSize">28dip</item>
+ <item name="stepTextSize">8dip</item>
</style>
<style name="DisplayTextStyle.Result">
<item name="android:paddingTop">8dip</item>
- <item name="android:paddingBottom">12dip</item>
+ <item name="android:paddingBottom">16dip</item>
<item name="android:paddingStart">36dip</item>
<item name="android:paddingEnd">36dip</item>
- <item name="android:textSize">24sp</item>
+ <item name="android:textSize">28dip</item>
</style>
<style name="PadButtonStyle.Advanced">
<item name="android:background">@drawable/pad_button_advanced_background</item>
<item name="android:textColor">@color/pad_button_advanced_text_color</item>
- <item name="android:textSize">15sp</item>
+ <item name="android:textSize">17dip</item>
</style>
<style name="PadButtonStyle.Advanced.Text">
<item name="android:textAllCaps">true</item>
- <item name="android:textSize">13sp</item>
+ <item name="android:textSize">14dip</item>
</style>
<style name="PadButtonStyle.Numeric">
- <item name="android:textSize">23sp</item>
+ <item name="android:textSize">20dip</item>
</style>
<style name="PadButtonStyle.Numeric.Equals">
@@ -54,37 +55,37 @@
</style>
<style name="PadButtonStyle.Operator">
- <item name="android:textSize">20sp</item>
+ <item name="android:textSize">17dip</item>
</style>
<style name="PadButtonStyle.Operator.Text">
<item name="android:textAllCaps">true</item>
- <item name="android:textSize">13sp</item>
+ <item name="android:textSize">14dip</item>
</style>
<style name="PadLayoutStyle.Advanced">
+ <item name="android:layout_weight">5</item>
<item name="android:layout_width">0dip</item>
- <item name="android:layout_weight">208</item>
- <item name="android:paddingTop">4dip</item>
- <item name="android:paddingBottom">4dip</item>
- <item name="android:paddingStart">8dip</item>
- <item name="android:paddingEnd">8dip</item>
+ <item name="android:paddingTop">2dip</item>
+ <item name="android:paddingBottom">8dip</item>
+ <item name="android:paddingStart">12dip</item>
+ <item name="android:paddingEnd">12dip</item>
</style>
<style name="PadLayoutStyle.Numeric">
<item name="android:layout_width">0dip</item>
- <item name="android:layout_weight">240</item>
- <item name="android:paddingTop">4dip</item>
- <item name="android:paddingBottom">4dip</item>
- <item name="android:paddingStart">8dip</item>
- <item name="android:paddingEnd">8dip</item>
+ <item name="android:layout_weight">5</item>
+ <item name="android:paddingTop">2dip</item>
+ <item name="android:paddingBottom">8dip</item>
+ <item name="android:paddingStart">12dip</item>
+ <item name="android:paddingEnd">12dip</item>
</style>
<style name="PadLayoutStyle.Operator">
<item name="android:layout_width">0dip</item>
- <item name="android:layout_weight">144</item>
- <item name="android:paddingTop">4dip</item>
- <item name="android:paddingBottom">4dip</item>
+ <item name="android:layout_weight">3</item>
+ <item name="android:paddingTop">2dip</item>
+ <item name="android:paddingBottom">8dip</item>
<item name="android:paddingStart">12dip</item>
<item name="android:paddingEnd">12dip</item>
</style>
diff --git a/res/values-land/layout.xml b/res/values-w520dp-h375dp-land/layout.xml
similarity index 71%
copy from res/values-land/layout.xml
copy to res/values-w520dp-h375dp-land/layout.xml
index 8bc268c..15b5b1b 100644
--- a/res/values-land/layout.xml
+++ b/res/values-w520dp-h375dp-land/layout.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2014 The Android Open Source Project
+ Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -17,4 +17,7 @@
<resources>
<item name="activity_calculator" type="layout">@layout/activity_calculator_land</item>
-</resources>
\ No newline at end of file
+ <item name="display" type="layout">@layout/display_two_line</item>
+ <item name="pad_advanced" type="layout">@layout/pad_advanced_3x5</item>
+ <item name="pad_operator" type="layout">@layout/pad_operator_two_col</item>
+</resources>
diff --git a/res/values-land/styles.xml b/res/values-w520dp-h375dp-land/styles.xml
similarity index 63%
copy from res/values-land/styles.xml
copy to res/values-w520dp-h375dp-land/styles.xml
index 3730145..d89ea24 100644
--- a/res/values-land/styles.xml
+++ b/res/values-w520dp-h375dp-land/styles.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2014 The Android Open Source Project
+ Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -15,38 +15,39 @@
limitations under the License.
-->
-<!-- Styles for landscape phone (e.g. Nexus 4/5). -->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<style name="DisplayTextStyle.Formula">
<item name="android:paddingTop">2dip</item>
- <item name="android:paddingBottom">6dip</item>
+ <item name="android:paddingBottom">10dip</item>
<item name="android:paddingStart">36dip</item>
<item name="android:paddingEnd">36dip</item>
- <item name="android:textSize">24sp</item>
+ <item name="minTextSize">32dip</item>
+ <item name="maxTextSize">32dip</item>
+ <item name="stepTextSize">8dip</item>
</style>
<style name="DisplayTextStyle.Result">
- <item name="android:paddingTop">8dip</item>
- <item name="android:paddingBottom">12dip</item>
+ <item name="android:paddingTop">12dip</item>
+ <item name="android:paddingBottom">18dip</item>
<item name="android:paddingStart">36dip</item>
<item name="android:paddingEnd">36dip</item>
- <item name="android:textSize">24sp</item>
+ <item name="android:textSize">32dip</item>
</style>
<style name="PadButtonStyle.Advanced">
<item name="android:background">@drawable/pad_button_advanced_background</item>
<item name="android:textColor">@color/pad_button_advanced_text_color</item>
- <item name="android:textSize">15sp</item>
+ <item name="android:textSize">20dip</item>
</style>
<style name="PadButtonStyle.Advanced.Text">
<item name="android:textAllCaps">true</item>
- <item name="android:textSize">13sp</item>
+ <item name="android:textSize">17dip</item>
</style>
<style name="PadButtonStyle.Numeric">
- <item name="android:textSize">23sp</item>
+ <item name="android:textSize">23dip</item>
</style>
<style name="PadButtonStyle.Numeric.Equals">
@@ -54,37 +55,37 @@
</style>
<style name="PadButtonStyle.Operator">
- <item name="android:textSize">20sp</item>
+ <item name="android:textSize">20dip</item>
</style>
<style name="PadButtonStyle.Operator.Text">
<item name="android:textAllCaps">true</item>
- <item name="android:textSize">13sp</item>
+ <item name="android:textSize">17dip</item>
</style>
<style name="PadLayoutStyle.Advanced">
+ <item name="android:layout_weight">5</item>
<item name="android:layout_width">0dip</item>
- <item name="android:layout_weight">208</item>
- <item name="android:paddingTop">4dip</item>
- <item name="android:paddingBottom">4dip</item>
- <item name="android:paddingStart">8dip</item>
- <item name="android:paddingEnd">8dip</item>
+ <item name="android:paddingTop">8dip</item>
+ <item name="android:paddingBottom">8dip</item>
+ <item name="android:paddingStart">12dip</item>
+ <item name="android:paddingEnd">12dip</item>
</style>
<style name="PadLayoutStyle.Numeric">
<item name="android:layout_width">0dip</item>
- <item name="android:layout_weight">240</item>
- <item name="android:paddingTop">4dip</item>
- <item name="android:paddingBottom">4dip</item>
- <item name="android:paddingStart">8dip</item>
- <item name="android:paddingEnd">8dip</item>
+ <item name="android:layout_weight">5</item>
+ <item name="android:paddingTop">8dip</item>
+ <item name="android:paddingBottom">8dip</item>
+ <item name="android:paddingStart">12dip</item>
+ <item name="android:paddingEnd">12dip</item>
</style>
<style name="PadLayoutStyle.Operator">
<item name="android:layout_width">0dip</item>
- <item name="android:layout_weight">144</item>
- <item name="android:paddingTop">4dip</item>
- <item name="android:paddingBottom">4dip</item>
+ <item name="android:layout_weight">3</item>
+ <item name="android:paddingTop">8dip</item>
+ <item name="android:paddingBottom">8dip</item>
<item name="android:paddingStart">12dip</item>
<item name="android:paddingEnd">12dip</item>
</style>
diff --git a/res/values-land/layout.xml b/res/values-w520dp-h500dp-land/layout.xml
similarity index 71%
copy from res/values-land/layout.xml
copy to res/values-w520dp-h500dp-land/layout.xml
index 8bc268c..15b5b1b 100644
--- a/res/values-land/layout.xml
+++ b/res/values-w520dp-h500dp-land/layout.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2014 The Android Open Source Project
+ Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -17,4 +17,7 @@
<resources>
<item name="activity_calculator" type="layout">@layout/activity_calculator_land</item>
-</resources>
\ No newline at end of file
+ <item name="display" type="layout">@layout/display_two_line</item>
+ <item name="pad_advanced" type="layout">@layout/pad_advanced_3x5</item>
+ <item name="pad_operator" type="layout">@layout/pad_operator_two_col</item>
+</resources>
diff --git a/res/values-sw600dp-port/styles.xml b/res/values-w520dp-h500dp-land/styles.xml
similarity index 61%
copy from res/values-sw600dp-port/styles.xml
copy to res/values-w520dp-h500dp-land/styles.xml
index f439521..883f6cd 100644
--- a/res/values-sw600dp-port/styles.xml
+++ b/res/values-w520dp-h500dp-land/styles.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2014 The Android Open Source Project
+ Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -15,41 +15,39 @@
limitations under the License.
-->
-
-<!-- Styles for portrait 600dip-wide tablet (e.g. Nexus 7). -->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<style name="DisplayTextStyle.Formula">
<item name="android:paddingTop">16dip</item>
- <item name="android:paddingBottom">32dip</item>
- <item name="android:paddingStart">44dip</item>
- <item name="android:paddingEnd">44dip</item>
- <item name="minTextSize">48sp</item>
- <item name="maxTextSize">72sp</item>
- <item name="stepTextSize">8sp</item>
+ <item name="android:paddingBottom">28dip</item>
+ <item name="android:paddingStart">16dip</item>
+ <item name="android:paddingEnd">16dip</item>
+ <item name="minTextSize">42dip</item>
+ <item name="maxTextSize">74dip</item>
+ <item name="stepTextSize">8dip</item>
</style>
<style name="DisplayTextStyle.Result">
- <item name="android:paddingTop">20dip</item>
- <item name="android:paddingBottom">48dip</item>
- <item name="android:paddingStart">44dip</item>
- <item name="android:paddingEnd">44dip</item>
- <item name="android:textSize">48sp</item>
+ <item name="android:paddingTop">16dip</item>
+ <item name="android:paddingBottom">42dip</item>
+ <item name="android:paddingStart">16dip</item>
+ <item name="android:paddingEnd">16dip</item>
+ <item name="android:textSize">42dip</item>
</style>
<style name="PadButtonStyle.Advanced">
<item name="android:background">@drawable/pad_button_advanced_background</item>
<item name="android:textColor">@color/pad_button_advanced_text_color</item>
- <item name="android:textSize">27sp</item>
+ <item name="android:textSize">23dip</item>
</style>
<style name="PadButtonStyle.Advanced.Text">
<item name="android:textAllCaps">true</item>
- <item name="android:textSize">24sp</item>
+ <item name="android:textSize">20dip</item>
</style>
<style name="PadButtonStyle.Numeric">
- <item name="android:textSize">36sp</item>
+ <item name="android:textSize">36dip</item>
</style>
<style name="PadButtonStyle.Numeric.Equals">
@@ -57,45 +55,39 @@
</style>
<style name="PadButtonStyle.Operator">
- <item name="android:textSize">36sp</item>
+ <item name="android:textSize">27dip</item>
</style>
<style name="PadButtonStyle.Operator.Text">
<item name="android:textAllCaps">true</item>
- <item name="android:textSize">24sp</item>
+ <item name="android:textSize">20dip</item>
</style>
<style name="PadLayoutStyle.Advanced">
- <item name="android:layout_height">0dip</item>
- <item name="android:layout_weight">264</item>
+ <item name="android:layout_weight">5</item>
+ <item name="android:layout_width">0dip</item>
<item name="android:paddingTop">8dip</item>
<item name="android:paddingBottom">8dip</item>
- <item name="android:paddingStart">16dip</item>
- <item name="android:paddingEnd">16dip</item>
+ <item name="android:paddingStart">12dip</item>
+ <item name="android:paddingEnd">12dip</item>
</style>
<style name="PadLayoutStyle.Numeric">
<item name="android:layout_width">0dip</item>
- <item name="android:layout_weight">500</item>
+ <item name="android:layout_weight">5</item>
<item name="android:paddingTop">8dip</item>
<item name="android:paddingBottom">8dip</item>
- <item name="android:paddingStart">16dip</item>
- <item name="android:paddingEnd">16dip</item>
+ <item name="android:paddingStart">12dip</item>
+ <item name="android:paddingEnd">12dip</item>
</style>
<style name="PadLayoutStyle.Operator">
<item name="android:layout_width">0dip</item>
- <item name="android:layout_weight">264</item>
+ <item name="android:layout_weight">3</item>
<item name="android:paddingTop">8dip</item>
<item name="android:paddingBottom">8dip</item>
- <item name="android:paddingStart">8dip</item>
- <item name="android:paddingEnd">8dip</item>
- </style>
-
- <style name="PadLinearLayoutStyle">
- <item name="android:layout_width">match_parent</item>
- <item name="android:layout_height">0dip</item>
- <item name="android:layout_weight">500</item>
+ <item name="android:paddingStart">12dip</item>
+ <item name="android:paddingEnd">12dip</item>
</style>
</resources>
diff --git a/res/values-land/layout.xml b/res/values-w520dp-h768dp-land/layout.xml
similarity index 71%
copy from res/values-land/layout.xml
copy to res/values-w520dp-h768dp-land/layout.xml
index 8bc268c..15b5b1b 100644
--- a/res/values-land/layout.xml
+++ b/res/values-w520dp-h768dp-land/layout.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2014 The Android Open Source Project
+ Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -17,4 +17,7 @@
<resources>
<item name="activity_calculator" type="layout">@layout/activity_calculator_land</item>
-</resources>
\ No newline at end of file
+ <item name="display" type="layout">@layout/display_two_line</item>
+ <item name="pad_advanced" type="layout">@layout/pad_advanced_3x5</item>
+ <item name="pad_operator" type="layout">@layout/pad_operator_two_col</item>
+</resources>
diff --git a/res/values-w520dp-h768dp-land/styles.xml b/res/values-w520dp-h768dp-land/styles.xml
new file mode 100644
index 0000000..9fdf68a
--- /dev/null
+++ b/res/values-w520dp-h768dp-land/styles.xml
@@ -0,0 +1,93 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <style name="DisplayTextStyle.Formula">
+ <item name="android:paddingTop">24dip</item>
+ <item name="android:paddingBottom">32dip</item>
+ <item name="android:paddingStart">44dip</item>
+ <item name="android:paddingEnd">44dip</item>
+ <item name="minTextSize">44dip</item>
+ <item name="maxTextSize">76dip</item>
+ <item name="stepTextSize">8dip</item>
+ </style>
+
+ <style name="DisplayTextStyle.Result">
+ <item name="android:paddingTop">24dip</item>
+ <item name="android:paddingBottom">56dip</item>
+ <item name="android:paddingStart">44dip</item>
+ <item name="android:paddingEnd">44dip</item>
+ <item name="android:textSize">44dip</item>
+ </style>
+
+ <style name="PadButtonStyle.Advanced">
+ <item name="android:background">@drawable/pad_button_advanced_background</item>
+ <item name="android:textColor">@color/pad_button_advanced_text_color</item>
+ <item name="android:textSize">30dip</item>
+ </style>
+
+ <style name="PadButtonStyle.Advanced.Text">
+ <item name="android:textAllCaps">true</item>s
+ <item name="android:textSize">26dip</item>
+ </style>
+
+ <style name="PadButtonStyle.Numeric">
+ <item name="android:textSize">38dip</item>
+ </style>
+
+ <style name="PadButtonStyle.Numeric.Equals">
+ <item name="android:visibility">gone</item>
+ </style>
+
+ <style name="PadButtonStyle.Operator">
+ <item name="android:textSize">36dip</item>
+ </style>
+
+ <style name="PadButtonStyle.Operator.Text">
+ <item name="android:textAllCaps">true</item>
+ <item name="android:textSize">26dip</item>
+ </style>
+
+ <style name="PadLayoutStyle.Advanced">
+ <item name="android:layout_weight">5</item>
+ <item name="android:layout_width">0dip</item>
+ <item name="android:paddingTop">8dip</item>
+ <item name="android:paddingBottom">8dip</item>
+ <item name="android:paddingStart">12dip</item>
+ <item name="android:paddingEnd">12dip</item>
+ </style>
+
+ <style name="PadLayoutStyle.Numeric">
+ <item name="android:layout_width">0dip</item>
+ <item name="android:layout_weight">5</item>
+ <item name="android:paddingTop">12dip</item>
+ <item name="android:paddingBottom">20dip</item>
+ <item name="android:paddingStart">12dip</item>
+ <item name="android:paddingEnd">12dip</item>
+ </style>
+
+ <style name="PadLayoutStyle.Operator">
+ <item name="android:layout_width">0dip</item>
+ <item name="android:layout_weight">3</item>
+ <item name="android:paddingTop">8dip</item>
+ <item name="android:paddingBottom">24dip</item>
+ <item name="android:paddingStart">12dip</item>
+ <item name="android:paddingEnd">12dip</item>
+ </style>
+
+</resources>
diff --git a/res/values-sw600dp-port/layout.xml b/res/values-w520dp-h768dp-port/layout.xml
similarity index 71%
copy from res/values-sw600dp-port/layout.xml
copy to res/values-w520dp-h768dp-port/layout.xml
index 46d71cb..98db0fe 100644
--- a/res/values-sw600dp-port/layout.xml
+++ b/res/values-w520dp-h768dp-port/layout.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2014 The Android Open Source Project
+ Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -17,4 +17,7 @@
<resources>
<item name="activity_calculator" type="layout">@layout/activity_calculator_tablet_port</item>
-</resources>
\ No newline at end of file
+ <item name="display" type="layout">@layout/display_two_line</item>
+ <item name="pad_advanced" type="layout">@layout/pad_advanced_5x3</item>
+ <item name="pad_operator" type="layout">@layout/pad_operator_two_col</item>
+</resources>
diff --git a/res/values-sw600dp-port/styles.xml b/res/values-w520dp-h768dp-port/styles.xml
similarity index 79%
rename from res/values-sw600dp-port/styles.xml
rename to res/values-w520dp-h768dp-port/styles.xml
index f439521..4d5e2db 100644
--- a/res/values-sw600dp-port/styles.xml
+++ b/res/values-w520dp-h768dp-port/styles.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- Copyright (C) 2014 The Android Open Source Project
+ Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -15,8 +15,6 @@
limitations under the License.
-->
-
-<!-- Styles for portrait 600dip-wide tablet (e.g. Nexus 7). -->
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<style name="DisplayTextStyle.Formula">
@@ -24,9 +22,9 @@
<item name="android:paddingBottom">32dip</item>
<item name="android:paddingStart">44dip</item>
<item name="android:paddingEnd">44dip</item>
- <item name="minTextSize">48sp</item>
- <item name="maxTextSize">72sp</item>
- <item name="stepTextSize">8sp</item>
+ <item name="minTextSize">48dip</item>
+ <item name="maxTextSize">72dip</item>
+ <item name="stepTextSize">8dip</item>
</style>
<style name="DisplayTextStyle.Result">
@@ -34,22 +32,22 @@
<item name="android:paddingBottom">48dip</item>
<item name="android:paddingStart">44dip</item>
<item name="android:paddingEnd">44dip</item>
- <item name="android:textSize">48sp</item>
+ <item name="android:textSize">48dip</item>
</style>
<style name="PadButtonStyle.Advanced">
<item name="android:background">@drawable/pad_button_advanced_background</item>
<item name="android:textColor">@color/pad_button_advanced_text_color</item>
- <item name="android:textSize">27sp</item>
+ <item name="android:textSize">32dip</item>
</style>
<style name="PadButtonStyle.Advanced.Text">
<item name="android:textAllCaps">true</item>
- <item name="android:textSize">24sp</item>
+ <item name="android:textSize">28dip</item>
</style>
<style name="PadButtonStyle.Numeric">
- <item name="android:textSize">36sp</item>
+ <item name="android:textSize">38dip</item>
</style>
<style name="PadButtonStyle.Numeric.Equals">
@@ -57,12 +55,12 @@
</style>
<style name="PadButtonStyle.Operator">
- <item name="android:textSize">36sp</item>
+ <item name="android:textSize">38dip</item>
</style>
<style name="PadButtonStyle.Operator.Text">
<item name="android:textAllCaps">true</item>
- <item name="android:textSize">24sp</item>
+ <item name="android:textSize">28dip</item>
</style>
<style name="PadLayoutStyle.Advanced">
@@ -92,10 +90,4 @@
<item name="android:paddingEnd">8dip</item>
</style>
- <style name="PadLinearLayoutStyle">
- <item name="android:layout_width">match_parent</item>
- <item name="android:layout_height">0dip</item>
- <item name="android:layout_weight">500</item>
- </style>
-
</resources>
diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml
index 4a0bd18..6152663 100644
--- a/res/values-zh-rCN/strings.xml
+++ b/res/values-zh-rCN/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"删除"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"显示反函数"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"隐藏反函数"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"无任何公式"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"数字和基本操作"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"高级操作"</string>
<string name="error_nan" msgid="5674077944929888710">"不是数字"</string>
<string name="error_syntax" msgid="4786987111228645602">"表达式错误"</string>
<string name="error_aborted" msgid="3402238176316342537">"已中止"</string>
diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml
index cfd5565..c18bf17 100644
--- a/res/values-zh-rHK/strings.xml
+++ b/res/values-zh-rHK/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"刪除"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"顯示反函數"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"隱藏反函數"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"沒有公式"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"數字和基本操作"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"進階操作"</string>
<string name="error_nan" msgid="5674077944929888710">"非數字"</string>
<string name="error_syntax" msgid="4786987111228645602">"算式錯誤"</string>
<string name="error_aborted" msgid="3402238176316342537">"已中止"</string>
diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml
index c13934b..97a7a5b 100644
--- a/res/values-zh-rTW/strings.xml
+++ b/res/values-zh-rTW/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"刪除"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"顯示反函數"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"隱藏反函數"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"沒有公式"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"數字和基本操作"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"進階操作"</string>
<string name="error_nan" msgid="5674077944929888710">"非數字"</string>
<string name="error_syntax" msgid="4786987111228645602">"運算式無效"</string>
<string name="error_aborted" msgid="3402238176316342537">"已中止"</string>
diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml
index 65ac7a9..03ed1f6 100644
--- a/res/values-zu/strings.xml
+++ b/res/values-zu/strings.xml
@@ -57,6 +57,9 @@
<string name="desc_del" msgid="7879515781929311432">"susa"</string>
<string name="desc_inv_off" msgid="3672218250519616068">"bonisa imisebenzi yemuva"</string>
<string name="desc_inv_on" msgid="2515675590767677178">"fihla imisebenzi yemuva"</string>
+ <string name="desc_formula" msgid="8056588859637585007">"Ayikho ifomula"</string>
+ <string name="desc_num_pad" msgid="515720457459745571">"Izinombolo nokusebenza okuyisisekelo"</string>
+ <string name="desc_adv_pad" msgid="3794276256462677914">"Ukusebenza okuthuthukisiwe"</string>
<string name="error_nan" msgid="5674077944929888710">"Akuyona inombolo"</string>
<string name="error_syntax" msgid="4786987111228645602">"Ukusho okubi"</string>
<string name="error_aborted" msgid="3402238176316342537">"Kukhanseliwe"</string>
diff --git a/res/values-port/dimens.xml b/res/values/dimens.xml
similarity index 93%
rename from res/values-port/dimens.xml
rename to res/values/dimens.xml
index dd66e9a..5218acd 100644
--- a/res/values-port/dimens.xml
+++ b/res/values/dimens.xml
@@ -18,6 +18,6 @@
<resources>
<!-- The margin between the pad pages when displayed using a view pager. -->
- <dimen name="pad_page_margin">-24dip</dimen>
+ <dimen name="pad_page_margin">24dip</dimen>
</resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 50bc983..d3d6b18 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -213,6 +213,19 @@
<!-- Content description for "inv" button to hide inverse functions. [CHAR_LIMIT=NONE] -->
<string name="desc_inv_on">hide inverse functions</string>
+ <!-- Content description for formula field when it is empty. [CHAR_LIMIT=NONE] -->
+ <string name="desc_formula">No formula</string>
+
+ <!-- Content description for the numeric/operation pad when slide-able. [CHAR_LIMIT=NONE] -->
+ <string name="desc_num_pad">Numbers and basic operations</string>
+ <!-- Content description for the advanced pad when slide-able. [CHAR_LIMIT=NONE] -->
+ <string name="desc_adv_pad">Advanced operations</string>
+ <!-- Content description for the pad pages when slide-able. -->
+ <string-array name="desc_pad_pages" translatable="false">
+ <item>@string/desc_num_pad</item>
+ <item>@string/desc_adv_pad</item>
+ </string-array>
+
<!-- Error displayed when expression evaluates to an undefined result. [CHAR_LIMIT=20] -->
<string name="error_nan">Not a number</string>
<!-- Error displayed when expression contains a syntax error. [CHAR_LIMIT=20] -->
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 2a074e8..4935103 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -17,24 +17,6 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <style name="Theme" parent="@android:style/Theme.Material.Light.DarkActionBar">
- <item name="android:colorPrimary">@color/calculator_accent_color</item>
- <item name="android:statusBarColor">@color/calculator_accent_color</item>
- <item name="android:windowSoftInputMode">stateAlwaysHidden</item>
- </style>
-
- <style name="Theme.Calculator">
- <item name="android:windowActionBar">false</item>
- <item name="android:windowActionModeOverlay">true</item>
- <item name="android:windowContentOverlay">@null</item>
- <item name="android:windowNoTitle">true</item>
- <item name="android:layoutDirection">ltr</item>
- </style>
-
- <style name="Theme.Licenses">
- <item name="android:title">@string/menu_licenses</item>
- </style>
-
<style name="DisplayTextStyle" parent="@android:style/Widget.Material.Light.TextView">
<item name="android:background">@android:color/transparent</item>
<item name="android:cursorVisible">false</item>
diff --git a/res/values/themes.xml b/res/values/themes.xml
new file mode 100644
index 0000000..b8c7600
--- /dev/null
+++ b/res/values/themes.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ -->
+
+<resources>
+
+ <style name="Theme" parent="@android:style/Theme.Material.Light.DarkActionBar">
+ <item name="android:colorPrimary">@color/calculator_accent_color</item>
+ <item name="android:statusBarColor">@color/calculator_accent_color</item>
+ <item name="android:windowSoftInputMode">stateAlwaysHidden</item>
+ </style>
+
+ <style name="Theme.Calculator">
+ <item name="android:windowActionBar">false</item>
+ <item name="android:windowActionModeOverlay">true</item>
+ <item name="android:windowContentOverlay">@null</item>
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:layoutDirection">ltr</item>
+ </style>
+
+ <style name="Theme.Licenses">
+ <item name="android:title">@string/menu_licenses</item>
+ </style>
+
+</resources>
diff --git a/src/com/android/calculator2/AlertDialogFragment.java b/src/com/android/calculator2/AlertDialogFragment.java
index 49f9549..47f482f 100644
--- a/src/com/android/calculator2/AlertDialogFragment.java
+++ b/src/com/android/calculator2/AlertDialogFragment.java
@@ -20,7 +20,6 @@
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
-import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.Nullable;
@@ -41,7 +40,7 @@
* {@link DialogInterface#BUTTON_POSITIVE}) or the position
* of the item clicked
*/
- public void onClick(AlertDialogFragment fragment, int which);
+ void onClick(AlertDialogFragment fragment, int which);
}
private static final String NAME = AlertDialogFragment.class.getName();
@@ -76,18 +75,21 @@
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Bundle args = getArguments() == null ? Bundle.EMPTY : getArguments();
- final Context context = getContext();
- final LayoutInflater inflater = LayoutInflater.from(context);
- final TextView textView = (TextView) inflater.inflate(R.layout.dialog_message,
- null /* root */);
+ final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+
+ final LayoutInflater inflater = LayoutInflater.from(builder.getContext());
+ final TextView textView = (TextView) inflater.inflate(
+ R.layout.dialog_message, null /* root */);
textView.setText(args.getCharSequence(KEY_MESSAGE));
- final AlertDialog.Builder builder = new AlertDialog.Builder(context)
- .setView(textView)
- .setNegativeButton(args.getCharSequence(KEY_BUTTON_NEGATIVE), null /* listener */);
+
+ builder.setView(textView);
+ builder.setNegativeButton(args.getCharSequence(KEY_BUTTON_NEGATIVE), null /* listener */);
+
final CharSequence positiveButtonLabel = args.getCharSequence(KEY_BUTTON_POSITIVE);
if (positiveButtonLabel != null) {
builder.setPositiveButton(positiveButtonLabel, this);
}
+
return builder.create();
}
diff --git a/src/com/android/calculator2/BoundedRational.java b/src/com/android/calculator2/BoundedRational.java
index dc132f6..e9e6f05 100644
--- a/src/com/android/calculator2/BoundedRational.java
+++ b/src/com/android/calculator2/BoundedRational.java
@@ -16,6 +16,7 @@
package com.android.calculator2;
+import java.util.Random;
import java.math.BigInteger;
import com.hp.creals.CR;
@@ -35,7 +36,7 @@
// much faster.
// TODO: Maybe eventually make this extend Number?
- private static final int MAX_SIZE = 800; // total, in bits
+ private static final int MAX_SIZE = 10000; // total, in bits
private final BigInteger mNum;
private final BigInteger mDen;
@@ -70,11 +71,11 @@
/**
* Convert to readable String.
- * Intended for output output to user. More expensive, less useful for debugging than
+ * Intended for output to user. More expensive, less useful for debugging than
* toString(). Not internationalized.
*/
public String toNiceString() {
- BoundedRational nicer = reduce().positiveDen();
+ final BoundedRational nicer = reduce().positiveDen();
String result = nicer.mNum.toString();
if (!nicer.mDen.equals(BigInteger.ONE)) {
result += "/" + nicer.mDen;
@@ -90,20 +91,51 @@
}
/**
+ * Returns a truncated (rounded towards 0) representation of the result.
+ * Includes n digits to the right of the decimal point.
+ * @param n result precision, >= 0
+ */
+ public String toStringTruncated(int n) {
+ String digits = mNum.abs().multiply(BigInteger.TEN.pow(n)).divide(mDen.abs()).toString();
+ int len = digits.length();
+ if (len < n + 1) {
+ digits = StringUtils.repeat('0', n + 1 - len) + digits;
+ len = n + 1;
+ }
+ return (signum() < 0 ? "-" : "") + digits.substring(0, len - n) + "."
+ + digits.substring(len - n);
+ }
+
+ /**
* Return a double approximation.
- * Primarily for debugging.
+ * The result is correctly rounded if numerator and denominator are
+ * exactly representable as a double.
+ * TODO: This should always be correctly rounded.
*/
public double doubleValue() {
return mNum.doubleValue() / mDen.doubleValue();
}
- public CR CRValue() {
+ public CR crValue() {
return CR.valueOf(mNum).divide(CR.valueOf(mDen));
}
+ public int intValue() {
+ BoundedRational reduced = reduce();
+ if (!reduced.mDen.equals(BigInteger.ONE)) {
+ throw new ArithmeticException("intValue of non-int");
+ }
+ return reduced.mNum.intValue();
+ }
+
// Approximate number of bits to left of binary point.
+ // Negative indicates leading zeroes to the right of binary point.
public int wholeNumberBits() {
- return mNum.bitLength() - mDen.bitLength();
+ if (mNum.signum() == 0) {
+ return Integer.MIN_VALUE;
+ } else {
+ return mNum.bitLength() - mDen.bitLength();
+ }
}
private boolean tooBig() {
@@ -135,12 +167,15 @@
return new BoundedRational(mNum.divide(divisor), mDen.divide(divisor));
}
+ static Random sReduceRng = new Random();
+
/**
* Return a possibly reduced version of this that's not tooBig().
* Return null if none exists.
*/
private BoundedRational maybeReduce() {
- if (!tooBig()) {
+ // Reduce randomly, with 1/16 probability, or if the result is too big.
+ if (!tooBig() && (sReduceRng.nextInt() & 0xf) != 0) {
return this;
}
BoundedRational result = positiveDen();
@@ -193,6 +228,10 @@
return new BoundedRational(num,den).maybeReduce();
}
+ /**
+ * Return the argument, but with the opposite sign.
+ * Returns null only for a null argument.
+ */
public static BoundedRational negate(BoundedRational r) {
if (r == null) {
return null;
@@ -204,15 +243,29 @@
return add(r1, negate(r2));
}
- static BoundedRational multiply(BoundedRational r1, BoundedRational r2) {
+ /**
+ * Return product of r1 and r2 without reducing the result.
+ */
+ private static BoundedRational rawMultiply(BoundedRational r1, BoundedRational r2) {
// It's tempting but marginally unsound to reduce 0 * null to 0. The null could represent
// an infinite value, for which we failed to throw an exception because it was too big.
if (r1 == null || r2 == null) {
return null;
}
+ // Optimize the case of our special ONE constant, since that's cheap and somewhat frequent.
+ if (r1 == ONE) {
+ return r2;
+ }
+ if (r2 == ONE) {
+ return r1;
+ }
final BigInteger num = r1.mNum.multiply(r2.mNum);
final BigInteger den = r1.mDen.multiply(r2.mDen);
- return new BoundedRational(num,den).maybeReduce();
+ return new BoundedRational(num,den);
+ }
+
+ static BoundedRational multiply(BoundedRational r1, BoundedRational r2) {
+ return rawMultiply(r1, r2).maybeReduce();
}
public static class ZeroDivisionException extends ArithmeticException {
@@ -222,7 +275,7 @@
}
/**
- * Return the reciprocal of r (or null).
+ * Return the reciprocal of r (or null if the argument was null).
*/
static BoundedRational inverse(BoundedRational r) {
if (r == null) {
@@ -261,10 +314,15 @@
public final static BoundedRational ZERO = new BoundedRational(0);
public final static BoundedRational HALF = new BoundedRational(1,2);
public final static BoundedRational MINUS_HALF = new BoundedRational(-1,2);
+ public final static BoundedRational THIRD = new BoundedRational(1,3);
+ public final static BoundedRational QUARTER = new BoundedRational(1,4);
+ public final static BoundedRational SIXTH = new BoundedRational(1,6);
public final static BoundedRational ONE = new BoundedRational(1);
public final static BoundedRational MINUS_ONE = new BoundedRational(-1);
public final static BoundedRational TWO = new BoundedRational(2);
public final static BoundedRational MINUS_TWO = new BoundedRational(-2);
+ public final static BoundedRational TEN = new BoundedRational(10);
+ public final static BoundedRational TWELVE = new BoundedRational(12);
public final static BoundedRational THIRTY = new BoundedRational(30);
public final static BoundedRational MINUS_THIRTY = new BoundedRational(-30);
public final static BoundedRational FORTY_FIVE = new BoundedRational(45);
@@ -272,195 +330,40 @@
public final static BoundedRational NINETY = new BoundedRational(90);
public final static BoundedRational MINUS_NINETY = new BoundedRational(-90);
- private static BoundedRational map0to0(BoundedRational r) {
- if (r == null) {
- return null;
- }
- if (r.mNum.signum() == 0) {
- return ZERO;
- }
- return null;
- }
-
- private static BoundedRational map0to1(BoundedRational r) {
- if (r == null) {
- return null;
- }
- if (r.mNum.signum() == 0) {
- return ONE;
- }
- return null;
- }
-
- private static BoundedRational map1to0(BoundedRational r) {
- if (r == null) {
- return null;
- }
- if (r.mNum.equals(r.mDen)) {
- return ZERO;
- }
- return null;
- }
-
- // Throw an exception if the argument is definitely out of bounds for asin or acos.
- private static void checkAsinDomain(BoundedRational r) {
- if (r == null) {
- return;
- }
- if (r.mNum.abs().compareTo(r.mDen.abs()) > 0) {
- throw new ArithmeticException("inverse trig argument out of range");
- }
- }
-
- public static BoundedRational sin(BoundedRational r) {
- return map0to0(r);
- }
-
- private final static BigInteger BIG360 = BigInteger.valueOf(360);
-
- public static BoundedRational degreeSin(BoundedRational r) {
- final BigInteger r_BI = asBigInteger(r);
- if (r_BI == null) {
- return null;
- }
- final int r_int = r_BI.mod(BIG360).intValue();
- if (r_int % 30 != 0) {
- return null;
- }
- switch (r_int / 10) {
- case 0:
- return ZERO;
- case 3: // 30 degrees
- return HALF;
- case 9:
- return ONE;
- case 15:
- return HALF;
- case 18: // 180 degrees
- return ZERO;
- case 21:
- return MINUS_HALF;
- case 27:
- return MINUS_ONE;
- case 33:
- return MINUS_HALF;
- default:
- return null;
- }
- }
-
- public static BoundedRational asin(BoundedRational r) {
- checkAsinDomain(r);
- return map0to0(r);
- }
-
- public static BoundedRational degreeAsin(BoundedRational r) {
- checkAsinDomain(r);
- final BigInteger r2_BI = asBigInteger(multiply(r, TWO));
- if (r2_BI == null) {
- return null;
- }
- final int r2_int = r2_BI.intValue();
- // Somewhat surprisingly, it seems to be the case that the following covers all rational
- // cases:
- switch (r2_int) {
- case -2: // Corresponding to -1 argument
- return MINUS_NINETY;
- case -1: // Corresponding to -1/2 argument
- return MINUS_THIRTY;
- case 0:
- return ZERO;
- case 1:
- return THIRTY;
- case 2:
- return NINETY;
- default:
- throw new AssertionError("Impossible asin arg");
- }
- }
-
- public static BoundedRational tan(BoundedRational r) {
- // Unlike the degree case, we cannot check for the singularity, since it occurs at an
- // irrational argument.
- return map0to0(r);
- }
-
- public static BoundedRational degreeTan(BoundedRational r) {
- final BoundedRational degSin = degreeSin(r);
- final BoundedRational degCos = degreeCos(r);
- if (degCos != null && degCos.mNum.signum() == 0) {
- throw new ArithmeticException("Tangent undefined");
- }
- return divide(degSin, degCos);
- }
-
- public static BoundedRational atan(BoundedRational r) {
- return map0to0(r);
- }
-
- public static BoundedRational degreeAtan(BoundedRational r) {
- final BigInteger r_BI = asBigInteger(r);
- if (r_BI == null) {
- return null;
- }
- if (r_BI.abs().compareTo(BigInteger.ONE) > 0) {
- return null;
- }
- final int r_int = r_BI.intValue();
- // Again, these seem to be all rational cases:
- switch (r_int) {
- case -1:
- return MINUS_FORTY_FIVE;
- case 0:
- return ZERO;
- case 1:
- return FORTY_FIVE;
- default:
- throw new AssertionError("Impossible atan arg");
- }
- }
-
- public static BoundedRational cos(BoundedRational r) {
- return map0to1(r);
- }
-
- public static BoundedRational degreeCos(BoundedRational r) {
- return degreeSin(add(r, NINETY));
- }
-
- public static BoundedRational acos(BoundedRational r) {
- checkAsinDomain(r);
- return map1to0(r);
- }
-
- public static BoundedRational degreeAcos(BoundedRational r) {
- final BoundedRational asin_r = degreeAsin(r);
- return subtract(NINETY, asin_r);
- }
-
private static final BigInteger BIG_TWO = BigInteger.valueOf(2);
/**
+ * Compute integral power of this, assuming this has been reduced and exp is >= 0.
+ */
+ private BoundedRational rawPow(BigInteger exp) {
+ if (exp.equals(BigInteger.ONE)) {
+ return this;
+ }
+ if (exp.and(BigInteger.ONE).intValue() == 1) {
+ return rawMultiply(rawPow(exp.subtract(BigInteger.ONE)), this);
+ }
+ if (exp.signum() == 0) {
+ return ONE;
+ }
+ BoundedRational tmp = rawPow(exp.shiftRight(1));
+ if (Thread.interrupted()) {
+ throw new CR.AbortedException();
+ }
+ return rawMultiply(tmp, tmp);
+ }
+
+ /**
* Compute an integral power of this.
*/
- private BoundedRational pow(BigInteger exp) {
+ public BoundedRational pow(BigInteger exp) {
if (exp.signum() < 0) {
return inverse(pow(exp.negate()));
}
if (exp.equals(BigInteger.ONE)) {
return this;
}
- if (exp.and(BigInteger.ONE).intValue() == 1) {
- return multiply(pow(exp.subtract(BigInteger.ONE)), this);
- }
- if (exp.signum() == 0) {
- return ONE;
- }
- BoundedRational tmp = pow(exp.shiftRight(1));
- if (Thread.interrupted()) {
- throw new CR.AbortedException();
- }
- return multiply(tmp, tmp);
+ // Reducing once at the beginning means there's no point in reducing later.
+ return reduce().rawPow(exp);
}
public static BoundedRational pow(BoundedRational base, BoundedRational exp) {
@@ -482,111 +385,6 @@
return base.pow(exp.mNum);
}
- public static BoundedRational ln(BoundedRational r) {
- if (r != null && r.signum() <= 0) {
- throw new ArithmeticException("log(non-positive)");
- }
- return map1to0(r);
- }
-
- public static BoundedRational exp(BoundedRational r) {
- return map0to1(r);
- }
-
- /**
- * Return the base 10 log of n, if n is a power of 10, -1 otherwise.
- * n must be positive.
- */
- private static long b10Log(BigInteger n) {
- // This algorithm is very naive, but we doubt it matters.
- long count = 0;
- while (n.mod(BigInteger.TEN).signum() == 0) {
- if (Thread.interrupted()) {
- throw new CR.AbortedException();
- }
- n = n.divide(BigInteger.TEN);
- ++count;
- }
- if (n.equals(BigInteger.ONE)) {
- return count;
- }
- return -1;
- }
-
- public static BoundedRational log(BoundedRational r) {
- if (r == null) {
- return null;
- }
- if (r.signum() <= 0) {
- throw new ArithmeticException("log(non-positive)");
- }
- r = r.reduce().positiveDen();
- if (r == null) {
- return null;
- }
- if (r.mDen.equals(BigInteger.ONE)) {
- long log = b10Log(r.mNum);
- if (log != -1) {
- return new BoundedRational(log);
- }
- } else if (r.mNum.equals(BigInteger.ONE)) {
- long log = b10Log(r.mDen);
- if (log != -1) {
- return new BoundedRational(-log);
- }
- }
- return null;
- }
-
- /**
- * Generalized factorial.
- * Compute n * (n - step) * (n - 2 * step) * etc. This can be used to compute factorial a bit
- * faster, especially if BigInteger uses sub-quadratic multiplication.
- */
- private static BigInteger genFactorial(long n, long step) {
- if (n > 4 * step) {
- BigInteger prod1 = genFactorial(n, 2 * step);
- if (Thread.interrupted()) {
- throw new CR.AbortedException();
- }
- BigInteger prod2 = genFactorial(n - step, 2 * step);
- if (Thread.interrupted()) {
- throw new CR.AbortedException();
- }
- return prod1.multiply(prod2);
- } else {
- if (n == 0) {
- return BigInteger.ONE;
- }
- BigInteger res = BigInteger.valueOf(n);
- for (long i = n - step; i > 1; i -= step) {
- res = res.multiply(BigInteger.valueOf(i));
- }
- return res;
- }
- }
-
- /**
- * Factorial function.
- * Always produces non-null (or exception) when called on non-null r.
- */
- public static BoundedRational fact(BoundedRational r) {
- if (r == null) {
- return null;
- }
- final BigInteger rAsInt = asBigInteger(r);
- if (rAsInt == null) {
- throw new ArithmeticException("Non-integral factorial argument");
- }
- if (rAsInt.signum() < 0) {
- throw new ArithmeticException("Negative factorial argument");
- }
- if (rAsInt.bitLength() > 30) {
- // Will fail. LongValue() may not work. Punt now.
- throw new ArithmeticException("Factorial argument too big");
- }
- return new BoundedRational(genFactorial(rAsInt.longValue(), 1));
- }
private static final BigInteger BIG_FIVE = BigInteger.valueOf(5);
private static final BigInteger BIG_MINUS_ONE = BigInteger.valueOf(-1);
diff --git a/src/com/android/calculator2/Calculator.java b/src/com/android/calculator2/Calculator.java
index 1cb3880..e2c16b3 100644
--- a/src/com/android/calculator2/Calculator.java
+++ b/src/com/android/calculator2/Calculator.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,9 +31,10 @@
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
+import android.app.ActionBar;
import android.app.Activity;
-import android.app.AlertDialog;
import android.content.ClipData;
+import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Resources;
@@ -42,23 +43,27 @@
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.NonNull;
+import android.support.v4.content.ContextCompat;
import android.support.v4.view.ViewPager;
-import android.text.SpannableString;
+import android.text.Editable;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
-import android.text.style.ForegroundColorSpan;
import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.text.style.ForegroundColorSpan;
import android.util.Property;
+import android.view.ActionMode;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
-import android.view.View.OnKeyListener;
import android.view.View.OnLongClickListener;
import android.view.ViewAnimationUtils;
import android.view.ViewGroupOverlay;
+import android.view.ViewTreeObserver;
import android.view.animation.AccelerateDecelerateInterpolator;
+import android.widget.HorizontalScrollView;
import android.widget.TextView;
import android.widget.Toolbar;
@@ -123,81 +128,55 @@
}
};
- // We currently assume that the formula does not change out from under us in
- // any way. We explicitly handle all input to the formula here.
- private final OnKeyListener mFormulaOnKeyListener = new OnKeyListener() {
+ private static final String NAME = "Calculator";
+ private static final String KEY_DISPLAY_STATE = NAME + "_display_state";
+ private static final String KEY_UNPROCESSED_CHARS = NAME + "_unprocessed_chars";
+ /**
+ * Associated value is a byte array holding the evaluator state.
+ */
+ private static final String KEY_EVAL_STATE = NAME + "_eval_state";
+ private static final String KEY_INVERSE_MODE = NAME + "_inverse_mode";
+
+ private final ViewTreeObserver.OnPreDrawListener mPreDrawListener =
+ new ViewTreeObserver.OnPreDrawListener() {
@Override
- public boolean onKey(View view, int keyCode, KeyEvent keyEvent) {
- stopActionMode();
- // Never consume DPAD key events.
- switch (keyCode) {
- case KeyEvent.KEYCODE_DPAD_UP:
- case KeyEvent.KEYCODE_DPAD_DOWN:
- case KeyEvent.KEYCODE_DPAD_LEFT:
- case KeyEvent.KEYCODE_DPAD_RIGHT:
- return false;
- }
- // Always cancel unrequested in-progress evaluation, so that we don't have
- // to worry about subsequent asynchronous completion.
- // Requested in-progress evaluations are handled below.
- if (mCurrentState != CalculatorState.EVALUATE) {
- mEvaluator.cancelAll(true);
- }
- // In other cases we go ahead and process the input normally after cancelling:
- if (keyEvent.getAction() != KeyEvent.ACTION_UP) {
- return true;
- }
- switch (keyCode) {
- case KeyEvent.KEYCODE_NUMPAD_ENTER:
- case KeyEvent.KEYCODE_ENTER:
- case KeyEvent.KEYCODE_DPAD_CENTER:
- mCurrentButton = mEqualButton;
- onEquals();
- return true;
- case KeyEvent.KEYCODE_DEL:
- mCurrentButton = mDeleteButton;
- onDelete();
- return true;
- default:
- cancelIfEvaluating(false);
- final int raw = keyEvent.getKeyCharacterMap()
- .get(keyCode, keyEvent.getMetaState());
- if ((raw & KeyCharacterMap.COMBINING_ACCENT) != 0) {
- return true; // discard
- }
- // Try to discard non-printing characters and the like.
- // The user will have to explicitly delete other junk that gets past us.
- if (Character.isIdentifierIgnorable(raw)
- || Character.isWhitespace(raw)) {
- return true;
- }
- char c = (char) raw;
- if (c == '=') {
- mCurrentButton = mEqualButton;
- onEquals();
- } else {
- addChars(String.valueOf(c), true);
- redisplayAfterFormulaChange();
- }
+ public boolean onPreDraw() {
+ mFormulaContainer.scrollTo(mFormulaText.getRight(), 0);
+ final ViewTreeObserver observer = mFormulaContainer.getViewTreeObserver();
+ if (observer.isAlive()) {
+ observer.removeOnPreDrawListener(this);
}
return false;
}
};
- private static final String NAME = Calculator.class.getName();
- private static final String KEY_DISPLAY_STATE = NAME + "_display_state";
- private static final String KEY_UNPROCESSED_CHARS = NAME + "_unprocessed_chars";
- private static final String KEY_EVAL_STATE = NAME + "_eval_state";
- // Associated value is a byte array holding both mCalculatorState
- // and the (much more complex) evaluator state.
+ private final TextWatcher mFormulaTextWatcher = new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence charSequence, int start, int count, int after) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence charSequence, int start, int count, int after) {
+ }
+
+ @Override
+ public void afterTextChanged(Editable editable) {
+ final ViewTreeObserver observer = mFormulaContainer.getViewTreeObserver();
+ if (observer.isAlive()) {
+ observer.removeOnPreDrawListener(mPreDrawListener);
+ observer.addOnPreDrawListener(mPreDrawListener);
+ }
+ }
+ };
private CalculatorState mCurrentState;
private Evaluator mEvaluator;
- private View mDisplayView;
+ private CalculatorDisplay mDisplayView;
private TextView mModeView;
private CalculatorText mFormulaText;
private CalculatorResult mResultText;
+ private HorizontalScrollView mFormulaContainer;
private ViewPager mPadViewPager;
private View mDeleteButton;
@@ -221,6 +200,9 @@
// TODO: should probably match this to the error color?
private ForegroundColorSpan mUnprocessedColorSpan = new ForegroundColorSpan(Color.RED);
+ // Whether the display is one line.
+ private boolean mOneLine;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -230,10 +212,19 @@
// Hide all default options in the ActionBar.
getActionBar().setDisplayOptions(0);
- mDisplayView = findViewById(R.id.display);
+ // Ensure the toolbar stays visible while the options menu is displayed.
+ getActionBar().addOnMenuVisibilityListener(new ActionBar.OnMenuVisibilityListener() {
+ @Override
+ public void onMenuVisibilityChanged(boolean isVisible) {
+ mDisplayView.setForceToolbarVisible(isVisible);
+ }
+ });
+
+ mDisplayView = (CalculatorDisplay) findViewById(R.id.display);
mModeView = (TextView) findViewById(R.id.mode);
mFormulaText = (CalculatorText) findViewById(R.id.formula);
mResultText = (CalculatorResult) findViewById(R.id.result);
+ mFormulaContainer = (HorizontalScrollView) findViewById(R.id.formula_container);
mPadViewPager = (ViewPager) findViewById(R.id.pad_pager);
mDeleteButton = findViewById(R.id.del);
@@ -246,6 +237,8 @@
mInverseToggle = (TextView) findViewById(R.id.toggle_inv);
mModeToggle = (TextView) findViewById(R.id.toggle_mode);
+ mOneLine = mResultText.getVisibility() == View.INVISIBLE;
+
mInvertibleButtons = new View[] {
findViewById(R.id.fun_sin),
findViewById(R.id.fun_cos),
@@ -290,12 +283,13 @@
mEvaluator.clear();
}
- mFormulaText.setOnKeyListener(mFormulaOnKeyListener);
mFormulaText.setOnTextSizeChangeListener(this);
mFormulaText.setOnPasteListener(this);
+ mFormulaText.addTextChangedListener(mFormulaTextWatcher);
mDeleteButton.setOnLongClickListener(this);
- onInverseToggled(mInverseToggle.isSelected());
+ onInverseToggled(savedInstanceState != null
+ && savedInstanceState.getBoolean(KEY_INVERSE_MODE));
onModeChanged(mEvaluator.getDegreeMode());
if (mCurrentState != CalculatorState.INPUT) {
@@ -313,6 +307,14 @@
}
@Override
+ protected void onResume() {
+ super.onResume();
+
+ // Always temporarily show the toolbar initially on launch.
+ showAndMaybeHideToolbar();
+ }
+
+ @Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
mEvaluator.cancelAll(true);
// If there's an animation in progress, cancel it first to ensure our state is up-to-date.
@@ -331,6 +333,7 @@
throw new AssertionError("Impossible IO exception", e);
}
outState.putByteArray(KEY_EVAL_STATE, byteArrayStream.toByteArray());
+ outState.putBoolean(KEY_INVERSE_MODE, mInverseToggle.isSelected());
}
// Set the state, updating delete label and display colors.
@@ -352,47 +355,63 @@
mClearButton.setVisibility(View.GONE);
}
+ if (mOneLine) {
+ if (mCurrentState == CalculatorState.RESULT
+ || mCurrentState == CalculatorState.EVALUATE
+ || mCurrentState == CalculatorState.ANIMATE) {
+ mFormulaText.setVisibility(View.VISIBLE);
+ mResultText.setVisibility(View.VISIBLE);
+ } else if (mCurrentState == CalculatorState.ERROR) {
+ mFormulaText.setVisibility(View.INVISIBLE);
+ mResultText.setVisibility(View.VISIBLE);
+ } else {
+ mFormulaText.setVisibility(View.VISIBLE);
+ mResultText.setVisibility(View.INVISIBLE);
+ }
+ }
+
if (mCurrentState == CalculatorState.ERROR) {
- final int errorColor = getColor(R.color.calculator_error_color);
+ final int errorColor =
+ ContextCompat.getColor(this, R.color.calculator_error_color);
mFormulaText.setTextColor(errorColor);
mResultText.setTextColor(errorColor);
getWindow().setStatusBarColor(errorColor);
} else if (mCurrentState != CalculatorState.RESULT) {
- mFormulaText.setTextColor(getColor(R.color.display_formula_text_color));
- mResultText.setTextColor(getColor(R.color.display_result_text_color));
- getWindow().setStatusBarColor(getColor(R.color.calculator_accent_color));
+ mFormulaText.setTextColor(
+ ContextCompat.getColor(this, R.color.display_formula_text_color));
+ mResultText.setTextColor(
+ ContextCompat.getColor(this, R.color.display_result_text_color));
+ getWindow().setStatusBarColor(
+ ContextCompat.getColor(this, R.color.calculator_accent_color));
}
invalidateOptionsMenu();
}
}
- // Stop any active ActionMode. Return true if there was one.
- private boolean stopActionMode() {
- if (mResultText.stopActionMode()) {
+ @Override
+ public void onActionModeStarted(ActionMode mode) {
+ super.onActionModeStarted(mode);
+ if (mode.getTag() == CalculatorText.TAG_ACTION_MODE) {
+ mFormulaContainer.scrollTo(mFormulaText.getRight(), 0);
+ }
+ }
+
+ /**
+ * Stop any active ActionMode or ContextMenu for copy/paste actions.
+ * Return true if there was one.
+ */
+ private boolean stopActionModeOrContextMenu() {
+ if (mResultText.stopActionModeOrContextMenu()) {
return true;
}
- if (mFormulaText.stopActionMode()) {
+ if (mFormulaText.stopActionModeOrContextMenu()) {
return true;
}
return false;
}
@Override
- public void onBackPressed() {
- if (!stopActionMode()) {
- if (mPadViewPager != null && mPadViewPager.getCurrentItem() != 0) {
- // Select the previous pad.
- mPadViewPager.setCurrentItem(mPadViewPager.getCurrentItem() - 1);
- } else {
- // If the user is currently looking at the first pad (or the pad is not paged),
- // allow the system to handle the Back button.
- super.onBackPressed();
- }
- }
- }
-
- @Override
public void onUserInteraction() {
super.onUserInteraction();
@@ -403,12 +422,83 @@
}
}
+ @Override
+ public void onBackPressed() {
+ if (!stopActionModeOrContextMenu()) {
+ if (mPadViewPager != null && mPadViewPager.getCurrentItem() != 0) {
+ // Select the previous pad.
+ mPadViewPager.setCurrentItem(mPadViewPager.getCurrentItem() - 1);
+ } else {
+ // If the user is currently looking at the first pad (or the pad is not paged),
+ // allow the system to handle the Back button.
+ super.onBackPressed();
+ }
+ }
+ }
+
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ // Allow the system to handle special key codes (e.g. "BACK" or "DPAD").
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_BACK:
+ case KeyEvent.KEYCODE_DPAD_UP:
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ return super.onKeyUp(keyCode, event);
+ }
+
+ // Stop the action mode or context menu if it's showing.
+ stopActionModeOrContextMenu();
+
+ // Always cancel unrequested in-progress evaluation, so that we don't have to worry about
+ // subsequent asynchronous completion.
+ // Requested in-progress evaluations are handled below.
+ if (mCurrentState != CalculatorState.EVALUATE) {
+ mEvaluator.cancelAll(true);
+ }
+
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_NUMPAD_ENTER:
+ case KeyEvent.KEYCODE_ENTER:
+ case KeyEvent.KEYCODE_DPAD_CENTER:
+ mCurrentButton = mEqualButton;
+ onEquals();
+ return true;
+ case KeyEvent.KEYCODE_DEL:
+ mCurrentButton = mDeleteButton;
+ onDelete();
+ return true;
+ default:
+ cancelIfEvaluating(false);
+ final int raw = event.getKeyCharacterMap().get(keyCode, event.getMetaState());
+ if ((raw & KeyCharacterMap.COMBINING_ACCENT) != 0) {
+ return true; // discard
+ }
+ // Try to discard non-printing characters and the like.
+ // The user will have to explicitly delete other junk that gets past us.
+ if (Character.isIdentifierIgnorable(raw) || Character.isWhitespace(raw)) {
+ return true;
+ }
+ char c = (char) raw;
+ if (c == '=') {
+ mCurrentButton = mEqualButton;
+ onEquals();
+ } else {
+ addChars(String.valueOf(c), true);
+ redisplayAfterFormulaChange();
+ }
+ return true;
+ }
+ }
+
/**
* Invoked whenever the inverse button is toggled to update the UI.
*
* @param showInverse {@code true} if inverse functions should be shown
*/
private void onInverseToggled(boolean showInverse) {
+ mInverseToggle.setSelected(showInverse);
if (showInverse) {
mInverseToggle.setContentDescription(getString(R.string.desc_inv_on));
for (View invertibleButton : mInvertibleButtons) {
@@ -447,6 +537,9 @@
mModeToggle.setText(R.string.mode_deg);
mModeToggle.setContentDescription(getString(R.string.desc_switch_deg));
}
+
+ // Show the toolbar to highlight the mode change.
+ showAndMaybeHideToolbar();
}
/**
@@ -492,22 +585,53 @@
// TODO: Could do this more incrementally.
redisplayFormula();
setState(CalculatorState.INPUT);
- if (mEvaluator.getExpr().hasInterestingOps()) {
- mEvaluator.evaluateAndShowResult();
- } else {
+ if (haveUnprocessed()) {
mResultText.clear();
+ // Force reevaluation when text is deleted, even if expression is unchanged.
+ mEvaluator.touch();
+ } else {
+ if (mEvaluator.getExpr().hasInterestingOps()) {
+ mEvaluator.evaluateAndShowResult();
+ } else {
+ mResultText.clear();
+ }
+ }
+ }
+
+ /**
+ * Show the toolbar.
+ * Automatically hide it again if it's not relevant to current formula.
+ */
+ private void showAndMaybeHideToolbar() {
+ final boolean shouldBeVisible =
+ mCurrentState == CalculatorState.INPUT && mEvaluator.hasTrigFuncs();
+ mDisplayView.showToolbar(!shouldBeVisible);
+ }
+
+ /**
+ * Display or hide the toolbar depending on calculator state.
+ */
+ private void showOrHideToolbar() {
+ final boolean shouldBeVisible =
+ mCurrentState == CalculatorState.INPUT && mEvaluator.hasTrigFuncs();
+ if (shouldBeVisible) {
+ mDisplayView.showToolbar(false);
+ } else {
+ mDisplayView.hideToolbar();
}
}
public void onButtonClick(View view) {
// Any animation is ended before we get here.
mCurrentButton = view;
- stopActionMode();
+ stopActionModeOrContextMenu();
+
// See onKey above for the rationale behind some of the behavior below:
if (mCurrentState != CalculatorState.EVALUATE) {
// Cancel evaluations that were not specifically requested.
mEvaluator.cancelAll(true);
}
+
final int id = view.getId();
switch (id) {
case R.id.eq:
@@ -518,7 +642,7 @@
break;
case R.id.clr:
onClear();
- break;
+ return; // Toolbar visibility adjusted at end of animation.
case R.id.toggle_inv:
final boolean selected = !mInverseToggle.isSelected();
mInverseToggle.setSelected(selected);
@@ -539,16 +663,23 @@
onModeChanged(mode);
setState(CalculatorState.INPUT);
mResultText.clear();
- if (mEvaluator.getExpr().hasInterestingOps()) {
+ if (!haveUnprocessed() && mEvaluator.getExpr().hasInterestingOps()) {
mEvaluator.evaluateAndShowResult();
}
- break;
+ return; // onModeChanged adjusted toolbar visibility.
default:
cancelIfEvaluating(false);
- addExplicitKeyToExpr(id);
- redisplayAfterFormulaChange();
+ if (haveUnprocessed()) {
+ // For consistency, append as uninterpreted characters.
+ // This may actually be useful for a left parenthesis.
+ addChars(KeyMaps.toString(this, id), true);
+ } else {
+ addExplicitKeyToExpr(id);
+ redisplayAfterFormulaChange();
+ }
break;
}
+ showOrHideToolbar();
}
void redisplayFormula() {
@@ -559,6 +690,8 @@
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
mFormulaText.changeTextTo(formula);
+ mFormulaText.setContentDescription(TextUtils.isEmpty(formula)
+ ? getString(R.string.desc_formula) : null);
}
@Override
@@ -638,11 +771,20 @@
}
}
+ private boolean haveUnprocessed() {
+ return mUnprocessedChars != null && !mUnprocessedChars.isEmpty();
+ }
+
private void onEquals() {
- // In non-INPUT state assume this was redundant and ignore it.
- if (mCurrentState == CalculatorState.INPUT && !mEvaluator.getExpr().isEmpty()) {
- setState(CalculatorState.EVALUATE);
- mEvaluator.requireResult();
+ // Ignore if in non-INPUT state, or if there are no operators.
+ if (mCurrentState == CalculatorState.INPUT) {
+ if (haveUnprocessed()) {
+ setState(CalculatorState.EVALUATE);
+ onError(R.string.error_syntax);
+ } else if (mEvaluator.getExpr().hasInterestingOps()) {
+ setState(CalculatorState.EVALUATE);
+ mEvaluator.requireResult();
+ }
}
}
@@ -655,18 +797,12 @@
// If there is an in-progress explicit evaluation, just cancel it and return.
if (cancelIfEvaluating(false)) return;
setState(CalculatorState.INPUT);
- if (mUnprocessedChars != null) {
- int len = mUnprocessedChars.length();
- if (len > 0) {
- mUnprocessedChars = mUnprocessedChars.substring(0, len-1);
- } else {
- mEvaluator.delete();
- }
+ if (haveUnprocessed()) {
+ mUnprocessedChars = mUnprocessedChars.substring(0, mUnprocessedChars.length() - 1);
} else {
mEvaluator.delete();
}
- if (mEvaluator.getExpr().isEmpty()
- && (mUnprocessedChars == null || mUnprocessedChars.isEmpty())) {
+ if (mEvaluator.getExpr().isEmpty() && !haveUnprocessed()) {
// Resulting formula won't be announced, since it's empty.
announceClearedForAccessibility();
}
@@ -685,7 +821,7 @@
revealView.setBottom(displayRect.bottom);
revealView.setLeft(displayRect.left);
revealView.setRight(displayRect.right);
- revealView.setBackgroundColor(getResources().getColor(colorRes));
+ revealView.setBackgroundColor(ContextCompat.getColor(this, colorRes));
groupOverlay.add(revealView);
final int[] clearLocation = new int[2];
@@ -732,7 +868,7 @@
}
private void onClear() {
- if (mEvaluator.getExpr().isEmpty()) {
+ if (mEvaluator.getExpr().isEmpty() && !haveUnprocessed()) {
return;
}
cancelIfEvaluating(true);
@@ -744,6 +880,7 @@
mResultText.clear();
mEvaluator.clear();
setState(CalculatorState.INPUT);
+ showOrHideToolbar();
redisplayFormula();
}
});
@@ -770,7 +907,6 @@
}
}
-
// Animate movement of result into the top formula slot.
// Result window now remains translated in the top slot while the result is displayed.
// (We convert it back to formula use only when the user provides new input.)
@@ -797,9 +933,15 @@
// Calculate the necessary translations so the result takes the place of the formula and
// the formula moves off the top of the screen.
- final float resultTranslationY = (mFormulaText.getBottom() - mResultText.getBottom())
+ final float resultTranslationY = (mFormulaContainer.getBottom() - mResultText.getBottom())
- (mFormulaText.getPaddingBottom() - mResultText.getPaddingBottom());
- final float formulaTranslationY = -mFormulaText.getBottom();
+ float formulaTranslationY = -mFormulaContainer.getBottom();
+ if (mOneLine) {
+ // Position the result text.
+ mResultText.setY(mResultText.getBottom());
+ formulaTranslationY = -(findViewById(R.id.toolbar).getBottom()
+ + mFormulaContainer.getBottom());
+ }
// Change the result's textColor to match the formula.
final int formulaTextColor = mFormulaText.getCurrentTextColor();
@@ -815,7 +957,8 @@
PropertyValuesHolder.ofFloat(View.SCALE_Y, resultScale),
PropertyValuesHolder.ofFloat(View.TRANSLATION_Y, resultTranslationY)),
ObjectAnimator.ofArgb(mResultText, TEXT_COLOR, formulaTextColor),
- ObjectAnimator.ofFloat(mFormulaText, View.TRANSLATION_Y, formulaTranslationY));
+ ObjectAnimator.ofFloat(mFormulaContainer, View.TRANSLATION_Y,
+ formulaTranslationY));
animatorSet.setDuration(getResources().getInteger(
android.R.integer.config_longAnimTime));
animatorSet.addListener(new AnimatorListenerAdapter() {
@@ -833,7 +976,7 @@
mResultText.setScaleY(resultScale);
mResultText.setTranslationY(resultTranslationY);
mResultText.setTextColor(formulaTextColor);
- mFormulaText.setTranslationY(formulaTranslationY);
+ mFormulaContainer.setTranslationY(formulaTranslationY);
setState(CalculatorState.RESULT);
}
}
@@ -848,7 +991,7 @@
mResultText.setScaleY(1.0f);
mResultText.setTranslationX(0.0f);
mResultText.setTranslationY(0.0f);
- mFormulaText.setTranslationY(0.0f);
+ mFormulaContainer.setTranslationY(0.0f);
mFormulaText.requestFocus();
}
@@ -878,7 +1021,7 @@
// Show the fraction option when displaying a rational result.
menu.findItem(R.id.menu_fraction).setVisible(mCurrentState == CalculatorState.RESULT
- && mEvaluator.getRational() != null);
+ && mEvaluator.getResult().exactlyDisplayable());
return true;
}
@@ -905,14 +1048,14 @@
}
private void displayFraction() {
- BoundedRational result = mEvaluator.getRational();
+ UnifiedReal result = mEvaluator.getResult();
displayMessage(KeyMaps.translateResult(result.toNiceString()));
}
// Display full result to currently evaluated precision
private void displayFull() {
Resources res = getResources();
- String msg = mResultText.getFullText() + " ";
+ String msg = mResultText.getFullText(true /* withSeparators */) + " ";
if (mResultText.fullTextIsExact()) {
msg += res.getString(R.string.exact);
} else {
@@ -926,8 +1069,8 @@
* Map them to the appropriate button pushes when possible. Leftover characters
* are added to mUnprocessedChars, which is presumed to immediately precede the newly
* added characters.
- * @param moreChars Characters to be added.
- * @param explicit These characters were explicitly typed by the user, not pasted.
+ * @param moreChars characters to be added
+ * @param explicit these characters were explicitly typed by the user, not pasted
*/
private void addChars(String moreChars, boolean explicit) {
if (mUnprocessedChars != null) {
@@ -940,8 +1083,13 @@
// Clear display immediately for incomplete function name.
switchToInput(KeyMaps.keyForChar(moreChars.charAt(current)));
}
+ char groupingSeparator = KeyMaps.translateResult(",").charAt(0);
while (current < len) {
char c = moreChars.charAt(current);
+ if (Character.isSpaceChar(c) || c == groupingSeparator) {
+ ++current;
+ continue;
+ }
int k = KeyMaps.keyForChar(c);
if (!explicit) {
int expEnd;
@@ -998,10 +1146,12 @@
// There are characters left, but we can't convert them to button presses.
mUnprocessedChars = moreChars.substring(current);
redisplayAfterFormulaChange();
+ showOrHideToolbar();
return;
}
mUnprocessedChars = null;
redisplayAfterFormulaChange();
+ showOrHideToolbar();
}
@Override
@@ -1027,4 +1177,12 @@
}
return true;
}
+
+ /**
+ * Clean up animation for context menu.
+ */
+ @Override
+ public void onContextMenuClosed(Menu menu) {
+ stopActionModeOrContextMenu();
+ }
}
diff --git a/src/com/android/calculator2/CalculatorDisplay.java b/src/com/android/calculator2/CalculatorDisplay.java
new file mode 100644
index 0000000..728fc11
--- /dev/null
+++ b/src/com/android/calculator2/CalculatorDisplay.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.calculator2;
+
+import android.content.Context;
+import android.transition.Fade;
+import android.transition.Transition;
+import android.transition.TransitionManager;
+import android.util.AttributeSet;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.accessibility.AccessibilityManager;
+import android.widget.LinearLayout;
+import android.widget.Toolbar;
+
+public class CalculatorDisplay extends LinearLayout
+ implements AccessibilityManager.AccessibilityStateChangeListener {
+
+ /**
+ * The duration in milliseconds after which to hide the toolbar.
+ */
+ private static final long AUTO_HIDE_DELAY_MILLIS = 3000L;
+
+ /**
+ * The duration in milliseconds to fade in/out the toolbar.
+ */
+ private static final long FADE_DURATION = 200L;
+
+ private final Runnable mHideToolbarRunnable = new Runnable() {
+ @Override
+ public void run() {
+ // Remove any duplicate callbacks to hide the toolbar.
+ removeCallbacks(this);
+
+ // Only animate if we have been laid out at least once.
+ if (isLaidOut()) {
+ TransitionManager.beginDelayedTransition(CalculatorDisplay.this, mTransition);
+ }
+ mToolbar.setVisibility(View.INVISIBLE);
+ }
+ };
+
+ private final AccessibilityManager mAccessibilityManager;
+ private final GestureDetector mTapDetector;
+
+ private Toolbar mToolbar;
+ private Transition mTransition;
+
+ private boolean mForceToolbarVisible;
+
+ public CalculatorDisplay(Context context) {
+ this(context, null /* attrs */);
+ }
+
+ public CalculatorDisplay(Context context, AttributeSet attrs) {
+ this(context, attrs, 0 /* defStyleAttr */);
+ }
+
+ public CalculatorDisplay(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+
+ mAccessibilityManager =
+ (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
+
+ mTapDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
+ @Override
+ public boolean onDown(MotionEvent e) {
+ // Remove callbacks to hide the toolbar.
+ removeCallbacks(mHideToolbarRunnable);
+
+ return true;
+ }
+
+ @Override
+ public boolean onSingleTapConfirmed(MotionEvent e) {
+ if (mToolbar.getVisibility() != View.VISIBLE) {
+ showToolbar(true);
+ } else {
+ hideToolbar();
+ }
+
+ return true;
+ }
+ });
+
+ // Draw the children in reverse order so that the toolbar is on top.
+ setChildrenDrawingOrderEnabled(true);
+ }
+
+ @Override
+ protected void onFinishInflate() {
+ super.onFinishInflate();
+
+ mToolbar = (Toolbar) findViewById(R.id.toolbar);
+ mTransition = new Fade()
+ .setDuration(FADE_DURATION)
+ .addTarget(mToolbar);
+ }
+
+ @Override
+ protected int getChildDrawingOrder(int childCount, int i) {
+ // Reverse the normal drawing order.
+ return (childCount - 1) - i;
+ }
+
+ @Override
+ protected void onAttachedToWindow() {
+ super.onAttachedToWindow();
+ mAccessibilityManager.addAccessibilityStateChangeListener(this);
+ }
+
+ @Override
+ protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
+ mAccessibilityManager.removeAccessibilityStateChangeListener(this);
+ }
+
+ @Override
+ public void onAccessibilityStateChanged(boolean enabled) {
+ // Always show the toolbar whenever accessibility is enabled.
+ showToolbar(true);
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent event) {
+ mTapDetector.onTouchEvent(event);
+ return super.onInterceptTouchEvent(event);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ return mTapDetector.onTouchEvent(event) || super.onTouchEvent(event);
+ }
+
+ /**
+ * Returns {@code true} if the toolbar should remain visible.
+ */
+ public boolean getForceToolbarVisible() {
+ return mForceToolbarVisible || mAccessibilityManager.isEnabled();
+ }
+
+ /**
+ * Forces the toolbar to remain visible.
+ *
+ * @param forceToolbarVisible {@code true} to keep the toolbar visible
+ */
+ public void setForceToolbarVisible(boolean forceToolbarVisible) {
+ if (mForceToolbarVisible != forceToolbarVisible) {
+ mForceToolbarVisible = forceToolbarVisible;
+ showToolbar(!forceToolbarVisible);
+ }
+ }
+
+ /**
+ * Shows the toolbar.
+ * @param autoHide Automatically ide toolbar again after delay
+ */
+ public void showToolbar(boolean autoHide) {
+ // Only animate if we have been laid out at least once.
+ if (isLaidOut()) {
+ TransitionManager.beginDelayedTransition(this, mTransition);
+ }
+ mToolbar.setVisibility(View.VISIBLE);
+
+ // Remove callbacks to hide the toolbar.
+ removeCallbacks(mHideToolbarRunnable);
+
+ // Auto hide the toolbar after 3 seconds.
+ if (autoHide && !getForceToolbarVisible()) {
+ postDelayed(mHideToolbarRunnable, AUTO_HIDE_DELAY_MILLIS);
+ }
+ }
+
+ /**
+ * Hides the toolbar.
+ */
+ public void hideToolbar() {
+ if (!getForceToolbarVisible()) {
+ post(mHideToolbarRunnable);
+ }
+ }
+}
diff --git a/src/com/android/calculator2/CalculatorExpr.java b/src/com/android/calculator2/CalculatorExpr.java
index 14d9236..41dfe13 100644
--- a/src/com/android/calculator2/CalculatorExpr.java
+++ b/src/com/android/calculator2/CalculatorExpr.java
@@ -16,10 +16,6 @@
package com.android.calculator2;
-
-import com.hp.creals.CR;
-import com.hp.creals.UnaryCRFunction;
-
import android.content.Context;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
@@ -42,12 +38,13 @@
* A token may also represent the result of a previously evaluated expression.
* The add() method adds a token to the end of the expression. The delete method() removes one.
* Clear() deletes the entire expression contents. Eval() evaluates the expression,
- * producing both a constructive real (CR), and possibly a BoundedRational result.
+ * producing a UnifiedReal result.
* Expressions are parsed only during evaluation; no explicit parse tree is maintained.
*
- * The write() method is used to save the current expression. Note that CR provides no
- * serialization facility. Thus we save all previously computed values by writing out the
- * expression that was used to compute them, and reevaluate on input.
+ * The write() method is used to save the current expression. Note that neither UnifiedReal
+ * nor the underlying CR provide a serialization facility. Thus we save all previously
+ * computed values by writing out the expression that was used to compute them, and reevaluate
+ * when reading it back in.
*/
class CalculatorExpr {
private ArrayList<Token> mExpr; // The actual representation
@@ -201,11 +198,17 @@
/**
* Produce human-readable string representation of constant, as typed.
+ * We do add digit grouping separators to the whole number, even if not typed.
* Result is internationalized.
*/
@Override
public String toString() {
- String result = mWhole;
+ String result;
+ if (mExponent != 0) {
+ result = mWhole;
+ } else {
+ result = StringUtils.addCommas(mWhole, 0, mWhole.length());
+ }
if (mSawDecimal) {
result += '.';
result += mFraction;
@@ -265,11 +268,11 @@
// Hash maps used to detect duplicate subexpressions when we write out CalculatorExprs and
// read them back in.
- private static final ThreadLocal<IdentityHashMap<CR,Integer>>outMap =
- new ThreadLocal<IdentityHashMap<CR,Integer>>();
+ private static final ThreadLocal<IdentityHashMap<UnifiedReal, Integer>>outMap =
+ new ThreadLocal<IdentityHashMap<UnifiedReal, Integer>>();
// Maps expressions to indices on output
- private static final ThreadLocal<HashMap<Integer,PreEval>>inMap =
- new ThreadLocal<HashMap<Integer,PreEval>>();
+ private static final ThreadLocal<HashMap<Integer, PreEval>>inMap =
+ new ThreadLocal<HashMap<Integer, PreEval>>();
// Maps expressions to indices on output
private static final ThreadLocal<Integer> exprIndex = new ThreadLocal<Integer>();
@@ -279,7 +282,7 @@
* This avoids a potential exponential blow-up in the expression size.
*/
public static void initExprOutput() {
- outMap.set(new IdentityHashMap<CR,Integer>());
+ outMap.set(new IdentityHashMap<UnifiedReal, Integer>());
exprIndex.set(Integer.valueOf(0));
}
@@ -288,7 +291,7 @@
* Initializes map that will be used to reconstruct shared subexpressions.
*/
public static void initExprInput() {
- inMap.set(new HashMap<Integer,PreEval>());
+ inMap.set(new HashMap<Integer, PreEval>());
}
/**
@@ -296,31 +299,26 @@
* We treat previously evaluated subexpressions as tokens. These are inserted when we either
* continue an expression after evaluating some of it, or copy an expression and paste it back
* in.
- * The representation includes both CR and possibly BoundedRational values. In order to
+ * The representation includes a UnifiedReal value. In order to
* support saving and restoring, we also include the underlying expression itself, and the
* context (currently just degree mode) used to evaluate it. The short string representation
* is also stored in order to avoid potentially expensive recomputation in the UI thread.
*/
private static class PreEval extends Token {
- public final CR value;
- public final BoundedRational ratValue;
+ public final UnifiedReal value;
private final CalculatorExpr mExpr;
private final EvalContext mContext;
private final String mShortRep; // Not internationalized.
- PreEval(CR val, BoundedRational ratVal, CalculatorExpr expr,
- EvalContext ec, String shortRep) {
+ PreEval(UnifiedReal val, CalculatorExpr expr, EvalContext ec, String shortRep) {
value = val;
- ratValue = ratVal;
mExpr = expr;
mContext = ec;
mShortRep = shortRep;
}
- // In writing out PreEvals, we are careful to avoid writing
- // out duplicates. We assume that two expressions are
- // duplicates if they have the same CR value. This avoids a
- // potential exponential blow up in certain off cases and
- // redundant evaluation after reading them back in.
- // The parameter hash map maps expressions we've seen
+ // In writing out PreEvals, we are careful to avoid writing out duplicates. We conclude
+ // that two expressions are duplicates if they have the same UnifiedReal value. This
+ // avoids a potential exponential blow up in certain off cases and redundant evaluation
+ // after reading them back in. The parameter hash map maps expressions we've seen
// before to their index.
@Override
public void write(DataOutput out) throws IOException {
@@ -359,12 +357,10 @@
Log.e("Calculator", "Unexpected syntax exception" + e);
}
value = res.val;
- ratValue = res.ratVal;
mShortRep = in.readUTF();
inMap.get().put(index, this);
} else {
value = prev.value;
- ratValue = prev.ratValue;
mExpr = prev.mExpr;
mContext = prev.mContext;
mShortRep = prev.mShortRep;
@@ -619,28 +615,26 @@
* The caller supplies the value, degree mode, and short string representation, which must
* have been previously computed. Thus this is guaranteed to terminate reasonably quickly.
*/
- public CalculatorExpr abbreviate(CR val, BoundedRational ratVal,
- boolean dm, String sr) {
+ public CalculatorExpr abbreviate(UnifiedReal val, boolean dm, String sr) {
CalculatorExpr result = new CalculatorExpr();
- Token t = new PreEval(val, ratVal, new CalculatorExpr((ArrayList<Token>) mExpr.clone()),
+ @SuppressWarnings("unchecked")
+ Token t = new PreEval(val, new CalculatorExpr((ArrayList<Token>) mExpr.clone()),
new EvalContext(dm, mExpr.size()), sr);
result.mExpr.add(t);
return result;
}
/**
- * Internal evaluation functions return an EvalRet triple.
+ * Internal evaluation functions return an EvalRet pair.
* We compute rational (BoundedRational) results when possible, both as a performance
* optimization, and to detect errors exactly when we can.
*/
private static class EvalRet {
public int pos; // Next position (expression index) to be parsed.
- public final CR val; // Constructive Real result of evaluating subexpression.
- public final BoundedRational ratVal; // Exact Rational value or null.
- EvalRet(int p, CR v, BoundedRational r) {
+ public final UnifiedReal val; // Constructive Real result of evaluating subexpression.
+ EvalRet(int p, UnifiedReal v) {
pos = p;
val = v;
- ratVal = r;
}
}
@@ -664,21 +658,17 @@
}
}
- private final CR RADIANS_PER_DEGREE = CR.PI.divide(CR.valueOf(180));
-
- private final CR DEGREES_PER_RADIAN = CR.valueOf(180).divide(CR.PI);
-
- private CR toRadians(CR x, EvalContext ec) {
+ private UnifiedReal toRadians(UnifiedReal x, EvalContext ec) {
if (ec.mDegreeMode) {
- return x.multiply(RADIANS_PER_DEGREE);
+ return x.multiply(UnifiedReal.RADIANS_PER_DEGREE);
} else {
return x;
}
}
- private CR fromRadians(CR x, EvalContext ec) {
+ private UnifiedReal fromRadians(UnifiedReal x, EvalContext ec) {
if (ec.mDegreeMode) {
- return x.multiply(DEGREES_PER_RADIAN);
+ return x.divide(UnifiedReal.RADIANS_PER_DEGREE);
} else {
return x;
}
@@ -719,255 +709,131 @@
private EvalRet evalUnary(int i, EvalContext ec) throws SyntaxException {
final Token t = mExpr.get(i);
- BoundedRational ratVal;
if (t instanceof Constant) {
Constant c = (Constant)t;
- ratVal = c.toRational();
- return new EvalRet(i+1, ratVal.CRValue(), ratVal);
+ return new EvalRet(i+1,new UnifiedReal(c.toRational()));
}
if (t instanceof PreEval) {
final PreEval p = (PreEval)t;
- return new EvalRet(i+1, p.value, p.ratValue);
+ return new EvalRet(i+1, p.value);
}
EvalRet argVal;
switch(((Operator)(t)).id) {
case R.id.const_pi:
- return new EvalRet(i+1, CR.PI, null);
+ return new EvalRet(i+1, UnifiedReal.PI);
case R.id.const_e:
- return new EvalRet(i+1, REAL_E, null);
+ return new EvalRet(i+1, UnifiedReal.E);
case R.id.op_sqrt:
// Seems to have highest precedence.
// Does not add implicit paren.
// Does seem to accept a leading minus.
if (isOperator(i+1, R.id.op_sub, ec)) {
argVal = evalUnary(i+2, ec);
- ratVal = BoundedRational.sqrt(BoundedRational.negate(argVal.ratVal));
- if (ratVal != null) {
- break;
- }
- return new EvalRet(argVal.pos,
- argVal.val.negate().sqrt(), null);
+ return new EvalRet(argVal.pos, argVal.val.negate().sqrt());
} else {
argVal = evalUnary(i+1, ec);
- ratVal = BoundedRational.sqrt(argVal.ratVal);
- if (ratVal != null) {
- break;
- }
- return new EvalRet(argVal.pos, argVal.val.sqrt(), null);
+ return new EvalRet(argVal.pos, argVal.val.sqrt());
}
case R.id.lparen:
argVal = evalExpr(i+1, ec);
if (isOperator(argVal.pos, R.id.rparen, ec)) {
argVal.pos++;
}
- return new EvalRet(argVal.pos, argVal.val, argVal.ratVal);
+ return new EvalRet(argVal.pos, argVal.val);
case R.id.fun_sin:
argVal = evalExpr(i+1, ec);
if (isOperator(argVal.pos, R.id.rparen, ec)) {
argVal.pos++;
}
- ratVal = ec.mDegreeMode ? BoundedRational.degreeSin(argVal.ratVal)
- : BoundedRational.sin(argVal.ratVal);
- if (ratVal != null) {
- break;
- }
- return new EvalRet(argVal.pos, toRadians(argVal.val,ec).sin(), null);
+ return new EvalRet(argVal.pos, toRadians(argVal.val, ec).sin());
case R.id.fun_cos:
argVal = evalExpr(i+1, ec);
if (isOperator(argVal.pos, R.id.rparen, ec)) {
argVal.pos++;
}
- ratVal = ec.mDegreeMode ? BoundedRational.degreeCos(argVal.ratVal)
- : BoundedRational.cos(argVal.ratVal);
- if (ratVal != null) {
- break;
- }
- return new EvalRet(argVal.pos, toRadians(argVal.val,ec).cos(), null);
+ return new EvalRet(argVal.pos, toRadians(argVal.val,ec).cos());
case R.id.fun_tan:
argVal = evalExpr(i+1, ec);
if (isOperator(argVal.pos, R.id.rparen, ec)) {
argVal.pos++;
}
- ratVal = ec.mDegreeMode ? BoundedRational.degreeTan(argVal.ratVal)
- : BoundedRational.tan(argVal.ratVal);
- if (ratVal != null) {
- break;
- }
- CR argCR = toRadians(argVal.val, ec);
- return new EvalRet(argVal.pos, argCR.sin().divide(argCR.cos()), null);
+ UnifiedReal arg = toRadians(argVal.val, ec);
+ return new EvalRet(argVal.pos, arg.sin().divide(arg.cos()));
case R.id.fun_ln:
argVal = evalExpr(i+1, ec);
if (isOperator(argVal.pos, R.id.rparen, ec)) {
argVal.pos++;
}
- ratVal = BoundedRational.ln(argVal.ratVal);
- if (ratVal != null) {
- break;
- }
- return new EvalRet(argVal.pos, argVal.val.ln(), null);
+ return new EvalRet(argVal.pos, argVal.val.ln());
case R.id.fun_exp:
argVal = evalExpr(i+1, ec);
if (isOperator(argVal.pos, R.id.rparen, ec)) {
argVal.pos++;
}
- ratVal = BoundedRational.exp(argVal.ratVal);
- if (ratVal != null) {
- break;
- }
- return new EvalRet(argVal.pos, argVal.val.exp(), null);
+ return new EvalRet(argVal.pos, argVal.val.exp());
case R.id.fun_log:
argVal = evalExpr(i+1, ec);
if (isOperator(argVal.pos, R.id.rparen, ec)) {
argVal.pos++;
}
- ratVal = BoundedRational.log(argVal.ratVal);
- if (ratVal != null) {
- break;
- }
- return new EvalRet(argVal.pos, argVal.val.ln().divide(CR.valueOf(10).ln()), null);
+ return new EvalRet(argVal.pos, argVal.val.ln().divide(UnifiedReal.TEN.ln()));
case R.id.fun_arcsin:
argVal = evalExpr(i+1, ec);
if (isOperator(argVal.pos, R.id.rparen, ec)) {
argVal.pos++;
}
- ratVal = ec.mDegreeMode ? BoundedRational.degreeAsin(argVal.ratVal)
- : BoundedRational.asin(argVal.ratVal);
- if (ratVal != null) {
- break;
- }
- return new EvalRet(argVal.pos,
- fromRadians(UnaryCRFunction.asinFunction.execute(argVal.val),ec), null);
+ return new EvalRet(argVal.pos, fromRadians(argVal.val.asin(), ec));
case R.id.fun_arccos:
argVal = evalExpr(i+1, ec);
if (isOperator(argVal.pos, R.id.rparen, ec)) {
argVal.pos++;
}
- ratVal = ec.mDegreeMode ? BoundedRational.degreeAcos(argVal.ratVal)
- : BoundedRational.acos(argVal.ratVal);
- if (ratVal != null) {
- break;
- }
- return new EvalRet(argVal.pos,
- fromRadians(UnaryCRFunction.acosFunction.execute(argVal.val),ec), null);
+ return new EvalRet(argVal.pos, fromRadians(argVal.val.acos(), ec));
case R.id.fun_arctan:
argVal = evalExpr(i+1, ec);
if (isOperator(argVal.pos, R.id.rparen, ec)) {
argVal.pos++;
}
- ratVal = ec.mDegreeMode ? BoundedRational.degreeAtan(argVal.ratVal)
- : BoundedRational.atan(argVal.ratVal);
- if (ratVal != null) {
- break;
- }
- return new EvalRet(argVal.pos,
- fromRadians(UnaryCRFunction.atanFunction.execute(argVal.val),ec), null);
+ return new EvalRet(argVal.pos, fromRadians(argVal.val.atan(),ec));
default:
throw new SyntaxException("Unrecognized token in expression");
}
- // We have a rational value.
- return new EvalRet(argVal.pos, ratVal.CRValue(), ratVal);
}
- /**
- * Compute an integral power of a constructive real.
- * Unlike the "general" case using logarithms, this handles a negative base.
- */
- private static CR pow(CR base, BigInteger exp) {
- if (exp.compareTo(BigInteger.ZERO) < 0) {
- return pow(base, exp.negate()).inverse();
- }
- if (exp.equals(BigInteger.ONE)) {
- return base;
- }
- if (exp.and(BigInteger.ONE).intValue() == 1) {
- return pow(base, exp.subtract(BigInteger.ONE)).multiply(base);
- }
- if (exp.equals(BigInteger.ZERO)) {
- return CR.valueOf(1);
- }
- CR tmp = pow(base, exp.shiftRight(1));
- return tmp.multiply(tmp);
- }
-
- // Number of bits past binary point to test for integer-ness.
- private static final int TEST_PREC = -100;
- private static final BigInteger MASK =
- BigInteger.ONE.shiftLeft(-TEST_PREC).subtract(BigInteger.ONE);
- private static final CR REAL_E = CR.valueOf(1).exp();
- private static final CR REAL_ONE_HUNDREDTH = CR.valueOf(100).inverse();
- private static final BoundedRational RATIONAL_ONE_HUNDREDTH = new BoundedRational(1,100);
- private static boolean isApprInt(CR x) {
- BigInteger appr = x.get_appr(TEST_PREC);
- return appr.and(MASK).signum() == 0;
- }
+ private static final UnifiedReal ONE_HUNDREDTH = new UnifiedReal(100).inverse();
private EvalRet evalSuffix(int i, EvalContext ec) throws SyntaxException {
final EvalRet tmp = evalUnary(i, ec);
int cpos = tmp.pos;
- CR crVal = tmp.val;
- BoundedRational ratVal = tmp.ratVal;
+ UnifiedReal val = tmp.val;
+
boolean isFact;
boolean isSquared = false;
while ((isFact = isOperator(cpos, R.id.op_fact, ec)) ||
(isSquared = isOperator(cpos, R.id.op_sqr, ec)) ||
isOperator(cpos, R.id.op_pct, ec)) {
if (isFact) {
- if (ratVal == null) {
- // Assume it was an integer, but we didn't figure it out.
- // KitKat may have used the Gamma function.
- if (!isApprInt(crVal)) {
- throw new ArithmeticException("factorial(non-integer)");
- }
- ratVal = new BoundedRational(crVal.BigIntegerValue());
- }
- ratVal = BoundedRational.fact(ratVal);
- crVal = ratVal.CRValue();
+ val = val.fact();
} else if (isSquared) {
- ratVal = BoundedRational.multiply(ratVal, ratVal);
- if (ratVal == null) {
- crVal = crVal.multiply(crVal);
- } else {
- crVal = ratVal.CRValue();
- }
+ val = val.multiply(val);
} else /* percent */ {
- ratVal = BoundedRational.multiply(ratVal, RATIONAL_ONE_HUNDREDTH);
- if (ratVal == null) {
- crVal = crVal.multiply(REAL_ONE_HUNDREDTH);
- } else {
- crVal = ratVal.CRValue();
- }
+ val = val.multiply(ONE_HUNDREDTH);
}
++cpos;
}
- return new EvalRet(cpos, crVal, ratVal);
+ return new EvalRet(cpos, val);
}
private EvalRet evalFactor(int i, EvalContext ec) throws SyntaxException {
final EvalRet result1 = evalSuffix(i, ec);
int cpos = result1.pos; // current position
- CR crVal = result1.val; // value so far
- BoundedRational ratVal = result1.ratVal; // int value so far
+ UnifiedReal val = result1.val; // value so far
if (isOperator(cpos, R.id.op_pow, ec)) {
final EvalRet exp = evalSignedFactor(cpos + 1, ec);
cpos = exp.pos;
- // Try completely rational evaluation first.
- ratVal = BoundedRational.pow(ratVal, exp.ratVal);
- if (ratVal != null) {
- return new EvalRet(cpos, ratVal.CRValue(), ratVal);
- }
- // Power with integer exponent is defined for negative base.
- // Thus we handle that case separately.
- // We punt if the exponent is an integer computed from irrational
- // values. That wouldn't work reliably with floating point either.
- BigInteger int_exp = BoundedRational.asBigInteger(exp.ratVal);
- if (int_exp != null) {
- crVal = pow(crVal, int_exp);
- } else {
- crVal = crVal.ln().multiply(exp.val).exp();
- }
- ratVal = null;
+ val = val.pow(exp.val);
}
- return new EvalRet(cpos, crVal, ratVal);
+ return new EvalRet(cpos, val);
}
private EvalRet evalSignedFactor(int i, EvalContext ec) throws SyntaxException {
@@ -975,10 +841,8 @@
int cpos = negative ? i + 1 : i;
EvalRet tmp = evalFactor(cpos, ec);
cpos = tmp.pos;
- CR crVal = negative ? tmp.val.negate() : tmp.val;
- BoundedRational ratVal = negative ? BoundedRational.negate(tmp.ratVal)
- : tmp.ratVal;
- return new EvalRet(cpos, crVal, ratVal);
+ final UnifiedReal result = negative ? tmp.val.negate() : tmp.val;
+ return new EvalRet(cpos, result);
}
private boolean canStartFactor(int i) {
@@ -1001,32 +865,21 @@
boolean is_mul = false;
boolean is_div = false;
int cpos = tmp.pos; // Current position in expression.
- CR crVal = tmp.val; // Current value.
- BoundedRational ratVal = tmp.ratVal; // Current rational value.
+ UnifiedReal val = tmp.val; // Current value.
while ((is_mul = isOperator(cpos, R.id.op_mul, ec))
|| (is_div = isOperator(cpos, R.id.op_div, ec))
|| canStartFactor(cpos)) {
if (is_mul || is_div) ++cpos;
tmp = evalSignedFactor(cpos, ec);
if (is_div) {
- ratVal = BoundedRational.divide(ratVal, tmp.ratVal);
- if (ratVal == null) {
- crVal = crVal.divide(tmp.val);
- } else {
- crVal = ratVal.CRValue();
- }
+ val = val.divide(tmp.val);
} else {
- ratVal = BoundedRational.multiply(ratVal, tmp.ratVal);
- if (ratVal == null) {
- crVal = crVal.multiply(tmp.val);
- } else {
- crVal = ratVal.CRValue();
- }
+ val = val.multiply(tmp.val);
}
cpos = tmp.pos;
is_mul = is_div = false;
}
- return new EvalRet(cpos, crVal, ratVal);
+ return new EvalRet(cpos, val);
}
/**
@@ -1055,81 +908,45 @@
return false;
}
Operator op = (Operator) mExpr.get(pos + 2);
- return op.id == R.id.op_add || op.id == R.id.op_sub;
+ return op.id == R.id.op_add || op.id == R.id.op_sub || op.id == R.id.rparen;
}
/**
* Compute the multiplicative factor corresponding to an N% addition or subtraction.
- * @param pos position of Constant or PreEval expression token corresponding to N
- * @param isSubtraction this is a subtraction, as opposed to addition
- * @param ec usable evaluation contex; only length matters
- * @return Rational and CR values; position is pos + 2, i.e. after percent sign
+ * @param pos position of Constant or PreEval expression token corresponding to N.
+ * @param isSubtraction this is a subtraction, as opposed to addition.
+ * @param ec usable evaluation contex; only length matters.
+ * @return UnifiedReal value and position, which is pos + 2, i.e. after percent sign
*/
private EvalRet getPercentFactor(int pos, boolean isSubtraction, EvalContext ec)
throws SyntaxException {
EvalRet tmp = evalUnary(pos, ec);
- BoundedRational ratVal = isSubtraction ? BoundedRational.negate(tmp.ratVal)
- : tmp.ratVal;
- CR crVal = isSubtraction ? tmp.val.negate() : tmp.val;
- ratVal = BoundedRational.add(BoundedRational.ONE,
- BoundedRational.multiply(ratVal, RATIONAL_ONE_HUNDREDTH));
- if (ratVal == null) {
- crVal = CR.ONE.add(crVal.multiply(REAL_ONE_HUNDREDTH));
- } else {
- crVal = ratVal.CRValue();
- }
- return new EvalRet(pos + 2 /* after percent sign */, crVal, ratVal);
+ UnifiedReal val = isSubtraction ? tmp.val.negate() : tmp.val;
+ val = UnifiedReal.ONE.add(val.multiply(ONE_HUNDREDTH));
+ return new EvalRet(pos + 2 /* after percent sign */, val);
}
private EvalRet evalExpr(int i, EvalContext ec) throws SyntaxException {
EvalRet tmp = evalTerm(i, ec);
boolean is_plus;
int cpos = tmp.pos;
- CR crVal = tmp.val;
- BoundedRational ratVal = tmp.ratVal;
+ UnifiedReal val = tmp.val;
while ((is_plus = isOperator(cpos, R.id.op_add, ec))
|| isOperator(cpos, R.id.op_sub, ec)) {
if (isPercent(cpos + 1)) {
tmp = getPercentFactor(cpos + 1, !is_plus, ec);
- ratVal = BoundedRational.multiply(ratVal, tmp.ratVal);
- if (ratVal == null) {
- crVal = crVal.multiply(tmp.val);
- } else {
- crVal = ratVal.CRValue();
- }
+ val = val.multiply(tmp.val);
} else {
tmp = evalTerm(cpos + 1, ec);
if (is_plus) {
- ratVal = BoundedRational.add(ratVal, tmp.ratVal);
- if (ratVal == null) {
- crVal = crVal.add(tmp.val);
- } else {
- crVal = ratVal.CRValue();
- }
+ val = val.add(tmp.val);
} else {
- ratVal = BoundedRational.subtract(ratVal, tmp.ratVal);
- if (ratVal == null) {
- crVal = crVal.subtract(tmp.val);
- } else {
- crVal = ratVal.CRValue();
- }
+ val = val.subtract(tmp.val);
}
}
cpos = tmp.pos;
}
- return new EvalRet(cpos, crVal, ratVal);
- }
-
- /**
- * Externally visible evaluation result.
- */
- public static class EvalResult {
- public final CR val;
- public final BoundedRational ratVal;
- EvalResult (CR v, BoundedRational rv) {
- val = v;
- ratVal = rv;
- }
+ return new EvalRet(cpos, val);
}
/**
@@ -1168,6 +985,21 @@
}
/**
+ * Does the expression contain trig operations?
+ */
+ public boolean hasTrigFuncs() {
+ for (Token t: mExpr) {
+ if (t instanceof Operator) {
+ Operator o = (Operator)t;
+ if (KeyMaps.isTrigFunc(o.id)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
* Evaluate the expression excluding trailing binary operators.
* Errors result in exceptions, most of which are unchecked. Should not be called
* concurrently with modification of the expression. May take a very long time; avoid calling
@@ -1175,8 +1007,8 @@
*
* @param degreeMode use degrees rather than radians
*/
- EvalResult eval(boolean degreeMode) throws SyntaxException
- // And unchecked exceptions thrown by CR
+ UnifiedReal eval(boolean degreeMode) throws SyntaxException
+ // And unchecked exceptions thrown by UnifiedReal, CR,
// and BoundedRational.
{
try {
@@ -1190,7 +1022,7 @@
if (res.pos != prefixLen) {
throw new SyntaxException("Failed to parse full expression");
}
- return new EvalResult(res.val, res.ratVal);
+ return res.val;
} catch (IndexOutOfBoundsException e) {
throw new SyntaxException("Unexpected expression end");
}
diff --git a/src/com/android/calculator2/CalculatorPadViewPager.java b/src/com/android/calculator2/CalculatorPadViewPager.java
index d4520c5..560260b 100644
--- a/src/com/android/calculator2/CalculatorPadViewPager.java
+++ b/src/com/android/calculator2/CalculatorPadViewPager.java
@@ -21,6 +21,7 @@
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
+import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
@@ -34,8 +35,41 @@
}
@Override
- public View instantiateItem(ViewGroup container, int position) {
- return getChildAt(position);
+ public View instantiateItem(ViewGroup container, final int position) {
+ final View child = getChildAt(position);
+
+ // Set a OnClickListener to scroll to item's position when it isn't the current item.
+ child.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ setCurrentItem(position, true /* smoothScroll */);
+ }
+ });
+ // Set an OnTouchListener to always return true for onTouch events so that a touch
+ // sequence cannot pass through the item to the item below.
+ child.setOnTouchListener(new OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ v.onTouchEvent(event);
+ return true;
+ }
+ });
+
+ // Set an OnHoverListener to always return true for onHover events so that focus cannot
+ // pass through the item to the item below.
+ child.setOnHoverListener(new OnHoverListener() {
+ @Override
+ public boolean onHover(View v, MotionEvent event) {
+ v.onHoverEvent(event);
+ return true;
+ }
+ });
+ // Make the item focusable so it can be selected via a11y.
+ child.setFocusable(true);
+ // Set the content description of the item which will be used by a11y to identify it.
+ child.setContentDescription(getPageTitle(position));
+
+ return child;
}
@Override
@@ -52,6 +86,13 @@
public float getPageWidth(int position) {
return position == 1 ? 7.0f / 9.0f : 1.0f;
}
+
+ @Override
+ public CharSequence getPageTitle(int position) {
+ final String[] pageDescriptions = getContext().getResources()
+ .getStringArray(R.array.desc_pad_pages);
+ return pageDescriptions[position];
+ }
};
private final OnPageChangeListener mOnPageChangeListener = new SimpleOnPageChangeListener() {
@@ -59,12 +100,20 @@
public void onPageSelected(int position) {
for (int i = getChildCount() - 1; i >= 0; --i) {
final View child = getChildAt(i);
+ // Only the "peeking" or covered page should be clickable.
+ child.setClickable(i != position);
+
// Prevent clicks and accessibility focus from going through to descendants of
// other pages which are covered by the current page.
- child.setClickable(i == position);
- child.setImportantForAccessibility(i == position
- ? IMPORTANT_FOR_ACCESSIBILITY_AUTO
- : IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
+ if (child instanceof ViewGroup) {
+ final ViewGroup childViewGroup = (ViewGroup) child;
+ for (int j = childViewGroup.getChildCount() - 1; j >= 0; --j) {
+ childViewGroup.getChildAt(j)
+ .setImportantForAccessibility(i == position
+ ? IMPORTANT_FOR_ACCESSIBILITY_AUTO
+ : IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
+ }
+ }
}
}
};
@@ -84,6 +133,29 @@
}
};
+ private final GestureDetector.SimpleOnGestureListener mGestureWatcher =
+ new GestureDetector.SimpleOnGestureListener() {
+ @Override
+ public boolean onDown(MotionEvent e) {
+ // Return true so calls to onSingleTapUp are not blocked.
+ return true;
+ }
+
+ @Override
+ public boolean onSingleTapUp(MotionEvent ev) {
+ if (mClickedItemIndex != -1) {
+ getChildAt(mClickedItemIndex).performClick();
+ mClickedItemIndex = -1;
+ return true;
+ }
+ return super.onSingleTapUp(ev);
+ }
+ };
+
+ private final GestureDetector mGestureDetector;
+
+ private int mClickedItemIndex = -1;
+
public CalculatorPadViewPager(Context context) {
this(context, null /* attrs */);
}
@@ -91,9 +163,12 @@
public CalculatorPadViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
+ mGestureDetector = new GestureDetector(context, mGestureWatcher);
+ mGestureDetector.setIsLongpressEnabled(false);
+
setAdapter(mStaticPagerAdapter);
setBackgroundColor(Color.BLACK);
- setPageMargin(getResources().getDimensionPixelSize(R.dimen.pad_page_margin));
+ setPageMargin(-getResources().getDimensionPixelSize(R.dimen.pad_page_margin));
setPageTransformer(false, mPageTransformer);
addOnPageChangeListener(mOnPageChangeListener);
}
@@ -111,26 +186,48 @@
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
- boolean shouldIntercept = super.onInterceptTouchEvent(ev);
+ // Always intercept touch events when a11y focused since otherwise they will be
+ // incorrectly offset by a11y before being dispatched to children.
+ boolean shouldIntercept = isAccessibilityFocused() || super.onInterceptTouchEvent(ev);
// Only allow the current item to receive touch events.
if (!shouldIntercept && ev.getActionMasked() == MotionEvent.ACTION_DOWN) {
final int x = (int) ev.getX() + getScrollX();
final int y = (int) ev.getY() + getScrollY();
+ // Reset the previously clicked item index.
+ mClickedItemIndex = -1;
+
final int childCount = getChildCount();
for (int i = childCount - 1; i >= 0; --i) {
final int childIndex = getChildDrawingOrder(childCount, i);
final View child = getChildAt(childIndex);
- if (child.getVisibility() == View.VISIBLE
+ if (child.isAccessibilityFocused()) {
+ // If a child is a11y focused then we must always intercept the touch event
+ // since it will be incorrectly offset by a11y.
+ shouldIntercept = true;
+ mClickedItemIndex = childIndex;
+ break;
+ } else if (mClickedItemIndex == -1
+ && child.getVisibility() == VISIBLE
&& x >= child.getLeft() && x < child.getRight()
&& y >= child.getTop() && y < child.getBottom()) {
- shouldIntercept = (childIndex != getCurrentItem());
- break;
+ shouldIntercept = childIndex != getCurrentItem();
+ mClickedItemIndex = childIndex;
+ // continue; since another child may be a11y focused.
}
}
}
return shouldIntercept;
}
+
+ @Override
+ public boolean onTouchEvent(MotionEvent ev) {
+ // Allow both the gesture detector and super to handle the touch event so they both see
+ // the full sequence of events. This should be safe since the gesture detector only
+ // handle clicks and super only handles swipes.
+ mGestureDetector.onTouchEvent(ev);
+ return super.onTouchEvent(ev);
+ }
}
diff --git a/src/com/android/calculator2/CalculatorResult.java b/src/com/android/calculator2/CalculatorResult.java
index 5c96867..234f602 100644
--- a/src/com/android/calculator2/CalculatorResult.java
+++ b/src/com/android/calculator2/CalculatorResult.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,11 +16,15 @@
package com.android.calculator2;
+import android.annotation.TargetApi;
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.ClipboardManager;
import android.content.Context;
import android.graphics.Rect;
+import android.os.Build;
+import android.support.v4.content.ContextCompat;
+import android.support.v4.os.BuildCompat;
import android.text.Layout;
import android.text.Spannable;
import android.text.SpannableString;
@@ -28,8 +32,10 @@
import android.text.TextPaint;
import android.text.style.BackgroundColorSpan;
import android.text.style.ForegroundColorSpan;
+import android.text.style.RelativeSizeSpan;
import android.util.AttributeSet;
import android.view.ActionMode;
+import android.view.ContextMenu;
import android.view.GestureDetector;
import android.view.Menu;
import android.view.MenuInflater;
@@ -41,19 +47,12 @@
// A text widget that is "infinitely" scrollable to the right,
// and obtains the text to display via a callback to Logic.
-public class CalculatorResult extends AlignedTextView {
+public class CalculatorResult extends AlignedTextView implements MenuItem.OnMenuItemClickListener {
static final int MAX_RIGHT_SCROLL = 10000000;
static final int INVALID = MAX_RIGHT_SCROLL + 10000;
// A larger value is unlikely to avoid running out of space
final OverScroller mScroller;
final GestureDetector mGestureDetector;
- class MyTouchListener implements View.OnTouchListener {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- return mGestureDetector.onTouchEvent(event);
- }
- }
- final MyTouchListener mTouchListener = new MyTouchListener();
private Evaluator mEvaluator;
private boolean mScrollable = false;
// A scrollable result is currently displayed.
@@ -67,23 +66,36 @@
// left of the display. Zero means decimal point is barely displayed
// on the right.
private int mLastPos; // Position already reflected in display. Pixels.
- private int mMinPos; // Minimum position before all digits disappear off the right. Pixels.
+ private int mMinPos; // Minimum position to avoid unnecessary blanks on the left. Pixels.
private int mMaxPos; // Maximum position before we start displaying the infinite
// sequence of trailing zeroes on the right. Pixels.
+ private int mWholeLen; // Length of the whole part of current result.
// In the following, we use a suffix of Offset to denote a character position in a numeric
// string relative to the decimal point. Positive is to the right and negative is to
// the left. 1 = tenths position, -1 = units. Integer.MAX_VALUE is sometimes used
// for the offset of the last digit in an a nonterminating decimal expansion.
// We use the suffix "Index" to denote a zero-based index into a string representing a
// result.
- // TODO: Apply the same convention to other classes.
private int mMaxCharOffset; // Character offset from decimal point of rightmost digit
- // that should be displayed. Essentially the same as
+ // that should be displayed, plus the length of any exponent
+ // needed to display that digit.
+ // Limited to MAX_RIGHT_SCROLL. Often the same as:
private int mLsdOffset; // Position of least-significant digit in result
private int mLastDisplayedOffset; // Offset of last digit actually displayed after adding
// exponent.
+ private boolean mWholePartFits; // Scientific notation not needed for initial display.
+ private float mNoExponentCredit;
+ // Fraction of digit width saved by avoiding scientific notation.
+ // Only accessed from UI thread.
+ private boolean mAppendExponent;
+ // The result fits entirely in the display, even with an exponent,
+ // but not with grouping separators. Since the result is not
+ // scrollable, and we do not add the exponent to max. scroll position,
+ // append an exponent insteadd of replacing trailing digits.
private final Object mWidthLock = new Object();
- // Protects the next two fields.
+ // Protects the next five fields. These fields are only
+ // Updated by the UI thread, and read accesses by the UI thread
+ // sometimes do not acquire the lock.
private int mWidthConstraint = -1;
// Our total width in pixels minus space for ellipsis.
private float mCharWidth = 1;
@@ -92,8 +104,15 @@
// TODO: We're not really using a fixed width font. But it appears
// to be close enough for the characters we use that the difference
// is not noticeable.
+ private float mGroupingSeparatorWidthRatio;
+ // Fraction of digit width occupied by a digit separator.
+ private float mDecimalCredit;
+ // Fraction of digit width saved by replacing digit with decimal point.
+ private float mNoEllipsisCredit;
+ // Fraction of digit width saved by both replacing ellipsis with digit
+ // and avoiding scientific notation.
private static final int MAX_WIDTH = 100;
- // Maximum number of digits displayed
+ // Maximum number of digits displayed.
public static final int MAX_LEADING_ZEROES = 6;
// Maximum number of leading zeroes after decimal point before we
// switch to scientific notation with negative exponent.
@@ -105,12 +124,26 @@
// have a decimal point and no ellipsis.
// We assume that we do not drop digits to make room for the decimal
// point in ordinary scientific notation. Thus >= 1.
- private ActionMode mActionMode;
+ private static final int MAX_COPY_EXTRA = 100;
+ // The number of extra digits we are willing to compute to copy
+ // a result as an exact number.
+ private static final int MAX_RECOMPUTE_DIGITS = 2000;
+ // The maximum number of digits we're willing to recompute in the UI
+ // thread. We only do this for known rational results, where we
+ // can bound the computation cost.
private final ForegroundColorSpan mExponentColorSpan;
+ private final BackgroundColorSpan mHighlightSpan;
+
+ private ActionMode mActionMode;
+ private ActionMode.Callback mCopyActionModeCallback;
+ private ContextMenu mContextMenu;
public CalculatorResult(Context context, AttributeSet attrs) {
super(context, attrs);
mScroller = new OverScroller(context);
+ mHighlightSpan = new BackgroundColorSpan(getHighlightColor());
+ mExponentColorSpan = new ForegroundColorSpan(
+ ContextCompat.getColor(context, R.color.display_result_exponent_text_color));
mGestureDetector = new GestureDetector(context,
new GestureDetector.SimpleOnGestureListener() {
@Override
@@ -124,7 +157,7 @@
mCurrentPos = mScroller.getFinalX();
}
mScroller.forceFinished(true);
- stopActionMode();
+ stopActionModeOrContextMenu();
CalculatorResult.this.cancelLongPress();
// Ignore scrolls of error string, etc.
if (!mScrollable) return true;
@@ -141,7 +174,7 @@
mCurrentPos = mScroller.getFinalX();
}
mScroller.forceFinished(true);
- stopActionMode();
+ stopActionModeOrContextMenu();
CalculatorResult.this.cancelLongPress();
if (!mScrollable) return true;
if (mCurrentPos + distance < mMinPos) {
@@ -158,51 +191,152 @@
@Override
public void onLongPress(MotionEvent e) {
if (mValid) {
- mActionMode = startActionMode(mCopyActionModeCallback,
- ActionMode.TYPE_FLOATING);
+ performLongClick();
}
}
});
- setOnTouchListener(mTouchListener);
- setHorizontallyScrolling(false); // do it ourselves
- setCursorVisible(false);
- mExponentColorSpan = new ForegroundColorSpan(
- context.getColor(R.color.display_result_exponent_text_color));
+ setOnTouchListener(new View.OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ return mGestureDetector.onTouchEvent(event);
+ }
+ });
- // Copy ActionMode is triggered explicitly, not through
- // setCustomSelectionActionModeCallback.
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ setupActionMode();
+ } else {
+ setupContextMenu();
+ }
+
+ setCursorVisible(false);
}
void setEvaluator(Evaluator evaluator) {
mEvaluator = evaluator;
}
+ // Compute maximum digit width the hard way.
+ private static float getMaxDigitWidth(TextPaint paint) {
+ // Compute the maximum advance width for each digit, thus accounting for between-character
+ // spaces. If we ever support other kinds of digits, we may have to avoid kerning effects
+ // that could reduce the advance width within this particular string.
+ final String allDigits = "0123456789";
+ final float[] widths = new float[allDigits.length()];
+ paint.getTextWidths(allDigits, widths);
+ float maxWidth = 0;
+ for (float x : widths) {
+ maxWidth = Math.max(x, maxWidth);
+ }
+ return maxWidth;
+ }
+
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ if (!isLaidOut()) {
+ // Set a minimum height so scaled error messages won't affect our layout.
+ setMinimumHeight(getLineHeight() + getCompoundPaddingBottom()
+ + getCompoundPaddingTop());
+ }
final TextPaint paint = getPaint();
final Context context = getContext();
- final float newCharWidth = Layout.getDesiredWidth("\u2007", paint);
+ final float newCharWidth = getMaxDigitWidth(paint);
// Digits are presumed to have no more than newCharWidth.
- // We sometimes replace a character by an ellipsis or, due to SCI_NOTATION_EXTRA, add
- // an extra decimal separator beyond the maximum number of characters we normally allow.
- // Empirically, our minus sign is also slightly wider than a digit, so we have to
- // account for that. We never have both an ellipsis and two minus signs, and
- // we assume an ellipsis is no narrower than a minus sign.
+ // There are two instances when we know that the result is otherwise narrower than
+ // expected:
+ // 1. For standard scientific notation (our type 1), we know that we have a norrow decimal
+ // point and no (usually wide) ellipsis symbol. We allow one extra digit
+ // (SCI_NOTATION_EXTRA) to compensate, and consider that in determining available width.
+ // 2. If we are using digit grouping separators and a decimal point, we give ourselves
+ // a fractional extra space for those separators, the value of which depends on whether
+ // there is also an ellipsis.
+ //
+ // Maximum extra space we need in various cases:
+ // Type 1 scientific notation, assuming ellipsis, minus sign and E are wider than a digit:
+ // Two minus signs + "E" + "." - 3 digits.
+ // Type 2 scientific notation:
+ // Ellipsis + "E" + "-" - 3 digits.
+ // In the absence of scientific notation, we may need a little less space.
+ // We give ourselves a bit of extra credit towards comma insertion and give
+ // ourselves more if we have either
+ // No ellipsis, or
+ // A decimal separator.
+
+ // Calculate extra space we need to reserve, in addition to character count.
final float decimalSeparatorWidth = Layout.getDesiredWidth(
context.getString(R.string.dec_point), paint);
- final float minusExtraWidth = Layout.getDesiredWidth(
- context.getString(R.string.op_sub), paint) - newCharWidth;
- final float ellipsisExtraWidth = Layout.getDesiredWidth(KeyMaps.ELLIPSIS, paint)
- - newCharWidth;
- final int extraWidth = (int) (Math.ceil(Math.max(decimalSeparatorWidth + minusExtraWidth,
- ellipsisExtraWidth)) + Math.max(minusExtraWidth, 0.0f));
+ final float minusWidth = Layout.getDesiredWidth(context.getString(R.string.op_sub), paint);
+ final float minusExtraWidth = Math.max(minusWidth - newCharWidth, 0.0f);
+ final float ellipsisWidth = Layout.getDesiredWidth(KeyMaps.ELLIPSIS, paint);
+ final float ellipsisExtraWidth = Math.max(ellipsisWidth - newCharWidth, 0.0f);
+ final float expWidth = Layout.getDesiredWidth(KeyMaps.translateResult("e"), paint);
+ final float expExtraWidth = Math.max(expWidth - newCharWidth, 0.0f);
+ final float type1Extra = 2 * minusExtraWidth + expExtraWidth + decimalSeparatorWidth;
+ final float type2Extra = ellipsisExtraWidth + expExtraWidth + minusExtraWidth;
+ final float extraWidth = Math.max(type1Extra, type2Extra);
+ final int intExtraWidth = (int) Math.ceil(extraWidth) + 1 /* to cover rounding sins */;
final int newWidthConstraint = MeasureSpec.getSize(widthMeasureSpec)
- - (getPaddingLeft() + getPaddingRight()) - extraWidth;
+ - (getPaddingLeft() + getPaddingRight()) - intExtraWidth;
+
+ // Calculate other width constants we need to handle grouping separators.
+ final float groupingSeparatorW =
+ Layout.getDesiredWidth(KeyMaps.translateResult(","), paint);
+ // Credits in the absence of any scientific notation:
+ float noExponentCredit = extraWidth - Math.max(ellipsisExtraWidth, minusExtraWidth);
+ final float noEllipsisCredit = extraWidth - minusExtraWidth; // includes noExponentCredit.
+ final float decimalCredit = Math.max(newCharWidth - decimalSeparatorWidth, 0.0f);
+
+ mNoExponentCredit = noExponentCredit / newCharWidth;
synchronized(mWidthLock) {
mWidthConstraint = newWidthConstraint;
mCharWidth = newCharWidth;
+ mNoEllipsisCredit = noEllipsisCredit / newCharWidth;
+ mDecimalCredit = decimalCredit / newCharWidth;
+ mGroupingSeparatorWidthRatio = groupingSeparatorW / newCharWidth;
+ }
+
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+
+ /**
+ * Return the number of additional digit widths required to add digit separators to
+ * the supplied string prefix.
+ * The string prefix is assumed to represent a whole number, after skipping leading non-digits.
+ * Callable from non-UI thread.
+ */
+ public float separatorChars(String s, int len) {
+ int start = 0;
+ while (start < len && !Character.isDigit(s.charAt(start))) {
+ ++start;
+ }
+ // We assume the rest consists of digits, and for consistency with the rest
+ // of the code, we assume all digits have width mCharWidth.
+ final int nDigits = len - start;
+ // We currently insert a digit separator every three digits.
+ final int nSeparators = (nDigits - 1) / 3;
+ synchronized(mWidthLock) {
+ // Always return an upper bound, even in the presence of rounding errors.
+ return nSeparators * mGroupingSeparatorWidthRatio;
+ }
+ }
+
+ /**
+ * Return extra width credit for absence of ellipsis, as fraction of a digit width.
+ * May be called by non-UI thread.
+ */
+ public float getNoEllipsisCredit() {
+ synchronized(mWidthLock) {
+ return mNoEllipsisCredit;
+ }
+ }
+
+ /**
+ * Return extra width credit for presence of a decimal point, as fraction of a digit width.
+ * May be called by non-UI thread.
+ */
+ public float getDecimalCredit() {
+ synchronized(mWidthLock) {
+ return mDecimalCredit;
}
}
@@ -217,6 +351,7 @@
/**
* Initiate display of a new result.
+ * Only called from UI thread.
* The parameters specify various properties of the result.
* @param initPrec Initial display precision computed by evaluator. (1 = tenths digit)
* @param msd Position of most significant digit. Offset from left of string.
@@ -238,47 +373,46 @@
* will eventually be replaced by an exponent.
* Just appending the exponent during formatting would be simpler, but would produce
* jumpier results during transitions.
+ * Only called from UI thread.
*/
private void initPositions(int initPrecOffset, int msdIndex, int lsdOffset,
String truncatedWholePart) {
- float charWidth;
int maxChars = getMaxChars();
+ mWholeLen = truncatedWholePart.length();
+ // Allow a tiny amount of slop for associativity/rounding differences in length
+ // calculation. If getPreferredPrec() decided it should fit, we want to make it fit, too.
+ // We reserved one extra pixel, so the extra length is OK.
+ final int nSeparatorChars = (int) Math.ceil(
+ separatorChars(truncatedWholePart, truncatedWholePart.length())
+ - getNoEllipsisCredit() - 0.0001f);
+ mWholePartFits = mWholeLen + nSeparatorChars <= maxChars;
mLastPos = INVALID;
mLsdOffset = lsdOffset;
- synchronized(mWidthLock) {
- charWidth = mCharWidth;
- }
- mCurrentPos = mMinPos = (int) Math.round(initPrecOffset * charWidth);
+ mAppendExponent = false;
// Prevent scrolling past initial position, which is calculated to show leading digits.
+ mCurrentPos = mMinPos = (int) Math.round(initPrecOffset * mCharWidth);
if (msdIndex == Evaluator.INVALID_MSD) {
// Possible zero value
if (lsdOffset == Integer.MIN_VALUE) {
// Definite zero value.
mMaxPos = mMinPos;
- mMaxCharOffset = (int) Math.round(mMaxPos/charWidth);
+ mMaxCharOffset = (int) Math.round(mMaxPos/mCharWidth);
mScrollable = false;
} else {
// May be very small nonzero value. Allow user to find out.
mMaxPos = mMaxCharOffset = MAX_RIGHT_SCROLL;
- mMinPos -= charWidth; // Allow for future minus sign.
+ mMinPos -= mCharWidth; // Allow for future minus sign.
mScrollable = true;
}
return;
}
- int wholeLen = truncatedWholePart.length();
int negative = truncatedWholePart.charAt(0) == '-' ? 1 : 0;
- if (msdIndex > wholeLen && msdIndex <= wholeLen + 3) {
+ if (msdIndex > mWholeLen && msdIndex <= mWholeLen + 3) {
// Avoid tiny negative exponent; pretend msdIndex is just to the right of decimal point.
- msdIndex = wholeLen - 1;
+ msdIndex = mWholeLen - 1;
}
- int minCharOffset = msdIndex - wholeLen;
- // Position of leftmost significant digit relative to dec. point.
- // Usually negative.
- mMaxCharOffset = MAX_RIGHT_SCROLL; // How far does it make sense to scroll right?
- // If msd is left of decimal point should logically be
- // mMinPos = - (int) Math.ceil(getPaint().measureText(truncatedWholePart)), but
- // we eventually translate to a character position by dividing by mCharWidth.
- // To avoid rounding issues, we use the analogous computation here.
+ // Set to position of leftmost significant digit relative to dec. point. Usually negative.
+ int minCharOffset = msdIndex - mWholeLen;
if (minCharOffset > -1 && minCharOffset < MAX_LEADING_ZEROES + 2) {
// Small number of leading zeroes, avoid scientific notation.
minCharOffset = -1;
@@ -293,11 +427,16 @@
if (mMaxCharOffset < -1) {
currentExpLen = expLen(-minCharOffset - 1);
} else if (minCharOffset > -1 || mMaxCharOffset >= maxChars) {
- // Number either entirely to the right of decimal point, or decimal point not
- // visible when scrolled to the right.
+ // Number is either entirely to the right of decimal point, or decimal point is
+ // not visible when scrolled to the right.
currentExpLen = expLen(-minCharOffset);
}
- mScrollable = (mMaxCharOffset + currentExpLen - minCharOffset + negative >= maxChars);
+ // Exponent length does not included added decimal point. But whenever we add a
+ // decimal point, we allow an extra character (SCI_NOTATION_EXTRA).
+ final int separatorLength = mWholePartFits && minCharOffset < -3 ? nSeparatorChars : 0;
+ mScrollable = (mMaxCharOffset + currentExpLen + separatorLength - minCharOffset
+ + negative >= maxChars);
+ // Now adjust mMaxCharOffset for any required exponent.
int newMaxCharOffset;
if (currentExpLen > 0) {
if (mScrollable) {
@@ -310,10 +449,32 @@
// Very unlikely; just drop exponent.
mMaxCharOffset = -1;
} else {
- mMaxCharOffset = newMaxCharOffset;
+ mMaxCharOffset = Math.min(newMaxCharOffset, MAX_RIGHT_SCROLL);
}
+ mMaxPos = Math.min((int) Math.round(mMaxCharOffset * mCharWidth),
+ MAX_RIGHT_SCROLL);
+ } else if (!mWholePartFits && !mScrollable) {
+ // Corner case in which entire number fits, but not with grouping separators. We
+ // will use an exponent in un-scrolled position, which may hide digits. Scrolling
+ // by one character will remove the exponent and reveal the last digits. Note
+ // that in the forced scientific notation case, the exponent length is not
+ // factored into mMaxCharOffset, since we do not want such an increase to impact
+ // scrolling behavior. In the unscrollable case, we thus have to append the
+ // exponent at the end using the forcePrecision argument to formatResult, in order
+ // to ensure that we get the entire result.
+ mScrollable = (mMaxCharOffset + expLen(-minCharOffset - 1) - minCharOffset
+ + negative >= maxChars);
+ if (mScrollable) {
+ mMaxPos = (int) Math.ceil(mMinPos + mCharWidth);
+ // Single character scroll will remove exponent and show remaining piece.
+ } else {
+ mMaxPos = mMinPos;
+ mAppendExponent = true;
+ }
+ } else {
+ mMaxPos = Math.min((int) Math.round(mMaxCharOffset * mCharWidth),
+ MAX_RIGHT_SCROLL);
}
- mMaxPos = Math.min((int) Math.round(mMaxCharOffset * charWidth), MAX_RIGHT_SCROLL);
if (!mScrollable) {
// Position the number consistently with our assumptions to make sure it
// actually fits.
@@ -325,10 +486,25 @@
}
}
+ /**
+ * Display error message indicated by resourceId.
+ * UI thread only.
+ */
void displayError(int resourceId) {
mValid = true;
mScrollable = false;
- setText(resourceId);
+ final String msg = getContext().getString(resourceId);
+ final float measuredWidth = Layout.getDesiredWidth(msg, getPaint());
+ if (measuredWidth > mWidthConstraint) {
+ // Multiply by .99 to avoid rounding effects.
+ final float scaleFactor = 0.99f * mWidthConstraint / measuredWidth;
+ final RelativeSizeSpan smallTextSpan = new RelativeSizeSpan(scaleFactor);
+ final SpannableString scaledMsg = new SpannableString(msg);
+ scaledMsg.setSpan(smallTextSpan, 0, msg.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ setText(scaledMsg);
+ } else {
+ setText(msg);
+ }
}
private final int MAX_COPY_SIZE = 1000000;
@@ -336,9 +512,10 @@
/*
* Return the most significant digit position in the given string or Evaluator.INVALID_MSD.
* Unlike Evaluator.getMsdIndexOf, we treat a final 1 as significant.
+ * Pure function; callable from anywhere.
*/
public static int getNaiveMsdIndexOf(String s) {
- int len = s.length();
+ final int len = s.length();
for (int i = 0; i < len; ++i) {
char c = s.charAt(i);
if (c != '-' && c != '.' && c != '0') {
@@ -348,41 +525,64 @@
return Evaluator.INVALID_MSD;
}
- // Format a result returned by Evaluator.getString() into a single line containing ellipses
- // (if appropriate) and an exponent (if appropriate). precOffset is the value that was passed
- // to getString and thus identifies the significance of the rightmost digit.
- // A value of 1 means the rightmost digits corresponds to tenths.
- // maxDigs is the maximum number of characters in the result.
- // We set lastDisplayedOffset[0] to the offset of the last digit actually appearing in
- // the display.
- // If forcePrecision is true, we make sure that the last displayed digit corresponds to
- // precOffset, and allow maxDigs to be exceeded in assing the exponent.
- // We add two distinct kinds of exponents:
- // (1) If the final result contains the leading digit we use standard scientific notation.
- // (2) If not, we add an exponent corresponding to an interpretation of the final result as
- // an integer.
- // We add an ellipsis on the left if the result was truncated.
- // We add ellipses and exponents in a way that leaves most digits in the position they
- // would have been in had we not done so.
- // This minimizes jumps as a result of scrolling. Result is NOT internationalized,
- // uses "E" for exponent.
- public String formatResult(String in, int precOffset, int maxDigs, boolean truncated,
- boolean negative, int lastDisplayedOffset[], boolean forcePrecision) {
+ /**
+ * Format a result returned by Evaluator.getString() into a single line containing ellipses
+ * (if appropriate) and an exponent (if appropriate).
+ * We add two distinct kinds of exponents:
+ * (1) If the final result contains the leading digit we use standard scientific notation.
+ * (2) If not, we add an exponent corresponding to an interpretation of the final result as
+ * an integer.
+ * We add an ellipsis on the left if the result was truncated.
+ * We add ellipses and exponents in a way that leaves most digits in the position they
+ * would have been in had we not done so. This minimizes jumps as a result of scrolling.
+ * Result is NOT internationalized, uses "E" for exponent.
+ * Called only from UI thread; We sometimes omit locking for fields.
+ * @param precOffset The value that was passed to getString. Identifies the significance of
+ the rightmost digit. A value of 1 means the rightmost digits corresponds to tenths.
+ * @param maxDigs The maximum number of characters in the result
+ * @param truncated The in parameter was already truncated, beyond possibly removing the
+ minus sign.
+ * @param negative The in parameter represents a negative result. (Minus sign may be removed
+ without setting truncated.)
+ * @param lastDisplayedOffset If not null, we set lastDisplayedOffset[0] to the offset of
+ the last digit actually appearing in the display.
+ * @param forcePrecision If true, we make sure that the last displayed digit corresponds to
+ precOffset, and allow maxDigs to be exceeded in adding the exponent and commas.
+ * @param forceSciNotation Force scientific notation. May be set because we don't have
+ space for grouping separators, but whole number otherwise fits.
+ * @param insertCommas Insert commas (literally, not internationalized) as digit separators.
+ We only ever do this for the integral part of a number, and only when no
+ exponent is displayed in the initial position. The combination of which means
+ that we only do it when no exponent is displayed.
+ We insert commas in a way that does consider the width of the actual localized digit
+ separator. Commas count towards maxDigs as the appropriate fraction of a digit.
+ */
+ private String formatResult(String in, int precOffset, int maxDigs, boolean truncated,
+ boolean negative, int lastDisplayedOffset[], boolean forcePrecision,
+ boolean forceSciNotation, boolean insertCommas) {
final int minusSpace = negative ? 1 : 0;
final int msdIndex = truncated ? -1 : getNaiveMsdIndexOf(in); // INVALID_MSD is OK.
String result = in;
+ boolean needEllipsis = false;
if (truncated || (negative && result.charAt(0) != '-')) {
+ needEllipsis = true;
result = KeyMaps.ELLIPSIS + result.substring(1, result.length());
// Ellipsis may be removed again in the type(1) scientific notation case.
}
final int decIndex = result.indexOf('.');
- lastDisplayedOffset[0] = precOffset;
- if ((decIndex == -1 || msdIndex != Evaluator.INVALID_MSD
+ if (lastDisplayedOffset != null) {
+ lastDisplayedOffset[0] = precOffset;
+ }
+ if (forceSciNotation || (decIndex == -1 || msdIndex != Evaluator.INVALID_MSD
&& msdIndex - decIndex > MAX_LEADING_ZEROES + 1) && precOffset != -1) {
- // No decimal point displayed, and it's not just to the right of the last digit,
- // or we should suppress leading zeroes.
+ // Either:
+ // 1) No decimal point displayed, and it's not just to the right of the last digit, or
+ // 2) we are at the front of a number whos integral part is too large to allow
+ // comma insertion, or
+ // 3) we should suppress leading zeroes.
// Add an exponent to let the user track which digits are currently displayed.
// Start with type (2) exponent if we dropped no digits. -1 accounts for decimal point.
+ // We currently never show digit separators together with an exponent.
final int initExponent = precOffset > 0 ? -precOffset : -precOffset - 1;
int exponent = initExponent;
boolean hasPoint = false;
@@ -395,6 +595,13 @@
// delete leading zeroes.
// We try to keep leading digits roughly in position, and never
// lengthen the result by more than SCI_NOTATION_EXTRA.
+ if (decIndex > msdIndex) {
+ // In the forceSciNotation, we can have a decimal point in the relevant digit
+ // range. Remove it.
+ result = result.substring(0, decIndex)
+ + result.substring(decIndex + 1, result.length());
+ // msdIndex and precOffset unaffected.
+ }
final int resLen = result.length();
String fraction = result.substring(msdIndex + 1, resLen);
result = (negative ? "-" : "") + result.substring(msdIndex, msdIndex + 1)
@@ -433,46 +640,121 @@
}
}
result = result.substring(0, result.length() - dropDigits);
- lastDisplayedOffset[0] -= dropDigits;
+ if (lastDisplayedOffset != null) {
+ lastDisplayedOffset[0] -= dropDigits;
+ }
}
result = result + "E" + Integer.toString(exponent);
+ } else if (insertCommas) {
+ // Add commas to the whole number section, and then truncate on left to fit,
+ // counting commas as a fractional digit.
+ final int wholeStart = needEllipsis ? 1 : 0;
+ int orig_length = result.length();
+ final float nCommaChars;
+ if (decIndex != -1) {
+ nCommaChars = separatorChars(result, decIndex);
+ result = StringUtils.addCommas(result, wholeStart, decIndex)
+ + result.substring(decIndex, orig_length);
+ } else {
+ nCommaChars = separatorChars(result, orig_length);
+ result = StringUtils.addCommas(result, wholeStart, orig_length);
+ }
+ if (needEllipsis) {
+ orig_length -= 1; // Exclude ellipsis.
+ }
+ final float len = orig_length + nCommaChars;
+ int deletedChars = 0;
+ final float ellipsisCredit = getNoEllipsisCredit();
+ final float decimalCredit = getDecimalCredit();
+ final float effectiveLen = len - (decIndex == -1 ? 0 : getDecimalCredit());
+ final float ellipsisAdjustment =
+ needEllipsis ? mNoExponentCredit : getNoEllipsisCredit();
+ // As above, we allow for a tiny amount of extra length here, for consistency with
+ // getPreferredPrec().
+ if (effectiveLen - ellipsisAdjustment > (float) (maxDigs - wholeStart) + 0.0001f
+ && !forcePrecision) {
+ float deletedWidth = 0.0f;
+ while (effectiveLen - mNoExponentCredit - deletedWidth
+ > (float) (maxDigs - 1 /* for ellipsis */)) {
+ if (result.charAt(deletedChars) == ',') {
+ deletedWidth += mGroupingSeparatorWidthRatio;
+ } else {
+ deletedWidth += 1.0f;
+ }
+ deletedChars++;
+ }
+ }
+ if (deletedChars > 0) {
+ result = KeyMaps.ELLIPSIS + result.substring(deletedChars, result.length());
+ } else if (needEllipsis) {
+ result = KeyMaps.ELLIPSIS + result;
+ }
}
return result;
}
/**
* Get formatted, but not internationalized, result from mEvaluator.
- * @param precOffset requested position (1 = tenths) of last included digit.
- * @param maxSize Maximum number of characters (more or less) in result.
- * @param lastDisplayedOffset Zeroth entry is set to actual offset of last included digit,
- * after adjusting for exponent, etc.
+ * @param precOffset requested position (1 = tenths) of last included digit
+ * @param maxSize maximum number of characters (more or less) in result
+ * @param lastDisplayedOffset zeroth entry is set to actual offset of last included digit,
+ * after adjusting for exponent, etc. May be null.
* @param forcePrecision Ensure that last included digit is at pos, at the expense
* of treating maxSize as a soft limit.
+ * @param forceSciNotation Force scientific notation, even if not required by maxSize.
+ * @param insertCommas Insert commas as digit separators.
*/
private String getFormattedResult(int precOffset, int maxSize, int lastDisplayedOffset[],
- boolean forcePrecision) {
+ boolean forcePrecision, boolean forceSciNotation, boolean insertCommas) {
final boolean truncated[] = new boolean[1];
final boolean negative[] = new boolean[1];
final int requestedPrecOffset[] = {precOffset};
final String rawResult = mEvaluator.getString(requestedPrecOffset, mMaxCharOffset,
maxSize, truncated, negative);
return formatResult(rawResult, requestedPrecOffset[0], maxSize, truncated[0], negative[0],
- lastDisplayedOffset, forcePrecision);
+ lastDisplayedOffset, forcePrecision, forceSciNotation, insertCommas);
}
- // Return entire result (within reason) up to current displayed precision.
- public String getFullText() {
+ /**
+ * Return entire result (within reason) up to current displayed precision.
+ * @param withSeparators Add digit separators
+ */
+ public String getFullText(boolean withSeparators) {
if (!mValid) return "";
if (!mScrollable) return getText().toString();
- int currentCharOffset = getCurrentCharOffset();
- int unused[] = new int[1];
return KeyMaps.translateResult(getFormattedResult(mLastDisplayedOffset, MAX_COPY_SIZE,
- unused, true));
+ null, true /* forcePrecision */, false /* forceSciNotation */, withSeparators));
}
+ /**
+ * Did the above produce a correct result?
+ * UI thread only.
+ */
public boolean fullTextIsExact() {
- return !mScrollable
- || mMaxCharOffset == getCurrentCharOffset() && mMaxCharOffset != MAX_RIGHT_SCROLL;
+ return !mScrollable || (mMaxCharOffset == getCharOffset(mCurrentPos)
+ && mMaxCharOffset != MAX_RIGHT_SCROLL);
+ }
+
+ /**
+ * Get entire result up to current displayed precision, or up to MAX_COPY_EXTRA additional
+ * digits, if it will lead to an exact result.
+ */
+ public String getFullCopyText() {
+ if (!mValid
+ || mLsdOffset == Integer.MAX_VALUE
+ || fullTextIsExact()
+ || mWholeLen > MAX_RECOMPUTE_DIGITS
+ || mWholeLen + mLsdOffset > MAX_RECOMPUTE_DIGITS
+ || mLsdOffset - mLastDisplayedOffset > MAX_COPY_EXTRA) {
+ return getFullText(false /* withSeparators */);
+ }
+ // It's reasonable to compute and copy the exact result instead.
+ final int nonNegLsdOffset = Math.max(0, mLsdOffset);
+ final String rawResult = mEvaluator.getResult().toStringTruncated(nonNegLsdOffset);
+ final String formattedResult = formatResult(rawResult, nonNegLsdOffset, MAX_COPY_SIZE,
+ false, rawResult.charAt(0) == '-', null, true /* forcePrecision */,
+ false /* forceSciNotation */, false /* insertCommas */);
+ return KeyMaps.translateResult(formattedResult);
}
/**
@@ -501,10 +783,12 @@
return mScrollable;
}
- int getCurrentCharOffset() {
- synchronized(mWidthLock) {
- return (int) Math.round(mCurrentPos / mCharWidth);
- }
+ /**
+ * Map pixel position to digit offset.
+ * UI thread only.
+ */
+ int getCharOffset(int pos) {
+ return (int) Math.round(pos / mCharWidth); // Lock not needed.
}
void clear() {
@@ -513,11 +797,19 @@
setText("");
}
+ /**
+ * Refresh display.
+ * Only called in UI thread.
+ */
void redisplay() {
- int currentCharOffset = getCurrentCharOffset();
+ int currentCharOffset = getCharOffset(mCurrentPos);
int maxChars = getMaxChars();
int lastDisplayedOffset[] = new int[1];
- String result = getFormattedResult(currentCharOffset, maxChars, lastDisplayedOffset, false);
+ String result = getFormattedResult(currentCharOffset, maxChars, lastDisplayedOffset,
+ mAppendExponent /* forcePrecision; preserve entire result */,
+ !mWholePartFits
+ && currentCharOffset == getCharOffset(mMinPos) /* forceSciNotation */,
+ mWholePartFits /* insertCommas */ );
int expIndex = result.indexOf('E');
result = KeyMaps.translateResult(result);
if (expIndex > 0 && result.indexOf('.') == -1) {
@@ -538,7 +830,7 @@
if (!mScrollable) return;
if (mScroller.computeScrollOffset()) {
mCurrentPos = mScroller.getCurrX();
- if (mCurrentPos != mLastPos) {
+ if (getCharOffset(mCurrentPos) != getCharOffset(mLastPos)) {
mLastPos = mCurrentPos;
redisplay();
}
@@ -548,76 +840,137 @@
}
}
- // Copy support:
+ /**
+ * Use ActionMode for copy support on M and higher.
+ */
+ @TargetApi(Build.VERSION_CODES.M)
+ private void setupActionMode() {
+ mCopyActionModeCallback = new ActionMode.Callback2() {
- private ActionMode.Callback2 mCopyActionModeCallback = new ActionMode.Callback2() {
+ @Override
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ final MenuInflater inflater = mode.getMenuInflater();
+ return createCopyMenu(inflater, menu);
+ }
- private BackgroundColorSpan mHighlightSpan;
+ @Override
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ return false; // Return false if nothing is done
+ }
- private void highlightResult() {
- final Spannable text = (Spannable) getText();
- mHighlightSpan = new BackgroundColorSpan(getHighlightColor());
- text.setSpan(mHighlightSpan, 0, text.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
- }
+ @Override
+ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ if (onMenuItemClick(item)) {
+ mode.finish();
+ return true;
+ } else {
+ return false;
+ }
+ }
- private void unhighlightResult() {
- final Spannable text = (Spannable) getText();
- text.removeSpan(mHighlightSpan);
- }
+ @Override
+ public void onDestroyActionMode(ActionMode mode) {
+ unhighlightResult();
+ mActionMode = null;
+ }
- @Override
- public boolean onCreateActionMode(ActionMode mode, Menu menu) {
- MenuInflater inflater = mode.getMenuInflater();
- inflater.inflate(R.menu.copy, menu);
- highlightResult();
- return true;
- }
+ @Override
+ public void onGetContentRect(ActionMode mode, View view, Rect outRect) {
+ super.onGetContentRect(mode, view, outRect);
- @Override
- public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
- return false; // Return false if nothing is done
- }
+ outRect.left += view.getPaddingLeft();
+ outRect.top += view.getPaddingTop();
+ outRect.right -= view.getPaddingRight();
+ outRect.bottom -= view.getPaddingBottom();
+ final int width = (int) Layout.getDesiredWidth(getText(), getPaint());
+ if (width < outRect.width()) {
+ outRect.left = outRect.right - width;
+ }
- @Override
- public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
- switch (item.getItemId()) {
- case R.id.menu_copy:
- copyContent();
- mode.finish();
- return true;
- default:
+ if (!BuildCompat.isAtLeastN()) {
+ // The CAB (prior to N) only takes the translation of a view into account, so
+ // if a scale is applied to the view then the offset outRect will end up being
+ // positioned incorrectly. We workaround that limitation by manually applying
+ // the scale to the outRect, which the CAB will then offset to the correct
+ // position.
+ final float scaleX = view.getScaleX();
+ final float scaleY = view.getScaleY();
+ outRect.left *= scaleX;
+ outRect.right *= scaleX;
+ outRect.top *= scaleY;
+ outRect.bottom *= scaleY;
+ }
+ }
+ };
+ setOnLongClickListener(new View.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v) {
+ if (mValid) {
+ mActionMode = startActionMode(mCopyActionModeCallback,
+ ActionMode.TYPE_FLOATING);
+ return true;
+ }
return false;
}
- }
+ });
+ }
- @Override
- public void onDestroyActionMode(ActionMode mode) {
- unhighlightResult();
- mActionMode = null;
- }
-
- @Override
- public void onGetContentRect(ActionMode mode, View view, Rect outRect) {
- super.onGetContentRect(mode, view, outRect);
- outRect.left += getPaddingLeft();
- outRect.top += getPaddingTop();
- outRect.right -= getPaddingRight();
- outRect.bottom -= getPaddingBottom();
- final int width = (int) Layout.getDesiredWidth(getText(), getPaint());
- if (width < outRect.width()) {
- outRect.left = outRect.right - width;
+ /**
+ * Use ContextMenu for copy support on L and lower.
+ */
+ private void setupContextMenu() {
+ setOnCreateContextMenuListener(new OnCreateContextMenuListener() {
+ @Override
+ public void onCreateContextMenu(ContextMenu contextMenu, View view,
+ ContextMenu.ContextMenuInfo contextMenuInfo) {
+ final MenuInflater inflater = new MenuInflater(getContext());
+ createCopyMenu(inflater, contextMenu);
+ mContextMenu = contextMenu;
+ for(int i = 0; i < contextMenu.size(); i ++) {
+ contextMenu.getItem(i).setOnMenuItemClickListener(CalculatorResult.this);
+ }
}
- }
- };
+ });
+ setOnLongClickListener(new View.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v) {
+ if (mValid) {
+ return showContextMenu();
+ }
+ return false;
+ }
+ });
+ }
- public boolean stopActionMode() {
+ private boolean createCopyMenu(MenuInflater inflater, Menu menu) {
+ inflater.inflate(R.menu.copy, menu);
+ highlightResult();
+ return true;
+ }
+
+ public boolean stopActionModeOrContextMenu() {
if (mActionMode != null) {
mActionMode.finish();
return true;
}
+ if (mContextMenu != null) {
+ unhighlightResult();
+ mContextMenu.close();
+ return true;
+ }
return false;
}
+ private void highlightResult() {
+ final Spannable text = (Spannable) getText();
+ text.setSpan(mHighlightSpan, 0, text.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+
+ private void unhighlightResult() {
+ final Spannable text = (Spannable) getText();
+ text.removeSpan(mHighlightSpan);
+ }
+
private void setPrimaryClip(ClipData clip) {
ClipboardManager clipboard = (ClipboardManager) getContext().
getSystemService(Context.CLIPBOARD_SERVICE);
@@ -625,7 +978,7 @@
}
private void copyContent() {
- final CharSequence text = getFullText();
+ final CharSequence text = getFullCopyText();
ClipboardManager clipboard =
(ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE);
// We include a tag URI, to allow us to recognize our own results and handle them
@@ -637,4 +990,20 @@
Toast.makeText(getContext(), R.string.text_copied_toast, Toast.LENGTH_SHORT).show();
}
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ switch (item.getItemId()) {
+ case R.id.menu_copy:
+ if (mEvaluator.reevaluationInProgress()) {
+ // Refuse to copy placeholder characters.
+ return false;
+ } else {
+ copyContent();
+ unhighlightResult();
+ return true;
+ }
+ default:
+ return false;
+ }
+ }
}
diff --git a/src/com/android/calculator2/CalculatorScrollView.java b/src/com/android/calculator2/CalculatorScrollView.java
new file mode 100644
index 0000000..bcf5650
--- /dev/null
+++ b/src/com/android/calculator2/CalculatorScrollView.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.calculator2;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.HorizontalScrollView;
+
+public class CalculatorScrollView extends HorizontalScrollView {
+
+ public CalculatorScrollView(Context context) {
+ this(context, null /* attrs */);
+ }
+
+ public CalculatorScrollView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0 /* defStyleAttr */);
+ }
+
+ public CalculatorScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ }
+
+ @Override
+ protected void measureChild(View child, int parentWidthMeasureSpec,
+ int parentHeightMeasureSpec) {
+ // Allow child to be as wide as they want.
+ parentWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
+ MeasureSpec.getSize(parentWidthMeasureSpec), MeasureSpec.UNSPECIFIED);
+
+ final ViewGroup.LayoutParams lp = child.getLayoutParams();
+ final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
+ 0 /* padding */, lp.width);
+ final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
+ getPaddingTop() + getPaddingBottom(), lp.height);
+
+ child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+ }
+
+ @Override
+ protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed,
+ int parentHeightMeasureSpec, int heightUsed) {
+ // Allow child to be as wide as they want.
+ parentWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
+ MeasureSpec.getSize(parentWidthMeasureSpec), MeasureSpec.UNSPECIFIED);
+
+ final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
+ final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
+ lp.leftMargin + lp.rightMargin, lp.width);
+ final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
+ getPaddingTop() + getPaddingBottom() + lp.topMargin + lp.bottomMargin, lp.height);
+
+ child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+ }
+}
diff --git a/src/com/android/calculator2/CalculatorText.java b/src/com/android/calculator2/CalculatorText.java
index f944071..de2a843 100644
--- a/src/com/android/calculator2/CalculatorText.java
+++ b/src/com/android/calculator2/CalculatorText.java
@@ -16,17 +16,19 @@
package com.android.calculator2;
+import android.annotation.TargetApi;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Rect;
+import android.os.Build;
import android.text.Layout;
import android.text.TextPaint;
-import android.text.method.ScrollingMovementMethod;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.ActionMode;
+import android.view.ContextMenu;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
@@ -36,54 +38,9 @@
/**
* TextView adapted for Calculator display.
*/
-public class CalculatorText extends AlignedTextView implements View.OnLongClickListener {
+public class CalculatorText extends AlignedTextView implements MenuItem.OnMenuItemClickListener {
- private final ActionMode.Callback2 mPasteActionModeCallback = new ActionMode.Callback2() {
-
- @Override
- public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
- if (item.getItemId() == R.id.menu_paste) {
- paste();
- mode.finish();
- return true;
- }
- return false;
- }
-
- @Override
- public boolean onCreateActionMode(ActionMode mode, Menu menu) {
- final ClipboardManager clipboard = (ClipboardManager) getContext()
- .getSystemService(Context.CLIPBOARD_SERVICE);
- if (clipboard.hasPrimaryClip()) {
- bringPointIntoView(length());
- MenuInflater inflater = mode.getMenuInflater();
- inflater.inflate(R.menu.paste, menu);
- return true;
- }
- // Prevents the selection action mode on double tap.
- return false;
- }
-
- @Override
- public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
- return false;
- }
-
- @Override
- public void onDestroyActionMode(ActionMode mode) {
- mActionMode = null;
- }
-
- @Override
- public void onGetContentRect(ActionMode mode, View view, Rect outRect) {
- super.onGetContentRect(mode, view, outRect);
- outRect.top += getTotalPaddingTop();
- outRect.right -= getTotalPaddingRight();
- outRect.bottom -= getTotalPaddingBottom();
- // Encourage menu positioning towards the right, possibly over formula.
- outRect.left = outRect.right;
- }
- };
+ public static final String TAG_ACTION_MODE = "ACTION_MODE";
// Temporary paint for use in layout methods.
private final TextPaint mTempPaint = new TextPaint();
@@ -93,9 +50,9 @@
private final float mStepTextSize;
private int mWidthConstraint = -1;
-
private ActionMode mActionMode;
-
+ private ActionMode.Callback mPasteActionModeCallback;
+ private ContextMenu mContextMenu;
private OnPasteListener mOnPasteListener;
private OnTextSizeChangeListener mOnTextSizeChangeListener;
@@ -120,47 +77,40 @@
(mMaximumTextSize - mMinimumTextSize) / 3);
a.recycle();
- // Allow scrolling by default.
- setMovementMethod(ScrollingMovementMethod.getInstance());
-
- // Reset the clickable flag, which is added when specifying a movement method.
- setClickable(false);
-
- // Add a long click to start the ActionMode manually.
- setOnLongClickListener(this);
- }
-
- @Override
- public boolean onLongClick(View v) {
- mActionMode = startActionMode(mPasteActionModeCallback, ActionMode.TYPE_FLOATING);
- return true;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ setupActionMode();
+ } else {
+ setupContextMenu();
+ }
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ if (!isLaidOut()) {
+ // Prevent shrinking/resizing with our variable textSize.
+ setTextSizeInternal(TypedValue.COMPLEX_UNIT_PX, mMaximumTextSize,
+ false /* notifyListener */);
+ setMinimumHeight(getLineHeight() + getCompoundPaddingBottom()
+ + getCompoundPaddingTop());
+ }
+
+ // Ensure we are at least as big as our parent.
+ final int width = MeasureSpec.getSize(widthMeasureSpec);
+ if (getMinimumWidth() != width) {
+ setMinimumWidth(width);
+ }
+
// Re-calculate our textSize based on new width.
- final int width = MeasureSpec.getSize(widthMeasureSpec)
+ mWidthConstraint = MeasureSpec.getSize(widthMeasureSpec)
- getPaddingLeft() - getPaddingRight();
- if (mWidthConstraint != width) {
- mWidthConstraint = width;
-
- if (!isLaidOut()) {
- // Prevent shrinking/resizing with our variable textSize.
- setTextSizeInternal(TypedValue.COMPLEX_UNIT_PX, mMaximumTextSize,
- false /* notifyListener */);
- setMinHeight(getLineHeight() + getCompoundPaddingBottom()
- + getCompoundPaddingTop());
- }
-
- setTextSizeInternal(TypedValue.COMPLEX_UNIT_PX, getVariableTextSize(getText()),
- false);
+ final float textSize = getVariableTextSize(getText());
+ if (getTextSize() != textSize) {
+ setTextSizeInternal(TypedValue.COMPLEX_UNIT_PX, textSize, false /* notifyListener */);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
- public int getWidthConstraint() { return mWidthConstraint; }
-
@Override
protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
super.onTextChanged(text, start, lengthBefore, lengthAfter);
@@ -232,13 +182,13 @@
*/
public void changeTextTo(CharSequence newText) {
final CharSequence oldText = getText();
- if (startsWith(newText, oldText)) {
- final int newLen = newText.length();
- final int oldLen = oldText.length();
- if (newLen == oldLen + 1) {
+ final char separator = KeyMaps.translateResult(",").charAt(0);
+ final CharSequence added = StringUtils.getExtensionIgnoring(newText, oldText, separator);
+ if (added != null) {
+ if (added.length() == 1) {
// The algorithm for pronouncing a single character doesn't seem
// to respect our hints. Don't give it the choice.
- final char c = newText.charAt(oldLen);
+ final char c = added.charAt(0);
final int id = KeyMaps.keyForChar(c);
final String descr = KeyMaps.toDescriptiveString(getContext(), id);
if (descr != null) {
@@ -246,20 +196,24 @@
} else {
announceForAccessibility(String.valueOf(c));
}
- } else if (newLen > oldLen) {
- announceForAccessibility(newText.subSequence(oldLen, newLen));
+ } else if (added.length() != 0) {
+ announceForAccessibility(added);
}
} else {
announceForAccessibility(newText);
}
- setText(newText);
+ setText(newText, BufferType.SPANNABLE);
}
- public boolean stopActionMode() {
+ public boolean stopActionModeOrContextMenu() {
if (mActionMode != null) {
mActionMode.finish();
return true;
}
+ if (mContextMenu != null) {
+ mContextMenu.close();
+ return true;
+ }
return false;
}
@@ -271,6 +225,95 @@
mOnPasteListener = listener;
}
+ /**
+ * Use ActionMode for paste support on M and higher.
+ */
+ @TargetApi(Build.VERSION_CODES.M)
+ private void setupActionMode() {
+ mPasteActionModeCallback = new ActionMode.Callback2() {
+
+ @Override
+ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
+ if (onMenuItemClick(item)) {
+ mode.finish();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public boolean onCreateActionMode(ActionMode mode, Menu menu) {
+ mode.setTag(TAG_ACTION_MODE);
+ final MenuInflater inflater = mode.getMenuInflater();
+ return createPasteMenu(inflater, menu);
+ }
+
+ @Override
+ public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
+ return false;
+ }
+
+ @Override
+ public void onDestroyActionMode(ActionMode mode) {
+ mActionMode = null;
+ }
+
+ @Override
+ public void onGetContentRect(ActionMode mode, View view, Rect outRect) {
+ super.onGetContentRect(mode, view, outRect);
+ outRect.top += getTotalPaddingTop();
+ outRect.right -= getTotalPaddingRight();
+ outRect.bottom -= getTotalPaddingBottom();
+ // Encourage menu positioning towards the right, possibly over formula.
+ outRect.left = outRect.right;
+ }
+ };
+ setOnLongClickListener(new View.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v) {
+ mActionMode = startActionMode(mPasteActionModeCallback, ActionMode.TYPE_FLOATING);
+ return true;
+ }
+ });
+ }
+
+ /**
+ * Use ContextMenu for paste support on L and lower.
+ */
+ private void setupContextMenu() {
+ setOnCreateContextMenuListener(new OnCreateContextMenuListener() {
+ @Override
+ public void onCreateContextMenu(ContextMenu contextMenu, View view,
+ ContextMenu.ContextMenuInfo contextMenuInfo) {
+ final MenuInflater inflater = new MenuInflater(getContext());
+ createPasteMenu(inflater, contextMenu);
+ mContextMenu = contextMenu;
+ for(int i = 0; i < contextMenu.size(); i++) {
+ contextMenu.getItem(i).setOnMenuItemClickListener(CalculatorText.this);
+ }
+ }
+ });
+ setOnLongClickListener(new View.OnLongClickListener() {
+ @Override
+ public boolean onLongClick(View v) {
+ return showContextMenu();
+ }
+ });
+ }
+
+ private boolean createPasteMenu(MenuInflater inflater, Menu menu) {
+ final ClipboardManager clipboard = (ClipboardManager) getContext()
+ .getSystemService(Context.CLIPBOARD_SERVICE);
+ if (clipboard.hasPrimaryClip()) {
+ bringPointIntoView(length());
+ inflater.inflate(R.menu.paste, menu);
+ return true;
+ }
+ // Prevents the selection action mode on double tap.
+ return false;
+ }
+
private void paste() {
final ClipboardManager clipboard = (ClipboardManager) getContext()
.getSystemService(Context.CLIPBOARD_SERVICE);
@@ -280,6 +323,15 @@
}
}
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ if (item.getItemId() == R.id.menu_paste) {
+ paste();
+ return true;
+ }
+ return false;
+ }
+
public interface OnTextSizeChangeListener {
void onTextSizeChanged(TextView textView, float oldSize);
}
diff --git a/src/com/android/calculator2/Evaluator.java b/src/com/android/calculator2/Evaluator.java
index 936d618..33960ba 100644
--- a/src/com/android/calculator2/Evaluator.java
+++ b/src/com/android/calculator2/Evaluator.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 The Android Open Source Project
+ * Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,7 +27,7 @@
import android.support.annotation.VisibleForTesting;
import android.util.Log;
-import com.hp.creals.CR;
+import com.hp.creals.CR; // For exception classes.
import java.io.DataInput;
import java.io.DataOutput;
@@ -61,15 +61,16 @@
* return a result containing placeholder ' ' characters. If we had to return palceholder
* characters, we start a background task, which invokes the onReevaluate() callback when it
* completes. In either case, the background task computes the appropriate result digits by
- * evaluating the constructive real (CR) returned by CalculatorExpr.eval() to the required
+ * evaluating the UnifiedReal returned by CalculatorExpr.eval() to the required
* precision.
*
* We cache the best decimal approximation we have already computed. We compute generously to
* allow for some scrolling without recomputation and to minimize the chance of digits flipping
* from "0000" to "9999". The best known result approximation is maintained as a string by
- * mResultString (and in a different format by the CR representation of the result). When we are
- * in danger of not having digits to display in response to further scrolling, we also initiate a
- * background computation to higher precision, as if we had generated placeholder characters.
+ * mResultString (and often in a different format by the CR representation of the result). When
+ * we are in danger of not having digits to display in response to further scrolling, we also
+ * initiate a background computation to higher precision, as if we had generated placeholder
+ * characters.
*
* The code is designed to ensure that the error in the displayed result (excluding any
* placeholder characters) is always strictly less than 1 in the last displayed digit. Typically
@@ -123,6 +124,8 @@
// The largest number of digits to the right of the decimal point to which we will evaluate to
// compute proper scientific notation for values close to zero. Chosen to ensure that we
// always to better than IEEE double precision at identifying nonzeros.
+ // This used only when we cannot a prior determine the most significant digit position, as
+ // we always can if we have a rational representation.
private static final int MAX_MSD_PREC_OFFSET = 320;
// If we can replace an exponent by this many leading zeroes, we do so. Also used in
@@ -145,6 +148,9 @@
// value.
private boolean mChangedValue;
+ // The main expression contains trig functions.
+ private boolean mHasTrigFuncs;
+
private SharedPreferences mSharedPrefs;
private boolean mDegreeMode; // Currently in degree (not radian) mode.
@@ -152,8 +158,7 @@
private final Handler mTimeoutHandler; // Used to schedule evaluation timeouts.
// The following are valid only if an evaluation completed successfully.
- private CR mVal; // Value of mExpr as constructive real.
- private BoundedRational mRatVal; // Value of mExpr as rational or null.
+ private UnifiedReal mVal; // Value of mExpr as UnifiedReal.
// We cache the best known decimal result in mResultString. Whenever that is
// non-null, it is computed to exactly mResultStringOffset, which is always > 0.
@@ -197,23 +202,20 @@
*/
private static class InitialResult {
public final int errorResourceId; // Error string or INVALID_RES_ID.
- public final CR val; // Constructive real value.
- public final BoundedRational ratVal; // Rational value or null.
+ public final UnifiedReal val; // Constructive real value.
public final String newResultString; // Null iff it can't be computed.
public final int newResultStringOffset;
public final int initDisplayOffset;
- InitialResult(CR v, BoundedRational rv, String s, int p, int idp) {
+ InitialResult(UnifiedReal v, String s, int p, int idp) {
errorResourceId = Calculator.INVALID_RES_ID;
val = v;
- ratVal = rv;
newResultString = s;
newResultStringOffset = p;
initDisplayOffset = idp;
}
InitialResult(int errorId) {
errorResourceId = errorId;
- val = CR.valueOf(0);
- ratVal = BoundedRational.ZERO;
+ val = UnifiedReal.ZERO;
newResultString = "BAD";
newResultStringOffset = 0;
initDisplayOffset = 0;
@@ -275,6 +277,7 @@
/**
* Maximum result bit length for unrequested, speculative evaluations.
+ * Also used to bound evaluation precision for small non-zero fractions.
*/
private final int QUICK_MAX_RESULT_BITS = 50000;
@@ -333,44 +336,50 @@
/**
* Is a computed result too big for decimal conversion?
*/
- private boolean isTooBig(CalculatorExpr.EvalResult res) {
+ private boolean isTooBig(UnifiedReal res) {
int maxBits = mRequired ? getMaxResultBits(mLongTimeout) : QUICK_MAX_RESULT_BITS;
- if (res.ratVal != null) {
- return res.ratVal.wholeNumberBits() > maxBits;
- } else {
- return res.val.get_appr(maxBits).bitLength() > 2;
- }
+ return res.approxWholeNumberBitsGreaterThan(maxBits);
}
@Override
protected InitialResult doInBackground(Void... nothing) {
try {
- CalculatorExpr.EvalResult res = mExpr.eval(mDm);
+ UnifiedReal res = mExpr.eval(mDm);
if (isTooBig(res)) {
// Avoid starting a long uninterruptible decimal conversion.
return new InitialResult(R.string.timeout);
}
int precOffset = INIT_PREC;
- String initResult = res.val.toString(precOffset);
+ String initResult = res.toStringTruncated(precOffset);
int msd = getMsdIndexOf(initResult);
- if (BoundedRational.asBigInteger(res.ratVal) == null
- && msd == INVALID_MSD) {
- precOffset = MAX_MSD_PREC_OFFSET;
- initResult = res.val.toString(precOffset);
- msd = getMsdIndexOf(initResult);
+ if (msd == INVALID_MSD) {
+ int leadingZeroBits = res.leadingBinaryZeroes();
+ if (leadingZeroBits < QUICK_MAX_RESULT_BITS) {
+ // Enough initial nonzero digits for most displays.
+ precOffset = 30 +
+ (int)Math.ceil(Math.log(2.0d) / Math.log(10.0d) * leadingZeroBits);
+ initResult = res.toStringTruncated(precOffset);
+ msd = getMsdIndexOf(initResult);
+ if (msd == INVALID_MSD) {
+ throw new AssertionError("Impossible zero result");
+ }
+ } else {
+ // Just try once more at higher fixed precision.
+ precOffset = MAX_MSD_PREC_OFFSET;
+ initResult = res.toStringTruncated(precOffset);
+ msd = getMsdIndexOf(initResult);
+ }
}
- final int lsdOffset = getLsdOffset(res.ratVal, initResult,
- initResult.indexOf('.'));
+ final int lsdOffset = getLsdOffset(res, initResult, initResult.indexOf('.'));
final int initDisplayOffset = getPreferredPrec(initResult, msd, lsdOffset);
final int newPrecOffset = initDisplayOffset + EXTRA_DIGITS;
if (newPrecOffset > precOffset) {
precOffset = newPrecOffset;
- initResult = res.val.toString(precOffset);
+ initResult = res.toStringTruncated(precOffset);
}
- return new InitialResult(res.val, res.ratVal,
- initResult, precOffset, initDisplayOffset);
+ return new InitialResult(res, initResult, precOffset, initDisplayOffset);
} catch (CalculatorExpr.SyntaxException e) {
return new InitialResult(R.string.error_syntax);
- } catch (BoundedRational.ZeroDivisionException e) {
+ } catch (UnifiedReal.ZeroDivisionException e) {
return new InitialResult(R.string.error_zero_divide);
} catch(ArithmeticException e) {
return new InitialResult(R.string.error_nan);
@@ -397,11 +406,6 @@
return;
}
mVal = result.val;
- mRatVal = result.ratVal;
- // TODO: If the new result ends in lots of zeroes, and we have a rational result which
- // is greater than (in absolute value) the result string, we should subtract 1 ulp
- // from the result string. That will prevent a later change from zeroes to nines. We
- // know that the correct, rounded-toward-zero result has nines.
mResultString = result.newResultString;
mResultStringOffset = result.newResultStringOffset;
final int dotIndex = mResultString.indexOf('.');
@@ -412,7 +416,7 @@
// TODO: Could optimize by remembering display size and checking for change.
int initPrecOffset = result.initDisplayOffset;
final int msdIndex = getMsdIndexOf(mResultString);
- final int leastDigOffset = getLsdOffset(mRatVal, mResultString, dotIndex);
+ final int leastDigOffset = getLsdOffset(mVal, mResultString, dotIndex);
final int newInitPrecOffset = getPreferredPrec(mResultString, msdIndex, leastDigOffset);
if (newInitPrecOffset < initPrecOffset) {
initPrecOffset = newInitPrecOffset;
@@ -461,10 +465,10 @@
}
// Earlier digits could not have changed without a 0 to 9 or 9 to 0 flip at end.
// The former is OK.
- if (!newDigs.substring(newLen - precDiff).equals(repeat('0', precDiff))) {
+ if (!newDigs.substring(newLen - precDiff).equals(StringUtils.repeat('0', precDiff))) {
throw new AssertionError("New approximation invalidates old one!");
}
- return oldDigs + repeat('9', precDiff);
+ return oldDigs + StringUtils.repeat('9', precDiff);
}
/**
@@ -489,7 +493,7 @@
protected ReevalResult doInBackground(Integer... prec) {
try {
final int precOffset = prec[0].intValue();
- return new ReevalResult(mVal.toString(precOffset), precOffset);
+ return new ReevalResult(mVal.toStringTruncated(precOffset), precOffset);
} catch(ArithmeticException e) {
return null;
} catch(CR.PrecisionOverflowException e) {
@@ -544,16 +548,16 @@
/**
* Return the rightmost nonzero digit position, if any.
- * @param ratVal Rational value of result or null.
+ * @param val UnifiedReal value of result.
* @param cache Current cached decimal string representation of result.
* @param decIndex Index of decimal point in cache.
* @result Position of rightmost nonzero digit relative to decimal point.
- * Integer.MIN_VALUE if ratVal is zero. Integer.MAX_VALUE if there is no lsd,
+ * Integer.MIN_VALUE if we cannot determine. Integer.MAX_VALUE if there is no lsd,
* or we cannot determine it.
*/
- int getLsdOffset(BoundedRational ratVal, String cache, int decIndex) {
- if (ratVal != null && ratVal.signum() == 0) return Integer.MIN_VALUE;
- int result = BoundedRational.digitsRequired(ratVal);
+ int getLsdOffset(UnifiedReal val, String cache, int decIndex) {
+ if (val.definitelyZero()) return Integer.MIN_VALUE;
+ int result = val.digitsRequired();
if (result == 0) {
int i;
for (i = -1; decIndex + i > 0 && cache.charAt(decIndex + i) == '0'; --i) { }
@@ -578,19 +582,25 @@
private int getPreferredPrec(String cache, int msd, int lastDigitOffset) {
final int lineLength = mResult.getMaxChars();
final int wholeSize = cache.indexOf('.');
+ final float rawSepChars = mResult.separatorChars(cache, wholeSize);
+ final float rawSepCharsNoDecimal = rawSepChars - mResult.getNoEllipsisCredit();
+ final float rawSepCharsWithDecimal = rawSepCharsNoDecimal - mResult.getDecimalCredit();
+ final int sepCharsNoDecimal = (int) Math.ceil(Math.max(rawSepCharsNoDecimal, 0.0f));
+ final int sepCharsWithDecimal = (int) Math.ceil(Math.max(rawSepCharsWithDecimal, 0.0f));
final int negative = cache.charAt(0) == '-' ? 1 : 0;
// Don't display decimal point if result is an integer.
if (lastDigitOffset == 0) {
lastDigitOffset = -1;
}
if (lastDigitOffset != Integer.MAX_VALUE) {
- if (wholeSize <= lineLength && lastDigitOffset <= 0) {
+ if (wholeSize <= lineLength - sepCharsNoDecimal && lastDigitOffset <= 0) {
// Exact integer. Prefer to display as integer, without decimal point.
return -1;
}
if (lastDigitOffset >= 0
- && wholeSize + lastDigitOffset + 1 /* decimal pt. */ <= lineLength) {
- // Display full exact number wo scientific notation.
+ && wholeSize + lastDigitOffset + 1 /* decimal pt. */
+ <= lineLength - sepCharsWithDecimal) {
+ // Display full exact number without scientific notation.
return lastDigitOffset;
}
}
@@ -598,17 +608,27 @@
// Display number without scientific notation. Treat leading zero as msd.
msd = wholeSize - 1;
}
- if (msd > wholeSize + MAX_MSD_PREC_OFFSET) {
- // Display a probable but uncertain 0 as "0.000000000",
- // without exponent. That's a judgment call, but less likely
- // to confuse naive users. A more informative and confusing
- // option would be to use a large negative exponent.
+ if (msd > QUICK_MAX_RESULT_BITS) {
+ // Display a probable but uncertain 0 as "0.000000000", without exponent. That's a
+ // judgment call, but less likely to confuse naive users. A more informative and
+ // confusing option would be to use a large negative exponent.
+ // Treat extremely large msd values as unknown to avoid slow computations.
return lineLength - 2;
}
- // Return position corresponding to having msd at left, effectively
- // presuming scientific notation that preserves the left part of the
- // result.
- return msd - wholeSize + lineLength - negative - 1;
+ // Return position corresponding to having msd at left, effectively presuming scientific
+ // notation that preserves the left part of the result.
+ // After adjustment for the space required by an exponent, evaluating to the resulting
+ // precision should not overflow the display.
+ int result = msd - wholeSize + lineLength - negative - 1;
+ if (wholeSize <= lineLength - sepCharsNoDecimal) {
+ // Fits without scientific notation; will need space for separators.
+ if (wholeSize < lineLength - sepCharsWithDecimal) {
+ result -= sepCharsWithDecimal;
+ } else {
+ result -= sepCharsNoDecimal;
+ }
+ }
+ return result;
}
private static final int SHORT_TARGET_LENGTH = 8;
@@ -657,7 +677,7 @@
}
if (msdIndex > dotIndex) {
if (msdIndex <= dotIndex + EXP_COST + 1) {
- // Preferred display format inthis cases is with leading zeroes, even if
+ // Preferred display format in this case is with leading zeroes, even if
// it doesn't fit entirely. Replicate that here.
msdIndex = dotIndex - 1;
} else if (lsdOffset <= SHORT_TARGET_LENGTH - negative - 2
@@ -677,7 +697,8 @@
final int totalDigits = lsdIndex - msdIndex + negative + 1;
if (totalDigits <= SHORT_TARGET_LENGTH && dotIndex > msdIndex && lsdOffset >= -1) {
// Fits, no exponent needed.
- return negativeSign + cache.substring(msdIndex, lsdIndex + 1);
+ final String wholeWithCommas = StringUtils.addCommas(cache, msdIndex, dotIndex);
+ return negativeSign + wholeWithCommas + cache.substring(dotIndex, lsdIndex + 1);
}
if (totalDigits <= SHORT_TARGET_LENGTH - 3) {
return negativeSign + cache.charAt(msdIndex) + "."
@@ -686,8 +707,10 @@
}
// We need to abbreviate.
if (dotIndex > msdIndex && dotIndex < msdIndex + SHORT_TARGET_LENGTH - negative - 1) {
- return negativeSign + cache.substring(msdIndex,
- msdIndex + SHORT_TARGET_LENGTH - negative - 1) + KeyMaps.ELLIPSIS;
+ final String wholeWithCommas = StringUtils.addCommas(cache, msdIndex, dotIndex);
+ return negativeSign + wholeWithCommas
+ + cache.substring(dotIndex, msdIndex + SHORT_TARGET_LENGTH - negative - 1)
+ + KeyMaps.ELLIPSIS;
}
// Need abbreviation + exponent
return negativeSign + cache.charAt(msdIndex) + "."
@@ -731,7 +754,7 @@
}
return mMsdIndex;
}
- if (mRatVal != null && mRatVal.signum() == 0) {
+ if (mVal.definitelyZero()) {
return INVALID_MSD; // None exists
}
int result = INVALID_MSD;
@@ -741,17 +764,6 @@
return result;
}
- /**
- * Return a string with n copies of c.
- */
- private static String repeat(char c, int n) {
- StringBuilder result = new StringBuilder();
- for (int i = 0; i < n; ++i) {
- result.append(c);
- }
- return result.toString();
- }
-
// Refuse to scroll past the point at which this many digits from the whole number
// part of the result are still displayed. Avoids sily displays like 1E1.
private static final int MIN_DISPLAYED_DIGS = 5;
@@ -768,8 +780,8 @@
* precOffset[0] = -1 means we drop the decimal point and start at the ones position. Should
* not be invoked before the onEvaluate() callback is received. This essentially just returns
* a substring of the full result; a leading minus sign or leading digits can be dropped.
- * Result uses US conventions; is NOT internationalized. Use getRational() to determine
- * whether the result is exact, or whether we dropped trailing digits.
+ * Result uses US conventions; is NOT internationalized. Use getResult() and UnifiedReal
+ * operations to determine whether the result is exact, or whether we dropped trailing digits.
*
* @param precOffset Zeroth element indicates desired and actual precision
* @param maxPrecOffset Maximum adjusted precOffset[0]
@@ -819,7 +831,7 @@
truncated[0] = (startIndex > getMsdIndex());
String result = mResultString.substring(startIndex, endIndex);
if (deficit > 0) {
- result += repeat(' ', deficit);
+ result += StringUtils.repeat(' ', deficit);
// Blank character is replaced during translation.
// Since we always compute past the decimal point, this never fills in the spot
// where the decimal point should go, and we can otherwise treat placeholders
@@ -833,8 +845,8 @@
* Return null if the result is irrational, or we couldn't track the rational value,
* e.g. because the denominator got too big.
*/
- public BoundedRational getRational() {
- return mRatVal;
+ public UnifiedReal getResult() {
+ return mVal;
}
private void clearCache() {
@@ -846,6 +858,7 @@
private void clearPreservingTimeout() {
mExpr.clear();
+ mHasTrigFuncs = false;
clearCache();
}
@@ -895,7 +908,7 @@
// Notify immediately, reusing existing result.
final int dotIndex = mResultString.indexOf('.');
final String truncatedWholePart = mResultString.substring(0, dotIndex);
- final int leastDigOffset = getLsdOffset(mRatVal, mResultString, dotIndex);
+ final int leastDigOffset = getLsdOffset(mVal, mResultString, dotIndex);
final int msdIndex = getMsdIndex();
final int preferredPrecOffset = getPreferredPrec(mResultString, msdIndex,
leastDigOffset);
@@ -905,6 +918,13 @@
}
/**
+ * Is a reevaluation still in progress?
+ */
+ public boolean reevaluationInProgress() {
+ return mCurrentReevaluator != null;
+ }
+
+ /**
* Cancel all current background tasks.
* @param quiet suppress cancellation message
* @return true if we cancelled an initial evaluation
@@ -949,6 +969,7 @@
mExpr = new CalculatorExpr(in);
mSavedName = in.readUTF();
mSaved = new CalculatorExpr(in);
+ mHasTrigFuncs = mExpr.hasTrigFuncs();
} catch (IOException e) {
Log.v("Calculator", "Exception while restoring:\n" + e);
}
@@ -984,7 +1005,14 @@
return true;
} else {
mChangedValue = mChangedValue || !KeyMaps.isBinary(id);
- return mExpr.add(id);
+ if (mExpr.add(id)) {
+ if (!mHasTrigFuncs) {
+ mHasTrigFuncs = KeyMaps.isTrigFunc(id);
+ }
+ return true;
+ } else {
+ return false;
+ }
}
}
@@ -994,6 +1022,7 @@
if (mExpr.isEmpty()) {
mLongTimeout = false;
}
+ mHasTrigFuncs = mExpr.hasTrigFuncs();
}
void setDegreeMode(boolean degreeMode) {
@@ -1014,8 +1043,8 @@
*/
private CalculatorExpr getResultExpr() {
final int dotIndex = mResultString.indexOf('.');
- final int leastDigOffset = getLsdOffset(mRatVal, mResultString, dotIndex);
- return mExpr.abbreviate(mVal, mRatVal, mDegreeMode,
+ final int leastDigOffset = getLsdOffset(mVal, mResultString, dotIndex);
+ return mExpr.abbreviate(mVal, mDegreeMode,
getShortString(mResultString, getMsdIndexOf(mResultString), leastDigOffset));
}
@@ -1031,6 +1060,14 @@
clearPreservingTimeout();
mExpr.append(abbrvExpr);
mChangedValue = true;
+ mHasTrigFuncs = false; // Degree mode no longer affects expression value.
+ }
+
+ /**
+ * Mark the expression as changed, preventing next evaluation request from being ignored.
+ */
+ public void touch() {
+ mChangedValue = true;
}
/**
@@ -1109,6 +1146,14 @@
}
/**
+ * Does the current main expression contain trig functions?
+ * Might its value depend on DEG/RAD mode?
+ */
+ public boolean hasTrigFuncs() {
+ return mHasTrigFuncs;
+ }
+
+ /**
* Maximum number of characters in a scientific notation exponent.
*/
private static final int MAX_EXP_CHARS = 8;
diff --git a/src/com/android/calculator2/KeyMaps.java b/src/com/android/calculator2/KeyMaps.java
index f99850b..e82f35d 100644
--- a/src/com/android/calculator2/KeyMaps.java
+++ b/src/com/android/calculator2/KeyMaps.java
@@ -183,10 +183,10 @@
}
/**
- * Does a button id correspond to a function that introduces an implicit lparen?
+ * Does a button id correspond to a trig function?
* Pure function.
*/
- public static boolean isFunc(int id) {
+ public static boolean isTrigFunc(int id) {
switch(id) {
case R.id.fun_sin:
case R.id.fun_cos:
@@ -194,6 +194,21 @@
case R.id.fun_arcsin:
case R.id.fun_arccos:
case R.id.fun_arctan:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * Does a button id correspond to a function that introduces an implicit lparen?
+ * Pure function.
+ */
+ public static boolean isFunc(int id) {
+ if (isTrigFunc(id)) {
+ return true;
+ }
+ switch(id) {
case R.id.fun_ln:
case R.id.fun_log:
case R.id.fun_exp:
@@ -463,10 +478,17 @@
sOutputForResultChar.put('E', "E");
sOutputForResultChar.put(' ', String.valueOf(CHAR_DIGIT_UNKNOWN));
sOutputForResultChar.put(ELLIPSIS.charAt(0), ELLIPSIS);
+ // Translate numbers for fraction display, but not the separating slash, which appears
+ // to be universal. We also do not translate the ln, sqrt, pi
sOutputForResultChar.put('/', "/");
- // Translate numbers for fraction display, but not
- // the separating slash, which appears to be
- // universal.
+ sOutputForResultChar.put('(', "(");
+ sOutputForResultChar.put(')', ")");
+ sOutputForResultChar.put('l', "l");
+ sOutputForResultChar.put('n', "n");
+ sOutputForResultChar.put(',',
+ String.valueOf(DecimalFormatSymbols.getInstance().getGroupingSeparator()));
+ sOutputForResultChar.put('\u221A', "\u221A"); // SQUARE ROOT
+ sOutputForResultChar.put('\u03C0', "\u03C0"); // GREEK SMALL LETTER PI
addButtonToOutputMap('-', R.id.op_sub);
addButtonToOutputMap('.', R.id.dec_point);
for (int i = 0; i <= 9; ++i) {
@@ -500,6 +522,7 @@
/**
* Return the localization of the string s representing a numeric answer.
* Callable only from UI thread.
+ * A trailing e is treated as the mathematical constant, not an exponent.
*/
public static String translateResult(String s) {
StringBuilder result = new StringBuilder();
@@ -507,13 +530,15 @@
validateMaps();
for (int i = 0; i < len; ++i) {
char c = s.charAt(i);
- String translation = sOutputForResultChar.get(c);
- if (translation == null) {
- // Should not get here. Report if we do.
- Log.v("Calculator", "Bad character:" + c);
- result.append(String.valueOf(c));
- } else {
- result.append(translation);
+ if (i < len - 1 || c != 'e') {
+ String translation = sOutputForResultChar.get(c);
+ if (translation == null) {
+ // Should not get here. Report if we do.
+ Log.v("Calculator", "Bad character:" + c);
+ result.append(String.valueOf(c));
+ } else {
+ result.append(translation);
+ }
}
}
return result.toString();
diff --git a/src/com/android/calculator2/StringUtils.java b/src/com/android/calculator2/StringUtils.java
new file mode 100644
index 0000000..4eef0e7
--- /dev/null
+++ b/src/com/android/calculator2/StringUtils.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.calculator2;
+
+/**
+ * Some helpful methods operating on strings.
+ */
+
+public class StringUtils {
+
+ /**
+ * Return a string with n copies of c.
+ */
+ public static String repeat(char c, int n) {
+ final StringBuilder result = new StringBuilder();
+ for (int i = 0; i < n; ++i) {
+ result.append(c);
+ }
+ return result.toString();
+ }
+
+ /**
+ * Return a copy of the supplied string with commas added every three digits.
+ * The substring indicated by the supplied range is assumed to contain only
+ * a whole number, with no decimal point.
+ * Inserting a digit separator every 3 digits appears to be
+ * at least somewhat acceptable, though not necessarily preferred, everywhere.
+ * The grouping separator in the result is NOT localized.
+ */
+ public static String addCommas(String s, int begin, int end) {
+ // Resist the temptation to use Java's NumberFormat, which converts to long or double
+ // and hence doesn't handle very large numbers.
+ StringBuilder result = new StringBuilder();
+ int current = begin;
+ while (current < end && (s.charAt(current) == '-' || s.charAt(current) == ' ')) {
+ ++current;
+ }
+ result.append(s, begin, current);
+ while (current < end) {
+ result.append(s.charAt(current));
+ ++current;
+ if ((end - current) % 3 == 0 && end != current) {
+ result.append(',');
+ }
+ }
+ return result.toString();
+ }
+
+ /**
+ * Ignoring all occurrences of c in both strings, check whether old is a prefix of new.
+ * If so, return the remaining subsequence of whole. If not, return null.
+ */
+ public static CharSequence getExtensionIgnoring(CharSequence whole, CharSequence prefix,
+ char c) {
+ int wIndex = 0;
+ int pIndex = 0;
+ final int wLen = whole.length();
+ final int pLen = prefix.length();
+ while (true) {
+ while (pIndex < pLen && prefix.charAt(pIndex) == c) {
+ ++pIndex;
+ }
+ while (wIndex < wLen && whole.charAt(wIndex) == c) {
+ ++wIndex;
+ }
+ if (pIndex == pLen) {
+ break;
+ }
+ if (wIndex == wLen || whole.charAt(wIndex) != prefix.charAt(pIndex) ) {
+ return null;
+ }
+ ++pIndex;
+ ++wIndex;
+ }
+ while (wIndex < wLen && whole.charAt(wIndex) == c) {
+ ++wIndex;
+ }
+ return whole.subSequence(wIndex, wLen);
+ }
+}
diff --git a/src/com/android/calculator2/UnifiedReal.java b/src/com/android/calculator2/UnifiedReal.java
new file mode 100644
index 0000000..d3bc947
--- /dev/null
+++ b/src/com/android/calculator2/UnifiedReal.java
@@ -0,0 +1,1158 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.calculator2;
+
+import java.math.BigInteger;
+import com.hp.creals.CR;
+import com.hp.creals.UnaryCRFunction;
+
+/**
+ * Computable real numbers, represented so that we can get exact decidable comparisons
+ * for a number of interesting special cases, including rational computations.
+ *
+ * A real number is represented as the product of two numbers with different representations:
+ * A) A BoundedRational that can only represent a subset of the rationals, but supports
+ * exact computable comparisons.
+ * B) A lazily evaluated "constructive real number" that provides operations to evaluate
+ * itself to any requested number of digits.
+ * Whenever possible, we choose (B) to be one of a small set of known constants about which we
+ * know more. For example, whenever we can, we represent rationals such that (B) is 1.
+ * This scheme allows us to do some very limited symbolic computation on numbers when both
+ * have the same (B) value, as well as in some other situations. We try to maximize that
+ * possibility.
+ *
+ * Arithmetic operations and operations that produce finite approximations may throw unchecked
+ * exceptions produced by the underlying CR and BoundedRational packages, including
+ * CR.PrecisionOverflowException and CR.AbortedException.
+ */
+public class UnifiedReal {
+
+ private final BoundedRational mRatFactor;
+ private final CR mCrFactor;
+ // TODO: It would be helpful to add flags to indicate whether the result is known
+ // irrational, etc. This sometimes happens even if mCrFactor is not one of the known ones.
+ // And exact comparisons between rationals and known irrationals are decidable.
+
+ /**
+ * Perform some nontrivial consistency checks.
+ * @hide
+ */
+ public static boolean enableChecks = true;
+
+ private static void check(boolean b) {
+ if (!b) {
+ throw new AssertionError();
+ }
+ }
+
+ private UnifiedReal(BoundedRational rat, CR cr) {
+ if (rat == null) {
+ throw new ArithmeticException("Building UnifiedReal from null");
+ }
+ // We don't normally traffic in null CRs, and hence don't test explicitly.
+ mCrFactor = cr;
+ mRatFactor = rat;
+ }
+
+ public UnifiedReal(CR cr) {
+ this(BoundedRational.ONE, cr);
+ }
+
+ public UnifiedReal(BoundedRational rat) {
+ this(rat, CR_ONE);
+ }
+
+ public UnifiedReal(BigInteger n) {
+ this(new BoundedRational(n));
+ }
+
+ public UnifiedReal(long n) {
+ this(new BoundedRational(n));
+ }
+
+ // Various helpful constants
+ private final static BigInteger BIG_24 = BigInteger.valueOf(24);
+ private final static int DEFAULT_COMPARE_TOLERANCE = -1000;
+
+ // Well-known CR constants we try to use in the mCrFactor position:
+ private final static CR CR_ONE = CR.ONE;
+ private final static CR CR_PI = CR.PI;
+ private final static CR CR_E = CR.ONE.exp();
+ private final static CR CR_SQRT2 = CR.valueOf(2).sqrt();
+ private final static CR CR_SQRT3 = CR.valueOf(3).sqrt();
+ private final static CR CR_LN2 = CR.valueOf(2).ln();
+ private final static CR CR_LN3 = CR.valueOf(3).ln();
+ private final static CR CR_LN5 = CR.valueOf(5).ln();
+ private final static CR CR_LN6 = CR.valueOf(6).ln();
+ private final static CR CR_LN7 = CR.valueOf(7).ln();
+ private final static CR CR_LN10 = CR.valueOf(10).ln();
+
+ // Square roots that we try to recognize.
+ // We currently recognize only a small fixed collection, since the sqrt() function needs to
+ // identify numbers of the form <SQRT[i]>*n^2, and we don't otherwise know of a good
+ // algorithm for that.
+ private final static CR sSqrts[] = {
+ null,
+ CR.ONE,
+ CR_SQRT2,
+ CR_SQRT3,
+ null,
+ CR.valueOf(5).sqrt(),
+ CR.valueOf(6).sqrt(),
+ CR.valueOf(7).sqrt(),
+ null,
+ null,
+ CR.valueOf(10).sqrt() };
+
+ // Natural logs of small integers that we try to recognize.
+ private final static CR sLogs[] = {
+ null,
+ null,
+ CR_LN2,
+ CR_LN3,
+ null,
+ CR_LN5,
+ CR_LN6,
+ CR_LN7,
+ null,
+ null,
+ CR_LN10 };
+
+
+ // Some convenient UnifiedReal constants.
+ public static final UnifiedReal PI = new UnifiedReal(CR_PI);
+ public static final UnifiedReal E = new UnifiedReal(CR_E);
+ public static final UnifiedReal ZERO = new UnifiedReal(BoundedRational.ZERO);
+ public static final UnifiedReal ONE = new UnifiedReal(BoundedRational.ONE);
+ public static final UnifiedReal MINUS_ONE = new UnifiedReal(BoundedRational.MINUS_ONE);
+ public static final UnifiedReal TWO = new UnifiedReal(BoundedRational.TWO);
+ public static final UnifiedReal MINUS_TWO = new UnifiedReal(BoundedRational.MINUS_TWO);
+ public static final UnifiedReal HALF = new UnifiedReal(BoundedRational.HALF);
+ public static final UnifiedReal MINUS_HALF = new UnifiedReal(BoundedRational.MINUS_HALF);
+ public static final UnifiedReal TEN = new UnifiedReal(BoundedRational.TEN);
+ public static final UnifiedReal RADIANS_PER_DEGREE
+ = new UnifiedReal(new BoundedRational(1, 180), CR_PI);
+ private static final UnifiedReal SIX = new UnifiedReal(6);
+ private static final UnifiedReal HALF_SQRT2 = new UnifiedReal(BoundedRational.HALF, CR_SQRT2);
+ private static final UnifiedReal SQRT3 = new UnifiedReal(CR_SQRT3);
+ private static final UnifiedReal HALF_SQRT3 = new UnifiedReal(BoundedRational.HALF, CR_SQRT3);
+ private static final UnifiedReal THIRD_SQRT3 = new UnifiedReal(BoundedRational.THIRD, CR_SQRT3);
+ private static final UnifiedReal PI_OVER_2 = new UnifiedReal(BoundedRational.HALF, CR_PI);
+ private static final UnifiedReal PI_OVER_3 = new UnifiedReal(BoundedRational.THIRD, CR_PI);
+ private static final UnifiedReal PI_OVER_4 = new UnifiedReal(BoundedRational.QUARTER, CR_PI);
+ private static final UnifiedReal PI_OVER_6 = new UnifiedReal(BoundedRational.SIXTH, CR_PI);
+
+
+ /**
+ * Given a constructive real cr, try to determine whether cr is the square root of
+ * a small integer. If so, return its square as a BoundedRational. Otherwise return null.
+ * We make this determination by simple table lookup, so spurious null returns are
+ * entirely possible, or even likely.
+ */
+ private static BoundedRational getSquare(CR cr) {
+ for (int i = 0; i < sSqrts.length; ++i) {
+ if (sSqrts[i] == cr) {
+ return new BoundedRational(i);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Given a constructive real cr, try to determine whether cr is the square root of
+ * a small integer. If so, return its square as a BoundedRational. Otherwise return null.
+ * We make this determination by simple table lookup, so spurious null returns are
+ * entirely possible, or even likely.
+ */
+ private BoundedRational getExp(CR cr) {
+ for (int i = 0; i < sLogs.length; ++i) {
+ if (sLogs[i] == cr) {
+ return new BoundedRational(i);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * If the argument is a well-known constructive real, return its name.
+ * The name of "CR_ONE" is the empty string.
+ * No named constructive reals are rational multiples of each other.
+ * Thus two UnifiedReals with different named mCrFactors can be equal only if both
+ * mRatFactors are zero or possibly if one is CR_PI and the other is CR_E.
+ * (The latter is apparently an open problem.)
+ */
+ private static String crName(CR cr) {
+ if (cr == CR_ONE) {
+ return "";
+ }
+ if (cr == CR_PI) {
+ return "\u03C0"; // GREEK SMALL LETTER PI
+ }
+ if (cr == CR_E) {
+ return "e";
+ }
+ for (int i = 0; i < sSqrts.length; ++i) {
+ if (cr == sSqrts[i]) {
+ return "\u221A" /* SQUARE ROOT */ + i;
+ }
+ }
+ for (int i = 0; i < sLogs.length; ++i) {
+ if (cr == sLogs[i]) {
+ return "ln(" + i + ")";
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Would crName() return non-Null?
+ */
+ private static boolean isNamed(CR cr) {
+ if (cr == CR_ONE || cr == CR_PI || cr == CR_E) {
+ return true;
+ }
+ for (CR r: sSqrts) {
+ if (cr == r) {
+ return true;
+ }
+ }
+ for (CR r: sLogs) {
+ if (cr == r) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Is cr known to be algebraic (as opposed to transcendental)?
+ * Currently only produces meaningful results for the above known special
+ * constructive reals.
+ */
+ private static boolean definitelyAlgebraic(CR cr) {
+ return cr == CR_ONE || getSquare(cr) != null;
+ }
+
+ /**
+ * Is this number known to be rational?
+ */
+ public boolean definitelyRational() {
+ return mCrFactor == CR_ONE || mRatFactor.signum() == 0;
+ }
+
+ /**
+ * Is this number known to be irrational?
+ * TODO: We could track the fact that something is irrational with an explicit flag, which
+ * could cover many more cases. Whether that matters in practice is TBD.
+ */
+ public boolean definitelyIrrational() {
+ return !definitelyRational() && isNamed(mCrFactor);
+ }
+
+ /**
+ * Is this number known to be algebraic?
+ */
+ public boolean definitelyAlgebraic() {
+ return definitelyAlgebraic(mCrFactor) || mRatFactor.signum() == 0;
+ }
+
+ /**
+ * Is this number known to be transcendental?
+ */
+ public boolean definitelyTranscendental() {
+ return !definitelyAlgebraic() && isNamed(mCrFactor);
+ }
+
+
+ /**
+ * Is it known that the two constructive reals differ by something other than a
+ * a rational factor, i.e. is it known that two UnifiedReals
+ * with those mCrFactors will compare unequal unless both mRatFactors are zero?
+ * If this returns true, then a comparison of two UnifiedReals using those two
+ * mCrFactors cannot diverge, though we don't know of a good runtime bound.
+ */
+ private static boolean definitelyIndependent(CR r1, CR r2) {
+ // The question here is whether r1 = x*r2, where x is rational, where r1 and r2
+ // are in our set of special known CRs, can have a solution.
+ // This cannot happen if one is CR_ONE and the other is not.
+ // (Since all others are irrational.)
+ // This cannot happen for two named square roots, which have no repeated factors.
+ // (To see this, square both sides of the equation and factor. Each prime
+ // factor in the numerator and denominator occurs twice.)
+ // This cannot happen for e or pi on one side, and a square root on the other.
+ // (One is transcendental, the other is algebraic.)
+ // This cannot happen for two of our special natural logs.
+ // (Otherwise ln(m) = (a/b)ln(n) ==> m = n^(a/b) ==> m^b = n^a, which is impossible
+ // because either m or n includes a prime factor not shared by the other.)
+ // This cannot happen for a log and a square root.
+ // (The Lindemann-Weierstrass theorem tells us, among other things, that if
+ // a is algebraic, then exp(a) is transcendental. Thus if l in our finite
+ // set of logs where algebraic, expl(l), must be transacendental.
+ // But exp(l) is an integer. Thus the logs are transcendental. But of course the
+ // square roots are algebraic. Thus they can't be rational multiples.)
+ // Unfortunately, we do not know whether e/pi is rational.
+ if (r1 == r2) {
+ return false;
+ }
+ CR other;
+ if (r1 == CR_E || r1 == CR_PI) {
+ return definitelyAlgebraic(r2);
+ }
+ if (r2 == CR_E || r2 == CR_PI) {
+ return definitelyAlgebraic(r1);
+ }
+ return isNamed(r1) && isNamed(r2);
+ }
+
+ /**
+ * Convert to String reflecting raw representation.
+ * Debug or log messages only, not pretty.
+ */
+ public String toString() {
+ return mRatFactor.toString() + "*" + mCrFactor.toString();
+ }
+
+ /**
+ * Convert to readable String.
+ * Intended for user output. Produces exact expression when possible.
+ */
+ public String toNiceString() {
+ if (mCrFactor == CR_ONE || mRatFactor.signum() == 0) {
+ return mRatFactor.toNiceString();
+ }
+ String name = crName(mCrFactor);
+ if (name != null) {
+ BigInteger bi = BoundedRational.asBigInteger(mRatFactor);
+ if (bi != null) {
+ if (bi.equals(BigInteger.ONE)) {
+ return name;
+ }
+ return mRatFactor.toNiceString() + name;
+ }
+ return "(" + mRatFactor.toNiceString() + ")" + name;
+ }
+ if (mRatFactor.equals(BoundedRational.ONE)) {
+ return mCrFactor.toString();
+ }
+ return crValue().toString();
+ }
+
+ /**
+ * Will toNiceString() produce an exact representation?
+ */
+ public boolean exactlyDisplayable() {
+ return crName(mCrFactor) != null;
+ }
+
+ // Number of extra bits used in evaluation below to prefer truncation to rounding.
+ // Must be <= 30.
+ private final static int EXTRA_PREC = 10;
+
+ /*
+ * Returns a truncated representation of the result.
+ * If exactlyTruncatable(), we round correctly towards zero. Otherwise the resulting digit
+ * string may occasionally be rounded up instead.
+ * The result includes n digits to the right of the decimal point.
+ * @param n result precision, >= 0
+ */
+ public String toStringTruncated(int n) {
+ if (mCrFactor == CR_ONE || mRatFactor == BoundedRational.ZERO) {
+ return mRatFactor.toStringTruncated(n);
+ }
+ final CR scaled = CR.valueOf(BigInteger.TEN.pow(n)).multiply(crValue());
+ boolean negative = false;
+ BigInteger intScaled;
+ if (exactlyTruncatable()) {
+ intScaled = scaled.get_appr(0);
+ if (intScaled.signum() < 0) {
+ negative = true;
+ intScaled = intScaled.negate();
+ }
+ if (CR.valueOf(intScaled).compareTo(scaled.abs()) > 0) {
+ intScaled = intScaled.subtract(BigInteger.ONE);
+ }
+ check(CR.valueOf(intScaled).compareTo(scaled.abs()) < 0);
+ } else {
+ // Approximate case. Exact comparisons are impossible.
+ intScaled = scaled.get_appr(-EXTRA_PREC);
+ if (intScaled.signum() < 0) {
+ negative = true;
+ intScaled = intScaled.negate();
+ }
+ intScaled = intScaled.shiftRight(EXTRA_PREC);
+ }
+ String digits = intScaled.toString();
+ int len = digits.length();
+ if (len < n + 1) {
+ digits = StringUtils.repeat('0', n + 1 - len) + digits;
+ len = n + 1;
+ }
+ return (negative ? "-" : "") + digits.substring(0, len - n) + "."
+ + digits.substring(len - n);
+ }
+
+ /*
+ * Can we compute correctly truncated approximations of this number?
+ */
+ public boolean exactlyTruncatable() {
+ // If the value is known rational, we can do exact comparisons.
+ // If the value is known irrational, then we can safely compare to rational approximations;
+ // equality is impossible; hence the comparison must converge.
+ // The only problem cases are the ones in which we don't know.
+ return mCrFactor == CR_ONE || mRatFactor == BoundedRational.ZERO || definitelyIrrational();
+ }
+
+ /**
+ * Return a double approximation.
+ * TODO: Result is correctly rounded if known to be rational.
+ */
+ public double doubleValue() {
+ if (mCrFactor == CR_ONE) {
+ return mRatFactor.doubleValue(); // Hopefully correctly rounded
+ } else {
+ return crValue().doubleValue(); // Approximately correctly rounded
+ }
+ }
+
+ public CR crValue() {
+ return mRatFactor.crValue().multiply(mCrFactor);
+ }
+
+ /**
+ * Are this and r exactly comparable?
+ */
+ public boolean isComparable(UnifiedReal u) {
+ // We check for ONE only to speed up the common case.
+ // The use of a tolerance here means we can spuriously return false, not true.
+ return mCrFactor == u.mCrFactor
+ && (isNamed(mCrFactor) || mCrFactor.signum(DEFAULT_COMPARE_TOLERANCE) != 0)
+ || mRatFactor.signum() == 0 && u.mRatFactor.signum() == 0
+ || definitelyIndependent(mCrFactor, u.mCrFactor)
+ || crValue().compareTo(u.crValue(), DEFAULT_COMPARE_TOLERANCE) != 0;
+ }
+
+ /**
+ * Return +1 if this is greater than r, -1 if this is less than r, or 0 of the two are
+ * known to be equal.
+ * May diverge if the two are equal and !isComparable(r).
+ */
+ public int compareTo(UnifiedReal u) {
+ if (definitelyZero() && u.definitelyZero()) return 0;
+ if (mCrFactor == u.mCrFactor) {
+ int signum = mCrFactor.signum(); // Can diverge if mCRFactor == 0.
+ return signum * mRatFactor.compareTo(u.mRatFactor);
+ }
+ return crValue().compareTo(u.crValue()); // Can also diverge.
+ }
+
+ /**
+ * Return +1 if this is greater than r, -1 if this is less than r, or possibly 0 of the two are
+ * within 2^a of each other.
+ */
+ public int compareTo(UnifiedReal u, int a) {
+ if (isComparable(u)) {
+ return compareTo(u);
+ } else {
+ return crValue().compareTo(u.crValue(), a);
+ }
+ }
+
+ /**
+ * Return compareTo(ZERO, a).
+ */
+ public int signum(int a) {
+ return compareTo(ZERO, a);
+ }
+
+ /**
+ * Return compareTo(ZERO).
+ * May diverge for ZERO argument if !isComparable(ZERO).
+ */
+ public int signum() {
+ return compareTo(ZERO);
+ }
+
+ /**
+ * Equality comparison. May erroneously return true if values differ by less than 2^a,
+ * and !isComparable(u).
+ */
+ public boolean approxEquals(UnifiedReal u, int a) {
+ if (isComparable(u)) {
+ if (definitelyIndependent(mCrFactor, u.mCrFactor)
+ && (mRatFactor.signum() != 0 || u.mRatFactor.signum() != 0)) {
+ // No need to actually evaluate, though we don't know which is larger.
+ return false;
+ } else {
+ return compareTo(u) == 0;
+ }
+ }
+ return crValue().compareTo(u.crValue(), a) == 0;
+ }
+
+ /**
+ * Returns true if values are definitely known to be equal, false in all other cases.
+ */
+ public boolean definitelyEquals(UnifiedReal u) {
+ return isComparable(u) && compareTo(u) == 0;
+ }
+
+ /**
+ * Returns true if values are definitely known not to be equal, false in all other cases.
+ */
+ public boolean definitelyNotEquals(UnifiedReal u) {
+ boolean isNamed = isNamed(mCrFactor);
+ boolean uIsNamed = isNamed(u.mCrFactor);
+ if (isNamed && uIsNamed) {
+ if (definitelyIndependent(mCrFactor, u.mCrFactor)) {
+ return mRatFactor.signum() != 0 || u.mRatFactor.signum() != 0;
+ } else if (mCrFactor == u.mCrFactor) {
+ return !mRatFactor.equals(u.mRatFactor);
+ }
+ return !mRatFactor.equals(u.mRatFactor);
+ }
+ if (mRatFactor.signum() == 0) {
+ return uIsNamed && u.mRatFactor.signum() != 0;
+ }
+ if (u.mRatFactor.signum() == 0) {
+ return isNamed && mRatFactor.signum() != 0;
+ }
+ return false;
+ }
+
+ // And some slightly faster convenience functions for special cases:
+
+ public boolean definitelyZero() {
+ return mRatFactor.signum() == 0;
+ }
+
+ public boolean definitelyNonZero() {
+ return isNamed(mCrFactor) && mRatFactor.signum() != 0;
+ }
+
+ public boolean definitelyOne() {
+ return mCrFactor == CR_ONE && mRatFactor.equals(BoundedRational.ONE);
+ }
+
+ /**
+ * Return equivalent BoundedRational, if known to exist, null otherwise
+ */
+ public BoundedRational boundedRationalValue() {
+ if (mCrFactor == CR_ONE || mRatFactor.signum() == 0) {
+ return mRatFactor;
+ }
+ return null;
+ }
+
+ /**
+ * Returns equivalent BigInteger result if it exists, null if not.
+ */
+ public BigInteger bigIntegerValue() {
+ final BoundedRational r = boundedRationalValue();
+ return BoundedRational.asBigInteger(r);
+ }
+
+ public UnifiedReal add(UnifiedReal u) {
+ if (mCrFactor == u.mCrFactor) {
+ BoundedRational nRatFactor = BoundedRational.add(mRatFactor, u.mRatFactor);
+ if (nRatFactor != null) {
+ return new UnifiedReal(nRatFactor, mCrFactor);
+ }
+ }
+ if (definitelyZero()) {
+ // Avoid creating new mCrFactor, even if they don't currently match.
+ return u;
+ }
+ if (u.definitelyZero()) {
+ return this;
+ }
+ return new UnifiedReal(crValue().add(u.crValue()));
+ }
+
+ public UnifiedReal negate() {
+ return new UnifiedReal(BoundedRational.negate(mRatFactor), mCrFactor);
+ }
+
+ public UnifiedReal subtract(UnifiedReal u) {
+ return add(u.negate());
+ }
+
+ public UnifiedReal multiply(UnifiedReal u) {
+ // Preserve a preexisting mCrFactor when we can.
+ if (mCrFactor == CR_ONE) {
+ BoundedRational nRatFactor = BoundedRational.multiply(mRatFactor, u.mRatFactor);
+ if (nRatFactor != null) {
+ return new UnifiedReal(nRatFactor, u.mCrFactor);
+ }
+ }
+ if (u.mCrFactor == CR_ONE) {
+ BoundedRational nRatFactor = BoundedRational.multiply(mRatFactor, u.mRatFactor);
+ if (nRatFactor != null) {
+ return new UnifiedReal(nRatFactor, mCrFactor);
+ }
+ }
+ if (definitelyZero() || u.definitelyZero()) {
+ return ZERO;
+ }
+ if (mCrFactor == u.mCrFactor) {
+ BoundedRational square = getSquare(mCrFactor);
+ if (square != null) {
+ BoundedRational nRatFactor = BoundedRational.multiply(
+ BoundedRational.multiply(square, mRatFactor), u.mRatFactor);
+ if (nRatFactor != null) {
+ return new UnifiedReal(nRatFactor);
+ }
+ }
+ }
+ // Probably a bit cheaper to multiply component-wise.
+ BoundedRational nRatFactor = BoundedRational.multiply(mRatFactor, u.mRatFactor);
+ if (nRatFactor != null) {
+ return new UnifiedReal(nRatFactor, mCrFactor.multiply(u.mCrFactor));
+ }
+ return new UnifiedReal(crValue().multiply(u.crValue()));
+ }
+
+ public static class ZeroDivisionException extends ArithmeticException {
+ public ZeroDivisionException() {
+ super("Division by zero");
+ }
+ }
+
+ /**
+ * Return the reciprocal.
+ */
+ public UnifiedReal inverse() {
+ if (definitelyZero()) {
+ throw new ZeroDivisionException();
+ }
+ BoundedRational square = getSquare(mCrFactor);
+ if (square != null) {
+ // 1/sqrt(n) = sqrt(n)/n
+ BoundedRational nRatFactor = BoundedRational.inverse(
+ BoundedRational.multiply(mRatFactor, square));
+ if (nRatFactor != null) {
+ return new UnifiedReal(nRatFactor, mCrFactor);
+ }
+ }
+ return new UnifiedReal(BoundedRational.inverse(mRatFactor), mCrFactor.inverse());
+ }
+
+ public UnifiedReal divide(UnifiedReal u) {
+ if (mCrFactor == u.mCrFactor) {
+ if (u.definitelyZero()) {
+ throw new ZeroDivisionException();
+ }
+ BoundedRational nRatFactor = BoundedRational.divide(mRatFactor, u.mRatFactor);
+ if (nRatFactor != null) {
+ return new UnifiedReal(nRatFactor, CR_ONE);
+ }
+ }
+ return multiply(u.inverse());
+ }
+
+ public UnifiedReal sqrt() {
+ if (mCrFactor == CR_ONE) {
+ BoundedRational ratSqrt;
+ // Check for all arguments of the form <perfect rational square> * small_int,
+ // where small_int has a known sqrt. This includes the small_int = 1 case.
+ for (int divisor = 1; divisor < sSqrts.length; ++divisor) {
+ if (sSqrts[divisor] != null) {
+ ratSqrt = BoundedRational.sqrt(
+ BoundedRational.divide(mRatFactor, new BoundedRational(divisor)));
+ if (ratSqrt != null) {
+ return new UnifiedReal(ratSqrt, sSqrts[divisor]);
+ }
+ }
+ }
+ }
+ return new UnifiedReal(crValue().sqrt());
+ }
+
+ /**
+ * Return (this mod 2pi)/(pi/6) as a BigInteger, or null if that isn't easily possible.
+ */
+ private BigInteger getPiTwelfths() {
+ if (definitelyZero()) return BigInteger.ZERO;
+ if (mCrFactor == CR_PI) {
+ BigInteger quotient = BoundedRational.asBigInteger(
+ BoundedRational.multiply(mRatFactor, BoundedRational.TWELVE));
+ if (quotient == null) {
+ return null;
+ }
+ return quotient.mod(BIG_24);
+ }
+ return null;
+ }
+
+ /**
+ * Computer the sin() for an integer multiple n of pi/12, if easily representable.
+ * @param n value between 0 and 23 inclusive.
+ */
+ private static UnifiedReal sinPiTwelfths(int n) {
+ if (n >= 12) {
+ UnifiedReal negResult = sinPiTwelfths(n - 12);
+ return negResult == null ? null : negResult.negate();
+ }
+ switch (n) {
+ case 0:
+ return ZERO;
+ case 2: // 30 degrees
+ return HALF;
+ case 3: // 45 degrees
+ return HALF_SQRT2;
+ case 4: // 60 degrees
+ return HALF_SQRT3;
+ case 6:
+ return ONE;
+ case 8:
+ return HALF_SQRT3;
+ case 9:
+ return HALF_SQRT2;
+ case 10:
+ return HALF;
+ default:
+ return null;
+ }
+ }
+
+ public UnifiedReal sin() {
+ BigInteger piTwelfths = getPiTwelfths();
+ if (piTwelfths != null) {
+ UnifiedReal result = sinPiTwelfths(piTwelfths.intValue());
+ if (result != null) {
+ return result;
+ }
+ }
+ return new UnifiedReal(crValue().sin());
+ }
+
+ private static UnifiedReal cosPiTwelfths(int n) {
+ int sinArg = n + 6;
+ if (sinArg >= 24) {
+ sinArg -= 24;
+ }
+ return sinPiTwelfths(sinArg);
+ }
+
+ public UnifiedReal cos() {
+ BigInteger piTwelfths = getPiTwelfths();
+ if (piTwelfths != null) {
+ UnifiedReal result = cosPiTwelfths(piTwelfths.intValue());
+ if (result != null) {
+ return result;
+ }
+ }
+ return new UnifiedReal(crValue().cos());
+ }
+
+ public UnifiedReal tan() {
+ BigInteger piTwelfths = getPiTwelfths();
+ if (piTwelfths != null) {
+ int i = piTwelfths.intValue();
+ if (i == 6 || i == 18) {
+ throw new ArithmeticException("Tangent undefined");
+ }
+ UnifiedReal top = sinPiTwelfths(i);
+ UnifiedReal bottom = cosPiTwelfths(i);
+ if (top != null && bottom != null) {
+ return top.divide(bottom);
+ }
+ }
+ return sin().divide(cos());
+ }
+
+ // Throw an exception if the argument is definitely out of bounds for asin or acos.
+ private void checkAsinDomain() {
+ if (isComparable(ONE) && (compareTo(ONE) > 0 || compareTo(MINUS_ONE) < 0)) {
+ throw new ArithmeticException("inverse trig argument out of range");
+ }
+ }
+
+ /**
+ * Return asin(n/2). n is between -2 and 2.
+ */
+ public static UnifiedReal asinHalves(int n){
+ if (n < 0) {
+ return (asinHalves(-n).negate());
+ }
+ switch (n) {
+ case 0:
+ return ZERO;
+ case 1:
+ return new UnifiedReal(BoundedRational.SIXTH, CR.PI);
+ case 2:
+ return new UnifiedReal(BoundedRational.HALF, CR.PI);
+ }
+ throw new AssertionError("asinHalves: Bad argument");
+ }
+
+ /**
+ * Return asin of this, assuming this is not an integral multiple of a half.
+ */
+ public UnifiedReal asinNonHalves()
+ {
+ if (compareTo(ZERO, -10) < 0) {
+ return negate().asinNonHalves().negate();
+ }
+ if (definitelyEquals(HALF_SQRT2)) {
+ return new UnifiedReal(BoundedRational.QUARTER, CR_PI);
+ }
+ if (definitelyEquals(HALF_SQRT3)) {
+ return new UnifiedReal(BoundedRational.THIRD, CR_PI);
+ }
+ return new UnifiedReal(crValue().asin());
+ }
+
+ public UnifiedReal asin() {
+ checkAsinDomain();
+ final BigInteger halves = multiply(TWO).bigIntegerValue();
+ if (halves != null) {
+ return asinHalves(halves.intValue());
+ }
+ if (mCrFactor == CR.ONE || mCrFactor != CR_SQRT2 ||mCrFactor != CR_SQRT3) {
+ return asinNonHalves();
+ }
+ return new UnifiedReal(crValue().asin());
+ }
+
+ public UnifiedReal acos() {
+ return PI_OVER_2.subtract(asin());
+ }
+
+ public UnifiedReal atan() {
+ if (compareTo(ZERO, -10) < 0) {
+ return negate().atan().negate();
+ }
+ final BigInteger asBI = bigIntegerValue();
+ if (asBI != null && asBI.compareTo(BigInteger.ONE) <= 0) {
+ final int asInt = asBI.intValue();
+ // These seem to be all rational cases:
+ switch (asInt) {
+ case 0:
+ return ZERO;
+ case 1:
+ return PI_OVER_4;
+ default:
+ throw new AssertionError("Impossible r_int");
+ }
+ }
+ if (definitelyEquals(THIRD_SQRT3)) {
+ return PI_OVER_6;
+ }
+ if (definitelyEquals(SQRT3)) {
+ return PI_OVER_3;
+ }
+ return new UnifiedReal(UnaryCRFunction.atanFunction.execute(crValue()));
+ }
+
+ private static final BigInteger BIG_TWO = BigInteger.valueOf(2);
+
+ /**
+ * Compute an integral power of this.
+ */
+ private UnifiedReal pow(BigInteger exp) {
+ if (exp.signum() < 0) {
+ return pow(exp.negate()).inverse();
+ }
+ if (exp.equals(BigInteger.ONE)) {
+ return this;
+ }
+ if (exp.signum() == 0) {
+ // Questionable if base has undefined value. Java.lang.Math.pow() returns 1 anyway,
+ // so we do the same.
+ return ONE;
+ }
+ if (mCrFactor == CR_ONE) {
+ final BoundedRational ratPow = mRatFactor.pow(exp);
+ if (ratPow != null) {
+ return new UnifiedReal(mRatFactor.pow(exp));
+ }
+ }
+ BoundedRational square = getSquare(mCrFactor);
+ if (square != null) {
+ final BoundedRational nRatFactor =
+ BoundedRational.multiply(mRatFactor.pow(exp), square.pow(exp.shiftRight(1)));
+ if (nRatFactor != null) {
+ if (exp.and(BigInteger.ONE).intValue() == 1) {
+ // Odd power: Multiply by remaining square root.
+ return new UnifiedReal(nRatFactor, mCrFactor);
+ } else {
+ return new UnifiedReal(nRatFactor);
+ }
+ }
+ }
+ return new UnifiedReal(crValue().ln().multiply(CR.valueOf(exp)).exp());
+ }
+
+ public UnifiedReal pow(UnifiedReal expon) {
+ if (mCrFactor == CR_E) {
+ if (mRatFactor.equals(BoundedRational.ONE)) {
+ return expon.exp();
+ } else {
+ UnifiedReal ratPart = new UnifiedReal(mRatFactor).pow(expon);
+ return expon.exp().multiply(ratPart);
+ }
+ }
+ final BoundedRational expAsBR = expon.boundedRationalValue();
+ if (expAsBR != null) {
+ BigInteger expAsBI = BoundedRational.asBigInteger(expAsBR);
+ if (expAsBI != null) {
+ return pow(expAsBI);
+ } else {
+ // Check for exponent that is a multiple of a half.
+ expAsBI = BoundedRational.asBigInteger(
+ BoundedRational.multiply(BoundedRational.TWO, expAsBR));
+ if (expAsBI != null) {
+ return pow(expAsBI).sqrt();
+ }
+ }
+ }
+ return new UnifiedReal(crValue().ln().multiply(expon.crValue()).exp());
+ }
+
+ /**
+ * Raise the argument to the 16th power.
+ */
+ private static long pow16(int n) {
+ if (n > 10) {
+ throw new AssertionError("Unexpexted pow16 argument");
+ }
+ long result = n*n;
+ result *= result;
+ result *= result;
+ result *= result;
+ return result;
+ }
+
+ /**
+ * Return the integral log with respect to the given base if it exists, 0 otherwise.
+ * n is presumed positive.
+ */
+ private static long getIntLog(BigInteger n, int base) {
+ double nAsDouble = n.doubleValue();
+ double approx = Math.log(nAsDouble)/Math.log(base);
+ // A relatively quick test first.
+ // Unfortunately, this doesn't help for values to big to fit in a Double.
+ if (!Double.isInfinite(nAsDouble) && Math.abs(approx - Math.rint(approx)) > 1.0e-6) {
+ return 0;
+ }
+ long result = 0;
+ BigInteger remaining = n;
+ BigInteger bigBase = BigInteger.valueOf(base);
+ BigInteger base16th = null; // base^16, computed lazily
+ while (n.mod(bigBase).signum() == 0) {
+ if (Thread.interrupted()) {
+ throw new CR.AbortedException();
+ }
+ n = n.divide(bigBase);
+ ++result;
+ // And try a slightly faster computation for large n:
+ if (base16th == null) {
+ base16th = BigInteger.valueOf(pow16(base));
+ }
+ while (n.mod(base16th).signum() == 0) {
+ n = n.divide(base16th);
+ result += 16;
+ }
+ }
+ if (n.equals(BigInteger.ONE)) {
+ return result;
+ }
+ return 0;
+ }
+
+ public UnifiedReal ln() {
+ if (isComparable(ZERO)) {
+ if (signum() <= 0) {
+ throw new ArithmeticException("log(non-positive)");
+ }
+ int compare1 = compareTo(ONE, DEFAULT_COMPARE_TOLERANCE);
+ if (compare1 == 0) {
+ if (definitelyEquals(ONE)) {
+ return ZERO;
+ }
+ } else if (compare1 < 0) {
+ return inverse().ln().negate();
+ }
+ final BigInteger bi = BoundedRational.asBigInteger(mRatFactor);
+ if (bi != null) {
+ if (mCrFactor == CR_ONE) {
+ // Check for a power of a small integer. We can use sLogs[] to return
+ // a more useful answer for those.
+ for (int i = 0; i < sLogs.length; ++i) {
+ if (sLogs[i] != null) {
+ long intLog = getIntLog(bi, i);
+ if (intLog != 0) {
+ return new UnifiedReal(new BoundedRational(intLog), sLogs[i]);
+ }
+ }
+ }
+ } else {
+ // Check for n^k * sqrt(n), for which we can also return a more useful answer.
+ BoundedRational square = getSquare(mCrFactor);
+ if (square != null) {
+ int intSquare = square.intValue();
+ if (sLogs[intSquare] != null) {
+ long intLog = getIntLog(bi, intSquare);
+ if (intLog != 0) {
+ BoundedRational nRatFactor =
+ BoundedRational.add(new BoundedRational(intLog),
+ BoundedRational.HALF);
+ if (nRatFactor != null) {
+ return new UnifiedReal(nRatFactor, sLogs[intSquare]);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return new UnifiedReal(crValue().ln());
+ }
+
+ public UnifiedReal exp() {
+ if (definitelyEquals(ZERO)) {
+ return ONE;
+ }
+ final BoundedRational crExp = getExp(mCrFactor);
+ if (crExp != null) {
+ if (mRatFactor.signum() < 0) {
+ return negate().exp().inverse();
+ }
+ boolean needSqrt = false;
+ BoundedRational ratExponent = mRatFactor;
+ BigInteger asBI = BoundedRational.asBigInteger(ratExponent);
+ if (asBI == null) {
+ // check for multiple of one half.
+ needSqrt = true;
+ ratExponent = BoundedRational.multiply(ratExponent, BoundedRational.TWO);
+ }
+ BoundedRational nRatFactor = BoundedRational.pow(crExp, ratExponent);
+ if (nRatFactor != null) {
+ UnifiedReal result = new UnifiedReal(nRatFactor);
+ if (needSqrt) {
+ result = result.sqrt();
+ }
+ return result;
+ }
+ }
+ return new UnifiedReal(crValue().exp());
+ }
+
+
+ /**
+ * Generalized factorial.
+ * Compute n * (n - step) * (n - 2 * step) * etc. This can be used to compute factorial a bit
+ * faster, especially if BigInteger uses sub-quadratic multiplication.
+ */
+ private static BigInteger genFactorial(long n, long step) {
+ if (n > 4 * step) {
+ BigInteger prod1 = genFactorial(n, 2 * step);
+ if (Thread.interrupted()) {
+ throw new CR.AbortedException();
+ }
+ BigInteger prod2 = genFactorial(n - step, 2 * step);
+ if (Thread.interrupted()) {
+ throw new CR.AbortedException();
+ }
+ return prod1.multiply(prod2);
+ } else {
+ if (n == 0) {
+ return BigInteger.ONE;
+ }
+ BigInteger res = BigInteger.valueOf(n);
+ for (long i = n - step; i > 1; i -= step) {
+ res = res.multiply(BigInteger.valueOf(i));
+ }
+ return res;
+ }
+ }
+
+
+ /**
+ * Factorial function.
+ * Fails if argument is clearly not an integer.
+ * May round to nearest integer if value is close.
+ */
+ public UnifiedReal fact() {
+ BigInteger asBI = bigIntegerValue();
+ if (asBI == null) {
+ asBI = crValue().get_appr(0); // Correct if it was an integer.
+ if (!approxEquals(new UnifiedReal(asBI), DEFAULT_COMPARE_TOLERANCE)) {
+ throw new ArithmeticException("Non-integral factorial argument");
+ }
+ }
+ if (asBI.signum() < 0) {
+ throw new ArithmeticException("Negative factorial argument");
+ }
+ if (asBI.bitLength() > 20) {
+ // Will fail. LongValue() may not work. Punt now.
+ throw new ArithmeticException("Factorial argument too big");
+ }
+ BigInteger biResult = genFactorial(asBI.longValue(), 1);
+ BoundedRational nRatFactor = new BoundedRational(biResult);
+ return new UnifiedReal(nRatFactor);
+ }
+
+ /**
+ * Return the number of decimal digits to the right of the decimal point required to represent
+ * the argument exactly.
+ * Return Integer.MAX_VALUE if that's not possible. Never returns a value less than zero, even
+ * if r is a power of ten.
+ */
+ public int digitsRequired() {
+ if (mCrFactor == CR_ONE || mRatFactor.signum() == 0) {
+ return BoundedRational.digitsRequired(mRatFactor);
+ } else {
+ return Integer.MAX_VALUE;
+ }
+ }
+
+ /**
+ * Return an upper bound on the number of leading zero bits.
+ * These are the number of 0 bits
+ * to the right of the binary point and to the left of the most significant digit.
+ * Return Integer.MAX_VALUE if we cannot bound it.
+ */
+ public int leadingBinaryZeroes() {
+ if (isNamed(mCrFactor)) {
+ // Only ln(2) is smaller than one, and could possibly add one zero bit.
+ // Adding 3 gives us a somewhat sloppy upper bound.
+ final int wholeBits = mRatFactor.wholeNumberBits();
+ if (wholeBits == Integer.MIN_VALUE) {
+ return Integer.MAX_VALUE;
+ }
+ if (wholeBits >= 3) {
+ return 0;
+ } else {
+ return -wholeBits + 3;
+ }
+ }
+ return Integer.MAX_VALUE;
+ }
+
+ /**
+ * Is the number of bits to the left of the decimal point greater than bound?
+ * The result is inexact: We roughly approximate the whole number bits.
+ */
+ public boolean approxWholeNumberBitsGreaterThan(int bound) {
+ if (isNamed(mCrFactor)) {
+ return mRatFactor.wholeNumberBits() > bound;
+ } else {
+ return crValue().get_appr(bound - 2).bitLength() > 2;
+ }
+ }
+}
diff --git a/tests/src/com/android/calculator2/BRTest.java b/tests/src/com/android/calculator2/BoundedRationalTest.java
similarity index 61%
rename from tests/src/com/android/calculator2/BRTest.java
rename to tests/src/com/android/calculator2/BoundedRationalTest.java
index 68214d0..a53d6ad 100644
--- a/tests/src/com/android/calculator2/BRTest.java
+++ b/tests/src/com/android/calculator2/BoundedRationalTest.java
@@ -26,23 +26,19 @@
import java.math.BigInteger;
-public class BRTest extends TestCase {
+public class BoundedRationalTest extends TestCase {
private static void check(boolean x, String s) {
if (!x) throw new AssertionFailedError(s);
}
final static int TEST_PREC = -100; // 100 bits to the right of
// binary point.
private static void checkEq(BoundedRational x, CR y, String s) {
- check(x.CRValue().compareTo(y, TEST_PREC) == 0, s);
+ check(x.crValue().compareTo(y, TEST_PREC) == 0, s);
}
private static void checkWeakEq(BoundedRational x, CR y, String s) {
if (x != null) checkEq(x, y, s);
}
- private final static UnaryCRFunction ASIN = UnaryCRFunction.asinFunction;
- private final static UnaryCRFunction ACOS = UnaryCRFunction.acosFunction;
- private final static UnaryCRFunction ATAN = UnaryCRFunction.atanFunction;
- private final static UnaryCRFunction TAN = UnaryCRFunction.tanFunction;
private final static BoundedRational BR_0 = new BoundedRational(0);
private final static BoundedRational BR_M1 = new BoundedRational(-1);
private final static BoundedRational BR_2 = new BoundedRational(2);
@@ -52,22 +48,10 @@
private final static BoundedRational BR_M390 = new BoundedRational(-390);
private final static CR CR_1 = CR.valueOf(1);
- private final static CR RADIANS_PER_DEGREE = CR.PI.divide(CR.valueOf(180));
- private final static CR DEGREES_PER_RADIAN = CR.valueOf(180).divide(CR.PI);
- private final static CR LN10 = CR.valueOf(10).ln();
-
- private static CR toRadians(CR x) {
- return x.multiply(RADIANS_PER_DEGREE);
- }
-
- private static CR fromRadians(CR x) {
- return x.multiply(DEGREES_PER_RADIAN);
- }
-
// We assume that x is simple enough that we don't overflow bounds.
private static void checkBR(BoundedRational x) {
check(x != null, "test data should not be null");
- CR xAsCR = x.CRValue();
+ CR xAsCR = x.crValue();
checkEq(BoundedRational.add(x, BoundedRational.ONE), xAsCR.add(CR_1),
"add 1:" + x);
checkEq(BoundedRational.subtract(x, BoundedRational.MINUS_THIRTY),
@@ -76,48 +60,15 @@
xAsCR.multiply(CR.valueOf(15)), "multiply 15:" + x);
checkEq(BoundedRational.divide(x, BR_15),
xAsCR.divide(CR.valueOf(15)), "divide 15:" + x);
- checkWeakEq(BoundedRational.sin(x), xAsCR.sin(), "sin:" + x);
- checkWeakEq(BoundedRational.cos(x), xAsCR.cos(), "cos:" + x);
- checkWeakEq(BoundedRational.tan(x), TAN.execute(xAsCR), "tan:" + x);
- checkWeakEq(BoundedRational.degreeSin(x), toRadians(xAsCR).sin(),
- "degree sin:" + x);
- checkWeakEq(BoundedRational.degreeCos(x), toRadians(xAsCR).cos(),
- "degree cos:" + x);
BigInteger big_x = BoundedRational.asBigInteger(x);
long long_x = (big_x == null? 0 : big_x.longValue());
- try {
- checkWeakEq(BoundedRational.degreeTan(x),
- TAN.execute(toRadians(xAsCR)), "degree tan:" + x);
- check((long_x - 90) % 180 != 0, "missed undefined tan: " + x);
- } catch (ArithmeticException ignored) {
- check((long_x - 90) % 180 == 0, "exception on defined tan: " + x);
- }
if (x.compareTo(BoundedRational.THIRTY) <= 0
&& x.compareTo(BoundedRational.MINUS_THIRTY) >= 0) {
- checkWeakEq(BoundedRational.exp(x), xAsCR.exp(), "exp:" + x);
checkWeakEq(BoundedRational.pow(BR_15, x),
CR.valueOf(15).ln().multiply(xAsCR).exp(),
"pow(15,x):" + x);
}
- if (x.compareTo(BoundedRational.ONE) <= 0
- && x.compareTo(BoundedRational.MINUS_ONE) >= 0) {
- checkWeakEq(BoundedRational.asin(x), ASIN.execute(xAsCR),
- "asin:" + x);
- checkWeakEq(BoundedRational.acos(x), ACOS.execute(xAsCR),
- "acos:" + x);
- checkWeakEq(BoundedRational.degreeAsin(x),
- fromRadians(ASIN.execute(xAsCR)), "degree asin:" + x);
- checkWeakEq(BoundedRational.degreeAcos(x),
- fromRadians(ACOS.execute(xAsCR)), "degree acos:" + x);
- }
- checkWeakEq(BoundedRational.atan(x), fromRadians(ATAN.execute(xAsCR)),
- "atan:" + x);
- checkWeakEq(BoundedRational.degreeAtan(x),
- fromRadians(ATAN.execute(xAsCR)), "degree atan:" + x);
if (x.signum() > 0) {
- checkWeakEq(BoundedRational.ln(x), xAsCR.ln(), "ln:" + x);
- checkWeakEq(BoundedRational.log(x), xAsCR.ln().divide(LN10),
- "log:" + x);
checkWeakEq(BoundedRational.sqrt(x), xAsCR.sqrt(), "sqrt:" + x);
checkEq(BoundedRational.pow(x, BR_15),
xAsCR.ln().multiply(CR.valueOf(15)).exp(),
@@ -128,7 +79,10 @@
public void testBR() {
BoundedRational b = new BoundedRational(4,-6);
check(b.toString().equals("4/-6"), "toString(4/-6)");
- check(b.toNiceString().equals("-2/3"),"toNiceString(4/-6)");
+ check(b.toNiceString().equals("-2/3"), "toNiceString(4/-6)");
+ check(b.toStringTruncated(1).equals("-0.6"), "(4/-6).toStringT(1)");
+ check(BR_15.toStringTruncated(0).equals("15."), "15.toStringT(1)");
+ check(BR_0.toStringTruncated(2).equals("0.00"), "0.toStringT(2)");
checkEq(BR_0, CR.valueOf(0), "0");
checkEq(BR_390, CR.valueOf(390), "390");
checkEq(BR_15, CR.valueOf(15), "15");
@@ -151,10 +105,6 @@
"digitsRequired(-1/2)");
check(BoundedRational.digitsRequired(new BoundedRational(1,-2)) == 1,
"digitsRequired(1/-2)");
- check(BoundedRational.fact(BoundedRational.ZERO).equals(BoundedRational.ONE), "0!");
- check(BoundedRational.fact(BoundedRational.ONE).equals(BoundedRational.ONE), "1!");
- check(BoundedRational.fact(BoundedRational.TWO).equals(BoundedRational.TWO), "2!");
- check(BoundedRational.fact(BR_15).equals(new BoundedRational(1307674368000L)), "15!");
// We check values that include all interesting degree values.
BoundedRational r = BR_M390;
while (!r.equals(BR_390)) {
@@ -183,39 +133,25 @@
public void testBRexceptions() {
try {
- BoundedRational.ln(BR_M1);
- check(false, "ln(-1)");
- } catch (ArithmeticException ignored) {}
- try {
- BoundedRational.log(BR_M2);
- check(false, "log(-2)");
+ BoundedRational.divide(BR_390, BoundedRational.ZERO);
+ check(false, "390/0");
} catch (ArithmeticException ignored) {}
try {
BoundedRational.sqrt(BR_M1);
check(false, "sqrt(-1)");
} catch (ArithmeticException ignored) {}
- try {
- BoundedRational.asin(BR_M2);
- check(false, "asin(-2)");
- } catch (ArithmeticException ignored) {}
- try {
- BoundedRational.degreeAcos(BR_2);
- check(false, "degree acos(2)");
- } catch (ArithmeticException ignored) {}
}
public void testBROverflow() {
BoundedRational sum = new BoundedRational(0);
long i;
- for (i = 1; i < 1000; ++i) {
+ for (i = 1; i < 4000; ++i) {
sum = BoundedRational.add(sum,
BoundedRational.inverse(new BoundedRational(i)));
if (sum == null) break;
}
- // Experimentally, this overflows at 139, which seems
- // plausible based on the Wolfram Alpha result.
- // This test is robust against minor changes in MAX_SIZE.
- check(i > 100, "Harmonic series overflowed at " + i);
- check(i < 1000, "Harmonic series didn't overflow");
+ // With MAX_SIZE = 10000, we seem to overflow at 3488.
+ check(i > 3000, "Harmonic series overflowed at " + i);
+ check(i < 4000, "Harmonic series didn't overflow");
}
}
diff --git a/tests/src/com/android/calculator2/UnifiedRealTest.java b/tests/src/com/android/calculator2/UnifiedRealTest.java
new file mode 100644
index 0000000..20ac2b1
--- /dev/null
+++ b/tests/src/com/android/calculator2/UnifiedRealTest.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// A test for UnifiedReal package.
+
+package com.android.calculator2;
+
+import com.hp.creals.CR;
+import com.hp.creals.UnaryCRFunction;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.TestCase;
+
+import java.math.BigInteger;
+
+public class UnifiedRealTest extends TestCase {
+ private static void check(boolean x, String s) {
+ if (!x) throw new AssertionFailedError(s);
+ }
+ final static int TEST_PREC = -100; // 100 bits to the right of
+ // binary point.
+ private static void checkEq(UnifiedReal x, CR y, String s) {
+ check(x.crValue().compareTo(y, TEST_PREC) == 0, s);
+ }
+
+ private final static UnaryCRFunction ASIN = UnaryCRFunction.asinFunction;
+ private final static UnaryCRFunction ACOS = UnaryCRFunction.acosFunction;
+ private final static UnaryCRFunction ATAN = UnaryCRFunction.atanFunction;
+ private final static UnaryCRFunction TAN = UnaryCRFunction.tanFunction;
+ private final static CR CR_1 = CR.ONE;
+
+ private final static CR RADIANS_PER_DEGREE = CR.PI.divide(CR.valueOf(180));
+ private final static CR DEGREES_PER_RADIAN = CR.valueOf(180).divide(CR.PI);
+ private final static CR LN10 = CR.valueOf(10).ln();
+
+ private final static UnifiedReal UR_30 = new UnifiedReal(30);
+ private final static UnifiedReal UR_MINUS30 = new UnifiedReal(-30);
+ private final static UnifiedReal UR_15 = new UnifiedReal(15);
+ private final static UnifiedReal UR_MINUS15 = new UnifiedReal(-15);
+
+ private static CR toRadians(CR x) {
+ return x.multiply(RADIANS_PER_DEGREE);
+ }
+
+ private static CR fromRadians(CR x) {
+ return x.multiply(DEGREES_PER_RADIAN);
+ }
+
+ private static UnifiedReal toRadians(UnifiedReal x) {
+ return x.multiply(UnifiedReal.RADIANS_PER_DEGREE);
+ }
+
+ private static UnifiedReal fromRadians(UnifiedReal x) {
+ return x.divide(UnifiedReal.RADIANS_PER_DEGREE);
+ }
+
+ // We assume that x is simple enough that we don't overflow bounds.
+ private static void checkUR(UnifiedReal x) {
+ CR xAsCr = x.crValue();
+ checkEq(x.add(UnifiedReal.ONE), xAsCr.add(CR_1), "add 1:" + x);
+ checkEq(x.subtract(UR_MINUS30), xAsCr.subtract(CR.valueOf(-30)), "sub -30:" + x);
+ checkEq(x.multiply(UR_15), xAsCr.multiply(CR.valueOf(15)), "multiply 15:" + x);
+ checkEq(x.divide(UR_15), xAsCr.divide(CR.valueOf(15)), "divide 15:" + x);
+ checkEq(x.sin(), xAsCr.sin(), "sin:" + x);
+ checkEq(x.cos(), xAsCr.cos(), "cos:" + x);
+ if (x.cos().definitelyNonZero()) {
+ checkEq(x.tan(), TAN.execute(xAsCr), "tan:" + x);
+ }
+ checkEq(toRadians(x).sin(), toRadians(xAsCr).sin(), "degree sin:" + x);
+ checkEq(toRadians(x).cos(), toRadians(xAsCr).cos(), "degree cos:" + x);
+ BigInteger big_x = x.bigIntegerValue();
+ long long_x = (big_x == null? 0 : big_x.longValue());
+ try {
+ checkEq(toRadians(x).tan(), TAN.execute(toRadians(xAsCr)), "degree tan:" + x);
+ check((long_x - 90) % 180 != 0, "missed undefined tan: " + x);
+ } catch (ArithmeticException ignored) {
+ check((long_x - 90) % 180 == 0, "exception on defined tan: " + x + " " + ignored);
+ }
+ if (x.compareTo(UR_30) <= 0 && x.compareTo(UR_MINUS30) >= 0) {
+ checkEq(x.exp(), xAsCr.exp(), "exp:" + x);
+ checkEq(UR_15.pow(x), CR.valueOf(15).ln().multiply(xAsCr).exp(), "pow(15,x):" + x); }
+ if (x.compareTo(UnifiedReal.ONE) <= 0
+ && x.compareTo(UnifiedReal.ONE.negate()) >= 0) {
+ checkEq(x.asin(), ASIN.execute(xAsCr), "asin:" + x);
+ checkEq(x.acos(), ACOS.execute(xAsCr), "acos:" + x);
+ checkEq(fromRadians(x.asin()), fromRadians(ASIN.execute(xAsCr)), "degree asin:" + x);
+ checkEq(fromRadians(x.acos()), fromRadians(ACOS.execute(xAsCr)), "degree acos:" + x);
+ }
+ checkEq(x.atan(), ATAN.execute(xAsCr), "atan:" + x);
+ if (x.signum() > 0) {
+ checkEq(x.ln(), xAsCr.ln(), "ln:" + x);
+ checkEq(x.sqrt(), xAsCr.sqrt(), "sqrt:" + x);
+ checkEq(x.pow(UR_15), xAsCr.ln().multiply(CR.valueOf(15)).exp(), "pow(x,15):" + x);
+ }
+ }
+
+ public void testUR() {
+ UnifiedReal b = new UnifiedReal(new BoundedRational(4,-6));
+ check(b.toString().equals("4/-6*1.0000000000"), "toString(4/-6)");
+ check(b.toNiceString().equals("-2/3"), "toNiceString(4/-6)");
+ check(b.toStringTruncated(1).equals("-0.6"), "(4/-6).toString(1)");
+ check(UR_15.toStringTruncated(0).equals("15."), "15.toString(1)");
+ check(UnifiedReal.ZERO.toStringTruncated(2).equals("0.00"), "0.toString(2)");
+ checkEq(UnifiedReal.ZERO, CR.valueOf(0), "0");
+ checkEq(new UnifiedReal(390), CR.valueOf(390), "390");
+ checkEq(UR_15, CR.valueOf(15), "15");
+ checkEq(new UnifiedReal(390).negate(), CR.valueOf(-390), "-390");
+ checkEq(UnifiedReal.ONE.negate(), CR.valueOf(-1), "-1");
+ checkEq(new UnifiedReal(2), CR.valueOf(2), "2");
+ checkEq(new UnifiedReal(-2), CR.valueOf(-2), "-2");
+ check(UnifiedReal.ZERO.signum() == 0, "signum(0)");
+ check(UnifiedReal.ZERO.definitelyZero(), "definitelyZero(0)");
+ check(!UnifiedReal.ZERO.definitelyNonZero(), "definitelyNonZero(0)");
+ check(!UnifiedReal.PI.definitelyZero(), "definitelyZero(pi)");
+ check(UnifiedReal.PI.definitelyNonZero(), "definitelyNonZero(pi)");
+ check(UnifiedReal.ONE.negate().signum() == -1, "signum(-1)");
+ check(new UnifiedReal(2).signum() == 1, "signum(2)");
+ check(UnifiedReal.E.signum() == 1, "signum(e)");
+ check(new UnifiedReal(400).bigIntegerValue().intValue() == 400, "400.bigIntegerValue()");
+ check(UnifiedReal.HALF.bigIntegerValue() == null, "1/2.bigIntegerValue()");
+ check(UnifiedReal.HALF.negate().bigIntegerValue() == null, "-1/2.bigIntegerValue()");
+ check(new UnifiedReal(new BoundedRational(15, -5)).bigIntegerValue().intValue() == -3,
+ "-15/5.asBigInteger()");
+ check(UnifiedReal.ZERO.digitsRequired() == 0, "digitsRequired(0)");
+ check(UnifiedReal.HALF.digitsRequired() == 1, "digitsRequired(1)");
+ check(UnifiedReal.HALF.negate().digitsRequired() == 1, "digitsRequired(-1)");
+ check(UnifiedReal.ONE.divide(new UnifiedReal(-2)).digitsRequired() == 1,
+ "digitsRequired(-2)");
+ check(UnifiedReal.ZERO.fact().definitelyEquals(UnifiedReal.ONE), "0!");
+ check(UnifiedReal.ONE.fact().definitelyEquals(UnifiedReal.ONE), "1!");
+ check(UnifiedReal.TWO.fact().definitelyEquals(UnifiedReal.TWO), "2!");
+ check(new UnifiedReal(15).fact().definitelyEquals(new UnifiedReal(1307674368000L)), "15!");
+ check(UnifiedReal.ONE.exactlyDisplayable(), "1 displayable");
+ check(UnifiedReal.PI.exactlyDisplayable(), "PI displayable");
+ check(UnifiedReal.E.exactlyDisplayable(), "E displayable");
+ check(UnifiedReal.E.divide(UnifiedReal.E).exactlyDisplayable(), "E/E displayable");
+ check(!UnifiedReal.E.divide(UnifiedReal.PI).exactlyDisplayable(), "!E/PI displayable");
+ UnifiedReal r = new UnifiedReal(9).multiply(new UnifiedReal(3).sqrt()).ln();
+ checkEq(r, CR.valueOf(9).multiply(CR.valueOf(3).sqrt()).ln(), "ln(9sqrt(3))");
+ check(r.exactlyDisplayable(), "5/2log3");
+ checkEq(r.exp(), CR.valueOf(9).multiply(CR.valueOf(3).sqrt()), "9sqrt(3)");
+ check(r.exp().exactlyDisplayable(), "9sqrt(3)");
+ check(!UnifiedReal.E.divide(UnifiedReal.PI).definitelyEquals(
+ UnifiedReal.E.divide(UnifiedReal.PI)), "E/PI = E/PI not testable");
+ check(new UnifiedReal(32).sqrt().definitelyEquals(
+ (new UnifiedReal(2).sqrt().multiply(new UnifiedReal(4)))), "sqrt(32)");
+ check(new UnifiedReal(32).ln().divide(UnifiedReal.TWO.ln())
+ .definitelyEquals(new UnifiedReal(5)), "ln(32)");
+ check(new UnifiedReal(10).sqrt().multiply(UnifiedReal.TEN.sqrt())
+ .definitelyEquals(UnifiedReal.TEN), "sqrt(10)^2");
+ check(UnifiedReal.ZERO.leadingBinaryZeroes() == Integer.MAX_VALUE, "0.leadingBinaryZeros");
+ check(new UnifiedReal(new BoundedRational(7,1024)).leadingBinaryZeroes() >= 8,
+ "fract.leadingBinaryZeros");
+ UnifiedReal tmp = UnifiedReal.TEN.pow(new UnifiedReal(-1000));
+ int tmp2 = tmp.leadingBinaryZeroes();
+ check(tmp2 >= 3320 && tmp2 < 4000, "leadingBinaryZeroes(10^-1000)");
+ tmp2 = tmp.multiply(UnifiedReal.PI).leadingBinaryZeroes();
+ check(tmp2 >= 3319 && tmp2 < 4000, "leadingBinaryZeroes(pix10^-1000)");
+ // We check values that include all interesting degree values.
+ r = new UnifiedReal(-390);
+ int i = 0;
+ while (!r.definitelyEquals(new UnifiedReal(390))) {
+ check(i++ < 100, "int loop counter arithmetic failed!");
+ if (i > 100) {
+ break;
+ }
+ checkUR(r);
+ r = r.add(new UnifiedReal(15));
+ }
+ r = UnifiedReal.PI.multiply(new UnifiedReal(-3));
+ final UnifiedReal limit = r.negate();
+ final UnifiedReal increment = UnifiedReal.PI.divide(new UnifiedReal(24));
+ i = 0;
+ while (!r.definitelyEquals(limit)) {
+ check(i++ < 200, "transcendental loop counter arithmetic failed!");
+ if (i > 100) {
+ break;
+ }
+ checkUR(r);
+ r = r.add(increment);
+ }
+ checkUR(UnifiedReal.HALF);
+ checkUR(UnifiedReal.MINUS_HALF);
+ checkUR(UnifiedReal.ONE);
+ checkUR(UnifiedReal.MINUS_ONE);
+ checkUR(new UnifiedReal(1000));
+ checkUR(new UnifiedReal(100));
+ checkUR(new UnifiedReal(new BoundedRational(4,9)));
+ check(new UnifiedReal(new BoundedRational(4,9)).sqrt().definitelyEquals(
+ UnifiedReal.TWO.divide(new UnifiedReal(3))), "sqrt(4/9)");
+ checkUR(new UnifiedReal(new BoundedRational(4,9)).negate());
+ checkUR(new UnifiedReal(new BoundedRational(5,9)));
+ checkUR(new UnifiedReal(new BoundedRational(5,10)));
+ checkUR(new UnifiedReal(new BoundedRational(5,10)));
+ checkUR(new UnifiedReal(new BoundedRational(4,13)));
+ checkUR(new UnifiedReal(36));
+ checkUR(new UnifiedReal(36).negate());
+ }
+
+ public void testFunctionsOnSmall() {
+ // This checks some of the special cases we should handle semi-symbolically.
+ UnifiedReal small = new UnifiedReal(2).pow(new UnifiedReal(-1000));
+ UnifiedReal small2 = new UnifiedReal(-1000).exp();
+ for (int i = 0; i <= 10; i++) {
+ UnifiedReal r = new UnifiedReal(i);
+ UnifiedReal sqrt = r.sqrt();
+ if (i > 1 && i != 4 && i != 9) {
+ check(sqrt.definitelyIrrational() && !sqrt.definitelyRational(), "sqrt !rational");
+ } else {
+ check(!sqrt.definitelyIrrational() && sqrt.definitelyRational(), "sqrt rational");
+ }
+ check(sqrt.definitelyAlgebraic() && !sqrt.definitelyTranscendental(), "sqrt algenraic");
+ check(sqrt.multiply(sqrt).definitelyEquals(r), "sqrt " + i);
+ check(!sqrt.multiply(sqrt).definitelyEquals(r.add(small)), "sqrt small " + i);
+ check(!sqrt.multiply(sqrt).definitelyEquals(r.add(small2)), "sqrt small2 " + i);
+ if (i > 0) {
+ UnifiedReal log = r.ln();
+ check(log.exp().definitelyEquals(r), "log " + i);
+ if (i > 1) {
+ check(log.definitelyTranscendental(), "log transcendental");
+ check(!log.definitelyAlgebraic(), "log !algebraic");
+ check(!log.definitelyRational(), "log !rational");
+ check(log.definitelyIrrational(), "log !rational again");
+ } else {
+ check(log.definitelyRational(), "log rational");
+ }
+ check(r.pow(r).ln().definitelyEquals(r.multiply(r.ln())), "ln(r^r)");
+ }
+ }
+ }
+
+ public void testURexceptions() {
+ try {
+ UnifiedReal.MINUS_ONE.ln();
+ check(false, "ln(-1)");
+ } catch (ArithmeticException ignored) {}
+ try {
+ UnifiedReal.MINUS_ONE.sqrt();
+ check(false, "sqrt(-1)");
+ } catch (ArithmeticException ignored) {}
+ try {
+ new UnifiedReal(-2).asin();
+ check(false, "asin(-2)");
+ } catch (ArithmeticException ignored) {}
+ try {
+ new UnifiedReal(-2).acos();
+ check(false, "acos(-2)");
+ } catch (ArithmeticException ignored) {}
+ }
+
+}