am fe2d9079: Fix a bug that the typed word with the blue underline indicator will be duplicated

* commit 'fe2d90798ea409ee39d6f63942eb01bb7eed98e3':
  Fix a bug that the typed word with the blue underline indicator will be duplicated
diff --git a/java/res/layout/sound_effect_volume_dialog.xml b/java/res/layout/sound_effect_volume_dialog.xml
new file mode 100644
index 0000000..c5b2f10
--- /dev/null
+++ b/java/res/layout/sound_effect_volume_dialog.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+**
+** Copyright 2011, 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.
+*/
+-->
+
+<LinearLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_margin="10dip">
+    <LinearLayout
+        android:orientation="horizontal"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:gravity="center_horizontal"
+        android:layout_margin="10dip">
+        <TextView android:id="@+id/sound_effect_volume_value"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textSize="20dip"/>
+    </LinearLayout>
+    <SeekBar
+        android:id="@+id/sound_effect_volume_bar"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:max="100"
+        android:layout_margin="10dip"/>
+</LinearLayout>
diff --git a/java/res/values/strings.xml b/java/res/values/strings.xml
index 8bc97f6..e00547a 100644
--- a/java/res/values/strings.xml
+++ b/java/res/values/strings.xml
@@ -342,6 +342,8 @@
 
     <!-- Title of an option for usability study mode -->
     <string name="prefs_usability_study_mode">Usability study mode</string>
-    <!-- Title of the settings for vibration duration -->
-    <string name="prefs_vibration_duration_settings">Vibration duration settings</string>
+    <!-- Title of the settings for keypress vibration duration -->
+    <string name="prefs_keypress_vibration_duration_settings">Keypress vibration duration settings</string>
+    <!-- Title of the settings for keypress sound volume -->
+    <string name="prefs_keypress_sound_volume_settings">Keypress sound volume settings</string>
 </resources>
diff --git a/java/res/xml/prefs.xml b/java/res/xml/prefs.xml
index b54df26..dcaa202 100644
--- a/java/res/xml/prefs.xml
+++ b/java/res/xml/prefs.xml
@@ -121,7 +121,10 @@
                 android:defaultValue="true" />
             <PreferenceScreen
                 android:key="pref_vibration_duration_settings"
-                android:title="@string/prefs_vibration_duration_settings"/>
+                android:title="@string/prefs_keypress_vibration_duration_settings"/>
+            <PreferenceScreen
+                android:key="pref_keypress_sound_volume"
+                android:title="@string/prefs_keypress_sound_volume_settings" />
             <!-- TODO: evaluate results and revive this option. The code
                 already supports it. -->
             <!-- <CheckBoxPreference -->
diff --git a/java/src/com/android/inputmethod/latin/LatinIME.java b/java/src/com/android/inputmethod/latin/LatinIME.java
index c05913b..2bd1735 100644
--- a/java/src/com/android/inputmethod/latin/LatinIME.java
+++ b/java/src/com/android/inputmethod/latin/LatinIME.java
@@ -28,7 +28,6 @@
 import android.inputmethodservice.InputMethodService;
 import android.media.AudioManager;
 import android.net.ConnectivityManager;
-import android.os.Build;
 import android.os.Debug;
 import android.os.Message;
 import android.os.SystemClock;
@@ -2134,16 +2133,9 @@
         }
     };
 
-    // update sound effect volume
+    // update keypress sound volume
     private void updateSoundEffectVolume() {
-        final String[] volumePerHardwareList = mResources.getStringArray(R.array.keypress_volumes);
-        final String hardwarePrefix = Build.HARDWARE + ",";
-        for (final String element : volumePerHardwareList) {
-            if (element.startsWith(hardwarePrefix)) {
-                mFxVolume = Float.parseFloat(element.substring(element.lastIndexOf(',') + 1));
-                break;
-            }
-        }
+        mFxVolume = Utils.getCurrentKeypressSoundVolume(mPrefs, mResources);
     }
 
     // update flags for silent mode
diff --git a/java/src/com/android/inputmethod/latin/Settings.java b/java/src/com/android/inputmethod/latin/Settings.java
index d9508f4..eeb0299 100644
--- a/java/src/com/android/inputmethod/latin/Settings.java
+++ b/java/src/com/android/inputmethod/latin/Settings.java
@@ -26,6 +26,7 @@
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.content.res.Resources;
+import android.media.AudioManager;
 import android.os.Bundle;
 import android.preference.CheckBoxPreference;
 import android.preference.ListPreference;
@@ -91,9 +92,11 @@
 
     public static final String PREF_USABILITY_STUDY_MODE = "usability_study_mode";
 
-    public static final String PREF_VIBRATION_DURATION_SETTINGS =
+    public static final String PREF_KEYPRESS_VIBRATION_DURATION_SETTINGS =
             "pref_vibration_duration_settings";
 
+    public static final String PREF_KEYPRESS_SOUND_VOLUME =
+            "pref_keypress_sound_volume";
     // Dialog ids
     private static final int VOICE_INPUT_CONFIRM_DIALOG = 0;
 
@@ -327,7 +330,8 @@
     }
 
     private PreferenceScreen mInputLanguageSelection;
-    private PreferenceScreen mVibrationDurationSettingsPref;
+    private PreferenceScreen mKeypressVibrationDurationSettingsPref;
+    private PreferenceScreen mKeypressSoundVolumeSettingsPref;
     private ListPreference mVoicePreference;
     private CheckBoxPreference mShowSettingsKeyPreference;
     private ListPreference mShowCorrectionSuggestionsPreference;
@@ -341,7 +345,8 @@
     private boolean mVoiceOn;
 
     private AlertDialog mDialog;
-    private TextView mVibrationSettingsTextView;
+    private TextView mKeypressVibrationDurationSettingsTextView;
+    private TextView mKeypressSoundVolumeSettingsTextView;
 
     private boolean mOkClicked = false;
     private String mVoiceModeOff;
@@ -477,19 +482,34 @@
             }
         }
 
-        mVibrationDurationSettingsPref =
-                (PreferenceScreen) findPreference(PREF_VIBRATION_DURATION_SETTINGS);
-        if (mVibrationDurationSettingsPref != null) {
-            mVibrationDurationSettingsPref.setOnPreferenceClickListener(
+        mKeypressVibrationDurationSettingsPref =
+                (PreferenceScreen) findPreference(PREF_KEYPRESS_VIBRATION_DURATION_SETTINGS);
+        if (mKeypressVibrationDurationSettingsPref != null) {
+            mKeypressVibrationDurationSettingsPref.setOnPreferenceClickListener(
                     new OnPreferenceClickListener() {
                         @Override
                         public boolean onPreferenceClick(Preference arg0) {
-                            showVibrationSettingsDialog();
+                            showKeypressVibrationDurationSettingsDialog();
                             return true;
                         }
                     });
-            updateVibrationDurationSettingsSummary(prefs, res);
+            updateKeypressVibrationDurationSettingsSummary(prefs, res);
         }
+
+        mKeypressSoundVolumeSettingsPref =
+                (PreferenceScreen) findPreference(PREF_KEYPRESS_SOUND_VOLUME);
+        if (mKeypressSoundVolumeSettingsPref != null) {
+            mKeypressSoundVolumeSettingsPref.setOnPreferenceClickListener(
+                    new OnPreferenceClickListener() {
+                        @Override
+                        public boolean onPreferenceClick(Preference arg0) {
+                            showKeypressSoundVolumeSettingDialog();
+                            return true;
+                        }
+                    });
+            updateKeypressSoundVolumeSummary(prefs, res);
+        }
+        refreshEnablingsOfKeypressSoundAndVibrationSettings(prefs, res);
     }
 
     @SuppressWarnings("unused")
@@ -537,6 +557,7 @@
         updateVoiceModeSummary();
         updateShowCorrectionSuggestionsSummary();
         updateKeyPreviewPopupDelaySummary();
+        refreshEnablingsOfKeypressSoundAndVibrationSettings(prefs, getResources());
     }
 
     @Override
@@ -637,26 +658,44 @@
         }
     }
 
-    private void updateVibrationDurationSettingsSummary(SharedPreferences sp, Resources res) {
-        if (mVibrationDurationSettingsPref != null) {
-            mVibrationDurationSettingsPref.setSummary(
+    private void refreshEnablingsOfKeypressSoundAndVibrationSettings(
+            SharedPreferences sp, Resources res) {
+        if (mKeypressVibrationDurationSettingsPref != null) {
+            final boolean hasVibrator = VibratorCompatWrapper.getInstance(this).hasVibrator();
+            final boolean vibrateOn = hasVibrator && sp.getBoolean(Settings.PREF_VIBRATE_ON,
+                    res.getBoolean(R.bool.config_default_vibration_enabled));
+            mKeypressVibrationDurationSettingsPref.setEnabled(vibrateOn);
+        }
+
+        if (mKeypressSoundVolumeSettingsPref != null) {
+            final boolean soundOn = sp.getBoolean(Settings.PREF_SOUND_ON,
+                    res.getBoolean(R.bool.config_default_sound_enabled));
+            mKeypressSoundVolumeSettingsPref.setEnabled(soundOn);
+        }
+    }
+
+    private void updateKeypressVibrationDurationSettingsSummary(
+            SharedPreferences sp, Resources res) {
+        if (mKeypressVibrationDurationSettingsPref != null) {
+            mKeypressVibrationDurationSettingsPref.setSummary(
                     Utils.getCurrentVibrationDuration(sp, res)
                             + res.getString(R.string.settings_ms));
         }
     }
 
-    private void showVibrationSettingsDialog() {
+    private void showKeypressVibrationDurationSettingsDialog() {
         final SharedPreferences sp = getPreferenceManager().getSharedPreferences();
         final Activity context = getActivityInternal();
         final Resources res = context.getResources();
         final AlertDialog.Builder builder = new AlertDialog.Builder(context);
-        builder.setTitle(R.string.prefs_vibration_duration_settings);
+        builder.setTitle(R.string.prefs_keypress_vibration_duration_settings);
         builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
             @Override
             public void onClick(DialogInterface dialog, int whichButton) {
-                final int ms = Integer.valueOf(mVibrationSettingsTextView.getText().toString());
-                sp.edit().putInt(Settings.PREF_VIBRATION_DURATION_SETTINGS, ms).apply();
-                updateVibrationDurationSettingsSummary(sp, res);
+                final int ms = Integer.valueOf(
+                        mKeypressVibrationDurationSettingsTextView.getText().toString());
+                sp.edit().putInt(Settings.PREF_KEYPRESS_VIBRATION_DURATION_SETTINGS, ms).apply();
+                updateKeypressVibrationDurationSettingsSummary(sp, res);
             }
         });
         builder.setNegativeButton(android.R.string.cancel,  new DialogInterface.OnClickListener() {
@@ -669,13 +708,13 @@
                 R.layout.vibration_settings_dialog, null);
         final int currentMs = Utils.getCurrentVibrationDuration(
                 getPreferenceManager().getSharedPreferences(), getResources());
-        mVibrationSettingsTextView = (TextView)v.findViewById(R.id.vibration_value);
+        mKeypressVibrationDurationSettingsTextView = (TextView)v.findViewById(R.id.vibration_value);
         final SeekBar sb = (SeekBar)v.findViewById(R.id.vibration_settings);
         sb.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
             @Override
             public void onProgressChanged(SeekBar arg0, int arg1, boolean arg2) {
                 final int tempMs = arg1;
-                mVibrationSettingsTextView.setText(String.valueOf(tempMs));
+                mKeypressVibrationDurationSettingsTextView.setText(String.valueOf(tempMs));
             }
 
             @Override
@@ -689,7 +728,67 @@
             }
         });
         sb.setProgress(currentMs);
-        mVibrationSettingsTextView.setText(String.valueOf(currentMs));
+        mKeypressVibrationDurationSettingsTextView.setText(String.valueOf(currentMs));
+        builder.setView(v);
+        builder.create().show();
+    }
+
+    private void updateKeypressSoundVolumeSummary(SharedPreferences sp, Resources res) {
+        if (mKeypressSoundVolumeSettingsPref != null) {
+            mKeypressSoundVolumeSettingsPref.setSummary(
+                    String.valueOf((int)(Utils.getCurrentKeypressSoundVolume(sp, res) * 100)));
+        }
+    }
+
+    private void showKeypressSoundVolumeSettingDialog() {
+        final AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+        final SharedPreferences sp = getPreferenceManager().getSharedPreferences();
+        final Activity context = getActivityInternal();
+        final Resources res = context.getResources();
+        final AlertDialog.Builder builder = new AlertDialog.Builder(context);
+        builder.setTitle(R.string.prefs_keypress_sound_volume_settings);
+        builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int whichButton) {
+                final float volume =
+                        ((float)Integer.valueOf(
+                                mKeypressSoundVolumeSettingsTextView.getText().toString())) / 100;
+                sp.edit().putFloat(Settings.PREF_KEYPRESS_SOUND_VOLUME, volume).apply();
+                updateKeypressSoundVolumeSummary(sp, res);
+            }
+        });
+        builder.setNegativeButton(android.R.string.cancel,  new DialogInterface.OnClickListener() {
+            @Override
+            public void onClick(DialogInterface dialog, int whichButton) {
+                dialog.dismiss();
+            }
+        });
+        final View v = context.getLayoutInflater().inflate(
+                R.layout.sound_effect_volume_dialog, null);
+        final int currentVolumeInt = (int)(Utils.getCurrentKeypressSoundVolume(
+                getPreferenceManager().getSharedPreferences(), getResources()) * 100);
+        mKeypressSoundVolumeSettingsTextView =
+                (TextView)v.findViewById(R.id.sound_effect_volume_value);
+        final SeekBar sb = (SeekBar)v.findViewById(R.id.sound_effect_volume_bar);
+        sb.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
+            @Override
+            public void onProgressChanged(SeekBar arg0, int arg1, boolean arg2) {
+                final int tempVolume = arg1;
+                mKeypressSoundVolumeSettingsTextView.setText(String.valueOf(tempVolume));
+            }
+
+            @Override
+            public void onStartTrackingTouch(SeekBar arg0) {
+            }
+
+            @Override
+            public void onStopTrackingTouch(SeekBar arg0) {
+                final float tempVolume = ((float)arg0.getProgress()) / 100;
+                am.playSoundEffect(AudioManager.FX_KEYPRESS_STANDARD, tempVolume);
+            }
+        });
+        sb.setProgress(currentVolumeInt);
+        mKeypressSoundVolumeSettingsTextView.setText(String.valueOf(currentVolumeInt));
         builder.setView(v);
         builder.create().show();
     }
diff --git a/java/src/com/android/inputmethod/latin/Utils.java b/java/src/com/android/inputmethod/latin/Utils.java
index f6343f1..b29ff19 100644
--- a/java/src/com/android/inputmethod/latin/Utils.java
+++ b/java/src/com/android/inputmethod/latin/Utils.java
@@ -779,7 +779,7 @@
     }
 
     public static int getCurrentVibrationDuration(SharedPreferences sp, Resources res) {
-        final int ms = sp.getInt(Settings.PREF_VIBRATION_DURATION_SETTINGS, -1);
+        final int ms = sp.getInt(Settings.PREF_KEYPRESS_VIBRATION_DURATION_SETTINGS, -1);
         if (ms >= 0) {
             return ms;
         }
@@ -794,6 +794,22 @@
         return -1;
     }
 
+    public static float getCurrentKeypressSoundVolume(SharedPreferences sp, Resources res) {
+        final float volume = sp.getFloat(Settings.PREF_KEYPRESS_SOUND_VOLUME, -1.0f);
+        if (volume >= 0) {
+            return volume;
+        }
+
+        final String[] volumePerHardwareList = res.getStringArray(R.array.keypress_volumes);
+        final String hardwarePrefix = Build.HARDWARE + ",";
+        for (final String element : volumePerHardwareList) {
+            if (element.startsWith(hardwarePrefix)) {
+                return Float.parseFloat(element.substring(element.lastIndexOf(',') + 1));
+            }
+        }
+        return -1.0f;
+    }
+
     public static boolean willAutoCorrect(SuggestedWords suggestions) {
         return !suggestions.mTypedWordValid && suggestions.mHasAutoCorrectionCandidate
                 && !suggestions.shouldBlockAutoCorrection();
diff --git a/native/src/correction.cpp b/native/src/correction.cpp
index 31493ee..0c56680 100644
--- a/native/src/correction.cpp
+++ b/native/src/correction.cpp
@@ -27,6 +27,87 @@
 
 namespace latinime {
 
+/////////////////////////////
+// edit distance funcitons //
+/////////////////////////////
+
+#if 0 /* no longer used */
+inline static int editDistance(
+        int* editDistanceTable, const unsigned short* input,
+        const int inputLength, const unsigned short* output, const int outputLength) {
+    // dp[li][lo] dp[a][b] = dp[ a * lo + b]
+    int* dp = editDistanceTable;
+    const int li = inputLength + 1;
+    const int lo = outputLength + 1;
+    for (int i = 0; i < li; ++i) {
+        dp[lo * i] = i;
+    }
+    for (int i = 0; i < lo; ++i) {
+        dp[i] = i;
+    }
+
+    for (int i = 0; i < li - 1; ++i) {
+        for (int j = 0; j < lo - 1; ++j) {
+            const uint32_t ci = Dictionary::toBaseLowerCase(input[i]);
+            const uint32_t co = Dictionary::toBaseLowerCase(output[j]);
+            const uint16_t cost = (ci == co) ? 0 : 1;
+            dp[(i + 1) * lo + (j + 1)] = min(dp[i * lo + (j + 1)] + 1,
+                    min(dp[(i + 1) * lo + j] + 1, dp[i * lo + j] + cost));
+            if (i > 0 && j > 0 && ci == Dictionary::toBaseLowerCase(output[j - 1])
+                    && co == Dictionary::toBaseLowerCase(input[i - 1])) {
+                dp[(i + 1) * lo + (j + 1)] = min(
+                        dp[(i + 1) * lo + (j + 1)], dp[(i - 1) * lo + (j - 1)] + cost);
+            }
+        }
+    }
+
+    if (DEBUG_EDIT_DISTANCE) {
+        LOGI("IN = %d, OUT = %d", inputLength, outputLength);
+        for (int i = 0; i < li; ++i) {
+            for (int j = 0; j < lo; ++j) {
+                LOGI("EDIT[%d][%d], %d", i, j, dp[i * lo + j]);
+            }
+        }
+    }
+    return dp[li * lo - 1];
+}
+#endif
+
+inline static void initEditDistance(int *editDistanceTable) {
+    for (int i = 0; i <= MAX_WORD_LENGTH_INTERNAL; ++i) {
+        editDistanceTable[i] = i;
+    }
+}
+
+inline static void calcEditDistanceOneStep(int *editDistanceTable, const unsigned short *input,
+        const int inputLength, const unsigned short *output, const int outputLength) {
+    // Let dp[i][j] be editDistanceTable[i * (inputLength + 1) + j].
+    // Assuming that dp[0][0] ... dp[outputLength - 1][inputLength] are already calculated,
+    // and calculate dp[ouputLength][0] ... dp[outputLength][inputLength].
+    int *const current = editDistanceTable + outputLength * (inputLength + 1);
+    const int *const prev = editDistanceTable + (outputLength - 1) * (inputLength + 1);
+    const int *const prevprev =
+            outputLength >= 2 ? editDistanceTable + (outputLength - 2) * (inputLength + 1) : NULL;
+    current[0] = outputLength;
+    const uint32_t co = Dictionary::toBaseLowerCase(output[outputLength - 1]);
+    const uint32_t prevCO =
+            outputLength >= 2 ? Dictionary::toBaseLowerCase(output[outputLength - 2]) : 0;
+    for (int i = 1; i <= inputLength; ++i) {
+        const uint32_t ci = Dictionary::toBaseLowerCase(input[i - 1]);
+        const uint16_t cost = (ci == co) ? 0 : 1;
+        current[i] = min(current[i - 1] + 1, min(prev[i] + 1, prev[i - 1] + cost));
+        if (i >= 2 && prevprev && ci == prevCO
+                && co == Dictionary::toBaseLowerCase(input[i - 2])) {
+            current[i] = min(current[i], prevprev[i - 2] + 1);
+        }
+    }
+}
+
+inline static int getCurrentEditDistance(
+        int *editDistanceTable, const int inputLength, const int outputLength) {
+    return editDistanceTable[(inputLength + 1) * (outputLength + 1) - 1];
+}
+
 //////////////////////
 // inline functions //
 //////////////////////
@@ -43,6 +124,7 @@
 
 Correction::Correction(const int typedLetterMultiplier, const int fullWordMultiplier)
         : TYPED_LETTER_MULTIPLIER(typedLetterMultiplier), FULL_WORD_MULTIPLIER(fullWordMultiplier) {
+    initEditDistance(mEditDistanceTable);
 }
 
 void Correction::initCorrection(const ProximityInfo *pi, const int inputLength,
@@ -197,13 +279,21 @@
 }
 
 bool Correction::needsToPrune() const {
+    // TODO: use edit distance here
     return mOutputIndex - 1 >= mMaxDepth || mProximityCount > mMaxEditDistance;
 }
 
+void Correction::addCharToCurrentWord(const int32_t c) {
+    mWord[mOutputIndex] = c;
+    const unsigned short *primaryInputWord = mProximityInfo->getPrimaryInputWord();
+    calcEditDistanceOneStep(mEditDistanceTable, primaryInputWord, mInputLength,
+            mWord, mOutputIndex + 1);
+}
+
 // TODO: inline?
 Correction::CorrectionType Correction::processSkipChar(
         const int32_t c, const bool isTerminal, const bool inputIndexIncremented) {
-    mWord[mOutputIndex] = c;
+    addCharToCurrentWord(c);
     if (needsToTraverseAllNodes() && isTerminal) {
         mTerminalInputIndex = mInputIndex - (inputIndexIncremented ? 1 : 0);
         mTerminalOutputIndex = mOutputIndex;
@@ -412,7 +502,7 @@
                 mProximityInfo->getNormalizedSquaredDistance(mInputIndex, proximityIndex);
     }
 
-    mWord[mOutputIndex] = c;
+    addCharToCurrentWord(c);
 
     // 4. Last char excessive correction
     mLastCharExceeded = mExcessiveCount == 0 && mSkippedCount == 0 && mTransposedCount == 0
@@ -526,47 +616,6 @@
      return false;
 }
 
-/* static */
-inline static int editDistance(
-        int* editDistanceTable, const unsigned short* input,
-        const int inputLength, const unsigned short* output, const int outputLength) {
-    // dp[li][lo] dp[a][b] = dp[ a * lo + b]
-    int* dp = editDistanceTable;
-    const int li = inputLength + 1;
-    const int lo = outputLength + 1;
-    for (int i = 0; i < li; ++i) {
-        dp[lo * i] = i;
-    }
-    for (int i = 0; i < lo; ++i) {
-        dp[i] = i;
-    }
-
-    for (int i = 0; i < li - 1; ++i) {
-        for (int j = 0; j < lo - 1; ++j) {
-            const uint32_t ci = Dictionary::toBaseLowerCase(input[i]);
-            const uint32_t co = Dictionary::toBaseLowerCase(output[j]);
-            const uint16_t cost = (ci == co) ? 0 : 1;
-            dp[(i + 1) * lo + (j + 1)] = min(dp[i * lo + (j + 1)] + 1,
-                    min(dp[(i + 1) * lo + j] + 1, dp[i * lo + j] + cost));
-            if (i > 0 && j > 0 && ci == Dictionary::toBaseLowerCase(output[j - 1])
-                    && co == Dictionary::toBaseLowerCase(input[i - 1])) {
-                dp[(i + 1) * lo + (j + 1)] = min(
-                        dp[(i + 1) * lo + (j + 1)], dp[(i - 1) * lo + (j - 1)] + cost);
-            }
-        }
-    }
-
-    if (DEBUG_EDIT_DISTANCE) {
-        LOGI("IN = %d, OUT = %d", inputLength, outputLength);
-        for (int i = 0; i < li; ++i) {
-            for (int j = 0; j < lo; ++j) {
-                LOGI("EDIT[%d][%d], %d", i, j, dp[i * lo + j]);
-            }
-        }
-    }
-    return dp[li * lo - 1];
-}
-
 //////////////////////
 // RankingAlgorithm //
 //////////////////////
@@ -612,9 +661,7 @@
     // TODO: Optimize this.
     // TODO: Ignoring edit distance for transposed char, for now
     if (transposedCount == 0 && (proximityMatchedCount > 0 || skipped || excessiveCount > 0)) {
-        const unsigned short* primaryInputWord = proximityInfo->getPrimaryInputWord();
-        ed = editDistance(editDistanceTable, primaryInputWord,
-                inputLength, word, outputIndex + 1);
+        ed = getCurrentEditDistance(editDistanceTable, inputLength, outputIndex + 1);
         const int matchWeight = powerIntCapped(typedLetterMultiplier,
                 max(inputLength, outputIndex + 1) - ed);
         multiplyIntCapped(matchWeight, &finalFreq);
diff --git a/native/src/correction.h b/native/src/correction.h
index 437ef77..d4e99f0 100644
--- a/native/src/correction.h
+++ b/native/src/correction.h
@@ -102,6 +102,7 @@
     inline bool isQuote(const unsigned short c);
     inline CorrectionType processSkipChar(
             const int32_t c, const bool isTerminal, const bool inputIndexIncremented);
+    inline void addCharToCurrentWord(const int32_t c);
 
     const int TYPED_LETTER_MULTIPLIER;
     const int FULL_WORD_MULTIPLIER;