diff --git a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
index aa15bd6bfc91a7233b122ac73c1d060088ce3015..1b1162b51f9a3943553062a8586072cb6c5818e3 100644
--- a/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
+++ b/java/src/com/android/inputmethod/latin/DictionaryFacilitator.java
@@ -597,8 +597,7 @@ public class DictionaryFacilitator {
             final SettingsValuesForSuggestion settingsValuesForSuggestion, final int sessionId) {
         final DictionaryGroup[] dictionaryGroups = mDictionaryGroups;
         final SuggestionResults suggestionResults = new SuggestionResults(
-                SuggestedWords.MAX_SUGGESTIONS,
-                prevWordsInfo.mPrevWordsInfo[0].mIsBeginningOfSentence);
+                SuggestedWords.MAX_SUGGESTIONS, prevWordsInfo.isBeginningOfSentenceContext());
         final float[] weightOfLangModelVsSpatialModel =
                 new float[] { Dictionary.NOT_A_WEIGHT_OF_LANG_MODEL_VS_SPATIAL_MODEL };
         for (final DictionaryGroup dictionaryGroup : dictionaryGroups) {
diff --git a/java/src/com/android/inputmethod/latin/PrevWordsInfo.java b/java/src/com/android/inputmethod/latin/PrevWordsInfo.java
index 1b7e8f96df1ed39ec3194e730147577fbf593856..1ef1bbb10cd12896c68ff0d5755c57d70d21453e 100644
--- a/java/src/com/android/inputmethod/latin/PrevWordsInfo.java
+++ b/java/src/com/android/inputmethod/latin/PrevWordsInfo.java
@@ -18,6 +18,7 @@ package com.android.inputmethod.latin;
 
 import android.text.TextUtils;
 
+import com.android.inputmethod.annotations.UsedForTesting;
 import com.android.inputmethod.latin.utils.StringUtils;
 
 import java.util.Arrays;
@@ -86,35 +87,66 @@ public class PrevWordsInfo {
     // For simplicity of implementation, elements may also be EMPTY_WORD_INFO transiently after the
     // WordComposer was reset and before starting a new composing word, but we should never be
     // calling getSuggetions* in this situation.
-    public final WordInfo[] mPrevWordsInfo;
+    private final WordInfo[] mPrevWordsInfo;
+    private final int mPrevWordsCount;
 
     // Construct from the previous word information.
-    public PrevWordsInfo(final WordInfo prevWordInfo) {
-        mPrevWordsInfo = new WordInfo[] { prevWordInfo };
+    public PrevWordsInfo(final WordInfo... prevWordsInfo) {
+        mPrevWordsInfo = prevWordsInfo;
+        mPrevWordsCount = prevWordsInfo.length;
     }
 
-    // Construct from WordInfo array. n-th element represents (n+1)-th previous word's information.
-    public PrevWordsInfo(final WordInfo[] prevWordsInfo) {
-        mPrevWordsInfo = prevWordsInfo;
+    // Construct from WordInfo array and size. The caller shouldn't change prevWordsInfo after
+    // calling this method.
+    private PrevWordsInfo(final PrevWordsInfo prevWordsInfo, final int prevWordsCount) {
+        if (prevWordsInfo.mPrevWordsCount < prevWordsCount) {
+            throw new IndexOutOfBoundsException("prevWordsInfo.mPrevWordsCount ("
+                    + prevWordsInfo.mPrevWordsCount + ") is smaller than prevWordsCount ("
+                    + prevWordsCount + ")");
+        }
+        mPrevWordsInfo = prevWordsInfo.mPrevWordsInfo;
+        mPrevWordsCount = prevWordsCount;
     }
 
     // Create next prevWordsInfo using current prevWordsInfo.
     public PrevWordsInfo getNextPrevWordsInfo(final WordInfo wordInfo) {
         final int nextPrevWordCount = Math.min(Constants.MAX_PREV_WORD_COUNT_FOR_N_GRAM,
-                mPrevWordsInfo.length + 1);
+                mPrevWordsCount + 1);
         final WordInfo[] prevWordsInfo = new WordInfo[nextPrevWordCount];
         prevWordsInfo[0] = wordInfo;
-        System.arraycopy(mPrevWordsInfo, 0, prevWordsInfo, 1, prevWordsInfo.length - 1);
+        System.arraycopy(mPrevWordsInfo, 0, prevWordsInfo, 1, nextPrevWordCount - 1);
         return new PrevWordsInfo(prevWordsInfo);
     }
 
     public boolean isValid() {
-        return mPrevWordsInfo.length > 0 && mPrevWordsInfo[0].isValid();
+        return mPrevWordsCount > 0 && mPrevWordsInfo[0].isValid();
+    }
+
+    public boolean isBeginningOfSentenceContext() {
+        return mPrevWordsCount > 0 && mPrevWordsInfo[0].mIsBeginningOfSentence;
+    }
+
+    // n is 1-indexed.
+    // TODO: Remove
+    public CharSequence getNthPrevWord(final int n) {
+        if (n <= 0 || n > mPrevWordsCount) {
+            return null;
+        }
+        return mPrevWordsInfo[n - 1].mWord;
+    }
+
+    // n is 1-indexed.
+    @UsedForTesting
+    public boolean isNthPrevWordBeginningOfSontence(final int n) {
+        if (n <= 0 || n > mPrevWordsCount) {
+            return false;
+        }
+        return mPrevWordsInfo[n - 1].mIsBeginningOfSentence;
     }
 
     public void outputToArray(final int[][] codePointArrays,
             final boolean[] isBeginningOfSentenceArray) {
-        for (int i = 0; i < mPrevWordsInfo.length; i++) {
+        for (int i = 0; i < mPrevWordsCount; i++) {
             final WordInfo wordInfo = mPrevWordsInfo[i];
             if (wordInfo == null || !wordInfo.isValid()) {
                 codePointArrays[i] = new int[0];
@@ -127,14 +159,12 @@ public class PrevWordsInfo {
     }
 
     public PrevWordsInfo getTrimmedPrevWordsInfo(final int maxPrevWordCount) {
-        final int newSize = Math.min(maxPrevWordCount, mPrevWordsInfo.length);
-        // TODO: Quit creating a new array.
-        final WordInfo[] prevWordsInfo = Arrays.copyOf(mPrevWordsInfo, newSize);
-        return new PrevWordsInfo(prevWordsInfo);
+        final int newSize = Math.min(maxPrevWordCount, mPrevWordsCount);
+        return new PrevWordsInfo(this /* prevWordsInfo */, newSize);
     }
 
     public int getPrevWordCount() {
-        return mPrevWordsInfo.length;
+        return mPrevWordsCount;
     }
 
     @Override
@@ -149,16 +179,22 @@ public class PrevWordsInfo {
         if (!(o instanceof PrevWordsInfo)) return false;
         final PrevWordsInfo prevWordsInfo = (PrevWordsInfo)o;
 
-        final int minLength = Math.min(mPrevWordsInfo.length, prevWordsInfo.mPrevWordsInfo.length);
+        final int minLength = Math.min(mPrevWordsCount, prevWordsInfo.mPrevWordsCount);
         for (int i = 0; i < minLength; i++) {
             if (!mPrevWordsInfo[i].equals(prevWordsInfo.mPrevWordsInfo[i])) {
                 return false;
             }
         }
-        final WordInfo[] longerWordsInfo =
-                (mPrevWordsInfo.length > prevWordsInfo.mPrevWordsInfo.length) ?
-                        mPrevWordsInfo : prevWordsInfo.mPrevWordsInfo;
-        for (int i = minLength; i < longerWordsInfo.length; i++) {
+        final WordInfo[] longerWordsInfo;
+        final int longerWordsInfoCount;
+        if (mPrevWordsCount > prevWordsInfo.mPrevWordsCount) {
+            longerWordsInfo = mPrevWordsInfo;
+            longerWordsInfoCount = mPrevWordsCount;
+        } else {
+            longerWordsInfo = prevWordsInfo.mPrevWordsInfo;
+            longerWordsInfoCount = prevWordsInfo.mPrevWordsCount;
+        }
+        for (int i = minLength; i < longerWordsInfoCount; i++) {
             if (longerWordsInfo[i] != null
                     && !WordInfo.EMPTY_WORD_INFO.equals(longerWordsInfo[i])) {
                 return false;
@@ -170,7 +206,7 @@ public class PrevWordsInfo {
     @Override
     public String toString() {
         final StringBuffer builder = new StringBuffer();
-        for (int i = 0; i < mPrevWordsInfo.length; i++) {
+        for (int i = 0; i < mPrevWordsCount; i++) {
             final WordInfo wordInfo = mPrevWordsInfo[i];
             builder.append("PrevWord[");
             builder.append(i);
diff --git a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java
index d1486f63074d6e0f00821bbbfe867f1b24ec4aa6..121c89e83faf87050108950eef55ae48c9e4bc6c 100644
--- a/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/personalization/UserHistoryDictionary.java
@@ -71,12 +71,11 @@ public class UserHistoryDictionary extends DecayingExpandableBinaryDictionaryBas
                 null /* shortcutTarget */, 0 /* shortcutFreq */, false /* isNotAWord */,
                 false /* isBlacklisted */, timestamp, distracterFilter);
 
-        final boolean isBeginningOfSentenceContext =
-                prevWordsInfo.mPrevWordsInfo[0].mIsBeginningOfSentence;
+        final boolean isBeginningOfSentenceContext = prevWordsInfo.isBeginningOfSentenceContext();
         final PrevWordsInfo prevWordsInfoToBeSaved =
                 prevWordsInfo.getTrimmedPrevWordsInfo(SUPPORTED_NGRAM - 1);
         for (int i = 0; i < prevWordsInfoToBeSaved.getPrevWordCount(); i++) {
-            final CharSequence prevWord = prevWordsInfoToBeSaved.mPrevWordsInfo[i].mWord;
+            final CharSequence prevWord = prevWordsInfoToBeSaved.getNthPrevWord(1 /* n */);
             if (prevWord == null || (prevWord.length() > Constants.DICTIONARY_MAX_WORD_LENGTH)) {
                 return;
             }
diff --git a/java/src/com/android/inputmethod/latin/utils/LanguageModelParam.java b/java/src/com/android/inputmethod/latin/utils/LanguageModelParam.java
index 7955541aa22f5975b3e8cd23aca71dceaed3750a..be928077fd4c7ad0ad89494d43fccbf3e61fa460 100644
--- a/java/src/com/android/inputmethod/latin/utils/LanguageModelParam.java
+++ b/java/src/com/android/inputmethod/latin/utils/LanguageModelParam.java
@@ -161,7 +161,7 @@ public final class LanguageModelParam {
         }
         final int bigramProbability = isValidWord ?
                 BIGRAM_PROBABILITY_FOR_VALID_WORD : BIGRAM_PROBABILITY_FOR_OOV_WORD;
-        return new LanguageModelParam(prevWordsInfo.mPrevWordsInfo[0].mWord, word,
+        return new LanguageModelParam(prevWordsInfo.getNthPrevWord(1 /* n */), word,
                 unigramProbability, bigramProbability, timestamp);
     }
 }
diff --git a/tests/src/com/android/inputmethod/latin/PrevWordsInfoTests.java b/tests/src/com/android/inputmethod/latin/PrevWordsInfoTests.java
new file mode 100644
index 0000000000000000000000000000000000000000..c571d985d5af1301d7544a5090be879bf08ed838
--- /dev/null
+++ b/tests/src/com/android/inputmethod/latin/PrevWordsInfoTests.java
@@ -0,0 +1,66 @@
+/*
+ * 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;
+
+import com.android.inputmethod.latin.PrevWordsInfo.WordInfo;
+
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.SmallTest;
+
+@SmallTest
+public class PrevWordsInfoTests extends AndroidTestCase {
+    public void testConstruct() {
+        assertEquals(new PrevWordsInfo(new WordInfo("a")), new PrevWordsInfo(new WordInfo("a")));
+        assertEquals(new PrevWordsInfo(WordInfo.BEGINNING_OF_SENTENCE),
+                new PrevWordsInfo(WordInfo.BEGINNING_OF_SENTENCE));
+        assertEquals(new PrevWordsInfo(WordInfo.EMPTY_WORD_INFO),
+                new PrevWordsInfo(WordInfo.EMPTY_WORD_INFO));
+        assertEquals(new PrevWordsInfo(WordInfo.EMPTY_WORD_INFO),
+                new PrevWordsInfo(WordInfo.EMPTY_WORD_INFO));
+    }
+
+    public void testIsBeginningOfSentenceContext() {
+        assertFalse(new PrevWordsInfo().isBeginningOfSentenceContext());
+        assertTrue(new PrevWordsInfo(WordInfo.BEGINNING_OF_SENTENCE)
+                .isBeginningOfSentenceContext());
+        assertTrue(PrevWordsInfo.BEGINNING_OF_SENTENCE.isBeginningOfSentenceContext());
+        assertFalse(new PrevWordsInfo(new WordInfo("a")).isBeginningOfSentenceContext());
+        assertFalse(new PrevWordsInfo(new WordInfo("")).isBeginningOfSentenceContext());
+        assertFalse(new PrevWordsInfo(WordInfo.EMPTY_WORD_INFO).isBeginningOfSentenceContext());
+        assertTrue(new PrevWordsInfo(WordInfo.BEGINNING_OF_SENTENCE, new WordInfo("a"))
+                .isBeginningOfSentenceContext());
+        assertFalse(new PrevWordsInfo(new WordInfo("a"), WordInfo.BEGINNING_OF_SENTENCE)
+                .isBeginningOfSentenceContext());
+        assertFalse(new PrevWordsInfo(WordInfo.EMPTY_WORD_INFO, WordInfo.BEGINNING_OF_SENTENCE)
+                .isBeginningOfSentenceContext());
+    }
+
+    public void testGetNextPrevWordsInfo() {
+        final PrevWordsInfo prevWordsInfo_a = new PrevWordsInfo(new WordInfo("a"));
+        final PrevWordsInfo prevWordsInfo_b_a =
+                prevWordsInfo_a.getNextPrevWordsInfo(new WordInfo("b"));
+        assertEquals("b", prevWordsInfo_b_a.getNthPrevWord(1));
+        assertEquals("a", prevWordsInfo_b_a.getNthPrevWord(2));
+        final PrevWordsInfo prevWordsInfo_bos_b =
+                prevWordsInfo_b_a.getNextPrevWordsInfo(WordInfo.BEGINNING_OF_SENTENCE);
+        assertTrue(prevWordsInfo_bos_b.isBeginningOfSentenceContext());
+        assertEquals("b", prevWordsInfo_bos_b.getNthPrevWord(2));
+        final PrevWordsInfo prevWordsInfo_c_bos =
+                prevWordsInfo_b_a.getNextPrevWordsInfo(new WordInfo("c"));
+        assertEquals("c", prevWordsInfo_c_bos.getNthPrevWord(1));
+    }
+}
diff --git a/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java b/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java
index f9d72269e9023ae68d2afd62ff1d88c8def42dce..2712dc228ab00eae4fb3606c6edc77cdb07e7a84 100644
--- a/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java
+++ b/tests/src/com/android/inputmethod/latin/RichInputConnectionAndTextRangeTests.java
@@ -30,7 +30,6 @@ import android.view.inputmethod.ExtractedTextRequest;
 import android.view.inputmethod.InputConnection;
 import android.view.inputmethod.InputConnectionWrapper;
 
-import com.android.inputmethod.latin.PrevWordsInfo.WordInfo;
 import com.android.inputmethod.latin.settings.SpacingAndPunctuations;
 import com.android.inputmethod.latin.utils.PrevWordsInfoUtils;
 import com.android.inputmethod.latin.utils.RunInLocale;
@@ -159,25 +158,24 @@ public class RichInputConnectionAndTextRangeTests extends AndroidTestCase {
     public void testGetPreviousWord() {
         // If one of the following cases breaks, the bigram suggestions won't work.
         assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
-                "abc def", mSpacingAndPunctuations, 2).mPrevWordsInfo[0].mWord, "abc");
+                "abc def", mSpacingAndPunctuations, 2).getNthPrevWord(1), "abc");
         assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
                 "abc", mSpacingAndPunctuations, 2), PrevWordsInfo.BEGINNING_OF_SENTENCE);
         assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
                 "abc. def", mSpacingAndPunctuations, 2), PrevWordsInfo.BEGINNING_OF_SENTENCE);
 
         assertFalse(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
-                "abc def", mSpacingAndPunctuations, 2).mPrevWordsInfo[0].mIsBeginningOfSentence);
+                "abc def", mSpacingAndPunctuations, 2).isBeginningOfSentenceContext());
         assertTrue(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
-                "abc", mSpacingAndPunctuations, 2).mPrevWordsInfo[0].mIsBeginningOfSentence);
+                "abc", mSpacingAndPunctuations, 2).isBeginningOfSentenceContext());
 
         // For n-gram
         assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
-                "abc def", mSpacingAndPunctuations, 1).mPrevWordsInfo[0].mWord, "def");
+                "abc def", mSpacingAndPunctuations, 1).getNthPrevWord(1), "def");
         assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
-                "abc def", mSpacingAndPunctuations, 1).mPrevWordsInfo[1].mWord, "abc");
-        assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
-                "abc def", mSpacingAndPunctuations, 2).mPrevWordsInfo[1],
-                WordInfo.BEGINNING_OF_SENTENCE);
+                "abc def", mSpacingAndPunctuations, 1).getNthPrevWord(2), "abc");
+        assertTrue(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
+                "abc def", mSpacingAndPunctuations, 2).isNthPrevWordBeginningOfSontence(2));
 
         // The following tests reflect the current behavior of the function
         // RichInputConnection#getNthPreviousWord.
@@ -187,20 +185,20 @@ public class RichInputConnectionAndTextRangeTests extends AndroidTestCase {
         // logical. These tests are just there to catch any unintentional
         // changes in the behavior of the RichInputConnection#getPreviousWord method.
         assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
-                "abc def ", mSpacingAndPunctuations, 2).mPrevWordsInfo[0].mWord, "abc");
+                "abc def ", mSpacingAndPunctuations, 2).getNthPrevWord(1), "abc");
         assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
-                "abc def.", mSpacingAndPunctuations, 2).mPrevWordsInfo[0].mWord, "abc");
+                "abc def.", mSpacingAndPunctuations, 2).getNthPrevWord(1), "abc");
         assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
-                "abc def .", mSpacingAndPunctuations, 2).mPrevWordsInfo[0].mWord, "def");
-        assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
-                "abc ", mSpacingAndPunctuations, 2), PrevWordsInfo.BEGINNING_OF_SENTENCE);
+                "abc def .", mSpacingAndPunctuations, 2).getNthPrevWord(1), "def");
+        assertTrue(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
+                "abc ", mSpacingAndPunctuations, 2).isBeginningOfSentenceContext());
 
         assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
-                "abc def", mSpacingAndPunctuations, 1).mPrevWordsInfo[0].mWord, "def");
+                "abc def", mSpacingAndPunctuations, 1).getNthPrevWord(1), "def");
         assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
-                "abc def ", mSpacingAndPunctuations, 1).mPrevWordsInfo[0].mWord, "def");
+                "abc def ", mSpacingAndPunctuations, 1).getNthPrevWord(1), "def");
         assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
-                "abc 'def", mSpacingAndPunctuations, 1).mPrevWordsInfo[0].mWord, "'def");
+                "abc 'def", mSpacingAndPunctuations, 1).getNthPrevWord(1), "'def");
         assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(
                 "abc def.", mSpacingAndPunctuations, 1), PrevWordsInfo.BEGINNING_OF_SENTENCE);
         assertEquals(PrevWordsInfoUtils.getPrevWordsInfoFromNthPreviousWord(