From d73edf23aca59e6a0a83a79cf24db3850ef473ff Mon Sep 17 00:00:00 2001
From: Keisuke Kuroyanagi <ksk@google.com>
Date: Thu, 27 Mar 2014 20:05:33 +0900
Subject: [PATCH] Use SuggestionResults to get suggestion.

Bug: 8187060
Bug: 13333066
Change-Id: I435096ecf8422453f9b167adb0ca3b9c8a840018
---
 ...oid_inputmethod_latin_BinaryDictionary.cpp | 43 +++------
 native/jni/src/defines.h                      |  5 +-
 .../suggest/core/dictionary/dictionary.cpp    | 36 +++----
 .../src/suggest/core/dictionary/dictionary.h  |  6 +-
 native/jni/src/suggest/core/policy/scoring.h  |  8 +-
 .../core/result/suggestion_results.cpp        | 45 +++++++--
 .../suggest/core/result/suggestion_results.h  |  6 +-
 .../core/result/suggestions_output_utils.cpp  | 95 +++++--------------
 .../core/result/suggestions_output_utils.h    | 13 ++-
 native/jni/src/suggest/core/suggest.cpp       |  9 +-
 native/jni/src/suggest/core/suggest.h         |  8 +-
 .../jni/src/suggest/core/suggest_interface.h  |  6 +-
 .../policyimpl/typing/typing_scoring.h        | 11 +--
 13 files changed, 122 insertions(+), 169 deletions(-)

diff --git a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
index ac0b4ab152..154ea98003 100644
--- a/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
+++ b/native/jni/com_android_inputmethod_latin_BinaryDictionary.cpp
@@ -199,47 +199,30 @@ static void latinime_BinaryDictionary_getSuggestions(JNIEnv *env, jclass clazz,
         ASSERT(false);
         return;
     }
-    int outputCodePoints[outputCodePointsLength];
-    int scores[scoresLength];
-    const jsize spaceIndicesLength = env->GetArrayLength(outSpaceIndicesArray);
-    int spaceIndices[spaceIndicesLength];
-    const jsize outputTypesLength = env->GetArrayLength(outTypesArray);
-    int outputTypes[outputTypesLength];
     const jsize outputAutoCommitFirstWordConfidenceLength =
             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];
-    memset(outputCodePoints, 0, sizeof(outputCodePoints));
-    memset(scores, 0, sizeof(scores));
-    memset(spaceIndices, 0, sizeof(spaceIndices));
-    memset(outputTypes, 0, sizeof(outputTypes));
-    memset(outputAutoCommitFirstWordConfidence, 0, sizeof(outputAutoCommitFirstWordConfidence));
+    if (outputAutoCommitFirstWordConfidenceLength != 1) {
+        // We only use the first result, as obviously we will only ever autocommit the first one
+        AKLOGE("Invalid outputAutoCommitFirstWordConfidenceLength: %d",
+                outputAutoCommitFirstWordConfidenceLength);
+        ASSERT(false);
+        return;
+    }
 
+    SuggestionResults suggestionResults(MAX_RESULTS);
     if (givenSuggestOptions.isGesture() || inputSize > 0) {
         // TODO: Use SuggestionResults to return suggestions.
-        count = dictionary->getSuggestions(pInfo, traverseSession, xCoordinates, yCoordinates,
+        dictionary->getSuggestions(pInfo, traverseSession, xCoordinates, yCoordinates,
                 times, pointerIds, inputCodePoints, inputSize, prevWordCodePoints,
-                prevWordCodePointsLength, &givenSuggestOptions, outputCodePoints,
-                scores, spaceIndices, outputTypes, outputAutoCommitFirstWordConfidence);
+                prevWordCodePointsLength, &givenSuggestOptions, &suggestionResults);
     } else {
-        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(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);
+    suggestionResults.outputSuggestions(env, outSuggestionCount, outCodePointsArray,
+            outScoresArray, outSpaceIndicesArray, outTypesArray,
+            outAutoCommitFirstWordConfidenceArray);
 }
 
 static jint latinime_BinaryDictionary_getProbability(JNIEnv *env, jclass clazz, jlong dict,
diff --git a/native/jni/src/defines.h b/native/jni/src/defines.h
index 4e6ff95567..3651cd5231 100644
--- a/native/jni/src/defines.h
+++ b/native/jni/src/defines.h
@@ -103,7 +103,8 @@ AK_FORCE_INLINE static int intArrayToCharArray(const int *const source, const in
 #define AKLOGI(fmt, ...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, fmt, ##__VA_ARGS__)
 #endif // defined(HOST_TOOL)
 
-#define DUMP_RESULT(words, frequencies) do { dumpResult(words, frequencies); } while (0)
+#define DUMP_SUGGESTION(words, frequencies, index, score) \
+        do { dumpWordInfo(words, frequencies, index, score); } while (0)
 #define DUMP_WORD(word, length) do { dumpWord(word, length); } while (0)
 #define INTS_TO_CHARS(input, length, output, outlength) do { \
         intArrayToCharArray(input, length, output, outlength); } while (0)
@@ -165,7 +166,7 @@ static inline void showStackTrace() {
 #else // defined(FLAG_DO_PROFILE) || defined(FLAG_DBG)
 #define AKLOGE(fmt, ...)
 #define AKLOGI(fmt, ...)
-#define DUMP_RESULT(words, frequencies)
+#define DUMP_SUGGESTION(words, frequencies, index, score)
 #define DUMP_WORD(word, length)
 #undef DO_ASSERT_TEST
 #define ASSERT(success)
diff --git a/native/jni/src/suggest/core/dictionary/dictionary.cpp b/native/jni/src/suggest/core/dictionary/dictionary.cpp
index 07b07f725a..ae4646d2e3 100644
--- a/native/jni/src/suggest/core/dictionary/dictionary.cpp
+++ b/native/jni/src/suggest/core/dictionary/dictionary.cpp
@@ -22,6 +22,7 @@
 
 #include "defines.h"
 #include "suggest/core/policy/dictionary_header_structure_policy.h"
+#include "suggest/core/result/suggestion_results.h"
 #include "suggest/core/session/dic_traverse_session.h"
 #include "suggest/core/suggest.h"
 #include "suggest/core/suggest_options.h"
@@ -43,34 +44,25 @@ Dictionary::Dictionary(JNIEnv *env, DictionaryStructureWithBufferPolicy::Structu
     logDictionaryInfo(env);
 }
 
-int Dictionary::getSuggestions(ProximityInfo *proximityInfo, DicTraverseSession *traverseSession,
+void Dictionary::getSuggestions(ProximityInfo *proximityInfo, DicTraverseSession *traverseSession,
         int *xcoordinates, int *ycoordinates, int *times, int *pointerIds, int *inputCodePoints,
         int inputSize, int *prevWordCodePoints, int prevWordLength,
-        const SuggestOptions *const suggestOptions, int *outWords, int *outputScores,
-        int *spaceIndices, int *outputTypes, int *outputAutoCommitFirstWordConfidence) const {
+        const SuggestOptions *const suggestOptions,
+        SuggestionResults *const outSuggestionResults) const {
     TimeKeeper::setCurrentTime();
-    int result = 0;
+    DicTraverseSession::initSessionInstance(
+            traverseSession, this, prevWordCodePoints, prevWordLength, suggestOptions);
     if (suggestOptions->isGesture()) {
-        DicTraverseSession::initSessionInstance(
-                traverseSession, this, prevWordCodePoints, prevWordLength, suggestOptions);
-        result = mGestureSuggest->getSuggestions(proximityInfo, traverseSession, xcoordinates,
-                ycoordinates, times, pointerIds, inputCodePoints, inputSize, outWords,
-                outputScores, spaceIndices, outputTypes, outputAutoCommitFirstWordConfidence);
-        if (DEBUG_DICT) {
-            DUMP_RESULT(outWords, outputScores);
-        }
-        return result;
+        mGestureSuggest->getSuggestions(proximityInfo, traverseSession, xcoordinates,
+                ycoordinates, times, pointerIds, inputCodePoints, inputSize,
+                outSuggestionResults);
     } else {
-        DicTraverseSession::initSessionInstance(
-                traverseSession, this, prevWordCodePoints, prevWordLength, suggestOptions);
-        result = mTypingSuggest->getSuggestions(proximityInfo, traverseSession, xcoordinates,
+        mTypingSuggest->getSuggestions(proximityInfo, traverseSession, xcoordinates,
                 ycoordinates, times, pointerIds, inputCodePoints, inputSize,
-                outWords, outputScores, spaceIndices, outputTypes,
-                outputAutoCommitFirstWordConfidence);
-        if (DEBUG_DICT) {
-            DUMP_RESULT(outWords, outputScores);
-        }
-        return result;
+                outSuggestionResults);
+    }
+    if (DEBUG_DICT) {
+        outSuggestionResults->dumpSuggestions();
     }
 }
 
diff --git a/native/jni/src/suggest/core/dictionary/dictionary.h b/native/jni/src/suggest/core/dictionary/dictionary.h
index 4d482e7424..df5fc9b7dd 100644
--- a/native/jni/src/suggest/core/dictionary/dictionary.h
+++ b/native/jni/src/suggest/core/dictionary/dictionary.h
@@ -62,11 +62,11 @@ class Dictionary {
     Dictionary(JNIEnv *env, DictionaryStructureWithBufferPolicy::StructurePolicyPtr
             dictionaryStructureWithBufferPolicy);
 
-    int getSuggestions(ProximityInfo *proximityInfo, DicTraverseSession *traverseSession,
+    void getSuggestions(ProximityInfo *proximityInfo, DicTraverseSession *traverseSession,
             int *xcoordinates, int *ycoordinates, int *times, int *pointerIds, int *inputCodePoints,
             int inputSize, int *prevWordCodePoints, int prevWordLength,
-            const SuggestOptions *const suggestOptions, int *outWords, int *outputScores,
-            int *spaceIndices, int *outputTypes, int *outputAutoCommitFirstWordConfidence) const;
+            const SuggestOptions *const suggestOptions,
+            SuggestionResults *const outSuggestionResults) const;
 
     void getPredictions(const int *word, int length,
             SuggestionResults *const outSuggestionResults) const;
diff --git a/native/jni/src/suggest/core/policy/scoring.h b/native/jni/src/suggest/core/policy/scoring.h
index 0251475d50..292194bf26 100644
--- a/native/jni/src/suggest/core/policy/scoring.h
+++ b/native/jni/src/suggest/core/policy/scoring.h
@@ -23,6 +23,7 @@ namespace latinime {
 
 class DicNode;
 class DicTraverseSession;
+class SuggestionResults;
 
 // This class basically tweaks suggestions and distances apart from CompoundDistance
 class Scoring {
@@ -30,11 +31,8 @@ class Scoring {
     virtual int calculateFinalScore(const float compoundDistance, const int inputSize,
             const ErrorTypeUtils::ErrorType containedErrorTypes, const bool forceCommit,
             const bool boostExactMatches) const = 0;
-    virtual bool getMostProbableString(const DicTraverseSession *const traverseSession,
-            const int terminalSize, const float languageWeight, int *const outputCodePoints,
-            int *const type, int *const freq) const = 0;
-    virtual void safetyNetForMostProbableString(const int scoreCount,
-            const int maxScore, int *const outputCodePoints, int *const scores) const = 0;
+    virtual void getMostProbableString(const DicTraverseSession *const traverseSession,
+            const float languageWeight, SuggestionResults *const outSuggestionResults) const = 0;
     virtual float getAdjustedLanguageWeight(DicTraverseSession *const traverseSession,
             DicNode *const terminals, const int size) const = 0;
     virtual float getDoubleLetterDemotionDistanceCost(
diff --git a/native/jni/src/suggest/core/result/suggestion_results.cpp b/native/jni/src/suggest/core/result/suggestion_results.cpp
index 2be757d839..da1c6bc722 100644
--- a/native/jni/src/suggest/core/result/suggestion_results.cpp
+++ b/native/jni/src/suggest/core/result/suggestion_results.cpp
@@ -54,13 +54,23 @@ void SuggestionResults::outputSuggestions(JNIEnv *env, jintArray outSuggestionCo
 
 void SuggestionResults::addPrediction(const int *const codePoints, const int codePointCount,
         const int probability) {
-    if (codePointCount <= 0 || codePointCount > MAX_WORD_LENGTH
-            || probability == NOT_A_PROBABILITY) {
+    if (probability == NOT_A_PROBABILITY) {
         // Invalid word.
         return;
     }
-    // Use probability as a score of the word.
-    const int score = probability;
+    addSuggestion(codePoints, codePointCount, probability, Dictionary::KIND_PREDICTION,
+            NOT_AN_INDEX, NOT_A_FIRST_WORD_CONFIDENCE);
+}
+
+void SuggestionResults::addSuggestion(const int *const codePoints, const int codePointCount,
+        const int score, const int type, const int indexToPartialCommit,
+        const int autocimmitFirstWordConfindence) {
+    if (codePointCount <= 0 || codePointCount > MAX_WORD_LENGTH) {
+        // Invalid word.
+        AKLOGE("Invalid word is added to the suggestion results. codePointCount: %d",
+                codePointCount);
+        return;
+    }
     if (getSuggestionCount() >= mMaxSuggestionCount) {
         const SuggestedWord &mWorstSuggestion = mSuggestedWords.top();
         if (score > mWorstSuggestion.getScore() || (score == mWorstSuggestion.getScore()
@@ -70,8 +80,31 @@ void SuggestionResults::addPrediction(const int *const codePoints, const int cod
             return;
         }
     }
-    mSuggestedWords.push(SuggestedWord(codePoints, codePointCount, score,
-            Dictionary::KIND_PREDICTION, NOT_AN_INDEX, NOT_A_FIRST_WORD_CONFIDENCE));
+    mSuggestedWords.push(SuggestedWord(codePoints, codePointCount, score, type,
+            indexToPartialCommit, autocimmitFirstWordConfindence));
+}
+
+void SuggestionResults::getSortedScores(int *const outScores) const {
+    auto copyOfSuggestedWords = mSuggestedWords;
+    while (!copyOfSuggestedWords.empty()) {
+        const SuggestedWord &suggestedWord = copyOfSuggestedWords.top();
+        outScores[copyOfSuggestedWords.size() - 1] = suggestedWord.getScore();
+        copyOfSuggestedWords.pop();
+    }
+}
+
+void SuggestionResults::dumpSuggestions() const {
+    std::vector<SuggestedWord> suggestedWords;
+    auto copyOfSuggestedWords = mSuggestedWords;
+    while (!copyOfSuggestedWords.empty()) {
+        suggestedWords.push_back(copyOfSuggestedWords.top());
+        copyOfSuggestedWords.pop();
+    }
+    int index = 0;
+    for (auto it = suggestedWords.rbegin(); it != suggestedWords.rend(); ++it) {
+        DUMP_SUGGESTION(it->getCodePoint(), it->getCodePointCount(), index, it->getScore());
+        index++;
+    }
 }
 
 } // namespace latinime
diff --git a/native/jni/src/suggest/core/result/suggestion_results.h b/native/jni/src/suggest/core/result/suggestion_results.h
index 0b841ca19f..020bab42b6 100644
--- a/native/jni/src/suggest/core/result/suggestion_results.h
+++ b/native/jni/src/suggest/core/result/suggestion_results.h
@@ -35,8 +35,12 @@ class SuggestionResults {
     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);
+    void addSuggestion(const int *const codePoints, const int codePointCount,
+            const int score, const int type, const int indexToPartialCommit,
+            const int autocimmitFirstWordConfindence);
+    void getSortedScores(int *const outScores) const;
+    void dumpSuggestions() const;
 
     int getSuggestionCount() const {
         return mSuggestedWords.size();
diff --git a/native/jni/src/suggest/core/result/suggestions_output_utils.cpp b/native/jni/src/suggest/core/result/suggestions_output_utils.cpp
index a276315100..b40f3226f1 100644
--- a/native/jni/src/suggest/core/result/suggestions_output_utils.cpp
+++ b/native/jni/src/suggest/core/result/suggestions_output_utils.cpp
@@ -24,6 +24,7 @@
 #include "suggest/core/dictionary/dictionary.h"
 #include "suggest/core/dictionary/error_type_utils.h"
 #include "suggest/core/policy/scoring.h"
+#include "suggest/core/result/suggestion_results.h"
 #include "suggest/core/session/dic_traverse_session.h"
 
 namespace latinime {
@@ -31,10 +32,9 @@ namespace latinime {
 const int SuggestionsOutputUtils::MIN_LEN_FOR_MULTI_WORD_AUTOCORRECT = 16;
 
 // TODO: Split this method.
-/* static */ int SuggestionsOutputUtils::outputSuggestions(
+/* static */ void SuggestionsOutputUtils::outputSuggestions(
         const Scoring *const scoringPolicy, DicTraverseSession *traverseSession,
-        int *outputScores, int *outputCodePoints, int *outputIndicesToPartialCommit,
-        int *outputTypes, int *outputAutoCommitFirstWordConfidence) {
+        SuggestionResults *const outSuggestionResults) {
 #if DEBUG_EVALUATE_MOST_PROBABLE_STRING
     const int terminalSize = 0;
 #else
@@ -49,18 +49,6 @@ const int SuggestionsOutputUtils::MIN_LEN_FOR_MULTI_WORD_AUTOCORRECT = 16;
 
     const float languageWeight = scoringPolicy->getAdjustedLanguageWeight(
             traverseSession, terminals, terminalSize);
-
-    int outputWordIndex = 0;
-    // Insert most probable word at index == 0 as long as there is one terminal at least
-    const bool hasMostProbableString =
-            scoringPolicy->getMostProbableString(traverseSession, terminalSize, languageWeight,
-                    &outputCodePoints[0], &outputTypes[0], &outputScores[0]);
-    if (hasMostProbableString) {
-        outputIndicesToPartialCommit[outputWordIndex] = NOT_AN_INDEX;
-        ++outputWordIndex;
-    }
-
-    int maxScore = S_INT_MIN;
     // Force autocorrection for obvious long multi-word suggestions when the top suggestion is
     // a long multiple words suggestion.
     // TODO: Implement a smarter auto-commit method for handling multi-word suggestions.
@@ -75,16 +63,12 @@ const int SuggestionsOutputUtils::MIN_LEN_FOR_MULTI_WORD_AUTOCORRECT = 16;
     // TODO: have partial commit work even with multiple pointers.
     const bool outputSecondWordFirstLetterInputIndex =
             traverseSession->isOnlyOnePointerUsed(0 /* pointerId */);
-    if (terminalSize > 0) {
-        // If we have no suggestions, don't write this
-        outputAutoCommitFirstWordConfidence[0] =
-                computeFirstWordConfidence(&terminals[0]);
-    }
     const bool boostExactMatches = traverseSession->getDictionaryStructurePolicy()->
             getHeaderStructurePolicy()->shouldBoostExactMatches();
+
+    int codePoints[MAX_WORD_LENGTH];
     // Output suggestion results here
-    for (int terminalIndex = 0; terminalIndex < terminalSize && outputWordIndex < MAX_RESULTS;
-            ++terminalIndex) {
+    for (int terminalIndex = 0; terminalIndex < terminalSize; ++terminalIndex) {
         DicNode *terminalDicNode = &terminals[terminalIndex];
         if (DEBUG_GEO_FULL) {
             terminalDicNode->dump("OUT:");
@@ -118,25 +102,18 @@ const int SuggestionsOutputUtils::MIN_LEN_FOR_MULTI_WORD_AUTOCORRECT = 16;
                 (forceCommitMultiWords && terminalDicNode->hasMultipleWords())
                          || (isValidWord && scoringPolicy->doesAutoCorrectValidWord()),
                 boostExactMatches);
-        if (maxScore < finalScore && isValidWord) {
-            maxScore = finalScore;
-        }
 
         // Don't output invalid words. However, we still need to submit their shortcuts if any.
         if (isValidWord) {
-            outputTypes[outputWordIndex] = Dictionary::KIND_CORRECTION | outputTypeFlags;
-            outputScores[outputWordIndex] = finalScore;
-            if (outputSecondWordFirstLetterInputIndex) {
-                outputIndicesToPartialCommit[outputWordIndex] =
-                        terminalDicNode->getSecondWordFirstInputIndex(
-                                traverseSession->getProximityInfoState(0));
-            } else {
-                outputIndicesToPartialCommit[outputWordIndex] = NOT_AN_INDEX;
-            }
-            // Populate the outputChars array with the suggested word.
-            const int startIndex = outputWordIndex * MAX_WORD_LENGTH;
-            terminalDicNode->outputResult(&outputCodePoints[startIndex]);
-            ++outputWordIndex;
+            terminalDicNode->outputResult(codePoints);
+            const int indexToPartialCommit = outputSecondWordFirstLetterInputIndex ?
+                    terminalDicNode->getSecondWordFirstInputIndex(
+                            traverseSession->getProximityInfoState(0)) :
+                    NOT_AN_INDEX;
+            outSuggestionResults->addSuggestion(codePoints,
+                    terminalDicNode->getTotalNodeCodePointCount(),
+                    finalScore, Dictionary::KIND_CORRECTION | outputTypeFlags,
+                    indexToPartialCommit, computeFirstWordConfidence(terminalDicNode));
         }
 
         if (!terminalDicNode->hasMultipleWords()) {
@@ -152,28 +129,11 @@ const int SuggestionsOutputUtils::MIN_LEN_FOR_MULTI_WORD_AUTOCORRECT = 16;
                              traverseSession->getInputSize(),
                              terminalDicNode->getContainedErrorTypes(),
                              true /* forceCommit */, boostExactMatches) : finalScore;
-            const int updatedOutputWordIndex = outputShortcuts(&shortcutIt,
-                    outputWordIndex, shortcutBaseScore, outputCodePoints, outputScores, outputTypes,
-                    sameAsTyped);
-            const int secondWordFirstInputIndex = terminalDicNode->getSecondWordFirstInputIndex(
-                    traverseSession->getProximityInfoState(0));
-            for (int i = outputWordIndex; i < updatedOutputWordIndex; ++i) {
-                if (outputSecondWordFirstLetterInputIndex) {
-                    outputIndicesToPartialCommit[i] = secondWordFirstInputIndex;
-                } else {
-                    outputIndicesToPartialCommit[i] = NOT_AN_INDEX;
-                }
-            }
-            outputWordIndex = updatedOutputWordIndex;
+            outputShortcuts(&shortcutIt, shortcutBaseScore, sameAsTyped, outSuggestionResults);
         }
         DicNode::managedDelete(terminalDicNode);
     }
-
-    if (hasMostProbableString) {
-        scoringPolicy->safetyNetForMostProbableString(outputWordIndex, maxScore,
-                &outputCodePoints[0], outputScores);
-    }
-    return outputWordIndex;
+    scoringPolicy->getMostProbableString(traverseSession, languageWeight, outSuggestionResults);
 }
 
 /* static */ int SuggestionsOutputUtils::computeFirstWordConfidence(
@@ -228,12 +188,11 @@ const int SuggestionsOutputUtils::MIN_LEN_FOR_MULTI_WORD_AUTOCORRECT = 16;
     return distanceContribution + lengthContribution + spaceContribution;
 }
 
-/* static */ int SuggestionsOutputUtils::outputShortcuts(
-        BinaryDictionaryShortcutIterator *const shortcutIt,
-        int outputWordIndex, const int finalScore, int *const outputCodePoints,
-        int *const outputScores, int *const outputTypes, const bool sameAsTyped) {
+/* static */ void SuggestionsOutputUtils::outputShortcuts(
+        BinaryDictionaryShortcutIterator *const shortcutIt, const int finalScore,
+        const bool sameAsTyped, SuggestionResults *const outSuggestionResults) {
     int shortcutTarget[MAX_WORD_LENGTH];
-    while (shortcutIt->hasNextShortcutTarget() && outputWordIndex < MAX_RESULTS) {
+    while (shortcutIt->hasNextShortcutTarget()) {
         bool isWhilelist;
         int shortcutTargetStringLength;
         shortcutIt->nextShortcutTarget(MAX_WORD_LENGTH, shortcutTarget,
@@ -250,15 +209,9 @@ const int SuggestionsOutputUtils::MIN_LEN_FOR_MULTI_WORD_AUTOCORRECT = 16;
             shortcutScore = std::max(S_INT_MIN + 1, shortcutScore) - 1;
             kind = Dictionary::KIND_SHORTCUT;
         }
-        outputTypes[outputWordIndex] = kind;
-        outputScores[outputWordIndex] = shortcutScore;
-        outputScores[outputWordIndex] = std::max(S_INT_MIN + 1, shortcutScore) - 1;
-        const int startIndex2 = outputWordIndex * MAX_WORD_LENGTH;
-        // Copy shortcut target code points to the output buffer.
-        memmove(&outputCodePoints[startIndex2], shortcutTarget,
-                shortcutTargetStringLength * sizeof(shortcutTarget[0]));
-        ++outputWordIndex;
+        outSuggestionResults->addSuggestion(shortcutTarget, shortcutTargetStringLength,
+                std::max(S_INT_MIN + 1, shortcutScore) - 1, kind, NOT_AN_INDEX,
+                NOT_A_FIRST_WORD_CONFIDENCE);
     }
-    return outputWordIndex;
 }
 } // namespace latinime
diff --git a/native/jni/src/suggest/core/result/suggestions_output_utils.h b/native/jni/src/suggest/core/result/suggestions_output_utils.h
index d456a545f1..26d4b40123 100644
--- a/native/jni/src/suggest/core/result/suggestions_output_utils.h
+++ b/native/jni/src/suggest/core/result/suggestions_output_utils.h
@@ -25,16 +25,15 @@ class BinaryDictionaryShortcutIterator;
 class DicNode;
 class DicTraverseSession;
 class Scoring;
+class SuggestionResults;
 
 class SuggestionsOutputUtils {
  public:
     /**
      * Outputs the final list of suggestions (i.e., terminal nodes).
      */
-    static int outputSuggestions(const Scoring *const scoringPolicy,
-            DicTraverseSession *traverseSession, int *outputScores, int *outputCodePoints,
-            int *outputIndicesToPartialCommit, int *outputTypes,
-            int *outputAutoCommitFirstWordConfidence);
+    static void outputSuggestions(const Scoring *const scoringPolicy,
+            DicTraverseSession *traverseSession, SuggestionResults *const outSuggestionResults);
 
  private:
     DISALLOW_IMPLICIT_CONSTRUCTORS(SuggestionsOutputUtils);
@@ -44,9 +43,9 @@ class SuggestionsOutputUtils {
 
     static int computeFirstWordConfidence(const DicNode *const terminalDicNode);
 
-    static int outputShortcuts(BinaryDictionaryShortcutIterator *const shortcutIt,
-            int outputWordIndex, const int finalScore, int *const outputCodePoints,
-            int *const outputScores, int *const outputTypes, const bool sameAsTyped);
+    static void outputShortcuts(BinaryDictionaryShortcutIterator *const shortcutIt,
+            const int finalScore, const bool sameAsTyped,
+            SuggestionResults *const outSuggestionResults);
 };
 } // namespace latinime
 #endif // LATINIME_SUGGESTIONS_OUTPUT_UTILS
diff --git a/native/jni/src/suggest/core/suggest.cpp b/native/jni/src/suggest/core/suggest.cpp
index f6de571a8d..2ea6452afc 100644
--- a/native/jni/src/suggest/core/suggest.cpp
+++ b/native/jni/src/suggest/core/suggest.cpp
@@ -42,10 +42,9 @@ const int Suggest::MIN_CONTINUOUS_SUGGESTION_INPUT_SIZE = 2;
  * automatically activated for sequential calls that share the same starting input.
  * TODO: Stop detecting continuous suggestion. Start using traverseSession instead.
  */
-int Suggest::getSuggestions(ProximityInfo *pInfo, void *traverseSession,
+void Suggest::getSuggestions(ProximityInfo *pInfo, void *traverseSession,
         int *inputXs, int *inputYs, int *times, int *pointerIds, int *inputCodePoints,
-        int inputSize, int *outWords, int *outputScores, int *outputIndices,
-        int *outputTypes, int *outputAutoCommitFirstWordConfidence) const {
+        int inputSize, SuggestionResults *const outSuggestionResults) const {
     PROF_OPEN;
     PROF_START(0);
     const float maxSpatialDistance = TRAVERSAL->getMaxSpatialDistance();
@@ -66,11 +65,9 @@ int Suggest::getSuggestions(ProximityInfo *pInfo, void *traverseSession,
     }
     PROF_END(1);
     PROF_START(2);
-    const int size = SuggestionsOutputUtils::outputSuggestions(SCORING, tSession, outputScores,
-            outWords, outputIndices, outputTypes, outputAutoCommitFirstWordConfidence);
+    SuggestionsOutputUtils::outputSuggestions(SCORING, tSession, outSuggestionResults);
     PROF_END(2);
     PROF_CLOSE;
-    return size;
 }
 
 /**
diff --git a/native/jni/src/suggest/core/suggest.h b/native/jni/src/suggest/core/suggest.h
index 33ea0b658f..13ad621db4 100644
--- a/native/jni/src/suggest/core/suggest.h
+++ b/native/jni/src/suggest/core/suggest.h
@@ -36,6 +36,7 @@ class DicNode;
 class DicTraverseSession;
 class ProximityInfo;
 class Scoring;
+class SuggestionResults;
 class Traversal;
 class Weighting;
 
@@ -46,10 +47,9 @@ class Suggest : public SuggestInterface {
               SCORING(suggestPolicy ? suggestPolicy->getScoring() : nullptr),
               WEIGHTING(suggestPolicy ? suggestPolicy->getWeighting() : nullptr) {}
     AK_FORCE_INLINE virtual ~Suggest() {}
-    int getSuggestions(ProximityInfo *pInfo, void *traverseSession, int *inputXs, int *inputYs,
-            int *times, int *pointerIds, int *inputCodePoints, int inputSize, int *outWords,
-            int *outputScores, int *outputIndices, int *outputTypes,
-            int *outputAutoCommitFirstWordConfidence) const;
+    void getSuggestions(ProximityInfo *pInfo, void *traverseSession, int *inputXs, int *inputYs,
+            int *times, int *pointerIds, int *inputCodePoints, int inputSize,
+            SuggestionResults *const outSuggestionResults) const;
 
  private:
     DISALLOW_IMPLICIT_CONSTRUCTORS(Suggest);
diff --git a/native/jni/src/suggest/core/suggest_interface.h b/native/jni/src/suggest/core/suggest_interface.h
index f10db830fe..c3ffea9a29 100644
--- a/native/jni/src/suggest/core/suggest_interface.h
+++ b/native/jni/src/suggest/core/suggest_interface.h
@@ -22,13 +22,13 @@
 namespace latinime {
 
 class ProximityInfo;
+class SuggestionResults;
 
 class SuggestInterface {
  public:
-    virtual int getSuggestions(ProximityInfo *pInfo, void *traverseSession, int *inputXs,
+    virtual void getSuggestions(ProximityInfo *pInfo, void *traverseSession, int *inputXs,
             int *inputYs, int *times, int *pointerIds, int *inputCodePoints, int inputSize,
-            int *outWords, int *outputScores, int *outputIndices, int *outputTypes,
-            int *outputAutoCommitFirstWordConfidence) const = 0;
+            SuggestionResults *const suggestionResults) const = 0;
     SuggestInterface() {}
     virtual ~SuggestInterface() {}
  private:
diff --git a/native/jni/src/suggest/policyimpl/typing/typing_scoring.h b/native/jni/src/suggest/policyimpl/typing/typing_scoring.h
index 8982800b7a..66ea624068 100644
--- a/native/jni/src/suggest/policyimpl/typing/typing_scoring.h
+++ b/native/jni/src/suggest/policyimpl/typing/typing_scoring.h
@@ -32,15 +32,8 @@ class TypingScoring : public Scoring {
  public:
     static const TypingScoring *getInstance() { return &sInstance; }
 
-    AK_FORCE_INLINE bool getMostProbableString(const DicTraverseSession *const traverseSession,
-            const int terminalSize, const float languageWeight, int *const outputCodePoints,
-            int *const type, int *const freq) const {
-        return false;
-    }
-
-    AK_FORCE_INLINE void safetyNetForMostProbableString(const int scoreCount, const int maxScore,
-            int *const outputCodePoints, int *const scores) const {
-    }
+    AK_FORCE_INLINE void getMostProbableString(const DicTraverseSession *const traverseSession,
+            const float languageWeight, SuggestionResults *const outSuggestionResults) const {}
 
     AK_FORCE_INLINE float getAdjustedLanguageWeight(DicTraverseSession *const traverseSession,
             DicNode *const terminals, const int size) const {
-- 
GitLab