dic_node.h revision 5a0381f7d00abf97f5792958420dbb8535977ab3
1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef LATINIME_DIC_NODE_H
18#define LATINIME_DIC_NODE_H
19
20#include "defines.h"
21#include "suggest/core/dicnode/dic_node_profiler.h"
22#include "suggest/core/dicnode/dic_node_release_listener.h"
23#include "suggest/core/dicnode/internal/dic_node_state.h"
24#include "suggest/core/dicnode/internal/dic_node_properties.h"
25#include "suggest/core/dictionary/digraph_utils.h"
26#include "suggest/core/dictionary/error_type_utils.h"
27#include "utils/char_utils.h"
28
29#if DEBUG_DICT
30#define LOGI_SHOW_ADD_COST_PROP \
31        do { char charBuf[50]; \
32        INTS_TO_CHARS(getOutputWordBuf(), getNodeCodePointCount(), charBuf, NELEMS(charBuf)); \
33        AKLOGI("%20s, \"%c\", size = %03d, total = %03d, index(0) = %02d, dist = %.4f, %s,,", \
34                __FUNCTION__, getNodeCodePoint(), inputSize, getTotalInputIndex(), \
35                getInputIndex(0), getNormalizedCompoundDistance(), charBuf); } while (0)
36#define DUMP_WORD_AND_SCORE(header) \
37        do { char charBuf[50]; char prevWordCharBuf[50]; \
38        INTS_TO_CHARS(getOutputWordBuf(), getNodeCodePointCount(), charBuf, NELEMS(charBuf)); \
39        INTS_TO_CHARS(mDicNodeState.mDicNodeStatePrevWord.getPrevWordBuf(), \
40                mDicNodeState.mDicNodeStatePrevWord.getPrevWordLength(), prevWordCharBuf, \
41                NELEMS(prevWordCharBuf)); \
42        AKLOGI("#%8s, %5f, %5f, %5f, %5f, %s, %s, %d, %5f,", header, \
43                getSpatialDistanceForScoring(), getLanguageDistanceForScoring(), \
44                getNormalizedCompoundDistance(), getRawLength(), prevWordCharBuf, charBuf, \
45                getInputIndex(0), getNormalizedCompoundDistanceAfterFirstWord()); \
46        } while (0)
47#else
48#define LOGI_SHOW_ADD_COST_PROP
49#define DUMP_WORD_AND_SCORE(header)
50#endif
51
52namespace latinime {
53
54// This struct is purely a bucket to return values. No instances of this struct should be kept.
55struct DicNode_InputStateG {
56    DicNode_InputStateG()
57            : mNeedsToUpdateInputStateG(false), mPointerId(0), mInputIndex(0),
58              mPrevCodePoint(0), mTerminalDiffCost(0.0f), mRawLength(0.0f),
59              mDoubleLetterLevel(NOT_A_DOUBLE_LETTER) {}
60
61    bool mNeedsToUpdateInputStateG;
62    int mPointerId;
63    int16_t mInputIndex;
64    int mPrevCodePoint;
65    float mTerminalDiffCost;
66    float mRawLength;
67    DoubleLetterLevel mDoubleLetterLevel;
68};
69
70class DicNode {
71    // Caveat: We define Weighting as a friend class of DicNode to let Weighting change
72    // the distance of DicNode.
73    // Caution!!! In general, we avoid using the "friend" access modifier.
74    // This is an exception to explicitly hide DicNode::addCost() from all classes but Weighting.
75    friend class Weighting;
76
77 public:
78#if DEBUG_DICT
79    DicNodeProfiler mProfiler;
80#endif
81    //////////////////
82    // Memory utils //
83    //////////////////
84    AK_FORCE_INLINE static void managedDelete(DicNode *node) {
85        node->remove();
86    }
87    // end
88    /////////////////
89
90    AK_FORCE_INLINE DicNode()
91            :
92#if DEBUG_DICT
93              mProfiler(),
94#endif
95              mDicNodeProperties(), mDicNodeState(), mIsCachedForNextSuggestion(false),
96              mIsUsed(false), mReleaseListener(nullptr) {}
97
98    DicNode(const DicNode &dicNode);
99    DicNode &operator=(const DicNode &dicNode);
100    ~DicNode() {}
101
102    // Init for copy
103    void initByCopy(const DicNode *const dicNode) {
104        mIsUsed = true;
105        mIsCachedForNextSuggestion = dicNode->mIsCachedForNextSuggestion;
106        mDicNodeProperties.init(&dicNode->mDicNodeProperties);
107        mDicNodeState.init(&dicNode->mDicNodeState);
108        PROF_NODE_COPY(&dicNode->mProfiler, mProfiler);
109    }
110
111    // Init for root with prevWordPtNodePos which is used for bigram
112    void initAsRoot(const int rootPtNodeArrayPos, const int prevWordPtNodePos) {
113        mIsUsed = true;
114        mIsCachedForNextSuggestion = false;
115        mDicNodeProperties.init(
116                NOT_A_DICT_POS /* pos */, rootPtNodeArrayPos, NOT_A_CODE_POINT /* nodeCodePoint */,
117                NOT_A_PROBABILITY /* probability */, false /* isTerminal */,
118                true /* hasChildren */, false /* isBlacklistedOrNotAWord */, 0 /* depth */,
119                0 /* terminalDepth */);
120        mDicNodeState.init(prevWordPtNodePos);
121        PROF_NODE_RESET(mProfiler);
122    }
123
124    // Init for root with previous word
125    void initAsRootWithPreviousWord(const DicNode *const dicNode, const int rootPtNodeArrayPos) {
126        mIsUsed = true;
127        mIsCachedForNextSuggestion = dicNode->mIsCachedForNextSuggestion;
128        mDicNodeProperties.init(
129                NOT_A_DICT_POS /* pos */, rootPtNodeArrayPos, NOT_A_CODE_POINT /* nodeCodePoint */,
130                NOT_A_PROBABILITY /* probability */, false /* isTerminal */,
131                true /* hasChildren */, false /* isBlacklistedOrNotAWord */,  0 /* depth */,
132                0 /* terminalDepth */);
133        mDicNodeState.initAsRootWithPreviousWord(&dicNode->mDicNodeState,
134                dicNode->mDicNodeProperties.getPtNodePos(),
135                dicNode->mDicNodeProperties.getDepth());
136        PROF_NODE_COPY(&dicNode->mProfiler, mProfiler);
137    }
138
139    void initAsPassingChild(DicNode *parentDicNode) {
140        mIsUsed = true;
141        mIsCachedForNextSuggestion = parentDicNode->mIsCachedForNextSuggestion;
142        const int parentCodePoint = parentDicNode->getNodeTypedCodePoint();
143        mDicNodeProperties.init(&parentDicNode->mDicNodeProperties, parentCodePoint);
144        mDicNodeState.init(&parentDicNode->mDicNodeState);
145        PROF_NODE_COPY(&parentDicNode->mProfiler, mProfiler);
146    }
147
148    void initAsChild(const DicNode *const dicNode, const int ptNodePos,
149            const int childrenPtNodeArrayPos, const int probability, const bool isTerminal,
150            const bool hasChildren, const bool isBlacklistedOrNotAWord,
151            const uint16_t mergedNodeCodePointCount, const int *const mergedNodeCodePoints) {
152        mIsUsed = true;
153        uint16_t newDepth = static_cast<uint16_t>(dicNode->getNodeCodePointCount() + 1);
154        mIsCachedForNextSuggestion = dicNode->mIsCachedForNextSuggestion;
155        const uint16_t newLeavingDepth = static_cast<uint16_t>(
156                dicNode->mDicNodeProperties.getLeavingDepth() + mergedNodeCodePointCount);
157        mDicNodeProperties.init(ptNodePos, childrenPtNodeArrayPos, mergedNodeCodePoints[0],
158                probability, isTerminal, hasChildren, isBlacklistedOrNotAWord, newDepth,
159                newLeavingDepth);
160        mDicNodeState.init(&dicNode->mDicNodeState, mergedNodeCodePointCount,
161                mergedNodeCodePoints);
162        PROF_NODE_COPY(&dicNode->mProfiler, mProfiler);
163    }
164
165    AK_FORCE_INLINE void remove() {
166        mIsUsed = false;
167        if (mReleaseListener) {
168            mReleaseListener->onReleased(this);
169        }
170    }
171
172    bool isUsed() const {
173        return mIsUsed;
174    }
175
176    bool isRoot() const {
177        return getNodeCodePointCount() == 0;
178    }
179
180    bool hasChildren() const {
181        return mDicNodeProperties.hasChildren();
182    }
183
184    bool isLeavingNode() const {
185        ASSERT(getNodeCodePointCount() <= mDicNodeProperties.getLeavingDepth());
186        return getNodeCodePointCount() == mDicNodeProperties.getLeavingDepth();
187    }
188
189    AK_FORCE_INLINE bool isFirstLetter() const {
190        return getNodeCodePointCount() == 1;
191    }
192
193    bool isCached() const {
194        return mIsCachedForNextSuggestion;
195    }
196
197    void setCached() {
198        mIsCachedForNextSuggestion = true;
199    }
200
201    // Used to expand the node in DicNodeUtils
202    int getNodeTypedCodePoint() const {
203        return mDicNodeState.mDicNodeStateOutput.getCodePointAt(getNodeCodePointCount());
204    }
205
206    // Check if the current word and the previous word can be considered as a valid multiple word
207    // suggestion.
208    bool isValidMultipleWordSuggestion() const {
209        if (isBlacklistedOrNotAWord()) {
210            return false;
211        }
212        // Treat suggestion as invalid if the current and the previous word are single character
213        // words.
214        const int prevWordLen = mDicNodeState.mDicNodeStatePrevWord.getPrevWordLength()
215                - mDicNodeState.mDicNodeStatePrevWord.getPrevWordStart() - 1;
216        const int currentWordLen = getNodeCodePointCount();
217        return (prevWordLen != 1 || currentWordLen != 1);
218    }
219
220    bool isFirstCharUppercase() const {
221        const int c = mDicNodeState.mDicNodeStateOutput.getCodePointAt(0);
222        return CharUtils::isAsciiUpper(c);
223    }
224
225    bool isFirstWord() const {
226        return mDicNodeState.mDicNodeStatePrevWord.getPrevWordPtNodePos() == NOT_A_DICT_POS;
227    }
228
229    bool isCompletion(const int inputSize) const {
230        return mDicNodeState.mDicNodeStateInput.getInputIndex(0) >= inputSize;
231    }
232
233    bool canDoLookAheadCorrection(const int inputSize) const {
234        return mDicNodeState.mDicNodeStateInput.getInputIndex(0) < inputSize - 1;
235    }
236
237    // Used to get bigram probability in DicNodeUtils
238    int getPtNodePos() const {
239        return mDicNodeProperties.getPtNodePos();
240    }
241
242    // Used to get bigram probability in DicNodeUtils
243    int getPrevWordTerminalPtNodePos() const {
244        return mDicNodeState.mDicNodeStatePrevWord.getPrevWordPtNodePos();
245    }
246
247    // Used in DicNodeUtils
248    int getChildrenPtNodeArrayPos() const {
249        return mDicNodeProperties.getChildrenPtNodeArrayPos();
250    }
251
252    int getProbability() const {
253        return mDicNodeProperties.getProbability();
254    }
255
256    AK_FORCE_INLINE bool isTerminalDicNode() const {
257        const bool isTerminalPtNode = mDicNodeProperties.isTerminal();
258        const int currentDicNodeDepth = getNodeCodePointCount();
259        const int terminalDicNodeDepth = mDicNodeProperties.getLeavingDepth();
260        return isTerminalPtNode && currentDicNodeDepth > 0
261                && currentDicNodeDepth == terminalDicNodeDepth;
262    }
263
264    bool shouldBeFilteredBySafetyNetForBigram() const {
265        const uint16_t currentDepth = getNodeCodePointCount();
266        const int prevWordLen = mDicNodeState.mDicNodeStatePrevWord.getPrevWordLength()
267                - mDicNodeState.mDicNodeStatePrevWord.getPrevWordStart() - 1;
268        return !(currentDepth > 0 && (currentDepth != 1 || prevWordLen != 1));
269    }
270
271    bool hasMatchedOrProximityCodePoints() const {
272        // This DicNode does not have matched or proximity code points when all code points have
273        // been handled as edit corrections so far.
274        return mDicNodeState.mDicNodeStateScoring.getEditCorrectionCount()
275                < getNodeCodePointCount();
276    }
277
278    bool isTotalInputSizeExceedingLimit() const {
279        const int prevWordsLen = mDicNodeState.mDicNodeStatePrevWord.getPrevWordLength();
280        const int currentWordDepth = getNodeCodePointCount();
281        // TODO: 3 can be 2? Needs to be investigated.
282        // TODO: Have a const variable for 3 (or 2)
283        return prevWordsLen + currentWordDepth > MAX_WORD_LENGTH - 3;
284    }
285
286    void outputResult(int *dest) const {
287        const uint16_t prevWordLength = mDicNodeState.mDicNodeStatePrevWord.getPrevWordLength();
288        const uint16_t currentDepth = getNodeCodePointCount();
289        DicNodeUtils::appendTwoWords(mDicNodeState.mDicNodeStatePrevWord.getPrevWordBuf(),
290                   prevWordLength, getOutputWordBuf(), currentDepth, dest);
291        DUMP_WORD_AND_SCORE("OUTPUT");
292    }
293
294    // "Total" in this context (and other methods in this class) means the whole suggestion. When
295    // this represents a multi-word suggestion, the referenced PtNode (in mDicNodeState) is only
296    // the one that corresponds to the last word of the suggestion, and all the previous words
297    // are concatenated together in mPrevWord - which contains a space at the end.
298    int getTotalNodeSpaceCount() const {
299        if (isFirstWord()) return 0;
300        return CharUtils::getSpaceCount(mDicNodeState.mDicNodeStatePrevWord.getPrevWordBuf(),
301                mDicNodeState.mDicNodeStatePrevWord.getPrevWordLength());
302    }
303
304    int getSecondWordFirstInputIndex(const ProximityInfoState *const pInfoState) const {
305        const int inputIndex = mDicNodeState.mDicNodeStatePrevWord.getSecondWordFirstInputIndex();
306        if (inputIndex == NOT_AN_INDEX) {
307            return NOT_AN_INDEX;
308        } else {
309            return pInfoState->getInputIndexOfSampledPoint(inputIndex);
310        }
311    }
312
313    bool hasMultipleWords() const {
314        return mDicNodeState.mDicNodeStatePrevWord.getPrevWordCount() > 0;
315    }
316
317    int getProximityCorrectionCount() const {
318        return mDicNodeState.mDicNodeStateScoring.getProximityCorrectionCount();
319    }
320
321    int getEditCorrectionCount() const {
322        return mDicNodeState.mDicNodeStateScoring.getEditCorrectionCount();
323    }
324
325    // Used to prune nodes
326    float getNormalizedCompoundDistance() const {
327        return mDicNodeState.mDicNodeStateScoring.getNormalizedCompoundDistance();
328    }
329
330    // Used to prune nodes
331    float getNormalizedSpatialDistance() const {
332        return mDicNodeState.mDicNodeStateScoring.getSpatialDistance()
333                / static_cast<float>(getInputIndex(0) + 1);
334    }
335
336    // Used to prune nodes
337    float getCompoundDistance() const {
338        return mDicNodeState.mDicNodeStateScoring.getCompoundDistance();
339    }
340
341    // Used to prune nodes
342    float getCompoundDistance(const float languageWeight) const {
343        return mDicNodeState.mDicNodeStateScoring.getCompoundDistance(languageWeight);
344    }
345
346    // Used to commit input partially
347    int getPrevWordPtNodePos() const {
348        return mDicNodeState.mDicNodeStatePrevWord.getPrevWordPtNodePos();
349    }
350
351    AK_FORCE_INLINE const int *getOutputWordBuf() const {
352        return mDicNodeState.mDicNodeStateOutput.getCodePointBuf();
353    }
354
355    int getPrevCodePointG(int pointerId) const {
356        return mDicNodeState.mDicNodeStateInput.getPrevCodePoint(pointerId);
357    }
358
359    // Whether the current codepoint can be an intentional omission, in which case the traversal
360    // algorithm will always check for a possible omission here.
361    bool canBeIntentionalOmission() const {
362        return CharUtils::isIntentionalOmissionCodePoint(getNodeCodePoint());
363    }
364
365    // Whether the omission is so frequent that it should incur zero cost.
366    bool isZeroCostOmission() const {
367        // TODO: do not hardcode and read from header
368        return (getNodeCodePoint() == KEYCODE_SINGLE_QUOTE);
369    }
370
371    // TODO: remove
372    float getTerminalDiffCostG(int path) const {
373        return mDicNodeState.mDicNodeStateInput.getTerminalDiffCost(path);
374    }
375
376    //////////////////////
377    // Temporary getter //
378    // TODO: Remove     //
379    //////////////////////
380    // TODO: Remove once touch path is merged into ProximityInfoState
381    // Note: Returned codepoint may be a digraph codepoint if the node is in a composite glyph.
382    int getNodeCodePoint() const {
383        const int codePoint = mDicNodeProperties.getDicNodeCodePoint();
384        const DigraphUtils::DigraphCodePointIndex digraphIndex =
385                mDicNodeState.mDicNodeStateScoring.getDigraphIndex();
386        if (digraphIndex == DigraphUtils::NOT_A_DIGRAPH_INDEX) {
387            return codePoint;
388        }
389        return DigraphUtils::getDigraphCodePointForIndex(codePoint, digraphIndex);
390    }
391
392    ////////////////////////////////
393    // Utils for cost calculation //
394    ////////////////////////////////
395    AK_FORCE_INLINE bool isSameNodeCodePoint(const DicNode *const dicNode) const {
396        return mDicNodeProperties.getDicNodeCodePoint()
397                == dicNode->mDicNodeProperties.getDicNodeCodePoint();
398    }
399
400    // TODO: remove
401    // TODO: rename getNextInputIndex
402    int16_t getInputIndex(int pointerId) const {
403        return mDicNodeState.mDicNodeStateInput.getInputIndex(pointerId);
404    }
405
406    ////////////////////////////////////
407    // Getter of features for scoring //
408    ////////////////////////////////////
409    float getSpatialDistanceForScoring() const {
410        return mDicNodeState.mDicNodeStateScoring.getSpatialDistance();
411    }
412
413    float getLanguageDistanceForScoring() const {
414        return mDicNodeState.mDicNodeStateScoring.getLanguageDistance();
415    }
416
417    // For space-aware gestures, we store the normalized distance at the char index
418    // that ends the first word of the suggestion. We call this the distance after
419    // first word.
420    float getNormalizedCompoundDistanceAfterFirstWord() const {
421        return mDicNodeState.mDicNodeStateScoring.getNormalizedCompoundDistanceAfterFirstWord();
422    }
423
424    float getLanguageDistanceRatePerWordForScoring() const {
425        const float langDist = getLanguageDistanceForScoring();
426        const float totalWordCount =
427                static_cast<float>(mDicNodeState.mDicNodeStatePrevWord.getPrevWordCount() + 1);
428        return langDist / totalWordCount;
429    }
430
431    float getRawLength() const {
432        return mDicNodeState.mDicNodeStateScoring.getRawLength();
433    }
434
435    bool isLessThanOneErrorForScoring() const {
436        return mDicNodeState.mDicNodeStateScoring.getEditCorrectionCount()
437                + mDicNodeState.mDicNodeStateScoring.getProximityCorrectionCount() <= 1;
438    }
439
440    DoubleLetterLevel getDoubleLetterLevel() const {
441        return mDicNodeState.mDicNodeStateScoring.getDoubleLetterLevel();
442    }
443
444    void setDoubleLetterLevel(DoubleLetterLevel doubleLetterLevel) {
445        mDicNodeState.mDicNodeStateScoring.setDoubleLetterLevel(doubleLetterLevel);
446    }
447
448    bool isInDigraph() const {
449        return mDicNodeState.mDicNodeStateScoring.getDigraphIndex()
450                != DigraphUtils::NOT_A_DIGRAPH_INDEX;
451    }
452
453    void advanceDigraphIndex() {
454        mDicNodeState.mDicNodeStateScoring.advanceDigraphIndex();
455    }
456
457    ErrorTypeUtils::ErrorType getContainedErrorTypes() const {
458        return mDicNodeState.mDicNodeStateScoring.getContainedErrorTypes();
459    }
460
461    bool isBlacklistedOrNotAWord() const {
462        return mDicNodeProperties.isBlacklistedOrNotAWord();
463    }
464
465    inline uint16_t getNodeCodePointCount() const {
466        return mDicNodeProperties.getDepth();
467    }
468
469    // Returns code point count including spaces
470    inline uint16_t getTotalNodeCodePointCount() const {
471        return getNodeCodePointCount() + mDicNodeState.mDicNodeStatePrevWord.getPrevWordLength();
472    }
473
474    AK_FORCE_INLINE void dump(const char *tag) const {
475#if DEBUG_DICT
476        DUMP_WORD_AND_SCORE(tag);
477#if DEBUG_DUMP_ERROR
478        mProfiler.dump();
479#endif
480#endif
481    }
482
483    void setReleaseListener(DicNodeReleaseListener *releaseListener) {
484        mReleaseListener = releaseListener;
485    }
486
487    AK_FORCE_INLINE bool compare(const DicNode *right) {
488        if (!isUsed() && !right->isUsed()) {
489            // Compare pointer values here for stable comparison
490            return this > right;
491        }
492        if (!isUsed()) {
493            return true;
494        }
495        if (!right->isUsed()) {
496            return false;
497        }
498        // Promote exact matches to prevent them from being pruned.
499        const bool leftExactMatch = ErrorTypeUtils::isExactMatch(getContainedErrorTypes());
500        const bool rightExactMatch = ErrorTypeUtils::isExactMatch(right->getContainedErrorTypes());
501        if (leftExactMatch != rightExactMatch) {
502            return leftExactMatch;
503        }
504        const float diff =
505                right->getNormalizedCompoundDistance() - getNormalizedCompoundDistance();
506        static const float MIN_DIFF = 0.000001f;
507        if (diff > MIN_DIFF) {
508            return true;
509        } else if (diff < -MIN_DIFF) {
510            return false;
511        }
512        const int depth = getNodeCodePointCount();
513        const int depthDiff = right->getNodeCodePointCount() - depth;
514        if (depthDiff != 0) {
515            return depthDiff > 0;
516        }
517        for (int i = 0; i < depth; ++i) {
518            const int codePoint = mDicNodeState.mDicNodeStateOutput.getCodePointAt(i);
519            const int rightCodePoint = right->mDicNodeState.mDicNodeStateOutput.getCodePointAt(i);
520            if (codePoint != rightCodePoint) {
521                return rightCodePoint > codePoint;
522            }
523        }
524        // Compare pointer values here for stable comparison
525        return this > right;
526    }
527
528 private:
529    DicNodeProperties mDicNodeProperties;
530    DicNodeState mDicNodeState;
531    // TODO: Remove
532    bool mIsCachedForNextSuggestion;
533    bool mIsUsed;
534    DicNodeReleaseListener *mReleaseListener;
535
536    AK_FORCE_INLINE int getTotalInputIndex() const {
537        int index = 0;
538        for (int i = 0; i < MAX_POINTER_COUNT_G; i++) {
539            index += mDicNodeState.mDicNodeStateInput.getInputIndex(i);
540        }
541        return index;
542    }
543
544    // Caveat: Must not be called outside Weighting
545    // This restriction is guaranteed by "friend"
546    AK_FORCE_INLINE void addCost(const float spatialCost, const float languageCost,
547            const bool doNormalization, const int inputSize,
548            const ErrorTypeUtils::ErrorType errorType) {
549        if (DEBUG_GEO_FULL) {
550            LOGI_SHOW_ADD_COST_PROP;
551        }
552        mDicNodeState.mDicNodeStateScoring.addCost(spatialCost, languageCost, doNormalization,
553                inputSize, getTotalInputIndex(), errorType);
554    }
555
556    // Saves the current normalized compound distance for space-aware gestures.
557    // See getNormalizedCompoundDistanceAfterFirstWord for details.
558    AK_FORCE_INLINE void saveNormalizedCompoundDistanceAfterFirstWordIfNoneYet() {
559        mDicNodeState.mDicNodeStateScoring.saveNormalizedCompoundDistanceAfterFirstWordIfNoneYet();
560    }
561
562    // Caveat: Must not be called outside Weighting
563    // This restriction is guaranteed by "friend"
564    AK_FORCE_INLINE void forwardInputIndex(const int pointerId, const int count,
565            const bool overwritesPrevCodePointByNodeCodePoint) {
566        if (count == 0) {
567            return;
568        }
569        mDicNodeState.mDicNodeStateInput.forwardInputIndex(pointerId, count);
570        if (overwritesPrevCodePointByNodeCodePoint) {
571            mDicNodeState.mDicNodeStateInput.setPrevCodePoint(0, getNodeCodePoint());
572        }
573    }
574
575    AK_FORCE_INLINE void updateInputIndexG(const DicNode_InputStateG *const inputStateG) {
576        if (mDicNodeState.mDicNodeStatePrevWord.getPrevWordCount() == 1 && isFirstLetter()) {
577            mDicNodeState.mDicNodeStatePrevWord.setSecondWordFirstInputIndex(
578                    inputStateG->mInputIndex);
579        }
580        mDicNodeState.mDicNodeStateInput.updateInputIndexG(inputStateG->mPointerId,
581                inputStateG->mInputIndex, inputStateG->mPrevCodePoint,
582                inputStateG->mTerminalDiffCost, inputStateG->mRawLength);
583        mDicNodeState.mDicNodeStateScoring.addRawLength(inputStateG->mRawLength);
584        mDicNodeState.mDicNodeStateScoring.setDoubleLetterLevel(inputStateG->mDoubleLetterLevel);
585    }
586};
587} // namespace latinime
588#endif // LATINIME_DIC_NODE_H
589