Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(168)

Unified Diff: third_party/android_prediction/suggest/core/dicnode/dic_node.h

Issue 1247903003: Add spellcheck and word suggestion to the prediction service (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: format README and CHROMIUM.diff Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/android_prediction/suggest/core/dicnode/dic_node.h
diff --git a/third_party/android_prediction/suggest/core/dicnode/dic_node.h b/third_party/android_prediction/suggest/core/dicnode/dic_node.h
new file mode 100644
index 0000000000000000000000000000000000000000..f42de7f040541fb8320a0a60e0faef0f462a83b7
--- /dev/null
+++ b/third_party/android_prediction/suggest/core/dicnode/dic_node.h
@@ -0,0 +1,519 @@
+/*
+ * Copyright (C) 2012 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_DIC_NODE_H
+#define LATINIME_DIC_NODE_H
+
+#include "third_party/android_prediction/defines.h"
+#include "third_party/android_prediction/suggest/core/dicnode/dic_node_profiler.h"
+#include "third_party/android_prediction/suggest/core/dicnode/dic_node_utils.h"
+#include "third_party/android_prediction/suggest/core/dicnode/internal/dic_node_state.h"
+#include "third_party/android_prediction/suggest/core/dicnode/internal/dic_node_properties.h"
+#include "third_party/android_prediction/suggest/core/dictionary/digraph_utils.h"
+#include "third_party/android_prediction/suggest/core/dictionary/error_type_utils.h"
+#include "third_party/android_prediction/suggest/core/layout/proximity_info_state.h"
+#include "third_party/android_prediction/utils/char_utils.h"
+
+#if DEBUG_DICT
+#define LOGI_SHOW_ADD_COST_PROP \
+ do { \
+ char charBuf[50]; \
+ INTS_TO_CHARS(getOutputWordBuf(), getNodeCodePointCount(), charBuf, NELEMS(charBuf)); \
+ AKLOGI("%20s, \"%c\", size = %03d, total = %03d, index(0) = %02d, dist = %.4f, %s,,", \
+ __FUNCTION__, getNodeCodePoint(), inputSize, getTotalInputIndex(), \
+ getInputIndex(0), getNormalizedCompoundDistance(), charBuf); \
+ } while (0)
+#define DUMP_WORD_AND_SCORE(header) \
+ do { \
+ char charBuf[50]; \
+ INTS_TO_CHARS(getOutputWordBuf(), \
+ getNodeCodePointCount() \
+ + mDicNodeState.mDicNodeStateOutput.getPrevWordsLength(), \
+ charBuf, NELEMS(charBuf)); \
+ AKLOGI("#%8s, %5f, %5f, %5f, %5f, %s, %d, %5f,", header, \
+ getSpatialDistanceForScoring(), \
+ mDicNodeState.mDicNodeStateScoring.getLanguageDistance(), \
+ getNormalizedCompoundDistance(), getRawLength(), charBuf, \
+ getInputIndex(0), getNormalizedCompoundDistanceAfterFirstWord()); \
+ } while (0)
+#else
+#define LOGI_SHOW_ADD_COST_PROP
+#define DUMP_WORD_AND_SCORE(header)
+#endif
+
+namespace latinime {
+
+// This struct is purely a bucket to return values. No instances of this struct should be kept.
+struct DicNode_InputStateG {
+ DicNode_InputStateG()
+ : mNeedsToUpdateInputStateG(false), mPointerId(0), mInputIndex(0),
+ mPrevCodePoint(0), mTerminalDiffCost(0.0f), mRawLength(0.0f),
+ mDoubleLetterLevel(NOT_A_DOUBLE_LETTER) {}
+
+ bool mNeedsToUpdateInputStateG;
+ int mPointerId;
+ int16_t mInputIndex;
+ int mPrevCodePoint;
+ float mTerminalDiffCost;
+ float mRawLength;
+ DoubleLetterLevel mDoubleLetterLevel;
+};
+
+class DicNode {
+ // Caveat: We define Weighting as a friend class of DicNode to let Weighting change
+ // the distance of DicNode.
+ // Caution!!! In general, we avoid using the "friend" access modifier.
+ // This is an exception to explicitly hide DicNode::addCost() from all classes but Weighting.
+ friend class Weighting;
+
+ public:
+#if DEBUG_DICT
+ DicNodeProfiler mProfiler;
+#endif
+
+ AK_FORCE_INLINE DicNode()
+ :
+#if DEBUG_DICT
+ mProfiler(),
+#endif
+ mDicNodeProperties(), mDicNodeState(), mIsCachedForNextSuggestion(false) {}
+
+ DicNode(const DicNode &dicNode);
+ DicNode &operator=(const DicNode &dicNode);
+ ~DicNode() {}
+
+ // Init for copy
+ void initByCopy(const DicNode *const dicNode) {
+ mIsCachedForNextSuggestion = dicNode->mIsCachedForNextSuggestion;
+ mDicNodeProperties.initByCopy(&dicNode->mDicNodeProperties);
+ mDicNodeState.initByCopy(&dicNode->mDicNodeState);
+ PROF_NODE_COPY(&dicNode->mProfiler, mProfiler);
+ }
+
+ // Init for root with prevWordsPtNodePos which is used for n-gram
+ void initAsRoot(const int rootPtNodeArrayPos, const int *const prevWordsPtNodePos) {
+ mIsCachedForNextSuggestion = false;
+ mDicNodeProperties.init(rootPtNodeArrayPos, prevWordsPtNodePos);
+ mDicNodeState.init();
+ PROF_NODE_RESET(mProfiler);
+ }
+
+ // Init for root with previous word
+ void initAsRootWithPreviousWord(const DicNode *const dicNode, const int rootPtNodeArrayPos) {
+ mIsCachedForNextSuggestion = dicNode->mIsCachedForNextSuggestion;
+ int newPrevWordsPtNodePos[MAX_PREV_WORD_COUNT_FOR_N_GRAM];
+ newPrevWordsPtNodePos[0] = dicNode->mDicNodeProperties.getPtNodePos();
+ for (size_t i = 1; i < NELEMS(newPrevWordsPtNodePos); ++i) {
+ newPrevWordsPtNodePos[i] = dicNode->getPrevWordsTerminalPtNodePos()[i - 1];
+ }
+ mDicNodeProperties.init(rootPtNodeArrayPos, newPrevWordsPtNodePos);
+ mDicNodeState.initAsRootWithPreviousWord(&dicNode->mDicNodeState,
+ dicNode->mDicNodeProperties.getDepth());
+ PROF_NODE_COPY(&dicNode->mProfiler, mProfiler);
+ }
+
+ void initAsPassingChild(const DicNode *parentDicNode) {
+ mIsCachedForNextSuggestion = parentDicNode->mIsCachedForNextSuggestion;
+ const int codePoint =
+ parentDicNode->mDicNodeState.mDicNodeStateOutput.getCurrentWordCodePointAt(
+ parentDicNode->getNodeCodePointCount());
+ mDicNodeProperties.init(&parentDicNode->mDicNodeProperties, codePoint);
+ mDicNodeState.initByCopy(&parentDicNode->mDicNodeState);
+ PROF_NODE_COPY(&parentDicNode->mProfiler, mProfiler);
+ }
+
+ void initAsChild(const DicNode *const dicNode, const int ptNodePos,
+ const int childrenPtNodeArrayPos, const int probability, const bool isTerminal,
+ const bool hasChildren, const bool isBlacklistedOrNotAWord,
+ const uint16_t mergedNodeCodePointCount, const int *const mergedNodeCodePoints) {
+ uint16_t newDepth = static_cast<uint16_t>(dicNode->getNodeCodePointCount() + 1);
+ mIsCachedForNextSuggestion = dicNode->mIsCachedForNextSuggestion;
+ const uint16_t newLeavingDepth = static_cast<uint16_t>(
+ dicNode->mDicNodeProperties.getLeavingDepth() + mergedNodeCodePointCount);
+ mDicNodeProperties.init(ptNodePos, childrenPtNodeArrayPos, mergedNodeCodePoints[0],
+ probability, isTerminal, hasChildren, isBlacklistedOrNotAWord, newDepth,
+ newLeavingDepth, dicNode->mDicNodeProperties.getPrevWordsTerminalPtNodePos());
+ mDicNodeState.init(&dicNode->mDicNodeState, mergedNodeCodePointCount,
+ mergedNodeCodePoints);
+ PROF_NODE_COPY(&dicNode->mProfiler, mProfiler);
+ }
+
+ bool isRoot() const {
+ return getNodeCodePointCount() == 0;
+ }
+
+ bool hasChildren() const {
+ return mDicNodeProperties.hasChildren();
+ }
+
+ bool isLeavingNode() const {
+ ASSERT(getNodeCodePointCount() <= mDicNodeProperties.getLeavingDepth());
+ return getNodeCodePointCount() == mDicNodeProperties.getLeavingDepth();
+ }
+
+ AK_FORCE_INLINE bool isFirstLetter() const {
+ return getNodeCodePointCount() == 1;
+ }
+
+ bool isCached() const {
+ return mIsCachedForNextSuggestion;
+ }
+
+ void setCached() {
+ mIsCachedForNextSuggestion = true;
+ }
+
+ // Check if the current word and the previous word can be considered as a valid multiple word
+ // suggestion.
+ bool isValidMultipleWordSuggestion() const {
+ if (isBlacklistedOrNotAWord()) {
+ return false;
+ }
+ // Treat suggestion as invalid if the current and the previous word are single character
+ // words.
+ const int prevWordLen = mDicNodeState.mDicNodeStateOutput.getPrevWordsLength()
+ - mDicNodeState.mDicNodeStateOutput.getPrevWordStart() - 1;
+ const int currentWordLen = getNodeCodePointCount();
+ return (prevWordLen != 1 || currentWordLen != 1);
+ }
+
+ bool isFirstCharUppercase() const {
+ const int c = mDicNodeState.mDicNodeStateOutput.getCurrentWordCodePointAt(0);
+ return CharUtils::isAsciiUpper(c);
+ }
+
+ bool isCompletion(const int inputSize) const {
+ return mDicNodeState.mDicNodeStateInput.getInputIndex(0) >= inputSize;
+ }
+
+ bool canDoLookAheadCorrection(const int inputSize) const {
+ return mDicNodeState.mDicNodeStateInput.getInputIndex(0) < inputSize - 1;
+ }
+
+ // Used to get n-gram probability in DicNodeUtils.
+ int getPtNodePos() const {
+ return mDicNodeProperties.getPtNodePos();
+ }
+
+ // TODO: Use view class to return PtNodePos array.
+ const int *getPrevWordsTerminalPtNodePos() const {
+ return mDicNodeProperties.getPrevWordsTerminalPtNodePos();
+ }
+
+ // Used in DicNodeUtils
+ int getChildrenPtNodeArrayPos() const {
+ return mDicNodeProperties.getChildrenPtNodeArrayPos();
+ }
+
+ int getProbability() const {
+ return mDicNodeProperties.getProbability();
+ }
+
+ AK_FORCE_INLINE bool isTerminalDicNode() const {
+ const bool isTerminalPtNode = mDicNodeProperties.isTerminal();
+ const int currentDicNodeDepth = getNodeCodePointCount();
+ const int terminalDicNodeDepth = mDicNodeProperties.getLeavingDepth();
+ return isTerminalPtNode && currentDicNodeDepth > 0
+ && currentDicNodeDepth == terminalDicNodeDepth;
+ }
+
+ bool shouldBeFilteredBySafetyNetForBigram() const {
+ const uint16_t currentDepth = getNodeCodePointCount();
+ const int prevWordLen = mDicNodeState.mDicNodeStateOutput.getPrevWordsLength()
+ - mDicNodeState.mDicNodeStateOutput.getPrevWordStart() - 1;
+ return !(currentDepth > 0 && (currentDepth != 1 || prevWordLen != 1));
+ }
+
+ bool hasMatchedOrProximityCodePoints() const {
+ // This DicNode does not have matched or proximity code points when all code points have
+ // been handled as edit corrections or completion so far.
+ const int editCorrectionCount = mDicNodeState.mDicNodeStateScoring.getEditCorrectionCount();
+ const int completionCount = mDicNodeState.mDicNodeStateScoring.getCompletionCount();
+ return (editCorrectionCount + completionCount) < getNodeCodePointCount();
+ }
+
+ bool isTotalInputSizeExceedingLimit() const {
+ // TODO: 3 can be 2? Needs to be investigated.
+ // TODO: Have a const variable for 3 (or 2)
+ return getTotalNodeCodePointCount() > MAX_WORD_LENGTH - 3;
+ }
+
+ void outputResult(int *dest) const {
+ memmove(dest, getOutputWordBuf(), getTotalNodeCodePointCount() * sizeof(dest[0]));
+ DUMP_WORD_AND_SCORE("OUTPUT");
+ }
+
+ // "Total" in this context (and other methods in this class) means the whole suggestion. When
+ // this represents a multi-word suggestion, the referenced PtNode (in mDicNodeState) is only
+ // the one that corresponds to the last word of the suggestion, and all the previous words
+ // are concatenated together in mDicNodeStateOutput.
+ int getTotalNodeSpaceCount() const {
+ if (!hasMultipleWords()) {
+ return 0;
+ }
+ return CharUtils::getSpaceCount(mDicNodeState.mDicNodeStateOutput.getCodePointBuf(),
+ mDicNodeState.mDicNodeStateOutput.getPrevWordsLength());
+ }
+
+ int getSecondWordFirstInputIndex(const ProximityInfoState *const pInfoState) const {
+ const int inputIndex = mDicNodeState.mDicNodeStateOutput.getSecondWordFirstInputIndex();
+ if (inputIndex == NOT_AN_INDEX) {
+ return NOT_AN_INDEX;
+ } else {
+ return pInfoState->getInputIndexOfSampledPoint(inputIndex);
+ }
+ }
+
+ bool hasMultipleWords() const {
+ return mDicNodeState.mDicNodeStateOutput.getPrevWordCount() > 0;
+ }
+
+ int getProximityCorrectionCount() const {
+ return mDicNodeState.mDicNodeStateScoring.getProximityCorrectionCount();
+ }
+
+ int getEditCorrectionCount() const {
+ return mDicNodeState.mDicNodeStateScoring.getEditCorrectionCount();
+ }
+
+ // Used to prune nodes
+ float getNormalizedCompoundDistance() const {
+ return mDicNodeState.mDicNodeStateScoring.getNormalizedCompoundDistance();
+ }
+
+ // Used to prune nodes
+ float getNormalizedSpatialDistance() const {
+ return mDicNodeState.mDicNodeStateScoring.getSpatialDistance()
+ / static_cast<float>(getInputIndex(0) + 1);
+ }
+
+ // Used to prune nodes
+ float getCompoundDistance() const {
+ return mDicNodeState.mDicNodeStateScoring.getCompoundDistance();
+ }
+
+ // Used to prune nodes
+ float getCompoundDistance(const float languageWeight) const {
+ return mDicNodeState.mDicNodeStateScoring.getCompoundDistance(languageWeight);
+ }
+
+ AK_FORCE_INLINE const int *getOutputWordBuf() const {
+ return mDicNodeState.mDicNodeStateOutput.getCodePointBuf();
+ }
+
+ int getPrevCodePointG(int pointerId) const {
+ return mDicNodeState.mDicNodeStateInput.getPrevCodePoint(pointerId);
+ }
+
+ // Whether the current codepoint can be an intentional omission, in which case the traversal
+ // algorithm will always check for a possible omission here.
+ bool canBeIntentionalOmission() const {
+ return CharUtils::isIntentionalOmissionCodePoint(getNodeCodePoint());
+ }
+
+ // Whether the omission is so frequent that it should incur zero cost.
+ bool isZeroCostOmission() const {
+ // TODO: do not hardcode and read from header
+ return (getNodeCodePoint() == KEYCODE_SINGLE_QUOTE);
+ }
+
+ // TODO: remove
+ float getTerminalDiffCostG(int path) const {
+ return mDicNodeState.mDicNodeStateInput.getTerminalDiffCost(path);
+ }
+
+ //////////////////////
+ // Temporary getter //
+ // TODO: Remove //
+ //////////////////////
+ // TODO: Remove once touch path is merged into ProximityInfoState
+ // Note: Returned codepoint may be a digraph codepoint if the node is in a composite glyph.
+ int getNodeCodePoint() const {
+ const int codePoint = mDicNodeProperties.getDicNodeCodePoint();
+ const DigraphUtils::DigraphCodePointIndex digraphIndex =
+ mDicNodeState.mDicNodeStateScoring.getDigraphIndex();
+ if (digraphIndex == DigraphUtils::NOT_A_DIGRAPH_INDEX) {
+ return codePoint;
+ }
+ return DigraphUtils::getDigraphCodePointForIndex(codePoint, digraphIndex);
+ }
+
+ ////////////////////////////////
+ // Utils for cost calculation //
+ ////////////////////////////////
+ AK_FORCE_INLINE bool isSameNodeCodePoint(const DicNode *const dicNode) const {
+ return mDicNodeProperties.getDicNodeCodePoint()
+ == dicNode->mDicNodeProperties.getDicNodeCodePoint();
+ }
+
+ // TODO: remove
+ // TODO: rename getNextInputIndex
+ int16_t getInputIndex(int pointerId) const {
+ return mDicNodeState.mDicNodeStateInput.getInputIndex(pointerId);
+ }
+
+ ////////////////////////////////////
+ // Getter of features for scoring //
+ ////////////////////////////////////
+ float getSpatialDistanceForScoring() const {
+ return mDicNodeState.mDicNodeStateScoring.getSpatialDistance();
+ }
+
+ // For space-aware gestures, we store the normalized distance at the char index
+ // that ends the first word of the suggestion. We call this the distance after
+ // first word.
+ float getNormalizedCompoundDistanceAfterFirstWord() const {
+ return mDicNodeState.mDicNodeStateScoring.getNormalizedCompoundDistanceAfterFirstWord();
+ }
+
+ float getRawLength() const {
+ return mDicNodeState.mDicNodeStateScoring.getRawLength();
+ }
+
+ DoubleLetterLevel getDoubleLetterLevel() const {
+ return mDicNodeState.mDicNodeStateScoring.getDoubleLetterLevel();
+ }
+
+ void setDoubleLetterLevel(DoubleLetterLevel doubleLetterLevel) {
+ mDicNodeState.mDicNodeStateScoring.setDoubleLetterLevel(doubleLetterLevel);
+ }
+
+ bool isInDigraph() const {
+ return mDicNodeState.mDicNodeStateScoring.getDigraphIndex()
+ != DigraphUtils::NOT_A_DIGRAPH_INDEX;
+ }
+
+ void advanceDigraphIndex() {
+ mDicNodeState.mDicNodeStateScoring.advanceDigraphIndex();
+ }
+
+ ErrorTypeUtils::ErrorType getContainedErrorTypes() const {
+ return mDicNodeState.mDicNodeStateScoring.getContainedErrorTypes();
+ }
+
+ bool isBlacklistedOrNotAWord() const {
+ return mDicNodeProperties.isBlacklistedOrNotAWord();
+ }
+
+ inline uint16_t getNodeCodePointCount() const {
+ return mDicNodeProperties.getDepth();
+ }
+
+ // Returns code point count including spaces
+ inline uint16_t getTotalNodeCodePointCount() const {
+ return getNodeCodePointCount() + mDicNodeState.mDicNodeStateOutput.getPrevWordsLength();
+ }
+
+ AK_FORCE_INLINE void dump(const char *tag) const {
+#if DEBUG_DICT
+ DUMP_WORD_AND_SCORE(tag);
+#if DEBUG_DUMP_ERROR
+ mProfiler.dump();
+#endif
+#endif
+ }
+
+ AK_FORCE_INLINE bool compare(const DicNode *right) const {
+ // Promote exact matches to prevent them from being pruned.
+ const bool leftExactMatch = ErrorTypeUtils::isExactMatch(getContainedErrorTypes());
+ const bool rightExactMatch = ErrorTypeUtils::isExactMatch(right->getContainedErrorTypes());
+ if (leftExactMatch != rightExactMatch) {
+ return leftExactMatch;
+ }
+ const float diff =
+ right->getNormalizedCompoundDistance() - getNormalizedCompoundDistance();
+ static const float MIN_DIFF = 0.000001f;
+ if (diff > MIN_DIFF) {
+ return true;
+ } else if (diff < -MIN_DIFF) {
+ return false;
+ }
+ const int depth = getNodeCodePointCount();
+ const int depthDiff = right->getNodeCodePointCount() - depth;
+ if (depthDiff != 0) {
+ return depthDiff > 0;
+ }
+ for (int i = 0; i < depth; ++i) {
+ const int codePoint = mDicNodeState.mDicNodeStateOutput.getCurrentWordCodePointAt(i);
+ const int rightCodePoint =
+ right->mDicNodeState.mDicNodeStateOutput.getCurrentWordCodePointAt(i);
+ if (codePoint != rightCodePoint) {
+ return rightCodePoint > codePoint;
+ }
+ }
+ // Compare pointer values here for stable comparison
+ return this > right;
+ }
+
+ private:
+ DicNodeProperties mDicNodeProperties;
+ DicNodeState mDicNodeState;
+ // TODO: Remove
+ bool mIsCachedForNextSuggestion;
+
+ AK_FORCE_INLINE int getTotalInputIndex() const {
+ int index = 0;
+ for (int i = 0; i < MAX_POINTER_COUNT_G; i++) {
+ index += mDicNodeState.mDicNodeStateInput.getInputIndex(i);
+ }
+ return index;
+ }
+
+ // Caveat: Must not be called outside Weighting
+ // This restriction is guaranteed by "friend"
+ AK_FORCE_INLINE void addCost(const float spatialCost, const float languageCost,
+ const bool doNormalization, const int inputSize,
+ const ErrorTypeUtils::ErrorType errorType) {
+ if (DEBUG_GEO_FULL) {
+ LOGI_SHOW_ADD_COST_PROP;
+ }
+ mDicNodeState.mDicNodeStateScoring.addCost(spatialCost, languageCost, doNormalization,
+ inputSize, getTotalInputIndex(), errorType);
+ }
+
+ // Saves the current normalized compound distance for space-aware gestures.
+ // See getNormalizedCompoundDistanceAfterFirstWord for details.
+ AK_FORCE_INLINE void saveNormalizedCompoundDistanceAfterFirstWordIfNoneYet() {
+ mDicNodeState.mDicNodeStateScoring.saveNormalizedCompoundDistanceAfterFirstWordIfNoneYet();
+ }
+
+ // Caveat: Must not be called outside Weighting
+ // This restriction is guaranteed by "friend"
+ AK_FORCE_INLINE void forwardInputIndex(const int pointerId, const int count,
+ const bool overwritesPrevCodePointByNodeCodePoint) {
+ if (count == 0) {
+ return;
+ }
+ mDicNodeState.mDicNodeStateInput.forwardInputIndex(pointerId, count);
+ if (overwritesPrevCodePointByNodeCodePoint) {
+ mDicNodeState.mDicNodeStateInput.setPrevCodePoint(0, getNodeCodePoint());
+ }
+ }
+
+ AK_FORCE_INLINE void updateInputIndexG(const DicNode_InputStateG *const inputStateG) {
+ if (mDicNodeState.mDicNodeStateOutput.getPrevWordCount() == 1 && isFirstLetter()) {
+ mDicNodeState.mDicNodeStateOutput.setSecondWordFirstInputIndex(
+ inputStateG->mInputIndex);
+ }
+ mDicNodeState.mDicNodeStateInput.updateInputIndexG(inputStateG->mPointerId,
+ inputStateG->mInputIndex, inputStateG->mPrevCodePoint,
+ inputStateG->mTerminalDiffCost, inputStateG->mRawLength);
+ mDicNodeState.mDicNodeStateScoring.addRawLength(inputStateG->mRawLength);
+ mDicNodeState.mDicNodeStateScoring.setDoubleLetterLevel(inputStateG->mDoubleLetterLevel);
+ }
+};
+} // namespace latinime
+#endif // LATINIME_DIC_NODE_H
« no previous file with comments | « third_party/android_prediction/defines.h ('k') | third_party/android_prediction/suggest/core/dicnode/dic_node.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698