merge in master-release history after reset to d94567d9637aae33905fc7f292a615996df6d3c9
diff --git a/java/res/drawable-hdpi/sym_keyboard_delete_lxx_dark.png b/java/res/drawable-hdpi/sym_keyboard_delete_lxx_dark.png
new file mode 100644
index 0000000..5f5eb3f
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_delete_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_done_lxx_dark.png b/java/res/drawable-hdpi/sym_keyboard_done_lxx_dark.png
new file mode 100644
index 0000000..f81130d
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_done_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_go_lxx_dark.png b/java/res/drawable-hdpi/sym_keyboard_go_lxx_dark.png
new file mode 100644
index 0000000..516a7f1
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_go_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_language_switch_lxx_dark.png b/java/res/drawable-hdpi/sym_keyboard_language_switch_lxx_dark.png
new file mode 100644
index 0000000..edf9a20
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_language_switch_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_next_lxx_dark.png b/java/res/drawable-hdpi/sym_keyboard_next_lxx_dark.png
new file mode 100644
index 0000000..99ac309
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_next_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_previous_lxx_dark.png b/java/res/drawable-hdpi/sym_keyboard_previous_lxx_dark.png
new file mode 100644
index 0000000..9019ebd
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_previous_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_search_lxx_dark.png b/java/res/drawable-hdpi/sym_keyboard_search_lxx_dark.png
new file mode 100644
index 0000000..de117d3
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_search_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_send_lxx_dark.png b/java/res/drawable-hdpi/sym_keyboard_send_lxx_dark.png
new file mode 100644
index 0000000..6db6d56
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_send_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_settings_lxx_dark.png b/java/res/drawable-hdpi/sym_keyboard_settings_lxx_dark.png
new file mode 100644
index 0000000..f261302
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_settings_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_smiley_lxx_dark.png b/java/res/drawable-hdpi/sym_keyboard_smiley_lxx_dark.png
new file mode 100644
index 0000000..3999d1e
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_smiley_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_space_led_holo.9.png b/java/res/drawable-hdpi/sym_keyboard_space_led_holo.9.png
deleted file mode 100644
index 34a1ebd..0000000
--- a/java/res/drawable-hdpi/sym_keyboard_space_led_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_voice_lxx_dark.png b/java/res/drawable-hdpi/sym_keyboard_voice_lxx_dark.png
new file mode 100644
index 0000000..6b68c8a
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_voice_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-hdpi/sym_keyboard_voice_off_lxx_dark.png b/java/res/drawable-hdpi/sym_keyboard_voice_off_lxx_dark.png
new file mode 100644
index 0000000..e67697b
--- /dev/null
+++ b/java/res/drawable-hdpi/sym_keyboard_voice_off_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_delete_lxx_dark.png b/java/res/drawable-mdpi/sym_keyboard_delete_lxx_dark.png
new file mode 100644
index 0000000..2d3ac97
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_delete_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_done_lxx_dark.png b/java/res/drawable-mdpi/sym_keyboard_done_lxx_dark.png
new file mode 100644
index 0000000..8a63c11
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_done_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_go_lxx_dark.png b/java/res/drawable-mdpi/sym_keyboard_go_lxx_dark.png
new file mode 100644
index 0000000..8905172
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_go_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_language_switch_lxx_dark.png b/java/res/drawable-mdpi/sym_keyboard_language_switch_lxx_dark.png
new file mode 100644
index 0000000..a90bf75
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_language_switch_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_next_lxx_dark.png b/java/res/drawable-mdpi/sym_keyboard_next_lxx_dark.png
new file mode 100644
index 0000000..61c1014
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_next_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_previous_lxx_dark.png b/java/res/drawable-mdpi/sym_keyboard_previous_lxx_dark.png
new file mode 100644
index 0000000..f176956
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_previous_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_search_lxx_dark.png b/java/res/drawable-mdpi/sym_keyboard_search_lxx_dark.png
new file mode 100644
index 0000000..722d402
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_search_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_send_lxx_dark.png b/java/res/drawable-mdpi/sym_keyboard_send_lxx_dark.png
new file mode 100644
index 0000000..a75a608
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_send_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_settings_lxx_dark.png b/java/res/drawable-mdpi/sym_keyboard_settings_lxx_dark.png
new file mode 100644
index 0000000..dea7add
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_settings_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_smiley_lxx_dark.png b/java/res/drawable-mdpi/sym_keyboard_smiley_lxx_dark.png
new file mode 100644
index 0000000..8276d99
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_smiley_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_space_led_holo.9.png b/java/res/drawable-mdpi/sym_keyboard_space_led_holo.9.png
deleted file mode 100644
index abd8b74..0000000
--- a/java/res/drawable-mdpi/sym_keyboard_space_led_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_voice_lxx_dark.png b/java/res/drawable-mdpi/sym_keyboard_voice_lxx_dark.png
new file mode 100644
index 0000000..5661f6b
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_voice_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-mdpi/sym_keyboard_voice_off_lxx_dark.png b/java/res/drawable-mdpi/sym_keyboard_voice_off_lxx_dark.png
new file mode 100644
index 0000000..5182f1e
--- /dev/null
+++ b/java/res/drawable-mdpi/sym_keyboard_voice_off_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_delete_lxx_dark.png b/java/res/drawable-xhdpi/sym_keyboard_delete_lxx_dark.png
new file mode 100644
index 0000000..7c9f34f
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_delete_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_done_lxx_dark.png b/java/res/drawable-xhdpi/sym_keyboard_done_lxx_dark.png
new file mode 100644
index 0000000..f25e3df
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_done_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_go_lxx_dark.png b/java/res/drawable-xhdpi/sym_keyboard_go_lxx_dark.png
new file mode 100644
index 0000000..6cd43cf
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_go_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_language_switch_lxx_dark.png b/java/res/drawable-xhdpi/sym_keyboard_language_switch_lxx_dark.png
new file mode 100644
index 0000000..f964346
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_language_switch_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_next_lxx_dark.png b/java/res/drawable-xhdpi/sym_keyboard_next_lxx_dark.png
new file mode 100644
index 0000000..9e05e84
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_next_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_previous_lxx_dark.png b/java/res/drawable-xhdpi/sym_keyboard_previous_lxx_dark.png
new file mode 100644
index 0000000..8995d87
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_previous_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_search_lxx_dark.png b/java/res/drawable-xhdpi/sym_keyboard_search_lxx_dark.png
new file mode 100644
index 0000000..28a4bd3
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_search_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_send_lxx_dark.png b/java/res/drawable-xhdpi/sym_keyboard_send_lxx_dark.png
new file mode 100644
index 0000000..9927af4
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_send_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_settings_lxx_dark.png b/java/res/drawable-xhdpi/sym_keyboard_settings_lxx_dark.png
new file mode 100644
index 0000000..8c83d9f
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_settings_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_smiley_lxx_dark.png b/java/res/drawable-xhdpi/sym_keyboard_smiley_lxx_dark.png
new file mode 100644
index 0000000..78923fa
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_smiley_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_space_led_holo.9.png b/java/res/drawable-xhdpi/sym_keyboard_space_led_holo.9.png
deleted file mode 100644
index ba4e9ec..0000000
--- a/java/res/drawable-xhdpi/sym_keyboard_space_led_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_voice_lxx_dark.png b/java/res/drawable-xhdpi/sym_keyboard_voice_lxx_dark.png
new file mode 100644
index 0000000..4e9631e
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_voice_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xhdpi/sym_keyboard_voice_off_lxx_dark.png b/java/res/drawable-xhdpi/sym_keyboard_voice_off_lxx_dark.png
new file mode 100644
index 0000000..66c0e3c
--- /dev/null
+++ b/java/res/drawable-xhdpi/sym_keyboard_voice_off_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_delete_lxx_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_delete_lxx_dark.png
new file mode 100644
index 0000000..dd95be6
--- /dev/null
+++ b/java/res/drawable-xxhdpi/sym_keyboard_delete_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_done_lxx_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_done_lxx_dark.png
new file mode 100644
index 0000000..ccd270e
--- /dev/null
+++ b/java/res/drawable-xxhdpi/sym_keyboard_done_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_go_lxx_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_go_lxx_dark.png
new file mode 100644
index 0000000..0617c15
--- /dev/null
+++ b/java/res/drawable-xxhdpi/sym_keyboard_go_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_language_switch_lxx_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_language_switch_lxx_dark.png
new file mode 100644
index 0000000..fc85e51
--- /dev/null
+++ b/java/res/drawable-xxhdpi/sym_keyboard_language_switch_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_next_lxx_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_next_lxx_dark.png
new file mode 100644
index 0000000..a04a41c
--- /dev/null
+++ b/java/res/drawable-xxhdpi/sym_keyboard_next_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_previous_lxx_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_previous_lxx_dark.png
new file mode 100644
index 0000000..c87d8f0
--- /dev/null
+++ b/java/res/drawable-xxhdpi/sym_keyboard_previous_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_search_lxx_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_search_lxx_dark.png
new file mode 100644
index 0000000..a32b6c5
--- /dev/null
+++ b/java/res/drawable-xxhdpi/sym_keyboard_search_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_send_lxx_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_send_lxx_dark.png
new file mode 100644
index 0000000..8073e2c
--- /dev/null
+++ b/java/res/drawable-xxhdpi/sym_keyboard_send_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_settings_lxx_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_settings_lxx_dark.png
new file mode 100644
index 0000000..d513ad9
--- /dev/null
+++ b/java/res/drawable-xxhdpi/sym_keyboard_settings_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_smiley_lxx_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_smiley_lxx_dark.png
new file mode 100644
index 0000000..a4df3bb
--- /dev/null
+++ b/java/res/drawable-xxhdpi/sym_keyboard_smiley_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_voice_lxx_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_voice_lxx_dark.png
new file mode 100644
index 0000000..a36724c
--- /dev/null
+++ b/java/res/drawable-xxhdpi/sym_keyboard_voice_lxx_dark.png
Binary files differ
diff --git a/java/res/drawable-xxhdpi/sym_keyboard_voice_off_lxx_dark.png b/java/res/drawable-xxhdpi/sym_keyboard_voice_off_lxx_dark.png
new file mode 100644
index 0000000..2706dca
--- /dev/null
+++ b/java/res/drawable-xxhdpi/sym_keyboard_voice_off_lxx_dark.png
Binary files differ
diff --git a/java/res/values/attrs.xml b/java/res/values/attrs.xml
index 79cc139..7363b61 100644
--- a/java/res/values/attrs.xml
+++ b/java/res/values/attrs.xml
@@ -72,8 +72,6 @@
     </declare-styleable>
 
     <declare-styleable name="MainKeyboardView">
-        <attr name="autoCorrectionSpacebarLedEnabled" format="boolean" />
-        <attr name="autoCorrectionSpacebarLedIcon" format="reference" />
         <!-- Size of the text for spacebar language label, in the proportion of key height. -->
         <attr name="languageOnSpacebarTextRatio" format="fraction" />
         <attr name="languageOnSpacebarTextColor" format="color" />
@@ -311,6 +309,9 @@
             <flag name="shiftedLetterActivated" value="0x20000" />
             <!-- If true, use EditorInfo.actionLabel for the key label. -->
             <flag name="fromCustomActionLabel" value="0x40000" />
+            <!-- If true, use functionalTextColor instead of ketTextColor to drawing the label on
+                 the key -->
+            <flag name="followFunctionalTextColor" value="0x80000" />
             <!-- If true, disable keyHintLabel. -->
             <flag name="disableKeyHintLabel" value="0x40000000" />
             <!-- If true, disable additionalMoreKeys. -->
@@ -367,6 +368,8 @@
         <attr name="keyTextShadowColor" format="color" />
         <!-- Color to use for the label in a key when in inactivated state. -->
         <attr name="keyTextInactivatedColor" format="color" />
+        <!-- Color to use for the label in a key that has followFunctionalTextColor keyLabelFlags. -->
+        <attr name="functionalTextColor" format="color" />
         <!-- Key hint letter (= one character hint label) color -->
         <attr name="keyHintLetterColor" format="color" />
         <!-- Key hint label color -->
diff --git a/java/res/values/keyboard-icons-lxx.xml b/java/res/values/keyboard-icons-lxx.xml
index ea4b6e6..8a38863 100644
--- a/java/res/values/keyboard-icons-lxx.xml
+++ b/java/res/values/keyboard-icons-lxx.xml
@@ -21,27 +21,27 @@
 <resources xmlns:android="http://schemas.android.com/apk/res/android">
     <style name="KeyboardIcons.LXX">
         <!-- Keyboard icons -->
+        <!-- TODO: Update those icons for LXX theme. -->
         <item name="iconShiftKey">@drawable/sym_keyboard_shift_holo_dark</item>
-        <item name="iconDeleteKey">@drawable/sym_keyboard_delete_holo_dark</item>
-        <item name="iconSettingsKey">@drawable/sym_keyboard_settings_holo_dark</item>
+        <item name="iconDeleteKey">@drawable/sym_keyboard_delete_lxx_dark</item>
+        <item name="iconSettingsKey">@drawable/sym_keyboard_settings_lxx_dark</item>
         <item name="iconSpaceKey">@drawable/sym_keyboard_space_holo_dark</item>
         <item name="iconEnterKey">@drawable/sym_keyboard_return_holo_dark</item>
-        <!-- TODO: Uncomment those icon definitions once we have those icon assets. -->
-        <!-- <item name="iconGoKey">@drawable/sym_keyboard_go_holo_dark</item> -->
-        <item name="iconSearchKey">@drawable/sym_keyboard_search_holo_dark</item>
-        <!-- <item name="iconSendKey">@drawable/sym_keyboard_send_holo_dark</item> -->
-        <!-- <item name="iconNextKey">@drawable/sym_keyboard_next_holo_dark</item> -->
-        <!-- <item name="iconDoneKey">@drawable/sym_keyboard_done_holo_dark</item> -->
-        <!-- <item name="iconPreviousKey">@drawable/sym_keyboard_previous_holo_dark</item> -->
+        <item name="iconGoKey">@drawable/sym_keyboard_go_lxx_dark</item>
+        <item name="iconSearchKey">@drawable/sym_keyboard_search_lxx_dark</item>
+        <item name="iconSendKey">@drawable/sym_keyboard_send_lxx_dark</item>
+        <item name="iconNextKey">@drawable/sym_keyboard_next_lxx_dark</item>
+        <item name="iconDoneKey">@drawable/sym_keyboard_done_lxx_dark</item>
+        <item name="iconPreviousKey">@drawable/sym_keyboard_previous_lxx_dark</item>
         <item name="iconTabKey">@drawable/sym_keyboard_tab_holo_dark</item>
-        <item name="iconShortcutKey">@drawable/sym_keyboard_voice_holo_dark</item>
+        <item name="iconShortcutKey">@drawable/sym_keyboard_voice_lxx_dark</item>
         <item name="iconSpaceKeyForNumberLayout">@drawable/sym_keyboard_space_holo_dark</item>
         <item name="iconShiftKeyShifted">@drawable/sym_keyboard_shift_locked_holo_dark</item>
-        <item name="iconShortcutKeyDisabled">@drawable/sym_keyboard_voice_off_holo_dark</item>
+        <item name="iconShortcutKeyDisabled">@drawable/sym_keyboard_voice_off_lxx_dark</item>
         <item name="iconTabKeyPreview">@drawable/sym_keyboard_feedback_tab</item>
-        <item name="iconLanguageSwitchKey">@drawable/sym_keyboard_language_switch_dark</item>
+        <item name="iconLanguageSwitchKey">@drawable/sym_keyboard_language_switch_lxx_dark</item>
         <item name="iconZwnjKey">@drawable/sym_keyboard_zwnj_holo_dark</item>
         <item name="iconZwjKey">@drawable/sym_keyboard_zwj_holo_dark</item>
-        <item name="iconEmojiKey">@drawable/sym_keyboard_smiley_holo_dark</item>
+        <item name="iconEmojiKey">@drawable/sym_keyboard_smiley_lxx_dark</item>
     </style>
 </resources>
diff --git a/java/res/values/themes-ics.xml b/java/res/values/themes-ics.xml
index 560cfc5..22fb2c8 100644
--- a/java/res/values/themes-ics.xml
+++ b/java/res/values/themes-ics.xml
@@ -53,6 +53,7 @@
         <item name="keyTypeface">bold</item>
         <item name="keyTextColor">@color/key_text_color_holo</item>
         <item name="keyTextInactivatedColor">@color/key_text_inactivated_color_holo</item>
+        <item name="functionalTextColor">@color/key_text_color_holo</item>
         <item name="keyHintLetterColor">@color/key_hint_letter_color_holo</item>
         <item name="keyHintLabelColor">@color/key_hint_label_color_holo</item>
         <item name="keyShiftedLetterHintInactivatedColor">@color/key_shifted_letter_hint_inactivated_color_holo</item>
@@ -70,8 +71,6 @@
         <item name="gestureFloatingPreviewColor">@color/gesture_floating_preview_color_holo</item>
         <item name="gestureTrailColor">@color/highlight_color_ics</item>
         <item name="slidingKeyInputPreviewColor">@color/highlight_translucent_color_ics</item>
-        <item name="autoCorrectionSpacebarLedEnabled">false</item>
-        <item name="autoCorrectionSpacebarLedIcon">@drawable/sym_keyboard_space_led_holo</item>
         <item name="languageOnSpacebarTextColor">@color/spacebar_text_color_holo</item>
         <item name="languageOnSpacebarTextShadowRadius">1.0</item>
         <item name="languageOnSpacebarTextShadowColor">@color/spacebar_text_shadow_color_holo</item>
diff --git a/java/res/values/themes-klp.xml b/java/res/values/themes-klp.xml
index 453e5cb..212f5db 100644
--- a/java/res/values/themes-klp.xml
+++ b/java/res/values/themes-klp.xml
@@ -53,6 +53,7 @@
         <item name="keyTypeface">bold</item>
         <item name="keyTextColor">@color/key_text_color_holo</item>
         <item name="keyTextInactivatedColor">@color/key_text_inactivated_color_holo</item>
+        <item name="functionalTextColor">@color/key_text_color_holo</item>
         <item name="keyHintLetterColor">@color/key_hint_letter_color_holo</item>
         <item name="keyHintLabelColor">@color/key_hint_label_color_holo</item>
         <item name="keyShiftedLetterHintInactivatedColor">@color/key_shifted_letter_hint_inactivated_color_holo</item>
@@ -70,8 +71,6 @@
         <item name="gestureFloatingPreviewColor">@color/gesture_floating_preview_color_holo</item>
         <item name="gestureTrailColor">@color/highlight_color_klp</item>
         <item name="slidingKeyInputPreviewColor">@color/highlight_translucent_color_klp</item>
-        <item name="autoCorrectionSpacebarLedEnabled">false</item>
-        <item name="autoCorrectionSpacebarLedIcon">@drawable/sym_keyboard_space_led_holo</item>
         <item name="languageOnSpacebarTextColor">@color/spacebar_text_color_holo</item>
         <item name="languageOnSpacebarTextShadowRadius">1.0</item>
         <item name="languageOnSpacebarTextShadowColor">@color/spacebar_text_shadow_color_holo</item>
diff --git a/java/res/values/themes-lxx.xml b/java/res/values/themes-lxx.xml
index 4f3ee80..3c7a2b5 100644
--- a/java/res/values/themes-lxx.xml
+++ b/java/res/values/themes-lxx.xml
@@ -53,6 +53,7 @@
         <item name="keyTypeface">bold</item>
         <item name="keyTextColor">@color/key_text_color_holo</item>
         <item name="keyTextInactivatedColor">@color/key_text_inactive_color_lxx</item>
+        <item name="functionalTextColor">@color/key_hint_letter_color_lxx</item>
         <item name="keyHintLetterColor">@color/key_hint_letter_color_lxx</item>
         <item name="keyHintLabelColor">@color/key_text_inactive_color_lxx</item>
         <item name="keyShiftedLetterHintInactivatedColor">@color/key_text_inactive_color_lxx</item>
@@ -70,8 +71,6 @@
         <item name="gestureFloatingPreviewColor">@color/gesture_floating_preview_color_holo</item>
         <item name="gestureTrailColor">@color/highlight_color_lxx</item>
         <item name="slidingKeyInputPreviewColor">@color/highlight_translucent_color_lxx</item>
-        <item name="autoCorrectionSpacebarLedEnabled">false</item>
-        <item name="autoCorrectionSpacebarLedIcon">@drawable/sym_keyboard_space_led_holo</item>
         <item name="languageOnSpacebarTextColor">@color/key_text_inactive_color_lxx</item>
         <!-- A negative value to disable text shadow layer. -->
         <item name="languageOnSpacebarTextShadowRadius">-1.0</item>
diff --git a/java/res/xml-sw600dp/key_styles_common.xml b/java/res/xml-sw600dp/key_styles_common.xml
index f9b959b..35b3576 100644
--- a/java/res/xml-sw600dp/key_styles_common.xml
+++ b/java/res/xml-sw600dp/key_styles_common.xml
@@ -140,7 +140,7 @@
     </switch>
     <key-style
         latin:styleName="baseForLayoutSwitchKeyStyle"
-        latin:keyLabelFlags="preserveCase"
+        latin:keyLabelFlags="preserveCase|followFunctionalTextColor"
         latin:keyActionFlags="noKeyPreview"
         latin:backgroundType="functional" />
     <key-style
diff --git a/java/res/xml-sw600dp/key_styles_enter.xml b/java/res/xml-sw600dp/key_styles_enter.xml
index 99ac108..742bb3e 100644
--- a/java/res/xml-sw600dp/key_styles_enter.xml
+++ b/java/res/xml-sw600dp/key_styles_enter.xml
@@ -100,7 +100,7 @@
     <key-style
         latin:styleName="defaultEnterKeyStyle"
         latin:keySpec="!icon/enter_key|!code/key_enter"
-        latin:keyLabelFlags="preserveCase|autoXScale|followKeyLabelRatio"
+        latin:keyLabelFlags="preserveCase|autoXScale|followKeyLabelRatio|followFunctionalTextColor"
         latin:keyActionFlags="noKeyPreview"
         latin:backgroundType="functional"
         latin:parentStyle="navigateMoreKeysStyle" />
diff --git a/java/res/xml/key_styles_common.xml b/java/res/xml/key_styles_common.xml
index 773995f..f8b4a2a 100644
--- a/java/res/xml/key_styles_common.xml
+++ b/java/res/xml/key_styles_common.xml
@@ -128,7 +128,7 @@
         latin:keyIconPreview="!icon/tab_key_preview" />
     <key-style
         latin:styleName="baseForLayoutSwitchKeyStyle"
-        latin:keyLabelFlags="preserveCase"
+        latin:keyLabelFlags="preserveCase|followFunctionalTextColor"
         latin:keyActionFlags="noKeyPreview"
         latin:backgroundType="functional" />
     <key-style
diff --git a/java/res/xml/key_styles_enter.xml b/java/res/xml/key_styles_enter.xml
index 8bba136..1d46dd5 100644
--- a/java/res/xml/key_styles_enter.xml
+++ b/java/res/xml/key_styles_enter.xml
@@ -255,7 +255,7 @@
     <!-- Enter key style -->
     <key-style
         latin:styleName="defaultEnterKeyStyle"
-        latin:keyLabelFlags="preserveCase|autoXScale|followKeyLabelRatio"
+        latin:keyLabelFlags="preserveCase|autoXScale|followKeyLabelRatio|followFunctionalTextColor"
         latin:keyActionFlags="noKeyPreview"
         latin:backgroundType="functional"
         latin:parentStyle="navigateMoreKeysStyle" />
diff --git a/java/res/xml/key_styles_number.xml b/java/res/xml/key_styles_number.xml
index 5c108cf..df4448c 100644
--- a/java/res/xml/key_styles_number.xml
+++ b/java/res/xml/key_styles_number.xml
@@ -30,7 +30,7 @@
         latin:parentStyle="numKeyBaseStyle" />
     <key-style
         latin:styleName="numModeKeyStyle"
-        latin:keyLabelFlags="fontNormal|followKeyLetterRatio"
+        latin:keyLabelFlags="fontNormal|followKeyLetterRatio|followFunctionalTextColor"
         latin:parentStyle="numKeyBaseStyle" />
     <key-style
         latin:styleName="numFunctionalKeyStyle"
diff --git a/java/res/xml/rows_number_normal.xml b/java/res/xml/rows_number_normal.xml
index 859a162..d8d1508 100644
--- a/java/res/xml/rows_number_normal.xml
+++ b/java/res/xml/rows_number_normal.xml
@@ -90,11 +90,6 @@
             latin:keyWidth="fillRight" />
     </Row>
     <Row>
-        <Key
-            latin:keyStyle="numSpaceKeyStyle" />
-        <Key
-            latin:keySpec="0"
-            latin:keyStyle="numKeyStyle" />
         <switch>
             <case
                 latin:mode="date"
@@ -125,6 +120,11 @@
             </default>
         </switch>
         <Key
+            latin:keySpec="0"
+            latin:keyStyle="numKeyStyle" />
+        <Key
+            latin:keyStyle="numSpaceKeyStyle" />
+        <Key
             latin:keyStyle="enterKeyStyle"
             latin:keyWidth="fillRight" />
     </Row>
diff --git a/java/src/com/android/inputmethod/keyboard/Key.java b/java/src/com/android/inputmethod/keyboard/Key.java
index 4c22507..a1269c8 100644
--- a/java/src/com/android/inputmethod/keyboard/Key.java
+++ b/java/src/com/android/inputmethod/keyboard/Key.java
@@ -86,6 +86,7 @@
     private static final int LABEL_FLAGS_PRESERVE_CASE = 0x10000;
     private static final int LABEL_FLAGS_SHIFTED_LETTER_ACTIVATED = 0x20000;
     private static final int LABEL_FLAGS_FROM_CUSTOM_ACTION_LABEL = 0x40000;
+    private static final int LABEL_FLAGS_FOLLOW_FUNCTIONAL_TEXT_COLOR = 0x80000;
     private static final int LABEL_FLAGS_DISABLE_HINT_LABEL = 0x40000000;
     private static final int LABEL_FLAGS_DISABLE_ADDITIONAL_MORE_KEYS = 0x80000000;
 
@@ -583,6 +584,9 @@
     }
 
     public final int selectTextColor(final KeyDrawParams params) {
+        if ((mLabelFlags & LABEL_FLAGS_FOLLOW_FUNCTIONAL_TEXT_COLOR) != 0) {
+            return params.mFunctionalTextColor;
+        }
         return isShiftedLetterActivated() ? params.mTextInactivatedColor : params.mTextColor;
     }
 
diff --git a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
index fc9faa6..61d51d1 100644
--- a/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
+++ b/java/src/com/android/inputmethod/keyboard/KeyboardSwitcher.java
@@ -61,10 +61,6 @@
     private final KeyboardTextsSet mKeyboardTextsSet = new KeyboardTextsSet();
     private SettingsValues mCurrentSettingsValues;
 
-    /** mIsAutoCorrectionActive indicates that auto corrected word will be input instead of
-     * what user actually typed. */
-    private boolean mIsAutoCorrectionActive;
-
     private KeyboardTheme mKeyboardTheme;
     private Context mThemeContext;
 
@@ -142,12 +138,7 @@
         }
     }
 
-    public void onFinishInputView() {
-        mIsAutoCorrectionActive = false;
-    }
-
     public void onHideWindow() {
-        mIsAutoCorrectionActive = false;
         if (mKeyboardView != null) {
             mKeyboardView.onHideWindow();
         }
@@ -168,7 +159,6 @@
                 mCurrentSettingsValues.mKeyPreviewShowUpDuration,
                 mCurrentSettingsValues.mKeyPreviewDismissEndScale,
                 mCurrentSettingsValues.mKeyPreviewDismissDuration);
-        keyboardView.updateAutoCorrectionState(mIsAutoCorrectionActive);
         keyboardView.updateShortcutKey(mSubtypeSwitcher.isShortcutImeReady());
         final boolean subtypeChanged = (oldKeyboard == null)
                 || !keyboard.mId.mLocale.equals(oldKeyboard.mId.mLocale);
@@ -254,7 +244,7 @@
     // Implements {@link KeyboardState.SwitchActions}.
     @Override
     public void setEmojiKeyboard() {
-        final Keyboard keyboard = mKeyboardView.getKeyboard();
+        final Keyboard keyboard = mKeyboardLayoutSet.getKeyboard(KeyboardId.ELEMENT_ALPHABET);
         mMainKeyboardFrame.setVisibility(View.GONE);
         mEmojiPalettesView.startEmojiPalettes(
                 mKeyboardTextsSet.getText(KeyboardTextsSet.SWITCH_TO_ALPHA_KEY_LABEL),
@@ -367,15 +357,6 @@
         }
     }
 
-    public void onAutoCorrectionStateChanged(final boolean isAutoCorrection) {
-        if (mIsAutoCorrectionActive != isAutoCorrection) {
-            mIsAutoCorrectionActive = isAutoCorrection;
-            if (mKeyboardView != null) {
-                mKeyboardView.updateAutoCorrectionState(isAutoCorrection);
-            }
-        }
-    }
-
     public int getKeyboardShiftMode() {
         final Keyboard keyboard = getKeyboard();
         if (keyboard == null) {
diff --git a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
index 4a09768..ac11dca 100644
--- a/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
+++ b/java/src/com/android/inputmethod/keyboard/MainKeyboardView.java
@@ -70,8 +70,6 @@
 /**
  * A view that is responsible for detecting key presses and touch movements.
  *
- * @attr ref R.styleable#MainKeyboardView_autoCorrectionSpacebarLedEnabled
- * @attr ref R.styleable#MainKeyboardView_autoCorrectionSpacebarLedIcon
  * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarTextRatio
  * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarTextColor
  * @attr ref R.styleable#MainKeyboardView_languageOnSpacebarTextShadowRadius
@@ -133,11 +131,6 @@
     private static final float LANGUAGE_ON_SPACEBAR_TEXT_SHADOW_RADIUS_DISABLED = -1.0f;
     // The minimum x-scale to fit the language name on spacebar.
     private static final float MINIMUM_XSCALE_OF_LANGUAGE_NAME = 0.8f;
-    // Stuff to draw auto correction LED on spacebar.
-    private boolean mAutoCorrectionSpacebarLedOn;
-    private final boolean mAutoCorrectionSpacebarLedEnabled;
-    private final Drawable mAutoCorrectionSpacebarLedIcon;
-    private static final int SPACE_LED_LENGTH_PERCENT = 80;
 
     // Stuff to draw altCodeWhileTyping keys.
     private final ObjectAnimator mAltCodeKeyWhileTypingFadeoutAnimator;
@@ -221,10 +214,6 @@
                 R.styleable.MainKeyboardView_backgroundDimAlpha, 0);
         mBackgroundDimAlphaPaint.setColor(Color.BLACK);
         mBackgroundDimAlphaPaint.setAlpha(backgroundDimAlpha);
-        mAutoCorrectionSpacebarLedEnabled = mainKeyboardViewAttr.getBoolean(
-                R.styleable.MainKeyboardView_autoCorrectionSpacebarLedEnabled, false);
-        mAutoCorrectionSpacebarLedIcon = mainKeyboardViewAttr.getDrawable(
-                R.styleable.MainKeyboardView_autoCorrectionSpacebarLedIcon);
         mLanguageOnSpacebarTextRatio = mainKeyboardViewAttr.getFraction(
                 R.styleable.MainKeyboardView_languageOnSpacebarTextRatio, 1, 1, 1.0f);
         mLanguageOnSpacebarTextColor = mainKeyboardViewAttr.getColor(
@@ -834,14 +823,6 @@
         invalidateKey(mSpaceKey);
     }
 
-    public void updateAutoCorrectionState(final boolean isAutoCorrection) {
-        if (!mAutoCorrectionSpacebarLedEnabled) {
-            return;
-        }
-        mAutoCorrectionSpacebarLedOn = isAutoCorrection;
-        invalidateKey(mSpaceKey);
-    }
-
     private void dimEntireKeyboard(final boolean dimmed) {
         final boolean needsRedrawing = mNeedsToDimEntireKeyboard != dimmed;
         mNeedsToDimEntireKeyboard = dimmed;
@@ -946,13 +927,7 @@
         }
 
         // Draw the spacebar icon at the bottom
-        if (mAutoCorrectionSpacebarLedOn) {
-            final int iconWidth = width * SPACE_LED_LENGTH_PERCENT / 100;
-            final int iconHeight = mAutoCorrectionSpacebarLedIcon.getIntrinsicHeight();
-            int x = (width - iconWidth) / 2;
-            int y = height - iconHeight;
-            drawIcon(canvas, mAutoCorrectionSpacebarLedIcon, x, y, iconWidth, iconHeight);
-        } else if (mSpacebarIcon != null) {
+        if (mSpacebarIcon != null) {
             final int iconWidth = mSpacebarIcon.getIntrinsicWidth();
             final int iconHeight = mSpacebarIcon.getIntrinsicHeight();
             int x = (width - iconWidth) / 2;
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyDrawParams.java b/java/src/com/android/inputmethod/keyboard/internal/KeyDrawParams.java
index 1716fa0..07ac06b 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyDrawParams.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyDrawParams.java
@@ -35,6 +35,7 @@
     public int mTextColor;
     public int mTextInactivatedColor;
     public int mTextShadowColor;
+    public int mFunctionalTextColor;
     public int mHintLetterColor;
     public int mHintLabelColor;
     public int mShiftedLetterHintInactivatedColor;
@@ -60,6 +61,7 @@
         mTextColor = copyFrom.mTextColor;
         mTextInactivatedColor = copyFrom.mTextInactivatedColor;
         mTextShadowColor = copyFrom.mTextShadowColor;
+        mFunctionalTextColor = copyFrom.mFunctionalTextColor;
         mHintLetterColor = copyFrom.mHintLetterColor;
         mHintLabelColor = copyFrom.mHintLabelColor;
         mShiftedLetterHintInactivatedColor = copyFrom.mShiftedLetterHintInactivatedColor;
@@ -93,6 +95,7 @@
         mTextColor = selectColor(attr.mTextColor, mTextColor);
         mTextInactivatedColor = selectColor(attr.mTextInactivatedColor, mTextInactivatedColor);
         mTextShadowColor = selectColor(attr.mTextShadowColor, mTextShadowColor);
+        mFunctionalTextColor = selectColor(attr.mFunctionalTextColor, mFunctionalTextColor);
         mHintLetterColor = selectColor(attr.mHintLetterColor, mHintLetterColor);
         mHintLabelColor = selectColor(attr.mHintLabelColor, mHintLabelColor);
         mShiftedLetterHintInactivatedColor = selectColor(
diff --git a/java/src/com/android/inputmethod/keyboard/internal/KeyVisualAttributes.java b/java/src/com/android/inputmethod/keyboard/internal/KeyVisualAttributes.java
index df386fc..133462a 100644
--- a/java/src/com/android/inputmethod/keyboard/internal/KeyVisualAttributes.java
+++ b/java/src/com/android/inputmethod/keyboard/internal/KeyVisualAttributes.java
@@ -40,6 +40,7 @@
     public final int mTextColor;
     public final int mTextInactivatedColor;
     public final int mTextShadowColor;
+    public final int mFunctionalTextColor;
     public final int mHintLetterColor;
     public final int mHintLabelColor;
     public final int mShiftedLetterHintInactivatedColor;
@@ -61,6 +62,7 @@
         R.styleable.Keyboard_Key_keyTextColor,
         R.styleable.Keyboard_Key_keyTextInactivatedColor,
         R.styleable.Keyboard_Key_keyTextShadowColor,
+        R.styleable.Keyboard_Key_functionalTextColor,
         R.styleable.Keyboard_Key_keyHintLetterColor,
         R.styleable.Keyboard_Key_keyHintLabelColor,
         R.styleable.Keyboard_Key_keyShiftedLetterHintInactivatedColor,
@@ -122,6 +124,7 @@
         mTextInactivatedColor = keyAttr.getColor(
                 R.styleable.Keyboard_Key_keyTextInactivatedColor, 0);
         mTextShadowColor = keyAttr.getColor(R.styleable.Keyboard_Key_keyTextShadowColor, 0);
+        mFunctionalTextColor = keyAttr.getColor(R.styleable.Keyboard_Key_functionalTextColor, 0);
         mHintLetterColor = keyAttr.getColor(R.styleable.Keyboard_Key_keyHintLetterColor, 0);
         mHintLabelColor = keyAttr.getColor(R.styleable.Keyboard_Key_keyHintLabelColor, 0);
         mShiftedLetterHintInactivatedColor = keyAttr.getColor(
diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index 999508e..1e31c96 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -214,8 +214,6 @@
     private static native void removeBigramWordsNative(long dict, int[] word0, int[] word1);
     private static native int addMultipleDictionaryEntriesNative(long dict,
             LanguageModelParam[] languageModelParams, int startIndex);
-    private static native int calculateProbabilityNative(long dict, int unigramProbability,
-            int bigramProbability);
     private static native String getPropertyNative(long dict, String query);
     private static native boolean isCorruptedNative(long dict);
     private static native boolean migrateNative(long dict, String dictFilePath,
@@ -552,12 +550,6 @@
     }
 
     @UsedForTesting
-    public int calculateProbability(final int unigramProbability, final int bigramProbability) {
-        if (!isValidDictionary()) return NOT_A_PROBABILITY;
-        return calculateProbabilityNative(mNativeDict, unigramProbability, bigramProbability);
-    }
-
-    @UsedForTesting
     public String getPropertyForTest(final String query) {
         if (!isValidDictionary()) return "";
         return getPropertyNative(mNativeDict, query);
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index 8a2ed10..74b83c1 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -84,6 +84,7 @@
 import com.android.inputmethod.latin.utils.CoordinateUtils;
 import com.android.inputmethod.latin.utils.DialogUtils;
 import com.android.inputmethod.latin.utils.DistracterFilter;
+import com.android.inputmethod.latin.utils.DistracterFilterUtils;
 import com.android.inputmethod.latin.utils.ImportantNoticeUtils;
 import com.android.inputmethod.latin.utils.IntentUtils;
 import com.android.inputmethod.latin.utils.JniUtils;
@@ -480,6 +481,7 @@
         KeyboardSwitcher.init(this);
         AudioAndHapticFeedbackManager.init(this);
         AccessibilityUtils.init(this);
+        StatsUtils.init(this);
 
         super.onCreate();
 
@@ -519,7 +521,7 @@
 
         DictionaryDecayBroadcastReciever.setUpIntervalAlarmForDictionaryDecaying(this);
 
-        StatsUtils.onCreateCompleted(this);
+        StatsUtils.onCreate(mSettings.getCurrent());
     }
 
     // Has to be package-visible for unit tests
@@ -538,6 +540,7 @@
             resetSuggestForLocale(locale);
         }
         refreshPersonalizationDictionarySession();
+        StatsUtils.onLoadSettings(currentSettingsValues);
     }
 
     private void refreshPersonalizationDictionarySession() {
@@ -902,7 +905,6 @@
 
     private void onFinishInputViewInternal(final boolean finishingInput) {
         super.onFinishInputView(finishingInput);
-        mKeyboardSwitcher.onFinishInputView();
         mKeyboardSwitcher.deallocateMemory();
         // Remove pending messages related to update suggestions
         mHandler.cancelUpdateSuggestionStrip();
@@ -1385,7 +1387,6 @@
         if (!hasSuggestionStripView()) {
             return;
         }
-        mKeyboardSwitcher.onAutoCorrectionStateChanged(suggestedWords.mWillAutoCorrect);
         if (!onEvaluateInputViewShown()) {
             return;
         }
@@ -1745,7 +1746,8 @@
 
     @UsedForTesting
     /* package for test */ DistracterFilter createDistracterFilter() {
-        return DistracterFilter.createDistracterFilter(mInputLogic.mSuggest, mKeyboardSwitcher);
+        return DistracterFilterUtils.createDistracterFilter(
+                mInputLogic.mSuggest, mKeyboardSwitcher);
     }
 
     public void dumpDictionaryForDebug(final String dictName) {
diff --git a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
index 613ff2b..f5f072b 100644
--- a/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
+++ b/java/src/com/android/inputmethod/latin/makedict/FormatSpec.java
@@ -193,8 +193,9 @@
     // Dictionary version used for testing.
     public static final int VERSION4_ONLY_FOR_TESTING = 399;
     public static final int VERSION4 = 401;
+    public static final int VERSION4_DEV = 402;
     static final int MINIMUM_SUPPORTED_VERSION = VERSION2;
-    static final int MAXIMUM_SUPPORTED_VERSION = VERSION4;
+    static final int MAXIMUM_SUPPORTED_VERSION = VERSION4_DEV;
 
     // TODO: Make this value adaptative to content data, store it in the header, and
     // use it in the reading code.
diff --git a/java/src/com/android/inputmethod/latin/settings/Settings.java b/java/src/com/android/inputmethod/latin/settings/Settings.java
index 4e4c888..d4f6bcd 100644
--- a/java/src/com/android/inputmethod/latin/settings/Settings.java
+++ b/java/src/com/android/inputmethod/latin/settings/Settings.java
@@ -87,6 +87,8 @@
     public static final String PREF_DEBUG_SETTINGS = "debug_settings";
     public static final String PREF_KEY_IS_INTERNAL = "pref_key_is_internal";
 
+    public static final String PREF_ENABLE_METRICS_LOGGING = "pref_enable_metrics_logging";
+
     // This preference key is deprecated. Use {@link #PREF_SHOW_LANGUAGE_SWITCH_KEY} instead.
     // This is being used only for the backward compatibility.
     private static final String PREF_SUPPRESS_LANGUAGE_SWITCH_KEY =
diff --git a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
index de2eb95..16fd058 100644
--- a/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
+++ b/java/src/com/android/inputmethod/latin/settings/SettingsValues.java
@@ -73,6 +73,7 @@
     public final boolean mPhraseGestureEnabled;
     public final int mKeyLongpressTimeout;
     public final Locale mLocale;
+    public final boolean mEnableMetricsLogging;
 
     // From the input box
     public final InputAttributes mInputAttributes;
@@ -134,7 +135,7 @@
         mAutoCorrectEnabled = Settings.readAutoCorrectEnabled(autoCorrectionThresholdRawValue, res);
         mBigramPredictionEnabled = readBigramPredictionEnabled(prefs, res);
         mDoubleSpacePeriodTimeout = res.getInteger(R.integer.config_double_space_period_timeout);
-
+        mEnableMetricsLogging = prefs.getBoolean(Settings.PREF_ENABLE_METRICS_LOGGING, true);
         // Compute other readable settings
         mKeyLongpressTimeout = Settings.readKeyLongpressTimeout(prefs, res);
         mKeypressVibrationDuration = Settings.readKeypressVibrationDuration(prefs, res);
diff --git a/java/src/com/android/inputmethod/latin/utils/DistracterFilter.java b/java/src/com/android/inputmethod/latin/utils/DistracterFilter.java
index f0963f7..0a03799 100644
--- a/java/src/com/android/inputmethod/latin/utils/DistracterFilter.java
+++ b/java/src/com/android/inputmethod/latin/utils/DistracterFilter.java
@@ -17,8 +17,6 @@
 package com.android.inputmethod.latin.utils;
 
 import com.android.inputmethod.keyboard.Keyboard;
-import com.android.inputmethod.keyboard.KeyboardSwitcher;
-import com.android.inputmethod.keyboard.MainKeyboardView;
 import com.android.inputmethod.latin.Constants;
 import com.android.inputmethod.latin.Suggest;
 import com.android.inputmethod.latin.Suggest.OnGetSuggestedWordsCallback;
@@ -54,18 +52,6 @@
         mKeyboard = keyboard;
     }
 
-    public static DistracterFilter createDistracterFilter(final Suggest suggest,
-            final KeyboardSwitcher keyboardSwitcher) {
-        final MainKeyboardView mainKeyboardView = keyboardSwitcher.getMainKeyboardView();
-        // TODO: Create Keyboard when mainKeyboardView is null.
-        // TODO: Figure out the most reasonable keyboard for the filter. Refer to the
-        // spellchecker's logic.
-        final Keyboard keyboard = (mainKeyboardView != null) ?
-                mainKeyboardView.getKeyboard() : null;
-        final DistracterFilter distracterFilter = new DistracterFilter(suggest, keyboard);
-        return distracterFilter;
-    }
-
     private static boolean suggestionExceedsDistracterThreshold(
             final SuggestedWordInfo suggestion, final String consideredWord,
             final float distracterThreshold) {
diff --git a/java/src/com/android/inputmethod/latin/utils/DistracterFilterUtils.java b/java/src/com/android/inputmethod/latin/utils/DistracterFilterUtils.java
new file mode 100644
index 0000000..df07f97
--- /dev/null
+++ b/java/src/com/android/inputmethod/latin/utils/DistracterFilterUtils.java
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+package com.android.inputmethod.latin.utils;
+
+import com.android.inputmethod.keyboard.Keyboard;
+import com.android.inputmethod.keyboard.KeyboardSwitcher;
+import com.android.inputmethod.keyboard.MainKeyboardView;
+import com.android.inputmethod.latin.Suggest;
+
+public class DistracterFilterUtils {
+    private DistracterFilterUtils() {
+        // This utility class is not publicly instantiable.
+    }
+
+    public static final DistracterFilter createDistracterFilter(final Suggest suggest,
+            final KeyboardSwitcher keyboardSwitcher) {
+        final MainKeyboardView mainKeyboardView = keyboardSwitcher.getMainKeyboardView();
+        // TODO: Create Keyboard when mainKeyboardView is null.
+        // TODO: Figure out the most reasonable keyboard for the filter. Refer to the
+        // spellchecker's logic.
+        final Keyboard keyboard = (mainKeyboardView != null) ?
+                mainKeyboardView.getKeyboard() : null;
+        final DistracterFilter distracterFilter = new DistracterFilter(suggest, keyboard);
+        return distracterFilter;
+    }
+}
diff --git a/java/src/com/android/inputmethod/latin/utils/StatsUtils.java b/java/src/com/android/inputmethod/latin/utils/StatsUtils.java
index a059f87..79c19d0 100644
--- a/java/src/com/android/inputmethod/latin/utils/StatsUtils.java
+++ b/java/src/com/android/inputmethod/latin/utils/StatsUtils.java
@@ -17,37 +17,18 @@
 package com.android.inputmethod.latin.utils;
 
 import android.content.Context;
-import android.content.SharedPreferences;
-import android.preference.PreferenceManager;
-import android.util.Log;
-
-import com.android.inputmethod.latin.settings.Settings;
+import com.android.inputmethod.latin.settings.SettingsValues;
 
 public final class StatsUtils {
-    private static final String TAG = StatsUtils.class.getSimpleName();
-    private static final StatsUtils sInstance = new StatsUtils();
-
-    public static void onCreateCompleted(final Context context) {
-        sInstance.onCreateCompletedInternal(context);
+    public static void init(final Context context) {
     }
 
-    private void onCreateCompletedInternal(final Context context) {
-        mContext = context;
-        final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
-        final Boolean usePersonalizedDict =
-                prefs.getBoolean(Settings.PREF_KEY_USE_PERSONALIZED_DICTS, true);
-        Log.d(TAG, "onCreateCompleted. context: " + context.toString() + "usePersonalizedDict: "
-                + usePersonalizedDict);
+    public static void onCreate(final SettingsValues settingsValues) {
+    }
+
+    public static void onLoadSettings(final SettingsValues settingsValues) {
     }
 
     public static void onDestroy() {
-        sInstance.onDestroyInternal();
     }
-
-    private void onDestroyInternal() {
-        Log.d(TAG, "onDestroy. context: " + mContext.toString());
-        mContext = null;
-    }
-
-    private Context mContext;
 }
diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
index 18b78c4..28aaf2d 100644
--- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
+++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
@@ -28,6 +28,7 @@
 #include "suggest/core/dictionary/property/unigram_property.h"
 #include "suggest/core/dictionary/property/word_property.h"
 #include "suggest/core/result/suggestion_results.h"
+#include "suggest/core/session/prev_words_info.h"
 #include "suggest/core/suggest_options.h"
 #include "suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.h"
 #include "utils/char_utils.h"
@@ -190,7 +191,9 @@
     ProximityInfo *pInfo = reinterpret_cast<ProximityInfo *>(proximityInfo);
     DicTraverseSession *traverseSession =
             reinterpret_cast<DicTraverseSession *>(dicTraverseSession);
-
+    if (!traverseSession) {
+        return;
+    }
     // Input values
     int xCoordinates[inputSize];
     int yCoordinates[inputSize];
@@ -245,15 +248,15 @@
     float languageWeight;
     env->GetFloatArrayRegion(inOutLanguageWeight, 0, 1 /* len */, &languageWeight);
     SuggestionResults suggestionResults(MAX_RESULTS);
+    const PrevWordsInfo prevWordsInfo(prevWordCodePoints, prevWordCodePointsLength,
+            false /* isStartOfSentence */);
     if (givenSuggestOptions.isGesture() || inputSize > 0) {
         // TODO: Use SuggestionResults to return suggestions.
         dictionary->getSuggestions(pInfo, traverseSession, xCoordinates, yCoordinates,
-                times, pointerIds, inputCodePoints, inputSize, prevWordCodePoints,
-                prevWordCodePointsLength, &givenSuggestOptions, languageWeight,
-                &suggestionResults);
+                times, pointerIds, inputCodePoints, inputSize, &prevWordsInfo,
+                &givenSuggestOptions, languageWeight, &suggestionResults);
     } else {
-        dictionary->getPredictions(prevWordCodePoints, prevWordCodePointsLength,
-                &suggestionResults);
+        dictionary->getPredictions(&prevWordsInfo, &suggestionResults);
     }
     suggestionResults.outputSuggestions(env, outSuggestionCount, outCodePointsArray,
             outScoresArray, outSpaceIndicesArray, outTypesArray,
@@ -280,8 +283,8 @@
     int word1CodePoints[word1Length];
     env->GetIntArrayRegion(word0, 0, word0Length, word0CodePoints);
     env->GetIntArrayRegion(word1, 0, word1Length, word1CodePoints);
-    return dictionary->getBigramProbability(word0CodePoints, word0Length, word1CodePoints,
-            word1Length);
+    const PrevWordsInfo prevWordsInfo(word0CodePoints, word0Length, false /* isStartOfSentence */);
+    return dictionary->getBigramProbability(&prevWordsInfo, word1CodePoints, word1Length);
 }
 
 // Method to iterate all words in the dictionary for makedict.
@@ -467,16 +470,6 @@
     return languageModelParamCount;
 }
 
-static int latinime_BinaryDictionary_calculateProbabilityNative(JNIEnv *env, jclass clazz,
-        jlong dict, jint unigramProbability, jint bigramProbability) {
-    Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict);
-    if (!dictionary) {
-        return NOT_A_PROBABILITY;
-    }
-    return dictionary->getDictionaryStructurePolicy()->getProbability(unigramProbability,
-            bigramProbability);
-}
-
 static jstring latinime_BinaryDictionary_getProperty(JNIEnv *env, jclass clazz, jlong dict,
         jstring query) {
     Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict);
@@ -670,11 +663,6 @@
         reinterpret_cast<void *>(latinime_BinaryDictionary_addMultipleDictionaryEntries)
     },
     {
-        const_cast<char *>("calculateProbabilityNative"),
-        const_cast<char *>("(JII)I"),
-        reinterpret_cast<void *>(latinime_BinaryDictionary_calculateProbabilityNative)
-    },
-    {
         const_cast<char *>("getPropertyNative"),
         const_cast<char *>("(JLjava/lang/String;)Ljava/lang/String;"),
         reinterpret_cast<void *>(latinime_BinaryDictionary_getProperty)
diff --git a/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp b/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp
index 3866433..7660641 100644
--- a/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp
+++ b/native/jni/com_android_inputmethod_latin_DicTraverseSession.cpp
@@ -22,6 +22,7 @@
 #include "jni.h"
 #include "jni_common.h"
 #include "suggest/core/session/dic_traverse_session.h"
+#include "suggest/core/session/prev_words_info.h"
 
 namespace latinime {
 class Dictionary;
@@ -34,16 +35,19 @@
 static void latinime_initDicTraverseSession(JNIEnv *env, jclass clazz, jlong traverseSession,
         jlong dictionary, jintArray previousWord, jint previousWordLength) {
     DicTraverseSession *ts = reinterpret_cast<DicTraverseSession *>(traverseSession);
+    if (!ts) {
+        return;
+    }
     Dictionary *dict = reinterpret_cast<Dictionary *>(dictionary);
     if (!previousWord) {
-        DicTraverseSession::initSessionInstance(
-                ts, dict, 0 /* prevWord */, 0 /* prevWordLength*/, 0 /* suggestOptions */);
+        PrevWordsInfo prevWordsInfo;
+        ts->init(dict, &prevWordsInfo, 0 /* suggestOptions */);
         return;
     }
     int prevWord[previousWordLength];
     env->GetIntArrayRegion(previousWord, 0, previousWordLength, prevWord);
-    DicTraverseSession::initSessionInstance(
-            ts, dict, prevWord, previousWordLength, 0 /* suggestOptions */);
+    PrevWordsInfo prevWordsInfo(prevWord, previousWordLength, false /* isStartOfSentence */);
+    ts->init(dict, &prevWordsInfo, 0 /* suggestOptions */);
 }
 
 static void latinime_releaseDicTraverseSession(JNIEnv *env, jclass clazz, jlong traverseSession) {
diff --git a/native/jni/src/defines.h b/native/jni/src/defines.h
index a80c975..24d04e5 100644
--- a/native/jni/src/defines.h
+++ b/native/jni/src/defines.h
@@ -336,6 +336,9 @@
 #define MAX_POINTER_COUNT 1
 #define MAX_POINTER_COUNT_G 2
 
+// (MAX_PREV_WORD_COUNT_FOR_N_GRAM + 1)-gram is supported.
+#define MAX_PREV_WORD_COUNT_FOR_N_GRAM 1
+
 #define DISALLOW_DEFAULT_CONSTRUCTOR(TypeName) \
   TypeName() = delete
 
diff --git a/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp b/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp
index f793363..847fa1b 100644
--- a/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp
+++ b/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp
@@ -26,6 +26,7 @@
 #include "suggest/core/dictionary/dictionary.h"
 #include "suggest/core/policy/dictionary_structure_with_buffer_policy.h"
 #include "suggest/core/result/suggestion_results.h"
+#include "suggest/core/session/prev_words_info.h"
 #include "utils/char_utils.h"
 
 namespace latinime {
@@ -42,19 +43,18 @@
 }
 
 /* Parameters :
- * prevWord: the word before, the one for which we need to look up bigrams.
- * prevWordLength: its length.
+ * prevWordsInfo: Information of previous words to get the predictions.
  * outSuggestionResults: SuggestionResults to put the predictions.
  */
-void BigramDictionary::getPredictions(const int *prevWord, const int prevWordLength,
+void BigramDictionary::getPredictions(const PrevWordsInfo *const prevWordsInfo,
         SuggestionResults *const outSuggestionResults) const {
-    int pos = getBigramListPositionForWord(prevWord, prevWordLength,
-            false /* forceLowerCaseSearch */);
+    int pos = getBigramListPositionForWord(prevWordsInfo->getPrevWordCodePoints(),
+            prevWordsInfo->getPrevWordCodePointCount(), false /* forceLowerCaseSearch */);
     // getBigramListPositionForWord returns 0 if this word isn't in the dictionary or has no bigrams
     if (NOT_A_DICT_POS == pos) {
         // If no bigrams for this exact word, search again in lower case.
-        pos = getBigramListPositionForWord(prevWord, prevWordLength,
-                true /* forceLowerCaseSearch */);
+        pos = getBigramListPositionForWord(prevWordsInfo->getPrevWordCodePoints(),
+                prevWordsInfo->getPrevWordCodePointCount(), true /* forceLowerCaseSearch */);
     }
     // If still no bigrams, we really don't have them!
     if (NOT_A_DICT_POS == pos) return;
@@ -96,9 +96,10 @@
     return mDictionaryStructurePolicy->getBigramsPositionOfPtNode(pos);
 }
 
-int BigramDictionary::getBigramProbability(const int *word0, int length0, const int *word1,
-        int length1) const {
-    int pos = getBigramListPositionForWord(word0, length0, false /* forceLowerCaseSearch */);
+int BigramDictionary::getBigramProbability(const PrevWordsInfo *const prevWordsInfo,
+        const int *word1, int length1) const {
+    int pos = getBigramListPositionForWord(prevWordsInfo->getPrevWordCodePoints(),
+            prevWordsInfo->getPrevWordCodePointCount(), false /* forceLowerCaseSearch */);
     // getBigramListPositionForWord returns 0 if this word isn't in the dictionary or has no bigrams
     if (NOT_A_DICT_POS == pos) return NOT_A_PROBABILITY;
     int nextWordPos = mDictionaryStructurePolicy->getTerminalPtNodePositionOfWord(word1, length1,
diff --git a/native/jni/src/suggest/core/dictionary/bigram_dictionary.h b/native/jni/src/suggest/core/dictionary/bigram_dictionary.h
index 12aaf20..bd3aed1 100644
--- a/native/jni/src/suggest/core/dictionary/bigram_dictionary.h
+++ b/native/jni/src/suggest/core/dictionary/bigram_dictionary.h
@@ -22,15 +22,17 @@
 namespace latinime {
 
 class DictionaryStructureWithBufferPolicy;
+class PrevWordsInfo;
 class SuggestionResults;
 
 class BigramDictionary {
  public:
     BigramDictionary(const DictionaryStructureWithBufferPolicy *const dictionaryStructurePolicy);
 
-    void getPredictions(const int *word, int length,
+    void getPredictions(const PrevWordsInfo *const prevWordsInfo,
             SuggestionResults *const outSuggestionResults) const;
-    int getBigramProbability(const int *word1, int length1, const int *word2, int length2) const;
+    int getBigramProbability(const PrevWordsInfo *const prevWordsInfo,
+            const int *word1, int length1) const;
     ~BigramDictionary();
 
  private:
diff --git a/native/jni/src/suggest/core/dictionary/dictionary.cpp b/native/jni/src/suggest/core/dictionary/dictionary.cpp
index fdc8936..c860d82 100644
--- a/native/jni/src/suggest/core/dictionary/dictionary.cpp
+++ b/native/jni/src/suggest/core/dictionary/dictionary.cpp
@@ -44,12 +44,11 @@
 
 void Dictionary::getSuggestions(ProximityInfo *proximityInfo, DicTraverseSession *traverseSession,
         int *xcoordinates, int *ycoordinates, int *times, int *pointerIds, int *inputCodePoints,
-        int inputSize, int *prevWordCodePoints, int prevWordLength,
+        int inputSize, const PrevWordsInfo *const prevWordsInfo,
         const SuggestOptions *const suggestOptions, const float languageWeight,
         SuggestionResults *const outSuggestionResults) const {
     TimeKeeper::setCurrentTime();
-    DicTraverseSession::initSessionInstance(
-            traverseSession, this, prevWordCodePoints, prevWordLength, suggestOptions);
+    traverseSession->init(this, prevWordsInfo, suggestOptions);
     const auto &suggest = suggestOptions->isGesture() ? mGestureSuggest : mTypingSuggest;
     suggest->getSuggestions(proximityInfo, traverseSession, xcoordinates,
             ycoordinates, times, pointerIds, inputCodePoints, inputSize,
@@ -59,11 +58,10 @@
     }
 }
 
-void Dictionary::getPredictions(const int *word, int length,
+void Dictionary::getPredictions(const PrevWordsInfo *const prevWordsInfo,
         SuggestionResults *const outSuggestionResults) const {
     TimeKeeper::setCurrentTime();
-    if (length <= 0) return;
-    mBigramDictionary.getPredictions(word, length, outSuggestionResults);
+    mBigramDictionary.getPredictions(prevWordsInfo, outSuggestionResults);
 }
 
 int Dictionary::getProbability(const int *word, int length) const {
@@ -76,10 +74,10 @@
     return getDictionaryStructurePolicy()->getUnigramProbabilityOfPtNode(pos);
 }
 
-int Dictionary::getBigramProbability(const int *word0, int length0, const int *word1,
+int Dictionary::getBigramProbability(const PrevWordsInfo *const prevWordsInfo, const int *word1,
         int length1) const {
     TimeKeeper::setCurrentTime();
-    return mBigramDictionary.getBigramProbability(word0, length0, word1, length1);
+    return mBigramDictionary.getBigramProbability(prevWordsInfo, word1, length1);
 }
 
 void Dictionary::addUnigramWord(const int *const word, const int length,
diff --git a/native/jni/src/suggest/core/dictionary/dictionary.h b/native/jni/src/suggest/core/dictionary/dictionary.h
index f0a7e5b..b63c61f 100644
--- a/native/jni/src/suggest/core/dictionary/dictionary.h
+++ b/native/jni/src/suggest/core/dictionary/dictionary.h
@@ -31,6 +31,7 @@
 
 class DictionaryStructureWithBufferPolicy;
 class DicTraverseSession;
+class PrevWordsInfo;
 class ProximityInfo;
 class SuggestionResults;
 class SuggestOptions;
@@ -62,16 +63,17 @@
 
     void getSuggestions(ProximityInfo *proximityInfo, DicTraverseSession *traverseSession,
             int *xcoordinates, int *ycoordinates, int *times, int *pointerIds, int *inputCodePoints,
-            int inputSize, int *prevWordCodePoints, int prevWordLength,
+            int inputSize, const PrevWordsInfo *const prevWordsInfo,
             const SuggestOptions *const suggestOptions, const float languageWeight,
             SuggestionResults *const outSuggestionResults) const;
 
-    void getPredictions(const int *word, int length,
+    void getPredictions(const PrevWordsInfo *const prevWordsInfo,
             SuggestionResults *const outSuggestionResults) const;
 
     int getProbability(const int *word, int length) const;
 
-    int getBigramProbability(const int *word0, int length0, const int *word1, int length1) const;
+    int getBigramProbability(const PrevWordsInfo *const prevWordsInfo,
+            const int *word1, int length1) const;
 
     void addUnigramWord(const int *const codePoints, const int codePointCount,
             const UnigramProperty *const unigramProperty);
diff --git a/native/jni/src/suggest/core/session/dic_traverse_session.cpp b/native/jni/src/suggest/core/session/dic_traverse_session.cpp
index 77b634e..b9e9db7 100644
--- a/native/jni/src/suggest/core/session/dic_traverse_session.cpp
+++ b/native/jni/src/suggest/core/session/dic_traverse_session.cpp
@@ -20,6 +20,7 @@
 #include "suggest/core/dictionary/dictionary.h"
 #include "suggest/core/policy/dictionary_header_structure_policy.h"
 #include "suggest/core/policy/dictionary_structure_with_buffer_policy.h"
+#include "suggest/core/session/prev_words_info.h"
 
 namespace latinime {
 
@@ -28,24 +29,26 @@
 const int DicTraverseSession::DICTIONARY_SIZE_THRESHOLD_TO_USE_LARGE_CACHE_FOR_SUGGESTION =
         256 * 1024;
 
-void DicTraverseSession::init(const Dictionary *const dictionary, const int *prevWord,
-        int prevWordLength, const SuggestOptions *const suggestOptions) {
+void DicTraverseSession::init(const Dictionary *const dictionary,
+        const PrevWordsInfo *const prevWordsInfo, const SuggestOptions *const suggestOptions) {
     mDictionary = dictionary;
     mMultiWordCostMultiplier = getDictionaryStructurePolicy()->getHeaderStructurePolicy()
             ->getMultiWordCostMultiplier();
     mSuggestOptions = suggestOptions;
-    if (!prevWord) {
-        mPrevWordPtNodePos = NOT_A_DICT_POS;
+    if (!prevWordsInfo->getPrevWordCodePoints()) {
+        mPrevWordsPtNodePos[0] = NOT_A_DICT_POS;
         return;
     }
     // TODO: merge following similar calls to getTerminalPosition into one case-insensitive call.
-    mPrevWordPtNodePos = getDictionaryStructurePolicy()->getTerminalPtNodePositionOfWord(
-            prevWord, prevWordLength, false /* forceLowerCaseSearch */);
-    if (mPrevWordPtNodePos == NOT_A_DICT_POS) {
+    mPrevWordsPtNodePos[0] = getDictionaryStructurePolicy()->getTerminalPtNodePositionOfWord(
+            prevWordsInfo->getPrevWordCodePoints(), prevWordsInfo->getPrevWordCodePointCount(),
+            false /* forceLowerCaseSearch */);
+    if (mPrevWordsPtNodePos[0] == NOT_A_DICT_POS) {
         // Check bigrams for lower-cased previous word if original was not found. Useful for
         // auto-capitalized words like "The [current_word]".
-        mPrevWordPtNodePos = getDictionaryStructurePolicy()->getTerminalPtNodePositionOfWord(
-                prevWord, prevWordLength, true /* forceLowerCaseSearch */);
+        mPrevWordsPtNodePos[0] = getDictionaryStructurePolicy()->getTerminalPtNodePositionOfWord(
+                prevWordsInfo->getPrevWordCodePoints(), prevWordsInfo->getPrevWordCodePointCount(),
+                true /* forceLowerCaseSearch */);
     }
 }
 
diff --git a/native/jni/src/suggest/core/session/dic_traverse_session.h b/native/jni/src/suggest/core/session/dic_traverse_session.h
index 843ca85..90aff06 100644
--- a/native/jni/src/suggest/core/session/dic_traverse_session.h
+++ b/native/jni/src/suggest/core/session/dic_traverse_session.h
@@ -29,6 +29,7 @@
 
 class Dictionary;
 class DictionaryStructureWithBufferPolicy;
+class PrevWordsInfo;
 class ProximityInfo;
 class SuggestOptions;
 
@@ -44,32 +45,25 @@
                 dictSize >= DICTIONARY_SIZE_THRESHOLD_TO_USE_LARGE_CACHE_FOR_SUGGESTION);
     }
 
-    static AK_FORCE_INLINE void initSessionInstance(DicTraverseSession *traverseSession,
-            const Dictionary *const dictionary, const int *prevWord, const int prevWordLength,
-            const SuggestOptions *const suggestOptions) {
-        if (traverseSession) {
-            DicTraverseSession *tSession = static_cast<DicTraverseSession *>(traverseSession);
-            tSession->init(dictionary, prevWord, prevWordLength, suggestOptions);
-        }
-    }
-
     static AK_FORCE_INLINE void releaseSessionInstance(DicTraverseSession *traverseSession) {
         delete traverseSession;
     }
 
     AK_FORCE_INLINE DicTraverseSession(JNIEnv *env, jstring localeStr, bool usesLargeCache)
-            : mPrevWordPtNodePos(NOT_A_DICT_POS), mProximityInfo(nullptr),
-              mDictionary(nullptr), mSuggestOptions(nullptr), mDicNodesCache(usesLargeCache),
-              mMultiBigramMap(), mInputSize(0), mMaxPointerCount(1),
+            : mProximityInfo(nullptr), mDictionary(nullptr), mSuggestOptions(nullptr),
+              mDicNodesCache(usesLargeCache), mMultiBigramMap(), mInputSize(0), mMaxPointerCount(1),
               mMultiWordCostMultiplier(1.0f) {
         // NOTE: mProximityInfoStates is an array of instances.
         // No need to initialize it explicitly here.
+        for (size_t i = 0; i < NELEMS(mPrevWordsPtNodePos); ++i) {
+            mPrevWordsPtNodePos[i] = NOT_A_DICT_POS;
+        }
     }
 
     // Non virtual inline destructor -- never inherit this class
     AK_FORCE_INLINE ~DicTraverseSession() {}
 
-    void init(const Dictionary *dictionary, const int *prevWord, int prevWordLength,
+    void init(const Dictionary *dictionary, const PrevWordsInfo *const prevWordsInfo,
             const SuggestOptions *const suggestOptions);
     // TODO: Remove and merge into init
     void setupForGetSuggestions(const ProximityInfo *pInfo, const int *inputCodePoints,
@@ -85,9 +79,7 @@
     //--------------------
     const ProximityInfo *getProximityInfo() const { return mProximityInfo; }
     const SuggestOptions *getSuggestOptions() const { return mSuggestOptions; }
-    int getPrevWordPtNodePos() const { return mPrevWordPtNodePos; }
-    // TODO: REMOVE
-    void setPrevWordPtNodePos(const int ptNodePos) { mPrevWordPtNodePos = ptNodePos; }
+    int getPrevWordPtNodePos() const { return mPrevWordsPtNodePos[0]; }
     DicNodesCache *getDicTraverseCache() { return &mDicNodesCache; }
     MultiBigramMap *getMultiBigramMap() { return &mMultiBigramMap; }
     const ProximityInfoState *getProximityInfoState(int id) const {
@@ -174,7 +166,7 @@
             const int *const inputYs, const int *const times, const int *const pointerIds,
             const int inputSize, const float maxSpatialDistance, const int maxPointerCount);
 
-    int mPrevWordPtNodePos;
+    int mPrevWordsPtNodePos[MAX_PREV_WORD_COUNT_FOR_N_GRAM];
     const ProximityInfo *mProximityInfo;
     const Dictionary *mDictionary;
     const SuggestOptions *mSuggestOptions;
diff --git a/native/jni/src/suggest/core/session/prev_words_info.h b/native/jni/src/suggest/core/session/prev_words_info.h
new file mode 100644
index 0000000..bc68594
--- /dev/null
+++ b/native/jni/src/suggest/core/session/prev_words_info.h
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+#ifndef LATINIME_PREV_WORDS_INFO_H
+#define LATINIME_PREV_WORDS_INFO_H
+
+#include "defines.h"
+
+namespace latinime {
+
+// TODO: Support n-gram.
+// TODO: Support beginning of sentence.
+// This class does not take ownership of any code point buffers.
+class PrevWordsInfo {
+ public:
+    // No prev word information.
+    PrevWordsInfo() {
+        clear();
+    }
+
+    PrevWordsInfo(const int *const prevWordCodePoints, const int prevWordCodePointCount,
+            const bool isBeginningOfSentence) {
+        clear();
+        mPrevWordCodePoints[0] = prevWordCodePoints;
+        mPrevWordCodePointCount[0] = prevWordCodePointCount;
+        mIsBeginningOfSentence[0] = isBeginningOfSentence;
+    }
+    const int *getPrevWordCodePoints() const {
+        return mPrevWordCodePoints[0];
+    }
+
+    int getPrevWordCodePointCount() const {
+        return mPrevWordCodePointCount[0];
+    }
+
+ private:
+    DISALLOW_COPY_AND_ASSIGN(PrevWordsInfo);
+
+    void clear() {
+        for (size_t i = 0; i < NELEMS(mPrevWordCodePoints); ++i) {
+            mPrevWordCodePoints[i] = nullptr;
+            mPrevWordCodePointCount[i] = 0;
+            mIsBeginningOfSentence[i] = false;
+        }
+    }
+
+    const int *mPrevWordCodePoints[MAX_PREV_WORD_COUNT_FOR_N_GRAM];
+    int mPrevWordCodePointCount[MAX_PREV_WORD_COUNT_FOR_N_GRAM];
+    bool mIsBeginningOfSentence[MAX_PREV_WORD_COUNT_FOR_N_GRAM];
+};
+} // namespace latinime
+#endif // LATINIME_PREV_WORDS_INFO_H
diff --git a/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h
index da24302..479d151 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/header/header_policy.h
@@ -143,6 +143,8 @@
                 return FormatUtils::VERSION_4_ONLY_FOR_TESTING;
             case FormatUtils::VERSION_4:
                 return FormatUtils::VERSION_4;
+            case FormatUtils::VERSION_4_DEV:
+                return FormatUtils::VERSION_4_DEV;
             default:
                 return FormatUtils::UNKNOWN_VERSION;
         }
diff --git a/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.cpp
index 2a9028a..a8f8f28 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/header/header_read_write_utils.cpp
@@ -100,6 +100,7 @@
             return false;
         case FormatUtils::VERSION_4_ONLY_FOR_TESTING:
         case FormatUtils::VERSION_4:
+        case FormatUtils::VERSION_4_DEV:
             return buffer->writeUintAndAdvancePosition(version /* data */,
                     HEADER_DICTIONARY_VERSION_SIZE, writingPos);
         default:
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.cpp
index c4d1860..59f1f29 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.cpp
@@ -19,6 +19,9 @@
 #include <climits>
 
 #include "defines.h"
+#include "suggest/policyimpl/dictionary/structure/backward/v401/ver4_dict_buffers.h"
+#include "suggest/policyimpl/dictionary/structure/backward/v401/ver4_dict_constants.h"
+#include "suggest/policyimpl/dictionary/structure/backward/v401/ver4_patricia_trie_policy.h"
 #include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_writing_utils.h"
 #include "suggest/policyimpl/dictionary/structure/v2/patricia_trie_policy.h"
 #include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h"
@@ -42,7 +45,7 @@
         if (isUpdatable) {
             AKLOGE("One file dictionaries don't support updating. path: %s", path);
             ASSERT(false);
-            return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(nullptr);
+            return nullptr;
         }
         return newPolicyForFileDict(path, bufOffset, size);
     }
@@ -54,26 +57,43 @@
                 const DictionaryHeaderStructurePolicy::AttributeMap *const attributeMap) {
     FormatUtils::FORMAT_VERSION dictFormatVersion = FormatUtils::getFormatVersion(formatVersion);
     switch (dictFormatVersion) {
-        case FormatUtils::VERSION_4_ONLY_FOR_TESTING:
         case FormatUtils::VERSION_4: {
-            HeaderPolicy headerPolicy(dictFormatVersion, locale, attributeMap);
-            Ver4DictBuffers::Ver4DictBuffersPtr dictBuffers =
-                    Ver4DictBuffers::createVer4DictBuffers(&headerPolicy,
-                            Ver4DictConstants::MAX_DICT_EXTENDED_REGION_SIZE);
-            if (!DynamicPtWritingUtils::writeEmptyDictionary(
-                    dictBuffers->getWritableTrieBuffer(), 0 /* rootPos */)) {
-                AKLOGE("Empty ver4 dictionary structure cannot be created on memory.");
-                return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(nullptr);
-            }
-            return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(
-                    new Ver4PatriciaTriePolicy(std::move(dictBuffers)));
+            return newPolicyForOnMemoryV4Dict<backward::v401::Ver4DictConstants,
+                    backward::v401::Ver4DictBuffers,
+                    backward::v401::Ver4DictBuffers::Ver4DictBuffersPtr,
+                    backward::v401::Ver4PatriciaTriePolicy>(
+                            dictFormatVersion, locale, attributeMap);
+        }
+        case FormatUtils::VERSION_4_ONLY_FOR_TESTING:
+        case FormatUtils::VERSION_4_DEV: {
+            return newPolicyForOnMemoryV4Dict<Ver4DictConstants, Ver4DictBuffers,
+                    Ver4DictBuffers::Ver4DictBuffersPtr, Ver4PatriciaTriePolicy>(
+                            dictFormatVersion, locale, attributeMap);
         }
         default:
             AKLOGE("DICT: dictionary format %d is not supported for on memory dictionary",
                     formatVersion);
             break;
     }
-    return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(nullptr);
+    return nullptr;
+}
+
+template<class DictConstants, class DictBuffers, class DictBuffersPtr, class StructurePolicy>
+/* static */ DictionaryStructureWithBufferPolicy::StructurePolicyPtr
+        DictionaryStructureWithBufferPolicyFactory::newPolicyForOnMemoryV4Dict(
+                const FormatUtils::FORMAT_VERSION formatVersion,
+                const std::vector<int> &locale,
+                const DictionaryHeaderStructurePolicy::AttributeMap *const attributeMap) {
+    HeaderPolicy headerPolicy(formatVersion, locale, attributeMap);
+    DictBuffersPtr dictBuffers = DictBuffers::createVer4DictBuffers(&headerPolicy,
+            DictConstants::MAX_DICT_EXTENDED_REGION_SIZE);
+    if (!DynamicPtWritingUtils::writeEmptyDictionary(
+            dictBuffers->getWritableTrieBuffer(), 0 /* rootPos */)) {
+        AKLOGE("Empty ver4 dictionary structure cannot be created on memory.");
+        return nullptr;
+    }
+    return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(
+            new StructurePolicy(std::move(dictBuffers)));
 }
 
 /* static */ DictionaryStructureWithBufferPolicy::StructurePolicyPtr
@@ -84,10 +104,10 @@
     getHeaderFilePathInDictDir(path, headerFilePathBufSize, headerFilePath);
     // Allocated buffer in MmapedBuffer::openBuffer() will be freed in the destructor of
     // MmappedBufferPtr if the instance has the responsibility.
-    MmappedBuffer::MmappedBufferPtr mmappedBuffer(
-            MmappedBuffer::openBuffer(headerFilePath, isUpdatable));
+    MmappedBuffer::MmappedBufferPtr mmappedBuffer =
+            MmappedBuffer::openBuffer(headerFilePath, isUpdatable);
     if (!mmappedBuffer) {
-        return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(nullptr);
+        return nullptr;
     }
     const FormatUtils::FORMAT_VERSION formatVersion = FormatUtils::detectFormatVersion(
             mmappedBuffer->getBuffer(), mmappedBuffer->getBufferSize());
@@ -95,34 +115,50 @@
         case FormatUtils::VERSION_2:
             AKLOGE("Given path is a directory but the format is version 2. path: %s", path);
             break;
-        case FormatUtils::VERSION_4_ONLY_FOR_TESTING:
         case FormatUtils::VERSION_4: {
-            const int dictDirPathBufSize = strlen(headerFilePath) + 1 /* terminator */;
-            char dictPath[dictDirPathBufSize];
-            if (!FileUtils::getFilePathWithoutSuffix(headerFilePath,
-                    Ver4DictConstants::HEADER_FILE_EXTENSION, dictDirPathBufSize, dictPath)) {
-                AKLOGE("Dictionary file name is not valid as a ver4 dictionary. path: %s", path);
-                ASSERT(false);
-                return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(nullptr);
-            }
-            Ver4DictBuffers::Ver4DictBuffersPtr dictBuffers(
-                    Ver4DictBuffers::openVer4DictBuffers(dictPath, std::move(mmappedBuffer),
-                            formatVersion));
-            if (!dictBuffers || !dictBuffers->isValid()) {
-                AKLOGE("DICT: The dictionary doesn't satisfy ver4 format requirements. path: %s",
-                        path);
-                ASSERT(false);
-                return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(nullptr);
-            }
-            return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(
-                    new Ver4PatriciaTriePolicy(std::move(dictBuffers)));
+            return newPolicyForV4Dict<backward::v401::Ver4DictConstants,
+                    backward::v401::Ver4DictBuffers,
+                    backward::v401::Ver4DictBuffers::Ver4DictBuffersPtr,
+                    backward::v401::Ver4PatriciaTriePolicy>(
+                            headerFilePath, formatVersion, std::move(mmappedBuffer));
+        }
+        case FormatUtils::VERSION_4_ONLY_FOR_TESTING:
+        case FormatUtils::VERSION_4_DEV: {
+            return newPolicyForV4Dict<Ver4DictConstants, Ver4DictBuffers,
+                    Ver4DictBuffers::Ver4DictBuffersPtr, Ver4PatriciaTriePolicy>(
+                            headerFilePath, formatVersion, std::move(mmappedBuffer));
         }
         default:
             AKLOGE("DICT: dictionary format is unknown, bad magic number. path: %s", path);
             break;
     }
     ASSERT(false);
-    return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(nullptr);
+    return nullptr;
+}
+
+template<class DictConstants, class DictBuffers, class DictBuffersPtr, class StructurePolicy>
+/* static */ DictionaryStructureWithBufferPolicy::StructurePolicyPtr
+        DictionaryStructureWithBufferPolicyFactory::newPolicyForV4Dict(
+                const char *const headerFilePath, const FormatUtils::FORMAT_VERSION formatVersion,
+                MmappedBuffer::MmappedBufferPtr &&mmappedBuffer) {
+    const int dictDirPathBufSize = strlen(headerFilePath) + 1 /* terminator */;
+    char dictPath[dictDirPathBufSize];
+    if (!FileUtils::getFilePathWithoutSuffix(headerFilePath,
+            DictConstants::HEADER_FILE_EXTENSION, dictDirPathBufSize, dictPath)) {
+        AKLOGE("Dictionary file name is not valid as a ver4 dictionary. path: %s", path);
+        ASSERT(false);
+        return nullptr;
+    }
+    DictBuffersPtr dictBuffers =
+            DictBuffers::openVer4DictBuffers(dictPath, std::move(mmappedBuffer), formatVersion);
+    if (!dictBuffers || !dictBuffers->isValid()) {
+        AKLOGE("DICT: The dictionary doesn't satisfy ver4 format requirements. path: %s",
+                path);
+        ASSERT(false);
+        return nullptr;
+    }
+    return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(
+            new StructurePolicy(std::move(dictBuffers)));
 }
 
 /* static */ DictionaryStructureWithBufferPolicy::StructurePolicyPtr
@@ -133,7 +169,7 @@
     MmappedBuffer::MmappedBufferPtr mmappedBuffer(
             MmappedBuffer::openBuffer(path, bufOffset, size, false /* isUpdatable */));
     if (!mmappedBuffer) {
-        return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(nullptr);
+        return nullptr;
     }
     switch (FormatUtils::detectFormatVersion(mmappedBuffer->getBuffer(),
             mmappedBuffer->getBufferSize())) {
@@ -142,6 +178,7 @@
                     new PatriciaTriePolicy(std::move(mmappedBuffer)));
         case FormatUtils::VERSION_4_ONLY_FOR_TESTING:
         case FormatUtils::VERSION_4:
+        case FormatUtils::VERSION_4_DEV:
             AKLOGE("Given path is a file but the format is version 4. path: %s", path);
             break;
         default:
@@ -149,7 +186,7 @@
             break;
     }
     ASSERT(false);
-    return DictionaryStructureWithBufferPolicy::StructurePolicyPtr(nullptr);
+    return nullptr;
 }
 
 /* static */ void DictionaryStructureWithBufferPolicyFactory::getHeaderFilePathInDictDir(
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.h b/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.h
index f71447e..768454d 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.h
@@ -22,6 +22,8 @@
 #include "defines.h"
 #include "suggest/core/policy/dictionary_header_structure_policy.h"
 #include "suggest/core/policy/dictionary_structure_with_buffer_policy.h"
+#include "suggest/policyimpl/dictionary/utils/format_utils.h"
+#include "suggest/policyimpl/dictionary/utils/mmapped_buffer.h"
 
 namespace latinime {
 
@@ -32,16 +34,26 @@
                     const int size, const bool isUpdatable);
 
     static DictionaryStructureWithBufferPolicy::StructurePolicyPtr
-            newPolicyForOnMemoryDict(const int formatVersion,
-                    const std::vector<int> &locale,
+            newPolicyForOnMemoryDict(const int formatVersion, const std::vector<int> &locale,
                     const DictionaryHeaderStructurePolicy::AttributeMap *const attributeMap);
 
  private:
     DISALLOW_IMPLICIT_CONSTRUCTORS(DictionaryStructureWithBufferPolicyFactory);
 
+    template<class DictConstants, class DictBuffers, class DictBuffersPtr, class StructurePolicy>
+    static DictionaryStructureWithBufferPolicy::StructurePolicyPtr
+            newPolicyForOnMemoryV4Dict(const FormatUtils::FORMAT_VERSION formatVersion,
+                    const std::vector<int> &locale,
+                    const DictionaryHeaderStructurePolicy::AttributeMap *const attributeMap);
+
     static DictionaryStructureWithBufferPolicy::StructurePolicyPtr
             newPolicyForDirectoryDict(const char *const path, const bool isUpdatable);
 
+    template<class DictConstants, class DictBuffers, class DictBuffersPtr, class StructurePolicy>
+    static DictionaryStructureWithBufferPolicy::StructurePolicyPtr newPolicyForV4Dict(
+            const char *const headerFilePath, const FormatUtils::FORMAT_VERSION formatVersion,
+                    MmappedBuffer::MmappedBufferPtr &&mmappedBuffer);
+
     static DictionaryStructureWithBufferPolicy::StructurePolicyPtr
             newPolicyForFileDict(const char *const path, const int bufOffset, const int size);
 
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_gc_event_listeners.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_gc_event_listeners.cpp
index 8f42df6..028e9ec 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_gc_event_listeners.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_gc_event_listeners.cpp
@@ -29,10 +29,10 @@
     // PtNode is useless when the PtNode is not a terminal and doesn't have any not useless
     // children.
     bool isUselessPtNode = !ptNodeParams->isTerminal();
-    if (ptNodeParams->isTerminal()) {
+    if (ptNodeParams->isTerminal() && !ptNodeParams->representsNonWordInfo()) {
         bool needsToKeepPtNode = true;
-        if (!mPtNodeWriter->updatePtNodeProbabilityAndGetNeedsToKeepPtNodeAfterGC(ptNodeParams,
-                &needsToKeepPtNode)) {
+        if (!mPtNodeWriter->updatePtNodeProbabilityAndGetNeedsToKeepPtNodeAfterGC(
+                ptNodeParams, &needsToKeepPtNode)) {
             AKLOGE("Cannot update PtNode probability or get needs to keep PtNode after GC.");
             return false;
         }
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_params.h b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_params.h
index bef401f..5704c2e 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_params.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/pt_common/pt_node_params.h
@@ -160,7 +160,8 @@
     }
 
     AK_FORCE_INLINE bool representsNonWordInfo() const {
-        return getCodePointCount() > 0 && CharUtils::isInUnicodeSpace(getCodePoints()[0]);
+        return getCodePointCount() > 0 && CharUtils::isInUnicodeSpace(getCodePoints()[0])
+                && isNotAWord();
     }
 
     // Parent node position
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.cpp
index 56f19db..d539227 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.cpp
@@ -38,8 +38,6 @@
     int level = 0;
     int count = 0;
     if (mHasHistoricalInfo) {
-        probability = bigramListBuffer->readUintAndAdvancePosition(
-                Ver4DictConstants::PROBABILITY_SIZE, bigramEntryPos);
         timestamp = bigramListBuffer->readUintAndAdvancePosition(
                 Ver4DictConstants::TIME_STAMP_FIELD_SIZE, bigramEntryPos);
         level = bigramListBuffer->readUintAndAdvancePosition(
@@ -47,7 +45,8 @@
         count = bigramListBuffer->readUintAndAdvancePosition(
                 Ver4DictConstants::WORD_COUNT_FIELD_SIZE, bigramEntryPos);
     } else {
-        probability = bigramFlags & Ver4DictConstants::BIGRAM_PROBABILITY_MASK;
+        probability = bigramListBuffer->readUintAndAdvancePosition(
+                Ver4DictConstants::PROBABILITY_SIZE, bigramEntryPos);
     }
     const int encodedTargetTerminalId = bigramListBuffer->readUintAndAdvancePosition(
             Ver4DictConstants::BIGRAM_TARGET_TERMINAL_ID_FIELD_SIZE, bigramEntryPos);
@@ -65,21 +64,13 @@
 bool BigramDictContent::writeBigramEntryAndAdvancePosition(
         const BigramEntry *const bigramEntryToWrite, int *const entryWritingPos) {
     BufferWithExtendableBuffer *const bigramListBuffer = getWritableContentBuffer();
-    const int bigramFlags = createAndGetBigramFlags(
-            mHasHistoricalInfo ? 0 : bigramEntryToWrite->getProbability(),
-            bigramEntryToWrite->hasNext());
+    const int bigramFlags = createAndGetBigramFlags(bigramEntryToWrite->hasNext());
     if (!bigramListBuffer->writeUintAndAdvancePosition(bigramFlags,
             Ver4DictConstants::BIGRAM_FLAGS_FIELD_SIZE, entryWritingPos)) {
         AKLOGE("Cannot write bigram flags. pos: %d, flags: %x", *entryWritingPos, bigramFlags);
         return false;
     }
     if (mHasHistoricalInfo) {
-        if (!bigramListBuffer->writeUintAndAdvancePosition(bigramEntryToWrite->getProbability(),
-                Ver4DictConstants::PROBABILITY_SIZE, entryWritingPos)) {
-            AKLOGE("Cannot write bigram probability. pos: %d, probability: %d", *entryWritingPos,
-                    bigramEntryToWrite->getProbability());
-            return false;
-        }
         const HistoricalInfo *const historicalInfo = bigramEntryToWrite->getHistoricalInfo();
         if (!bigramListBuffer->writeUintAndAdvancePosition(historicalInfo->getTimeStamp(),
                 Ver4DictConstants::TIME_STAMP_FIELD_SIZE, entryWritingPos)) {
@@ -99,6 +90,13 @@
                     historicalInfo->getCount());
             return false;
         }
+    } else {
+        if (!bigramListBuffer->writeUintAndAdvancePosition(bigramEntryToWrite->getProbability(),
+                Ver4DictConstants::PROBABILITY_SIZE, entryWritingPos)) {
+            AKLOGE("Cannot write bigram probability. pos: %d, probability: %d", *entryWritingPos,
+                    bigramEntryToWrite->getProbability());
+            return false;
+        }
     }
     const int targetTerminalIdToWrite =
             (bigramEntryToWrite->getTargetTerminalId() == Ver4DictConstants::NOT_A_TERMINAL_ID) ?
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.h b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.h
index 944e0f9..b8bdb63 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/content/bigram_dict_content.h
@@ -95,9 +95,8 @@
  private:
     DISALLOW_COPY_AND_ASSIGN(BigramDictContent);
 
-    int createAndGetBigramFlags(const int probability, const bool hasNext) const {
-        return (probability & Ver4DictConstants::BIGRAM_PROBABILITY_MASK)
-                | (hasNext ? Ver4DictConstants::BIGRAM_HAS_NEXT_MASK : 0);
+    int createAndGetBigramFlags(const bool hasNext) const {
+        return hasNext ? Ver4DictConstants::BIGRAM_HAS_NEXT_MASK : 0;
     }
 
     bool runGCBigramList(const int bigramListPos,
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp
index 8373dc5..7da9e30 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_policy.cpp
@@ -115,9 +115,7 @@
         } else if (bigramProbability == NOT_A_PROBABILITY) {
             return ProbabilityUtils::backoff(unigramProbability);
         } else {
-            // bigramProbability is a bigram probability delta.
-            return ProbabilityUtils::computeProbabilityForBigram(unigramProbability,
-                    bigramProbability);
+            return bigramProbability;
         }
     }
 }
@@ -398,7 +396,7 @@
             const int probability = bigramEntry.hasHistoricalInfo() ?
                     ForgettingCurveUtils::decodeProbability(
                             bigramEntry.getHistoricalInfo(), mHeaderPolicy) :
-                    getProbability(word1Probability, bigramEntry.getProbability());
+                    bigramEntry.getProbability();
             bigrams.emplace_back(&word1, probability,
                     historicalInfo->getTimeStamp(), historicalInfo->getLevel(),
                     historicalInfo->getCount());
diff --git a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.cpp b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.cpp
index f31c502..e868ddf 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/structure/v4/ver4_patricia_trie_writing_helper.cpp
@@ -213,13 +213,16 @@
     // Delete unigrams.
     while (static_cast<int>(priorityQueue.size()) > maxUnigramCount) {
         const int ptNodePos = priorityQueue.top().getDictPos();
+        priorityQueue.pop();
         const PtNodeParams ptNodeParams =
                 ptNodeReader->fetchNodeInfoInBufferFromPtNodePos(ptNodePos);
+        if (ptNodeParams.representsNonWordInfo()) {
+            continue;
+        }
         if (!ptNodeWriter->markPtNodeAsWillBecomeNonTerminal(&ptNodeParams)) {
             AKLOGE("Cannot mark PtNode as willBecomeNonterminal. PtNode pos: %d", ptNodePos);
             return false;
         }
-        priorityQueue.pop();
     }
     return true;
 }
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.cpp
index 80970c7..105363d 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.cpp
@@ -23,6 +23,7 @@
 #include <sys/types.h>
 
 #include "suggest/policyimpl/dictionary/header/header_policy.h"
+#include "suggest/policyimpl/dictionary/structure/backward/v401/ver4_dict_buffers.h"
 #include "suggest/policyimpl/dictionary/structure/pt_common/dynamic_pt_writing_utils.h"
 #include "suggest/policyimpl/dictionary/structure/v4/ver4_dict_buffers.h"
 #include "suggest/policyimpl/dictionary/utils/buffer_with_extendable_buffer.h"
@@ -40,10 +41,16 @@
     TimeKeeper::setCurrentTime();
     const FormatUtils::FORMAT_VERSION formatVersion = FormatUtils::getFormatVersion(dictVersion);
     switch (formatVersion) {
-        case FormatUtils::VERSION_4_ONLY_FOR_TESTING:
         case FormatUtils::VERSION_4:
-            return createEmptyV4DictFile(filePath, localeAsCodePointVector, attributeMap,
-                    formatVersion);
+            return createEmptyV4DictFile<backward::v401::Ver4DictConstants,
+                    backward::v401::Ver4DictBuffers,
+                    backward::v401::Ver4DictBuffers::Ver4DictBuffersPtr>(
+                            filePath, localeAsCodePointVector, attributeMap, formatVersion);
+        case FormatUtils::VERSION_4_ONLY_FOR_TESTING:
+        case FormatUtils::VERSION_4_DEV:
+            return createEmptyV4DictFile<Ver4DictConstants, Ver4DictBuffers,
+                    Ver4DictBuffers::Ver4DictBuffersPtr>(
+                            filePath, localeAsCodePointVector, attributeMap, formatVersion);
         default:
             AKLOGE("Cannot create dictionary %s because format version %d is not supported.",
                     filePath, dictVersion);
@@ -51,14 +58,14 @@
     }
 }
 
+template<class DictConstants, class DictBuffers, class DictBuffersPtr>
 /* static */ bool DictFileWritingUtils::createEmptyV4DictFile(const char *const dirPath,
         const std::vector<int> localeAsCodePointVector,
         const DictionaryHeaderStructurePolicy::AttributeMap *const attributeMap,
         const FormatUtils::FORMAT_VERSION formatVersion) {
     HeaderPolicy headerPolicy(formatVersion, localeAsCodePointVector, attributeMap);
-    Ver4DictBuffers::Ver4DictBuffersPtr dictBuffers(
-            Ver4DictBuffers::createVer4DictBuffers(&headerPolicy,
-                    Ver4DictConstants::MAX_DICT_EXTENDED_REGION_SIZE));
+    DictBuffersPtr dictBuffers = DictBuffers::createVer4DictBuffers(&headerPolicy,
+            DictConstants::MAX_DICT_EXTENDED_REGION_SIZE);
     headerPolicy.fillInAndWriteHeaderToBuffer(true /* updatesLastDecayedTime */,
             0 /* unigramCount */, 0 /* bigramCount */,
             0 /* extendedRegionSize */, dictBuffers->getWritableHeaderBuffer());
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h b/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h
index a822989..5df5856 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/dict_file_writing_utils.h
@@ -45,6 +45,12 @@
  private:
     DISALLOW_IMPLICIT_CONSTRUCTORS(DictFileWritingUtils);
 
+    static bool createEmptyV401DictFile(const char *const filePath,
+            const std::vector<int> localeAsCodePointVector,
+            const DictionaryHeaderStructurePolicy::AttributeMap *const attributeMap,
+            const FormatUtils::FORMAT_VERSION formatVersion);
+
+    template<class DictConstants, class DictBuffers, class DictBuffersPtr>
     static bool createEmptyV4DictFile(const char *const filePath,
             const std::vector<int> localeAsCodePointVector,
             const DictionaryHeaderStructurePolicy::AttributeMap *const attributeMap,
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.cpp
index a8518cd..ba405b0 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.cpp
@@ -33,6 +33,8 @@
             return VERSION_4_ONLY_FOR_TESTING;
         case VERSION_4:
             return VERSION_4;
+        case VERSION_4_DEV:
+            return VERSION_4_DEV;
         default:
             return UNKNOWN_VERSION;
     }
@@ -62,6 +64,8 @@
                 return VERSION_4_ONLY_FOR_TESTING;
             } else if (ByteArrayUtils::readUint16(dict, 4) == VERSION_4) {
                 return VERSION_4;
+            } else if (ByteArrayUtils::readUint16(dict, 4) == VERSION_4_DEV) {
+                return VERSION_4_DEV;
             } else {
                 return UNKNOWN_VERSION;
             }
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.h b/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.h
index 20dfb9d..c47f30c 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.h
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/format_utils.h
@@ -33,6 +33,7 @@
         VERSION_2 = 2,
         VERSION_4_ONLY_FOR_TESTING = 399,
         VERSION_4 = 401,
+        VERSION_4_DEV = 402,
         UNKNOWN_VERSION = -1
     };
 
diff --git a/native/jni/src/suggest/policyimpl/dictionary/utils/mmapped_buffer.cpp b/native/jni/src/suggest/policyimpl/dictionary/utils/mmapped_buffer.cpp
index d3e0c23..4a126ff 100644
--- a/native/jni/src/suggest/policyimpl/dictionary/utils/mmapped_buffer.cpp
+++ b/native/jni/src/suggest/policyimpl/dictionary/utils/mmapped_buffer.cpp
@@ -33,7 +33,7 @@
     const int mmapFd = open(path, O_RDONLY);
     if (mmapFd < 0) {
         AKLOGE("DICT: Can't open the source. path=%s errno=%d", path, errno);
-        return MmappedBufferPtr(nullptr);
+        return nullptr;
     }
     const int pagesize = sysconf(_SC_PAGESIZE);
     const int offset = bufferOffset % pagesize;
@@ -45,13 +45,13 @@
     if (mmappedBuffer == MAP_FAILED) {
         AKLOGE("DICT: Can't mmap dictionary. errno=%d", errno);
         close(mmapFd);
-        return MmappedBufferPtr(nullptr);
+        return nullptr;
     }
     uint8_t *const buffer = static_cast<uint8_t *>(mmappedBuffer) + offset;
     if (!buffer) {
         AKLOGE("DICT: buffer is null");
         close(mmapFd);
-        return MmappedBufferPtr(nullptr);
+        return nullptr;
     }
     return MmappedBufferPtr(new MmappedBuffer(buffer, bufferSize, mmappedBuffer, alignedSize,
             mmapFd, isUpdatable));
@@ -61,7 +61,7 @@
         const char *const path, const bool isUpdatable) {
     const int fileSize = FileUtils::getFileSize(path);
     if (fileSize == -1) {
-        return MmappedBufferPtr(nullptr);
+        return nullptr;
     } else if (fileSize == 0) {
         return MmappedBufferPtr(new MmappedBuffer(isUpdatable));
     } else {
@@ -76,7 +76,7 @@
     const int filePathLength = snprintf(filePath, filePathBufferSize, "%s%s", dirPath,
             fileName);
     if (filePathLength >= filePathBufferSize) {
-        return MmappedBufferPtr(nullptr);
+        return nullptr;
     }
     return openBuffer(filePath, isUpdatable);
 }
diff --git a/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp b/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp
index fa9600c..3fc566e 100644
--- a/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp
+++ b/native/jni/src/suggest/policyimpl/typing/scoring_params.cpp
@@ -37,6 +37,7 @@
 const float ScoringParams::PROXIMITY_COST = 0.0694f;
 const float ScoringParams::FIRST_CHAR_PROXIMITY_COST = 0.072f;
 const float ScoringParams::FIRST_PROXIMITY_COST = 0.07788f;
+const float ScoringParams::INTENTIONAL_OMISSION_COST = 0.1f;
 const float ScoringParams::OMISSION_COST = 0.467f;
 const float ScoringParams::OMISSION_COST_SAME_CHAR = 0.345f;
 const float ScoringParams::OMISSION_COST_FIRST_CHAR = 0.5256f;
diff --git a/native/jni/src/suggest/policyimpl/typing/scoring_params.h b/native/jni/src/suggest/policyimpl/typing/scoring_params.h
index b669620..b12de6d 100644
--- a/native/jni/src/suggest/policyimpl/typing/scoring_params.h
+++ b/native/jni/src/suggest/policyimpl/typing/scoring_params.h
@@ -44,6 +44,7 @@
     static const float PROXIMITY_COST;
     static const float FIRST_CHAR_PROXIMITY_COST;
     static const float FIRST_PROXIMITY_COST;
+    static const float INTENTIONAL_OMISSION_COST;
     static const float OMISSION_COST;
     static const float OMISSION_COST_SAME_CHAR;
     static const float OMISSION_COST_FIRST_CHAR;
diff --git a/native/jni/src/suggest/policyimpl/typing/typing_weighting.h b/native/jni/src/suggest/policyimpl/typing/typing_weighting.h
index 0ba439b..8407717 100644
--- a/native/jni/src/suggest/policyimpl/typing/typing_weighting.h
+++ b/native/jni/src/suggest/policyimpl/typing/typing_weighting.h
@@ -54,12 +54,15 @@
 
     float getOmissionCost(const DicNode *const parentDicNode, const DicNode *const dicNode) const {
         const bool isZeroCostOmission = parentDicNode->isZeroCostOmission();
+        const bool isIntentionalOmission = parentDicNode->canBeIntentionalOmission();
         const bool sameCodePoint = dicNode->isSameNodeCodePoint(parentDicNode);
         // If the traversal omitted the first letter then the dicNode should now be on the second.
         const bool isFirstLetterOmission = dicNode->getNodeCodePointCount() == 2;
         float cost = 0.0f;
         if (isZeroCostOmission) {
             cost = 0.0f;
+        } else if (isIntentionalOmission) {
+            cost = ScoringParams::INTENTIONAL_OMISSION_COST;
         } else if (isFirstLetterOmission) {
             cost = ScoringParams::OMISSION_COST_FIRST_CHAR;
         } else {
diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
index 35d9a4e..90b90ff 100644
--- a/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
+++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryDecayingTests.java
@@ -46,6 +46,8 @@
     private static final String TEST_DICT_FILE_EXTENSION = ".testDict";
     private static final String TEST_LOCALE = "test";
     private static final int DUMMY_PROBABILITY = 0;
+    private static final int[] DICT_FORMAT_VERSIONS =
+            new int[] { FormatSpec.VERSION4, FormatSpec.VERSION4_DEV };
 
     private int mCurrentTime = 0;
 
@@ -94,7 +96,8 @@
     private File createEmptyDictionaryAndGetFile(final String dictId,
             final int formatVersion) throws IOException {
         if (formatVersion == FormatSpec.VERSION4
-                || formatVersion == FormatSpec.VERSION4_ONLY_FOR_TESTING) {
+                || formatVersion == FormatSpec.VERSION4_ONLY_FOR_TESTING
+                || formatVersion == FormatSpec.VERSION4_DEV) {
             return createEmptyVer4DictionaryAndGetFile(dictId, formatVersion);
         } else {
             throw new IOException("Dictionary format version " + formatVersion
@@ -120,7 +123,7 @@
             return file;
         } else {
             throw new IOException("Empty dictionary " + file.getAbsolutePath()
-                    + " cannot be created.");
+                    + " cannot be created. Foramt version: " + formatVersion);
         }
     }
 
@@ -133,7 +136,9 @@
     }
 
     public void testReadDictInJavaSide() {
-        testReadDictInJavaSide(FormatSpec.VERSION4);
+        for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+            testReadDictInJavaSide(formatVersion);
+        }
     }
 
     private void testReadDictInJavaSide(final int formatVersion) {
@@ -178,10 +183,6 @@
     }
 
     public void testControlCurrentTime() {
-        testControlCurrentTime(FormatSpec.VERSION4);
-    }
-
-    private void testControlCurrentTime(final int formatVersion) {
         final int TEST_COUNT = 1000;
         final long seed = System.currentTimeMillis();
         final Random random = new Random(seed);
@@ -197,7 +198,9 @@
     }
 
     public void testAddValidAndInvalidWords() {
-        testAddValidAndInvalidWords(FormatSpec.VERSION4);
+        for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+            testAddValidAndInvalidWords(formatVersion);
+        }
     }
 
     private void testAddValidAndInvalidWords(final int formatVersion) {
@@ -240,7 +243,9 @@
     }
 
     public void testDecayingProbability() {
-        testDecayingProbability(FormatSpec.VERSION4);
+        for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+            testDecayingProbability(formatVersion);
+        }
     }
 
     private void testDecayingProbability(final int formatVersion) {
@@ -295,7 +300,9 @@
     }
 
     public void testAddManyUnigramsToDecayingDict() {
-        testAddManyUnigramsToDecayingDict(FormatSpec.VERSION4);
+        for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+            testAddManyUnigramsToDecayingDict(formatVersion);
+        }
     }
 
     private void testAddManyUnigramsToDecayingDict(final int formatVersion) {
@@ -354,7 +361,9 @@
     }
 
     public void testOverflowUnigrams() {
-        testOverflowUnigrams(FormatSpec.VERSION4);
+        for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+            testOverflowUnigrams(formatVersion);
+        }
     }
 
     private void testOverflowUnigrams(final int formatVersion) {
@@ -413,7 +422,9 @@
     }
 
     public void testAddManyBigramsToDecayingDict() {
-        testAddManyBigramsToDecayingDict(FormatSpec.VERSION4);
+        for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+            testAddManyBigramsToDecayingDict(formatVersion);
+        }
     }
 
     private void testAddManyBigramsToDecayingDict(final int formatVersion) {
@@ -487,7 +498,9 @@
     }
 
     public void testOverflowBigrams() {
-        testOverflowBigrams(FormatSpec.VERSION4);
+        for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+            testOverflowBigrams(formatVersion);
+        }
     }
 
     private void testOverflowBigrams(final int formatVersion) {
@@ -566,7 +579,9 @@
     }
 
     public void testDictMigration() {
-        testDictMigration(FormatSpec.VERSION4_ONLY_FOR_TESTING, FormatSpec.VERSION4);
+        for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+            testDictMigration(FormatSpec.VERSION4_ONLY_FOR_TESTING, formatVersion);
+        }
     }
 
     private void testDictMigration(final int fromFormatVersion, final int toFormatVersion) {
diff --git a/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java b/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java
index 770e76e..c87c2a9 100644
--- a/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java
+++ b/tests/src/com/android/inputmethod/latin/BinaryDictionaryTests.java
@@ -43,11 +43,18 @@
 public class BinaryDictionaryTests extends AndroidTestCase {
     private static final String TEST_DICT_FILE_EXTENSION = ".testDict";
     private static final String TEST_LOCALE = "test";
+    private static final int[] DICT_FORMAT_VERSIONS =
+            new int[] { FormatSpec.VERSION4, FormatSpec.VERSION4_DEV };
+
+    private static boolean canCheckBigramProbability(final int formatVersion) {
+        return formatVersion >= FormatSpec.VERSION4_DEV;
+    }
 
     private File createEmptyDictionaryAndGetFile(final String dictId,
             final int formatVersion) throws IOException {
         if (formatVersion == FormatSpec.VERSION4
-                || formatVersion == FormatSpec.VERSION4_ONLY_FOR_TESTING) {
+                || formatVersion == FormatSpec.VERSION4_ONLY_FOR_TESTING
+                || formatVersion == FormatSpec.VERSION4_DEV) {
             return createEmptyVer4DictionaryAndGetFile(dictId, formatVersion);
         } else {
             throw new IOException("Dictionary format version " + formatVersion
@@ -67,12 +74,14 @@
             return file;
         } else {
             throw new IOException("Empty dictionary " + file.getAbsolutePath()
-                    + " cannot be created.");
+                    + " cannot be created. Format version: " + formatVersion);
         }
     }
 
     public void testIsValidDictionary() {
-        testIsValidDictionary(FormatSpec.VERSION4);
+        for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+            testIsValidDictionary(formatVersion);
+        }
     }
 
     private void testIsValidDictionary(final int formatVersion) {
@@ -100,7 +109,9 @@
     }
 
     public void testConstructingDictionaryOnMemory() {
-        testConstructingDictionaryOnMemory(FormatSpec.VERSION4);
+        for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+            testConstructingDictionaryOnMemory(formatVersion);
+        }
     }
 
     private void testConstructingDictionaryOnMemory(final int formatVersion) {
@@ -131,7 +142,9 @@
     }
 
     public void testAddTooLongWord() {
-        testAddTooLongWord(FormatSpec.VERSION4);
+        for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+            testAddTooLongWord(formatVersion);
+        }
     }
 
     private void testAddTooLongWord(final int formatVersion) {
@@ -190,7 +203,9 @@
     }
 
     public void testAddUnigramWord() {
-        testAddUnigramWord(FormatSpec.VERSION4);
+        for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+            testAddUnigramWord(formatVersion);
+        }
     }
 
     private void testAddUnigramWord(final int formatVersion) {
@@ -232,7 +247,9 @@
     }
 
     public void testRandomlyAddUnigramWord() {
-        testRandomlyAddUnigramWord(FormatSpec.VERSION4);
+        for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+            testRandomlyAddUnigramWord(formatVersion);
+        }
     }
 
     private void testRandomlyAddUnigramWord(final int formatVersion) {
@@ -268,7 +285,9 @@
     }
 
     public void testAddBigramWords() {
-        testAddBigramWords(FormatSpec.VERSION4);
+        for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+            testAddBigramWords(formatVersion);
+        }
     }
 
     private void testAddBigramWords(final int formatVersion) {
@@ -283,8 +302,8 @@
                 Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
 
         final int unigramProbability = 100;
-        final int bigramProbability = 10;
-        final int updatedBigramProbability = 15;
+        final int bigramProbability = 150;
+        final int updatedBigramProbability = 200;
         addUnigramWord(binaryDictionary, "aaa", unigramProbability);
         addUnigramWord(binaryDictionary, "abb", unigramProbability);
         addUnigramWord(binaryDictionary, "bcc", unigramProbability);
@@ -293,25 +312,26 @@
         addBigramWords(binaryDictionary, "abb", "aaa", bigramProbability);
         addBigramWords(binaryDictionary, "abb", "bcc", bigramProbability);
 
-        final int probability = binaryDictionary.calculateProbability(unigramProbability,
-                bigramProbability);
-        assertEquals(true, binaryDictionary.isValidBigram("aaa", "abb"));
-        assertEquals(true, binaryDictionary.isValidBigram("aaa", "bcc"));
-        assertEquals(true, binaryDictionary.isValidBigram("abb", "aaa"));
-        assertEquals(true, binaryDictionary.isValidBigram("abb", "bcc"));
-        assertEquals(probability, binaryDictionary.getBigramProbability("aaa", "abb"));
-        assertEquals(probability, binaryDictionary.getBigramProbability("aaa", "bcc"));
-        assertEquals(probability, binaryDictionary.getBigramProbability("abb", "aaa"));
-        assertEquals(probability, binaryDictionary.getBigramProbability("abb", "bcc"));
+        assertTrue(binaryDictionary.isValidBigram("aaa", "abb"));
+        assertTrue(binaryDictionary.isValidBigram("aaa", "bcc"));
+        assertTrue(binaryDictionary.isValidBigram("abb", "aaa"));
+        assertTrue(binaryDictionary.isValidBigram("abb", "bcc"));
+        if (canCheckBigramProbability(formatVersion)) {
+            assertEquals(bigramProbability, binaryDictionary.getBigramProbability("aaa", "abb"));
+            assertEquals(bigramProbability, binaryDictionary.getBigramProbability("aaa", "bcc"));
+            assertEquals(bigramProbability, binaryDictionary.getBigramProbability("abb", "aaa"));
+            assertEquals(bigramProbability, binaryDictionary.getBigramProbability("abb", "bcc"));
+        }
 
         addBigramWords(binaryDictionary, "aaa", "abb", updatedBigramProbability);
-        final int updatedProbability = binaryDictionary.calculateProbability(unigramProbability,
-                updatedBigramProbability);
-        assertEquals(updatedProbability, binaryDictionary.getBigramProbability("aaa", "abb"));
+        if (canCheckBigramProbability(formatVersion)) {
+            assertEquals(updatedBigramProbability,
+                    binaryDictionary.getBigramProbability("aaa", "abb"));
+        }
 
-        assertEquals(false, binaryDictionary.isValidBigram("bcc", "aaa"));
-        assertEquals(false, binaryDictionary.isValidBigram("bcc", "bbc"));
-        assertEquals(false, binaryDictionary.isValidBigram("aaa", "aaa"));
+        assertFalse(binaryDictionary.isValidBigram("bcc", "aaa"));
+        assertFalse(binaryDictionary.isValidBigram("bcc", "bbc"));
+        assertFalse(binaryDictionary.isValidBigram("aaa", "aaa"));
         assertEquals(Dictionary.NOT_A_PROBABILITY,
                 binaryDictionary.getBigramProbability("bcc", "aaa"));
         assertEquals(Dictionary.NOT_A_PROBABILITY,
@@ -326,17 +346,26 @@
         addUnigramWord(binaryDictionary, "fgh", unigramProbability);
         addUnigramWord(binaryDictionary, "abc", unigramProbability);
         addUnigramWord(binaryDictionary, "f", unigramProbability);
-        assertEquals(probability, binaryDictionary.getBigramProbability("abcde", "fghij"));
+
+        if (canCheckBigramProbability(formatVersion)) {
+            assertEquals(bigramProbability,
+                    binaryDictionary.getBigramProbability("abcde", "fghij"));
+        }
         assertEquals(Dictionary.NOT_A_PROBABILITY,
                 binaryDictionary.getBigramProbability("abcde", "fgh"));
         addBigramWords(binaryDictionary, "abcde", "fghij", updatedBigramProbability);
-        assertEquals(updatedProbability, binaryDictionary.getBigramProbability("abcde", "fghij"));
+        if (canCheckBigramProbability(formatVersion)) {
+            assertEquals(updatedBigramProbability,
+                    binaryDictionary.getBigramProbability("abcde", "fghij"));
+        }
 
         dictFile.delete();
     }
 
     public void testRandomlyAddBigramWords() {
-        testRandomlyAddBigramWords(FormatSpec.VERSION4);
+        for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+            testRandomlyAddBigramWords(formatVersion);
+        }
     }
 
     private void testRandomlyAddBigramWords(final int formatVersion) {
@@ -379,25 +408,30 @@
             }
             final Pair<String, String> bigram = new Pair<String, String>(word0, word1);
             bigramWords.add(bigram);
-            final int bigramProbability = random.nextInt(0xF);
+            final int unigramProbability = unigramProbabilities.get(word1);
+            final int bigramProbability =
+                    unigramProbability + random.nextInt(0xFF - unigramProbability);
             bigramProbabilities.put(bigram, bigramProbability);
             addBigramWords(binaryDictionary, word0, word1, bigramProbability);
         }
 
         for (final Pair<String, String> bigram : bigramWords) {
-            final int unigramProbability = unigramProbabilities.get(bigram.second);
             final int bigramProbability = bigramProbabilities.get(bigram);
-            final int probability = binaryDictionary.calculateProbability(unigramProbability,
-                    bigramProbability);
-            assertEquals(probability,
-                    binaryDictionary.getBigramProbability(bigram.first, bigram.second));
+            assertEquals(bigramProbability != Dictionary.NOT_A_PROBABILITY,
+                    binaryDictionary.isValidBigram(bigram.first, bigram.second));
+            if (canCheckBigramProbability(formatVersion)) {
+                assertEquals(bigramProbability,
+                        binaryDictionary.getBigramProbability(bigram.first, bigram.second));
+            }
         }
 
         dictFile.delete();
     }
 
     public void testRemoveBigramWords() {
-        testRemoveBigramWords(FormatSpec.VERSION4);
+        for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+            testRemoveBigramWords(formatVersion);
+        }
     }
 
     private void testRemoveBigramWords(final int formatVersion) {
@@ -411,7 +445,7 @@
                 0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
                 Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
         final int unigramProbability = 100;
-        final int bigramProbability = 10;
+        final int bigramProbability = 150;
         addUnigramWord(binaryDictionary, "aaa", unigramProbability);
         addUnigramWord(binaryDictionary, "abb", unigramProbability);
         addUnigramWord(binaryDictionary, "bcc", unigramProbability);
@@ -420,23 +454,23 @@
         addBigramWords(binaryDictionary, "abb", "aaa", bigramProbability);
         addBigramWords(binaryDictionary, "abb", "bcc", bigramProbability);
 
-        assertEquals(true, binaryDictionary.isValidBigram("aaa", "abb"));
-        assertEquals(true, binaryDictionary.isValidBigram("aaa", "bcc"));
-        assertEquals(true, binaryDictionary.isValidBigram("abb", "aaa"));
-        assertEquals(true, binaryDictionary.isValidBigram("abb", "bcc"));
+        assertTrue(binaryDictionary.isValidBigram("aaa", "abb"));
+        assertTrue(binaryDictionary.isValidBigram("aaa", "bcc"));
+        assertTrue(binaryDictionary.isValidBigram("abb", "aaa"));
+        assertTrue(binaryDictionary.isValidBigram("abb", "bcc"));
 
         binaryDictionary.removeBigramWords("aaa", "abb");
-        assertEquals(false, binaryDictionary.isValidBigram("aaa", "abb"));
+        assertFalse(binaryDictionary.isValidBigram("aaa", "abb"));
         addBigramWords(binaryDictionary, "aaa", "abb", bigramProbability);
-        assertEquals(true, binaryDictionary.isValidBigram("aaa", "abb"));
+        assertTrue(binaryDictionary.isValidBigram("aaa", "abb"));
 
 
         binaryDictionary.removeBigramWords("aaa", "bcc");
-        assertEquals(false, binaryDictionary.isValidBigram("aaa", "bcc"));
+        assertFalse(binaryDictionary.isValidBigram("aaa", "bcc"));
         binaryDictionary.removeBigramWords("abb", "aaa");
-        assertEquals(false, binaryDictionary.isValidBigram("abb", "aaa"));
+        assertFalse(binaryDictionary.isValidBigram("abb", "aaa"));
         binaryDictionary.removeBigramWords("abb", "bcc");
-        assertEquals(false, binaryDictionary.isValidBigram("abb", "bcc"));
+        assertFalse(binaryDictionary.isValidBigram("abb", "bcc"));
 
         binaryDictionary.removeBigramWords("aaa", "abb");
         // Test remove non-existing bigram operation.
@@ -447,7 +481,9 @@
     }
 
     public void testFlushDictionary() {
-        testFlushDictionary(FormatSpec.VERSION4);
+        for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+            testFlushDictionary(formatVersion);
+        }
     }
 
     private void testFlushDictionary(final int formatVersion) {
@@ -499,7 +535,9 @@
     }
 
     public void testFlushWithGCDictionary() {
-        testFlushWithGCDictionary(FormatSpec.VERSION4);
+        for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+            testFlushWithGCDictionary(formatVersion);
+        }
     }
 
     private void testFlushWithGCDictionary(final int formatVersion) {
@@ -514,7 +552,7 @@
                 Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
 
         final int unigramProbability = 100;
-        final int bigramProbability = 10;
+        final int bigramProbability = 150;
         addUnigramWord(binaryDictionary, "aaa", unigramProbability);
         addUnigramWord(binaryDictionary, "abb", unigramProbability);
         addUnigramWord(binaryDictionary, "bcc", unigramProbability);
@@ -528,18 +566,18 @@
         binaryDictionary = new BinaryDictionary(dictFile.getAbsolutePath(),
                 0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
                 Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
-        final int probability = binaryDictionary.calculateProbability(unigramProbability,
-                bigramProbability);
         assertEquals(unigramProbability, binaryDictionary.getFrequency("aaa"));
         assertEquals(unigramProbability, binaryDictionary.getFrequency("abb"));
         assertEquals(unigramProbability, binaryDictionary.getFrequency("bcc"));
-        assertEquals(probability, binaryDictionary.getBigramProbability("aaa", "abb"));
-        assertEquals(probability, binaryDictionary.getBigramProbability("aaa", "bcc"));
-        assertEquals(probability, binaryDictionary.getBigramProbability("abb", "aaa"));
-        assertEquals(probability, binaryDictionary.getBigramProbability("abb", "bcc"));
-        assertEquals(false, binaryDictionary.isValidBigram("bcc", "aaa"));
-        assertEquals(false, binaryDictionary.isValidBigram("bcc", "bbc"));
-        assertEquals(false, binaryDictionary.isValidBigram("aaa", "aaa"));
+        if (canCheckBigramProbability(formatVersion)) {
+            assertEquals(bigramProbability, binaryDictionary.getBigramProbability("aaa", "abb"));
+            assertEquals(bigramProbability, binaryDictionary.getBigramProbability("aaa", "bcc"));
+            assertEquals(bigramProbability, binaryDictionary.getBigramProbability("abb", "aaa"));
+            assertEquals(bigramProbability, binaryDictionary.getBigramProbability("abb", "bcc"));
+        }
+        assertFalse(binaryDictionary.isValidBigram("bcc", "aaa"));
+        assertFalse(binaryDictionary.isValidBigram("bcc", "bbc"));
+        assertFalse(binaryDictionary.isValidBigram("aaa", "aaa"));
         binaryDictionary.flushWithGC();
         binaryDictionary.close();
 
@@ -547,7 +585,9 @@
     }
 
     public void testAddBigramWordsAndFlashWithGC() {
-        testAddBigramWordsAndFlashWithGC(FormatSpec.VERSION4);
+        for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+            testAddBigramWordsAndFlashWithGC(formatVersion);
+        }
     }
 
     // TODO: Evaluate performance of GC
@@ -592,7 +632,9 @@
             }
             final Pair<String, String> bigram = new Pair<String, String>(word0, word1);
             bigramWords.add(bigram);
-            final int bigramProbability = random.nextInt(0xF);
+            final int unigramProbability = unigramProbabilities.get(word1);
+            final int bigramProbability =
+                    unigramProbability + random.nextInt(0xFF - unigramProbability);
             bigramProbabilities.put(bigram, bigramProbability);
             addBigramWords(binaryDictionary, word0, word1, bigramProbability);
         }
@@ -603,20 +645,24 @@
                 0 /* offset */, dictFile.length(), true /* useFullEditDistance */,
                 Locale.getDefault(), TEST_LOCALE, true /* isUpdatable */);
 
+
         for (final Pair<String, String> bigram : bigramWords) {
-            final int unigramProbability = unigramProbabilities.get(bigram.second);
             final int bigramProbability = bigramProbabilities.get(bigram);
-            final int probability = binaryDictionary.calculateProbability(unigramProbability,
-                    bigramProbability);
-            assertEquals(probability,
-                    binaryDictionary.getBigramProbability(bigram.first, bigram.second));
+            assertEquals(bigramProbability != Dictionary.NOT_A_PROBABILITY,
+                    binaryDictionary.isValidBigram(bigram.first, bigram.second));
+            if (canCheckBigramProbability(formatVersion)) {
+                assertEquals(bigramProbability,
+                        binaryDictionary.getBigramProbability(bigram.first, bigram.second));
+            }
         }
 
         dictFile.delete();
     }
 
     public void testRandomOperationsAndFlashWithGC() {
-        testRandomOperationsAndFlashWithGC(FormatSpec.VERSION4);
+        for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+            testRandomOperationsAndFlashWithGC(formatVersion);
+        }
     }
 
     private void testRandomOperationsAndFlashWithGC(final int formatVersion) {
@@ -682,7 +728,9 @@
                     if (TextUtils.equals(word0, word1)) {
                         continue;
                     }
-                    final int bigramProbability = random.nextInt(0xF);
+                    final int unigramProbability = unigramProbabilities.get(word1);
+                    final int bigramProbability =
+                            unigramProbability + random.nextInt(0xFF - unigramProbability);
                     final Pair<String, String> bigram = new Pair<String, String>(word0, word1);
                     bigramWords.add(bigram);
                     bigramProbabilities.put(bigram, bigramProbability);
@@ -707,17 +755,20 @@
             // Test whether the all bigram operations are collectlly handled.
             for (int i = 0; i < bigramWords.size(); i++) {
                 final Pair<String, String> bigram = bigramWords.get(i);
-                final int unigramProbability = unigramProbabilities.get(bigram.second);
                 final int probability;
                 if (bigramProbabilities.containsKey(bigram)) {
                     final int bigramProbability = bigramProbabilities.get(bigram);
-                    probability = binaryDictionary.calculateProbability(unigramProbability,
-                            bigramProbability);
+                    probability = bigramProbability;
                 } else {
                     probability = Dictionary.NOT_A_PROBABILITY;
                 }
-                assertEquals(probability,
-                        binaryDictionary.getBigramProbability(bigram.first, bigram.second));
+
+                if (canCheckBigramProbability(formatVersion)) {
+                    assertEquals(probability,
+                            binaryDictionary.getBigramProbability(bigram.first, bigram.second));
+                }
+                assertEquals(probability != Dictionary.NOT_A_PROBABILITY,
+                        binaryDictionary.isValidBigram(bigram.first, bigram.second));
             }
             binaryDictionary.flushWithGC();
             binaryDictionary.close();
@@ -727,7 +778,9 @@
     }
 
     public void testAddManyUnigramsAndFlushWithGC() {
-        testAddManyUnigramsAndFlushWithGC(FormatSpec.VERSION4);
+        for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+            testAddManyUnigramsAndFlushWithGC(formatVersion);
+        }
     }
 
     private void testAddManyUnigramsAndFlushWithGC(final int formatVersion) {
@@ -775,7 +828,9 @@
     }
 
     public void testUnigramAndBigramCount() {
-        testUnigramAndBigramCount(FormatSpec.VERSION4);
+        for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+            testUnigramAndBigramCount(formatVersion);
+        }
     }
 
     private void testUnigramAndBigramCount(final int formatVersion) {
@@ -834,7 +889,9 @@
     }
 
     public void testAddMultipleDictionaryEntries() {
-        testAddMultipleDictionaryEntries(FormatSpec.VERSION4);
+        for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+            testAddMultipleDictionaryEntries(formatVersion);
+        }
     }
 
     private void testAddMultipleDictionaryEntries(final int formatVersion) {
@@ -861,7 +918,7 @@
         for (int i = 0; i < languageModelParams.length; i++) {
             final String word = CodePointUtils.generateWord(random, codePointSet);
             final int probability = random.nextInt(0xFF);
-            final int bigramProbability = random.nextInt(0xF);
+            final int bigramProbability = probability + random.nextInt(0xFF - probability);
             unigramProbabilities.put(word, probability);
             if (prevWord == null) {
                 languageModelParams[i] = new LanguageModelParam(word, probability,
@@ -887,16 +944,20 @@
         for (Map.Entry<Pair<String, String>, Integer> entry : bigramProbabilities.entrySet()) {
             final String word0 = entry.getKey().first;
             final String word1 = entry.getKey().second;
-            final int unigramProbability = unigramProbabilities.get(word1);
             final int bigramProbability = entry.getValue();
-            final int probability = binaryDictionary.calculateProbability(
-                    unigramProbability, bigramProbability);
-            assertEquals(probability, binaryDictionary.getBigramProbability(word0, word1));
+            assertEquals(bigramProbability != Dictionary.NOT_A_PROBABILITY,
+                    binaryDictionary.isValidBigram(word0, word1));
+            if (canCheckBigramProbability(formatVersion)) {
+                assertEquals(bigramProbability,
+                        binaryDictionary.getBigramProbability(word0, word1));
+            }
         }
     }
 
     public void testGetWordProperties() {
-        testGetWordProperties(FormatSpec.VERSION4);
+        for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+            testGetWordProperties(formatVersion);
+        }
     }
 
     private void testGetWordProperties(final int formatVersion) {
@@ -959,7 +1020,9 @@
             }
             final String word0 = words.get(word0Index);
             final String word1 = words.get(word1Index);
-            final int bigramProbability = random.nextInt(0xF);
+            final int unigramProbability = wordProbabilities.get(word1);
+            final int bigramProbability =
+                    unigramProbability + random.nextInt(0xFF - unigramProbability);
             binaryDictionary.addBigramWords(word0, word1, bigramProbability,
                     BinaryDictionary.NOT_A_VALID_TIMESTAMP);
             if (binaryDictionary.needsToRunGC(false /* mindsBlockByGC */)) {
@@ -984,18 +1047,19 @@
             for (int j = 0; j < wordProperty.mBigrams.size(); j++) {
                 final String word1 = wordProperty.mBigrams.get(j).mWord;
                 assertTrue(bigramWord1s.contains(word1));
-                final int bigramProbabilityDelta = bigramProbabilities.get(
-                        new Pair<String, String>(word0, word1));
-                final int unigramProbability = wordProbabilities.get(word1);
-                final int bigramProbablity = binaryDictionary.calculateProbability(
-                        unigramProbability, bigramProbabilityDelta);
-                assertEquals(wordProperty.mBigrams.get(j).getProbability(), bigramProbablity);
+                if (canCheckBigramProbability(formatVersion)) {
+                    final int bigramProbability = bigramProbabilities.get(
+                            new Pair<String, String>(word0, word1));
+                    assertEquals(bigramProbability, wordProperty.mBigrams.get(j).getProbability());
+                }
             }
         }
     }
 
     public void testIterateAllWords() {
-        testIterateAllWords(FormatSpec.VERSION4);
+        for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+            testIterateAllWords(formatVersion);
+        }
     }
 
     private void testIterateAllWords(final int formatVersion) {
@@ -1045,7 +1109,9 @@
             }
             final String word0 = words.get(word0Index);
             final String word1 = words.get(word1Index);
-            final int bigramProbability = random.nextInt(0xF);
+            final int unigramProbability = wordProbabilitiesToCheckLater.get(word1);
+            final int bigramProbability =
+                    unigramProbability + random.nextInt(0xFF - unigramProbability);
             binaryDictionary.addBigramWords(word0, word1, bigramProbability,
                     BinaryDictionary.NOT_A_VALID_TIMESTAMP);
             if (binaryDictionary.needsToRunGC(false /* mindsBlockByGC */)) {
@@ -1076,12 +1142,11 @@
             for (int j = 0; j < wordProperty.mBigrams.size(); j++) {
                 final String word1 = wordProperty.mBigrams.get(j).mWord;
                 assertTrue(bigramWord1s.contains(word1));
-                final int unigramProbability = wordProbabilitiesToCheckLater.get(word1);
                 final Pair<String, String> bigram = new Pair<String, String>(word0, word1);
-                final int bigramProbabilityDelta = bigramProbabilitiesToCheckLater.get(bigram);
-                final int bigramProbablity = binaryDictionary.calculateProbability(
-                        unigramProbability, bigramProbabilityDelta);
-                assertEquals(wordProperty.mBigrams.get(j).getProbability(), bigramProbablity);
+                if (canCheckBigramProbability(formatVersion)) {
+                    final int bigramProbability = bigramProbabilitiesToCheckLater.get(bigram);
+                    assertEquals(bigramProbability, wordProperty.mBigrams.get(j).getProbability());
+                }
                 bigramSet.remove(bigram);
             }
             token = result.mNextToken;
@@ -1091,7 +1156,9 @@
     }
 
     public void testAddShortcuts() {
-        testAddShortcuts(FormatSpec.VERSION4);
+        for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+            testAddShortcuts(formatVersion);
+        }
     }
 
     private void testAddShortcuts(final int formatVersion) {
@@ -1151,7 +1218,9 @@
     }
 
     public void testAddManyShortcuts() {
-        testAddManyShortcuts(FormatSpec.VERSION4);
+        for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+            testAddManyShortcuts(formatVersion);
+        }
     }
 
     private void testAddManyShortcuts(final int formatVersion) {
@@ -1227,7 +1296,9 @@
     }
 
     public void testDictMigration() {
-        testDictMigration(FormatSpec.VERSION4_ONLY_FOR_TESTING, FormatSpec.VERSION4);
+        for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+            testDictMigration(FormatSpec.VERSION4_ONLY_FOR_TESTING, formatVersion);
+        }
     }
 
     private void testDictMigration(final int fromFormatVersion, final int toFormatVersion) {
@@ -1243,7 +1314,7 @@
         final int unigramProbability = 100;
         addUnigramWord(binaryDictionary, "aaa", unigramProbability);
         addUnigramWord(binaryDictionary, "bbb", unigramProbability);
-        final int bigramProbability = 10;
+        final int bigramProbability = 150;
         addBigramWords(binaryDictionary, "aaa", "bbb", bigramProbability);
         final int shortcutProbability = 10;
         binaryDictionary.addUnigramWord("ccc", unigramProbability, "xxx", shortcutProbability,
@@ -1260,7 +1331,9 @@
         assertEquals(toFormatVersion, binaryDictionary.getFormatVersion());
         assertEquals(unigramProbability, binaryDictionary.getFrequency("aaa"));
         assertEquals(unigramProbability, binaryDictionary.getFrequency("bbb"));
-        // TODO: Add tests for bigram frequency when the implementation gets ready.
+        if (canCheckBigramProbability(toFormatVersion)) {
+            assertEquals(bigramProbability, binaryDictionary.getBigramProbability("aaa", "bbb"));
+        }
         assertTrue(binaryDictionary.isValidBigram("aaa", "bbb"));
         WordProperty wordProperty = binaryDictionary.getWordProperty("ccc");
         assertEquals(1, wordProperty.mShortcutTargets.size());
@@ -1271,7 +1344,9 @@
     }
 
     public void testLargeDictMigration() {
-        testLargeDictMigration(FormatSpec.VERSION4_ONLY_FOR_TESTING, FormatSpec.VERSION4);
+        for (final int formatVersion : DICT_FORMAT_VERSIONS) {
+            testLargeDictMigration(FormatSpec.VERSION4_ONLY_FOR_TESTING, formatVersion);
+        }
     }
 
     private void testLargeDictMigration(final int fromFormatVersion, final int toFormatVersion) {
@@ -1317,7 +1392,9 @@
             }
             final String word0 = words.get(word0Index);
             final String word1 = words.get(word1Index);
-            final int bigramProbability = random.nextInt(0xF);
+            final int unigramProbability = unigramProbabilities.get(word1);
+            final int bigramProbability =
+                    random.nextInt(0xFF - unigramProbability) + unigramProbability;
             binaryDictionary.addBigramWords(word0, word1, bigramProbability,
                     BinaryDictionary.NOT_A_VALID_TIMESTAMP);
             if (binaryDictionary.needsToRunGC(true /* mindsBlockByGC */)) {
@@ -1336,7 +1413,10 @@
                 binaryDictionary.getPropertyForTest(BinaryDictionary.UNIGRAM_COUNT_QUERY)));
 
         for (final Pair<String, String> bigram : bigrams) {
-            // TODO: Add tests for bigram frequency when the implementation gets ready.
+            if (canCheckBigramProbability(toFormatVersion)) {
+                assertEquals((int)bigramProbabilities.get(bigram),
+                        binaryDictionary.getBigramProbability(bigram.first, bigram.second));
+            }
             assertTrue(binaryDictionary.isValidBigram(bigram.first, bigram.second));
         }
         assertEquals(bigramProbabilities.size(), Integer.parseInt(