From e137ec0a91cf93b0a99fd1e1556ee835d026f731 Mon Sep 17 00:00:00 2001
From: Keisuke Kuroyanagi <ksk@google.com>
Date: Fri, 7 Mar 2014 19:36:19 +0900
Subject: [PATCH] Introduce SuggestionResults and use it for predictions.

Bug: 8187060
Bug: 13333066
Change-Id: I1ead897024508b7e40fbd93af7d14bfe74b93826
---
 .../inputmethod/latin/BinaryDictionary.java   | 12 +--
 native/jni/NativeFileList.mk                  |  4 +-
 ...oid_inputmethod_latin_BinaryDictionary.cpp | 56 ++++++++-----
 .../core/dictionary/bigram_dictionary.cpp     | 77 ++---------------
 .../core/dictionary/bigram_dictionary.h       |  7 +-
 .../suggest/core/dictionary/dictionary.cpp    |  9 +-
 .../src/suggest/core/dictionary/dictionary.h  |  5 +-
 .../src/suggest/core/result/suggested_word.h  | 83 +++++++++++++++++++
 .../core/result/suggestion_results.cpp        | 77 +++++++++++++++++
 .../suggest/core/result/suggestion_results.h  | 53 ++++++++++++
 10 files changed, 274 insertions(+), 109 deletions(-)
 create mode 100644 native/jni/src/suggest/core/result/suggested_word.h
 create mode 100644 native/jni/src/suggest/core/result/suggestion_results.cpp
 create mode 100644 native/jni/src/suggest/core/result/suggestion_results.h

diff --git a/java/src/com/android/inputmethod/latin/BinaryDictionary.java b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
index a3a329a712..f659b3f798 100644
--- a/java/src/com/android/inputmethod/latin/BinaryDictionary.java
+++ b/java/src/com/android/inputmethod/latin/BinaryDictionary.java
@@ -87,6 +87,7 @@ public final class BinaryDictionary extends Dictionary {
     private final String mDictFilePath;
     private final boolean mIsUpdatable;
     private final int[] mInputCodePoints = new int[MAX_WORD_LENGTH];
+    private final int[] mOutputSuggestionCount = new int[1];
     private final int[] mOutputCodePoints = new int[MAX_WORD_LENGTH * MAX_RESULTS];
     private final int[] mSpaceIndices = new int[MAX_RESULTS];
     private final int[] mOutputScores = new int[MAX_RESULTS];
@@ -158,10 +159,10 @@ public final class BinaryDictionary extends Dictionary {
             ArrayList<int[]> outBigramTargets, ArrayList<int[]> outBigramProbabilityInfo,
             ArrayList<int[]> outShortcutTargets, ArrayList<Integer> outShortcutProbabilities);
     private static native int getNextWordNative(long dict, int token, int[] outCodePoints);
-    private static native int getSuggestionsNative(long dict, long proximityInfo,
+    private static native void getSuggestionsNative(long dict, long proximityInfo,
             long traverseSession, int[] xCoordinates, int[] yCoordinates, int[] times,
             int[] pointerIds, int[] inputCodePoints, int inputSize, int commitPoint,
-            int[] suggestOptions, int[] prevWordCodePointArray,
+            int[] suggestOptions, int[] prevWordCodePointArray, int[] outputSuggestionCount,
             int[] outputCodePoints, int[] outputScores, int[] outputIndices, int[] outputTypes,
             int[] outputAutoCommitFirstWordConfidence);
     private static native void addUnigramWordNative(long dict, int[] word, int probability,
@@ -258,12 +259,13 @@ public final class BinaryDictionary extends Dictionary {
         mNativeSuggestOptions.setIsGesture(isGesture);
         mNativeSuggestOptions.setAdditionalFeaturesOptions(additionalFeaturesOptions);
         // proximityInfo and/or prevWordForBigrams may not be null.
-        final int count = getSuggestionsNative(mNativeDict, proximityInfo.getNativeProximityInfo(),
+        getSuggestionsNative(mNativeDict, proximityInfo.getNativeProximityInfo(),
                 getTraverseSession(sessionId).getSession(), ips.getXCoordinates(),
                 ips.getYCoordinates(), ips.getTimes(), ips.getPointerIds(), mInputCodePoints,
                 inputSize, 0 /* commitPoint */, mNativeSuggestOptions.getOptions(),
-                prevWordCodePointArray, mOutputCodePoints, mOutputScores, mSpaceIndices,
-                mOutputTypes, mOutputAutoCommitFirstWordConfidence);
+                prevWordCodePointArray, mOutputSuggestionCount, mOutputCodePoints, mOutputScores,
+                mSpaceIndices, mOutputTypes, mOutputAutoCommitFirstWordConfidence);
+        final int count = mOutputSuggestionCount[0];
         final ArrayList<SuggestedWordInfo> suggestions = CollectionUtils.newArrayList();
         for (int j = 0; j < count; ++j) {
             final int start = j * MAX_WORD_LENGTH;
diff --git a/native/jni/NativeFileList.mk b/native/jni/NativeFileList.mk
index 1f58246084..2ee4caa422 100644
--- a/native/jni/NativeFileList.mk
+++ b/native/jni/NativeFileList.mk
@@ -40,8 +40,10 @@ LATIN_IME_CORE_SRC_FILES := \
         proximity_info_state.cpp \
         proximity_info_state_utils.cpp) \
     suggest/core/policy/weighting.cpp \
-    suggest/core/result/suggestions_output_utils.cpp \
     suggest/core/session/dic_traverse_session.cpp \
+    $(addprefix suggest/core/result/, \
+        suggestion_results.cpp \
+        suggestions_output_utils.cpp) \
     $(addprefix suggest/policyimpl/dictionary/, \
         header/header_policy.cpp \
         header/header_read_write_utils.cpp \
diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
index 5b972837f3..eec354abc1 100644
--- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
+++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
@@ -25,6 +25,7 @@
 #include "jni_common.h"
 #include "suggest/core/dictionary/dictionary.h"
 #include "suggest/core/dictionary/word_property.h"
+#include "suggest/core/result/suggestion_results.h"
 #include "suggest/core/suggest_options.h"
 #include "suggest/policyimpl/dictionary/structure/dictionary_structure_with_buffer_policy_factory.h"
 #include "utils/char_utils.h"
@@ -139,15 +140,20 @@ static int latinime_BinaryDictionary_getFormatVersion(JNIEnv *env, jclass clazz,
     return headerPolicy->getFormatVersionNumber();
 }
 
-static int latinime_BinaryDictionary_getSuggestions(JNIEnv *env, jclass clazz, jlong dict,
+static void latinime_BinaryDictionary_getSuggestions(JNIEnv *env, jclass clazz, jlong dict,
         jlong proximityInfo, jlong dicTraverseSession, jintArray xCoordinatesArray,
         jintArray yCoordinatesArray, jintArray timesArray, jintArray pointerIdsArray,
         jintArray inputCodePointsArray, jint inputSize, jint commitPoint, jintArray suggestOptions,
-        jintArray prevWordCodePointsForBigrams, jintArray outputCodePointsArray,
-        jintArray scoresArray, jintArray spaceIndicesArray, jintArray outputTypesArray,
-        jintArray outputAutoCommitFirstWordConfidenceArray) {
+        jintArray prevWordCodePointsForBigrams, jintArray outSuggestionCount,
+        jintArray outCodePointsArray, jintArray outScoresArray, jintArray outSpaceIndicesArray,
+        jintArray outTypesArray, jintArray outAutoCommitFirstWordConfidenceArray) {
     Dictionary *dictionary = reinterpret_cast<Dictionary *>(dict);
-    if (!dictionary) return 0;
+    // Assign 0 to outSuggestionCount here in case of returning earlier in this method.
+    int count = 0;
+    env->SetIntArrayRegion(outSuggestionCount, 0, 1 /* len */, &count);
+    if (!dictionary) {
+        return;
+    }
     ProximityInfo *pInfo = reinterpret_cast<ProximityInfo *>(proximityInfo);
     DicTraverseSession *traverseSession =
             reinterpret_cast<DicTraverseSession *>(dicTraverseSession);
@@ -181,26 +187,26 @@ static int latinime_BinaryDictionary_getSuggestions(JNIEnv *env, jclass clazz, j
 
     // Output values
     /* By the way, let's check the output array length here to make sure */
-    const jsize outputCodePointsLength = env->GetArrayLength(outputCodePointsArray);
+    const jsize outputCodePointsLength = env->GetArrayLength(outCodePointsArray);
     if (outputCodePointsLength != (MAX_WORD_LENGTH * MAX_RESULTS)) {
         AKLOGE("Invalid outputCodePointsLength: %d", outputCodePointsLength);
         ASSERT(false);
-        return 0;
+        return;
     }
-    const jsize scoresLength = env->GetArrayLength(scoresArray);
+    const jsize scoresLength = env->GetArrayLength(outScoresArray);
     if (scoresLength != MAX_RESULTS) {
         AKLOGE("Invalid scoresLength: %d", scoresLength);
         ASSERT(false);
-        return 0;
+        return;
     }
     int outputCodePoints[outputCodePointsLength];
     int scores[scoresLength];
-    const jsize spaceIndicesLength = env->GetArrayLength(spaceIndicesArray);
+    const jsize spaceIndicesLength = env->GetArrayLength(outSpaceIndicesArray);
     int spaceIndices[spaceIndicesLength];
-    const jsize outputTypesLength = env->GetArrayLength(outputTypesArray);
+    const jsize outputTypesLength = env->GetArrayLength(outTypesArray);
     int outputTypes[outputTypesLength];
     const jsize outputAutoCommitFirstWordConfidenceLength =
-            env->GetArrayLength(outputAutoCommitFirstWordConfidenceArray);
+            env->GetArrayLength(outAutoCommitFirstWordConfidenceArray);
     // We only use the first result, as obviously we will only ever autocommit the first one
     ASSERT(outputAutoCommitFirstWordConfidenceLength == 1);
     int outputAutoCommitFirstWordConfidence[outputAutoCommitFirstWordConfidenceLength];
@@ -210,26 +216,30 @@ static int latinime_BinaryDictionary_getSuggestions(JNIEnv *env, jclass clazz, j
     memset(outputTypes, 0, sizeof(outputTypes));
     memset(outputAutoCommitFirstWordConfidence, 0, sizeof(outputAutoCommitFirstWordConfidence));
 
-    int count;
     if (givenSuggestOptions.isGesture() || inputSize > 0) {
+        // TODO: Use SuggestionResults to return suggestions.
         count = dictionary->getSuggestions(pInfo, traverseSession, xCoordinates, yCoordinates,
                 times, pointerIds, inputCodePoints, inputSize, prevWordCodePoints,
                 prevWordCodePointsLength, commitPoint, &givenSuggestOptions, outputCodePoints,
                 scores, spaceIndices, outputTypes, outputAutoCommitFirstWordConfidence);
     } else {
-        count = dictionary->getBigrams(prevWordCodePoints, prevWordCodePointsLength,
-                outputCodePoints, scores, outputTypes);
+        SuggestionResults suggestionResults(MAX_RESULTS);
+        dictionary->getPredictions(prevWordCodePoints, prevWordCodePointsLength,
+                &suggestionResults);
+        suggestionResults.outputSuggestions(env, outSuggestionCount, outCodePointsArray,
+                outScoresArray, outSpaceIndicesArray, outTypesArray,
+                outAutoCommitFirstWordConfidenceArray);
+        return;
     }
 
     // Copy back the output values
-    env->SetIntArrayRegion(outputCodePointsArray, 0, outputCodePointsLength, outputCodePoints);
-    env->SetIntArrayRegion(scoresArray, 0, scoresLength, scores);
-    env->SetIntArrayRegion(spaceIndicesArray, 0, spaceIndicesLength, spaceIndices);
-    env->SetIntArrayRegion(outputTypesArray, 0, outputTypesLength, outputTypes);
-    env->SetIntArrayRegion(outputAutoCommitFirstWordConfidenceArray, 0,
+    env->SetIntArrayRegion(outSuggestionCount, 0, 1 /* len */, &count);
+    env->SetIntArrayRegion(outCodePointsArray, 0, outputCodePointsLength, outputCodePoints);
+    env->SetIntArrayRegion(outScoresArray, 0, scoresLength, scores);
+    env->SetIntArrayRegion(outSpaceIndicesArray, 0, spaceIndicesLength, spaceIndices);
+    env->SetIntArrayRegion(outTypesArray, 0, outputTypesLength, outputTypes);
+    env->SetIntArrayRegion(outAutoCommitFirstWordConfidenceArray, 0,
             outputAutoCommitFirstWordConfidenceLength, outputAutoCommitFirstWordConfidence);
-
-    return count;
 }
 
 static jint latinime_BinaryDictionary_getProbability(JNIEnv *env, jclass clazz, jlong dict,
@@ -496,7 +506,7 @@ static const JNINativeMethod sMethods[] = {
     },
     {
         const_cast<char *>("getSuggestionsNative"),
-        const_cast<char *>("(JJJ[I[I[I[I[III[I[I[I[I[I[I[I)I"),
+        const_cast<char *>("(JJJ[I[I[I[I[III[I[I[I[I[I[I[I[I)V"),
         reinterpret_cast<void *>(latinime_BinaryDictionary_getSuggestions)
     },
     {
diff --git a/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp b/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp
index 0859df423f..f793363a86 100644
--- a/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp
+++ b/native/jni/src/suggest/core/dictionary/bigram_dictionary.cpp
@@ -25,6 +25,7 @@
 #include "suggest/core/dictionary/binary_dictionary_bigrams_iterator.h"
 #include "suggest/core/dictionary/dictionary.h"
 #include "suggest/core/policy/dictionary_structure_with_buffer_policy.h"
+#include "suggest/core/result/suggestion_results.h"
 #include "utils/char_utils.h"
 
 namespace latinime {
@@ -40,71 +41,13 @@ BigramDictionary::BigramDictionary(
 BigramDictionary::~BigramDictionary() {
 }
 
-void BigramDictionary::addWordBigram(int *word, int length, int probability, int *bigramProbability,
-        int *bigramCodePoints, int *outputTypes) const {
-    if (length >= MAX_WORD_LENGTH) {
-        length = MAX_WORD_LENGTH - 1;
-    }
-    word[length] = 0;
-    if (DEBUG_DICT_FULL) {
-#ifdef FLAG_DBG
-        char s[length + 1];
-        for (int i = 0; i <= length; i++) s[i] = static_cast<char>(word[i]);
-        AKLOGI("Bigram: Found word = %s, freq = %d :", s, probability);
-#endif
-    }
-
-    // Find the right insertion point
-    int insertAt = 0;
-    while (insertAt < MAX_RESULTS) {
-        if (probability > bigramProbability[insertAt] || (bigramProbability[insertAt] == probability
-                && length < CharUtils::getCodePointCount(MAX_WORD_LENGTH,
-                        bigramCodePoints + insertAt * MAX_WORD_LENGTH))) {
-            break;
-        }
-        insertAt++;
-    }
-    if (DEBUG_DICT_FULL) {
-        AKLOGI("Bigram: InsertAt -> %d MAX_RESULTS: %d", insertAt, MAX_RESULTS);
-    }
-    if (insertAt >= MAX_RESULTS) {
-        return;
-    }
-    // Shift result buffers to insert the new entry.
-    memmove(bigramProbability + (insertAt + 1), bigramProbability + insertAt,
-            (MAX_RESULTS - insertAt - 1) * sizeof(bigramProbability[0]));
-    memmove(outputTypes + (insertAt + 1), outputTypes + insertAt,
-            (MAX_RESULTS - insertAt - 1) * sizeof(outputTypes[0]));
-    memmove(bigramCodePoints + (insertAt + 1) * MAX_WORD_LENGTH,
-            bigramCodePoints + insertAt * MAX_WORD_LENGTH,
-            (MAX_RESULTS - insertAt - 1) * sizeof(bigramCodePoints[0]) * MAX_WORD_LENGTH);
-    // Put the result.
-    bigramProbability[insertAt] = probability;
-    outputTypes[insertAt] = Dictionary::KIND_PREDICTION;
-    int *dest = bigramCodePoints + insertAt * MAX_WORD_LENGTH;
-    while (length--) {
-        *dest++ = *word++;
-    }
-    *dest = 0; // NULL terminate
-    if (DEBUG_DICT_FULL) {
-        AKLOGI("Bigram: Added word at %d", insertAt);
-    }
-}
-
 /* Parameters :
  * prevWord: the word before, the one for which we need to look up bigrams.
  * prevWordLength: its length.
- * outBigramCodePoints: an array for output, at the same format as outwords for getSuggestions.
- * outBigramProbability: an array to output frequencies.
- * outputTypes: an array to output types.
- * This method returns the number of bigrams this word has, for backward compatibility.
+ * outSuggestionResults: SuggestionResults to put the predictions.
  */
-int BigramDictionary::getPredictions(const int *prevWord, const int prevWordLength,
-        int *const outBigramCodePoints, int *const outBigramProbability,
-        int *const outputTypes) const {
-    // TODO: remove unused arguments, and refrain from storing stuff in members of this class
-    // TODO: have "in" arguments before "out" ones, and make out args explicit in the name
-
+void BigramDictionary::getPredictions(const int *prevWord, const int prevWordLength,
+        SuggestionResults *const outSuggestionResults) const {
     int pos = getBigramListPositionForWord(prevWord, prevWordLength,
             false /* forceLowerCaseSearch */);
     // getBigramListPositionForWord returns 0 if this word isn't in the dictionary or has no bigrams
@@ -114,11 +57,10 @@ int BigramDictionary::getPredictions(const int *prevWord, const int prevWordLeng
                 true /* forceLowerCaseSearch */);
     }
     // If still no bigrams, we really don't have them!
-    if (NOT_A_DICT_POS == pos) return 0;
+    if (NOT_A_DICT_POS == pos) return;
 
-    int bigramCount = 0;
     int unigramProbability = 0;
-    int bigramBuffer[MAX_WORD_LENGTH];
+    int bigramCodePoints[MAX_WORD_LENGTH];
     BinaryDictionaryBigramsIterator bigramsIt(
             mDictionaryStructurePolicy->getBigramsStructurePolicy(), pos);
     while (bigramsIt.hasNext()) {
@@ -128,7 +70,7 @@ int BigramDictionary::getPredictions(const int *prevWord, const int prevWordLeng
         }
         const int codePointCount = mDictionaryStructurePolicy->
                 getCodePointsAndProbabilityAndReturnCodePointCount(bigramsIt.getBigramPos(),
-                        MAX_WORD_LENGTH, bigramBuffer, &unigramProbability);
+                        MAX_WORD_LENGTH, bigramCodePoints, &unigramProbability);
         if (codePointCount <= 0) {
             continue;
         }
@@ -139,11 +81,8 @@ int BigramDictionary::getPredictions(const int *prevWord, const int prevWordLeng
         // here, but it can't get too bad.
         const int probability = mDictionaryStructurePolicy->getProbability(
                 unigramProbability, bigramsIt.getProbability());
-        addWordBigram(bigramBuffer, codePointCount, probability, outBigramProbability,
-                outBigramCodePoints, outputTypes);
-        ++bigramCount;
+        outSuggestionResults->addPrediction(bigramCodePoints, codePointCount, probability);
     }
-    return std::min(bigramCount, MAX_RESULTS);
 }
 
 // Returns a pointer to the start of the bigram list.
diff --git a/native/jni/src/suggest/core/dictionary/bigram_dictionary.h b/native/jni/src/suggest/core/dictionary/bigram_dictionary.h
index 8af7ee75d9..12aaf20d31 100644
--- a/native/jni/src/suggest/core/dictionary/bigram_dictionary.h
+++ b/native/jni/src/suggest/core/dictionary/bigram_dictionary.h
@@ -22,21 +22,20 @@
 namespace latinime {
 
 class DictionaryStructureWithBufferPolicy;
+class SuggestionResults;
 
 class BigramDictionary {
  public:
     BigramDictionary(const DictionaryStructureWithBufferPolicy *const dictionaryStructurePolicy);
 
-    int getPredictions(const int *word, int length, int *outBigramCodePoints,
-            int *outBigramProbability, int *outputTypes) const;
+    void getPredictions(const int *word, int length,
+            SuggestionResults *const outSuggestionResults) const;
     int getBigramProbability(const int *word1, int length1, const int *word2, int length2) const;
     ~BigramDictionary();
 
  private:
     DISALLOW_IMPLICIT_CONSTRUCTORS(BigramDictionary);
 
-    void addWordBigram(int *word, int length, int probability, int *bigramProbability,
-            int *bigramCodePoints, int *outputTypes) const;
     int getBigramListPositionForWord(const int *prevWord, const int prevWordLength,
             const bool forceLowerCaseSearch) const;
 
diff --git a/native/jni/src/suggest/core/dictionary/dictionary.cpp b/native/jni/src/suggest/core/dictionary/dictionary.cpp
index 59a8a55003..ffa96e167f 100644
--- a/native/jni/src/suggest/core/dictionary/dictionary.cpp
+++ b/native/jni/src/suggest/core/dictionary/dictionary.cpp
@@ -74,12 +74,11 @@ int Dictionary::getSuggestions(ProximityInfo *proximityInfo, DicTraverseSession
     }
 }
 
-int Dictionary::getBigrams(const int *word, int length, int *outWords, int *outputScores,
-        int *outputTypes) const {
+void Dictionary::getPredictions(const int *word, int length,
+        SuggestionResults *const outSuggestionResults) const {
     TimeKeeper::setCurrentTime();
-    if (length <= 0) return 0;
-    return mBigramDictionary->getPredictions(word, length, outWords, outputScores,
-            outputTypes);
+    if (length <= 0) return;
+    mBigramDictionary->getPredictions(word, length, outSuggestionResults);
 }
 
 int Dictionary::getProbability(const int *word, int length) const {
diff --git a/native/jni/src/suggest/core/dictionary/dictionary.h b/native/jni/src/suggest/core/dictionary/dictionary.h
index a7f19c9e63..2dea9fff84 100644
--- a/native/jni/src/suggest/core/dictionary/dictionary.h
+++ b/native/jni/src/suggest/core/dictionary/dictionary.h
@@ -33,6 +33,7 @@ namespace latinime {
 class DictionaryStructureWithBufferPolicy;
 class DicTraverseSession;
 class ProximityInfo;
+class SuggestionResults;
 class SuggestOptions;
 class WordProperty;
 
@@ -67,8 +68,8 @@ class Dictionary {
             const SuggestOptions *const suggestOptions, int *outWords, int *outputScores,
             int *spaceIndices, int *outputTypes, int *outputAutoCommitFirstWordConfidence) const;
 
-    int getBigrams(const int *word, int length, int *outWords, int *outputScores,
-            int *outputTypes) const;
+    void getPredictions(const int *word, int length,
+            SuggestionResults *const outSuggestionResults) const;
 
     int getProbability(const int *word, int length) const;
 
diff --git a/native/jni/src/suggest/core/result/suggested_word.h b/native/jni/src/suggest/core/result/suggested_word.h
new file mode 100644
index 0000000000..48b29d6a62
--- /dev/null
+++ b/native/jni/src/suggest/core/result/suggested_word.h
@@ -0,0 +1,83 @@
+/*
+ * 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_SUGGESTED_WORD_H
+#define LATINIME_SUGGESTED_WORD_H
+
+#include <vector>
+
+#include "defines.h"
+#include "suggest/core/dictionary/dictionary.h"
+
+namespace latinime {
+
+class SuggestedWord {
+ public:
+    class Comparator {
+     public:
+        bool operator()(const SuggestedWord &left, const SuggestedWord &right) {
+            if (left.getScore() != right.getScore()) {
+                return left.getScore() < right.getScore();
+            }
+            return left.getCodePointCount() > right.getCodePointCount();
+        }
+
+     private:
+        DISALLOW_ASSIGNMENT_OPERATOR(Comparator);
+    };
+
+    SuggestedWord(const int *const codePoints, const int codePointCount,
+            const int score, const int type, const int indexToPartialCommit,
+            const int autoCommitFirstWordConfidence)
+            : mCodePoints(codePoints, codePoints + codePointCount), mScore(score),
+              mType(type), mIndexToPartialCommit(indexToPartialCommit),
+              mAutoCommitFirstWordConfidence(autoCommitFirstWordConfidence) {}
+
+    const int *getCodePoint() const {
+        return &mCodePoints.at(0);
+    }
+
+    int getCodePointCount() const {
+        return mCodePoints.size();
+    }
+
+    int getScore() const {
+        return mScore;
+    }
+
+    int getType() const {
+        return mType;
+    }
+
+    int getIndexToPartialCommit() const {
+        return mIndexToPartialCommit;
+    }
+
+    int getAutoCommitFirstWordConfidence() const {
+        return mAutoCommitFirstWordConfidence;
+    }
+
+ private:
+    DISALLOW_DEFAULT_CONSTRUCTOR(SuggestedWord);
+
+    std::vector<int> mCodePoints;
+    int mScore;
+    int mType;
+    int mIndexToPartialCommit;
+    int mAutoCommitFirstWordConfidence;
+};
+} // namespace latinime
+#endif /* LATINIME_SUGGESTED_WORD_H */
diff --git a/native/jni/src/suggest/core/result/suggestion_results.cpp b/native/jni/src/suggest/core/result/suggestion_results.cpp
new file mode 100644
index 0000000000..2be757d839
--- /dev/null
+++ b/native/jni/src/suggest/core/result/suggestion_results.cpp
@@ -0,0 +1,77 @@
+/*
+ * 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.
+ */
+
+#include "suggest/core/result/suggestion_results.h"
+
+namespace latinime {
+
+void SuggestionResults::outputSuggestions(JNIEnv *env, jintArray outSuggestionCount,
+        jintArray outputCodePointsArray, jintArray outScoresArray, jintArray outSpaceIndicesArray,
+        jintArray outTypesArray, jintArray outAutoCommitFirstWordConfidenceArray) {
+    int outputIndex = 0;
+    while (!mSuggestedWords.empty()) {
+        const SuggestedWord &suggestedWord = mSuggestedWords.top();
+        suggestedWord.getCodePointCount();
+        const int start = outputIndex * MAX_WORD_LENGTH;
+        env->SetIntArrayRegion(outputCodePointsArray, start, suggestedWord.getCodePointCount(),
+                suggestedWord.getCodePoint());
+        if (suggestedWord.getCodePointCount() < MAX_WORD_LENGTH) {
+            const int terminal = 0;
+            env->SetIntArrayRegion(outputCodePointsArray, start + suggestedWord.getCodePointCount(),
+                    1 /* len */, &terminal);
+        }
+        const int score = suggestedWord.getScore();
+        env->SetIntArrayRegion(outScoresArray, outputIndex, 1 /* len */, &score);
+        const int indexToPartialCommit = suggestedWord.getIndexToPartialCommit();
+        env->SetIntArrayRegion(outSpaceIndicesArray, outputIndex, 1 /* len */,
+                &indexToPartialCommit);
+        const int type = suggestedWord.getType();
+        env->SetIntArrayRegion(outTypesArray, outputIndex, 1 /* len */, &type);
+        if (mSuggestedWords.size() == 1) {
+            const int autoCommitFirstWordConfidence =
+                    suggestedWord.getAutoCommitFirstWordConfidence();
+            env->SetIntArrayRegion(outAutoCommitFirstWordConfidenceArray, 0 /* start */,
+                    1 /* len */, &autoCommitFirstWordConfidence);
+        }
+        ++outputIndex;
+        mSuggestedWords.pop();
+    }
+    env->SetIntArrayRegion(outSuggestionCount, 0 /* start */, 1 /* len */, &outputIndex);
+}
+
+void SuggestionResults::addPrediction(const int *const codePoints, const int codePointCount,
+        const int probability) {
+    if (codePointCount <= 0 || codePointCount > MAX_WORD_LENGTH
+            || probability == NOT_A_PROBABILITY) {
+        // Invalid word.
+        return;
+    }
+    // Use probability as a score of the word.
+    const int score = probability;
+    if (getSuggestionCount() >= mMaxSuggestionCount) {
+        const SuggestedWord &mWorstSuggestion = mSuggestedWords.top();
+        if (score > mWorstSuggestion.getScore() || (score == mWorstSuggestion.getScore()
+                && codePointCount < mWorstSuggestion.getCodePointCount())) {
+            mSuggestedWords.pop();
+        } else {
+            return;
+        }
+    }
+    mSuggestedWords.push(SuggestedWord(codePoints, codePointCount, score,
+            Dictionary::KIND_PREDICTION, NOT_AN_INDEX, NOT_A_FIRST_WORD_CONFIDENCE));
+}
+
+} // namespace latinime
diff --git a/native/jni/src/suggest/core/result/suggestion_results.h b/native/jni/src/suggest/core/result/suggestion_results.h
new file mode 100644
index 0000000000..0b841ca19f
--- /dev/null
+++ b/native/jni/src/suggest/core/result/suggestion_results.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2013 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_SUGGESTION_RESULTS_H
+#define LATINIME_SUGGESTION_RESULTS_H
+
+#include <queue>
+#include <vector>
+
+#include "defines.h"
+#include "jni.h"
+#include "suggest/core/result/suggested_word.h"
+
+namespace latinime {
+
+class SuggestionResults {
+ public:
+    explicit SuggestionResults(const int maxSuggestionCount)
+            : mMaxSuggestionCount(maxSuggestionCount), mSuggestedWords() {}
+
+    // Returns suggestion count.
+    void outputSuggestions(JNIEnv *env, jintArray outSuggestionCount, jintArray outCodePointsArray,
+            jintArray outScoresArray, jintArray outSpaceIndicesArray, jintArray outTypesArray,
+            jintArray outAutoCommitFirstWordConfidenceArray);
+
+    void addPrediction(const int *const codePoints, const int codePointCount, const int score);
+
+    int getSuggestionCount() const {
+        return mSuggestedWords.size();
+    }
+
+ private:
+    DISALLOW_IMPLICIT_CONSTRUCTORS(SuggestionResults);
+
+    const int mMaxSuggestionCount;
+    std::priority_queue<
+            SuggestedWord, std::vector<SuggestedWord>, SuggestedWord::Comparator> mSuggestedWords;
+};
+} // namespace latinime
+#endif // LATINIME_SUGGESTION_RESULTS_H
-- 
GitLab