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