dic_node.h revision 9d618d1431ec78328bd0eecb90ade8bfcef9b025
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(DicNode *dicNode, const int pos, const int childrenPos, const int probability,
161            const bool isTerminal, const bool hasChildren, const bool isBlacklistedOrNotAWord,
162            const uint16_t mergedNodeCodePointCount, const int *const mergedNodeCodePoints) {
163        mIsUsed = true;
164        uint16_t newDepth = static_cast<uint16_t>(dicNode->getNodeCodePointCount() + 1);
165        mIsCachedForNextSuggestion = dicNode->mIsCachedForNextSuggestion;
166        const uint16_t newLeavingDepth = static_cast<uint16_t>(
167                dicNode->mDicNodeProperties.getLeavingDepth() + mergedNodeCodePointCount);
168        mDicNodeProperties.init(pos, childrenPos, mergedNodeCodePoints[0], probability,
169                isTerminal, hasChildren, isBlacklistedOrNotAWord, newDepth, newLeavingDepth);
170        mDicNodeState.init(&dicNode->mDicNodeState, mergedNodeCodePointCount,
171                mergedNodeCodePoints);
172        PROF_NODE_COPY(&dicNode->mProfiler, mProfiler);
173    }
174
175    AK_FORCE_INLINE void remove() {
176        mIsUsed = false;
177        if (mReleaseListener) {
178            mReleaseListener->onReleased(this);
179        }
180    }
181
182    bool isUsed() const {
183        return mIsUsed;
184    }
185
186    bool isRoot() const {
187        return getNodeCodePointCount() == 0;
188    }
189
190    bool hasChildren() const {
191        return mDicNodeProperties.hasChildren();
192    }
193
194    bool isLeavingNode() const {
195        ASSERT(getNodeCodePointCount() <= mDicNodeProperties.getLeavingDepth());
196        return getNodeCodePointCount() == mDicNodeProperties.getLeavingDepth();
197    }
198
199    AK_FORCE_INLINE bool isFirstLetter() const {
200        return getNodeCodePointCount() == 1;
201    }
202
203    bool isCached() const {
204        return mIsCachedForNextSuggestion;
205    }
206
207    void setCached() {
208        mIsCachedForNextSuggestion = true;
209    }
210
211    // Used to expand the node in DicNodeUtils
212    int getNodeTypedCodePoint() const {
213        return mDicNodeState.mDicNodeStateOutput.getCodePointAt(getNodeCodePointCount());
214    }
215
216    // Check if the current word and the previous word can be considered as a valid multiple word
217    // suggestion.
218    bool isValidMultipleWordSuggestion() const {
219        if (isBlacklistedOrNotAWord()) {
220            return false;
221        }
222        // Treat suggestion as invalid if the current and the previous word are single character
223        // words.
224        const int prevWordLen = mDicNodeState.mDicNodeStatePrevWord.getPrevWordLength()
225                - mDicNodeState.mDicNodeStatePrevWord.getPrevWordStart() - 1;
226        const int currentWordLen = getNodeCodePointCount();
227        return (prevWordLen != 1 || currentWordLen != 1);
228    }
229
230    bool isFirstCharUppercase() const {
231        const int c = getOutputWordBuf()[0];
232        return CharUtils::isAsciiUpper(c);
233    }
234
235    bool isFirstWord() const {
236        return mDicNodeState.mDicNodeStatePrevWord.getPrevWordNodePos() == NOT_A_VALID_WORD_POS;
237    }
238
239    bool isCompletion(const int inputSize) const {
240        return mDicNodeState.mDicNodeStateInput.getInputIndex(0) >= inputSize;
241    }
242
243    bool canDoLookAheadCorrection(const int inputSize) const {
244        return mDicNodeState.mDicNodeStateInput.getInputIndex(0) < inputSize - 1;
245    }
246
247    // Used to get bigram probability in DicNodeUtils
248    int getPos() const {
249        return mDicNodeProperties.getPos();
250    }
251
252    // Used to get bigram probability in DicNodeUtils
253    int getPrevWordPos() const {
254        return mDicNodeState.mDicNodeStatePrevWord.getPrevWordNodePos();
255    }
256
257    // Used in DicNodeUtils
258    int getChildrenPos() const {
259        return mDicNodeProperties.getChildrenPos();
260    }
261
262    int getProbability() const {
263        return mDicNodeProperties.getProbability();
264    }
265
266    AK_FORCE_INLINE bool isTerminalWordNode() const {
267        const bool isTerminalNodes = mDicNodeProperties.isTerminal();
268        const int currentNodeDepth = getNodeCodePointCount();
269        const int terminalNodeDepth = mDicNodeProperties.getLeavingDepth();
270        return isTerminalNodes && currentNodeDepth > 0 && currentNodeDepth == terminalNodeDepth;
271    }
272
273    bool shouldBeFilterdBySafetyNetForBigram() const {
274        const uint16_t currentDepth = getNodeCodePointCount();
275        const int prevWordLen = mDicNodeState.mDicNodeStatePrevWord.getPrevWordLength()
276                - mDicNodeState.mDicNodeStatePrevWord.getPrevWordStart() - 1;
277        return !(currentDepth > 0 && (currentDepth != 1 || prevWordLen != 1));
278    }
279
280    bool isTotalInputSizeExceedingLimit() const {
281        const int prevWordsLen = mDicNodeState.mDicNodeStatePrevWord.getPrevWordLength();
282        const int currentWordDepth = getNodeCodePointCount();
283        // TODO: 3 can be 2? Needs to be investigated.
284        // TODO: Have a const variable for 3 (or 2)
285        return prevWordsLen + currentWordDepth > MAX_WORD_LENGTH - 3;
286    }
287
288    // TODO: This may be defective. Needs to be revised.
289    bool truncateNode(const DicNode *const topNode, const int inputCommitPoint) {
290        const int prevWordLenOfTop = mDicNodeState.mDicNodeStatePrevWord.getPrevWordLength();
291        int newPrevWordStartIndex = inputCommitPoint;
292        int charCount = 0;
293        // Find new word start index
294        for (int i = 0; i < prevWordLenOfTop; ++i) {
295            const int c = mDicNodeState.mDicNodeStatePrevWord.getPrevWordCodePointAt(i);
296            // TODO: Check other separators.
297            if (c != KEYCODE_SPACE && c != KEYCODE_SINGLE_QUOTE) {
298                if (charCount == inputCommitPoint) {
299                    newPrevWordStartIndex = i;
300                    break;
301                }
302                ++charCount;
303            }
304        }
305        if (!mDicNodeState.mDicNodeStatePrevWord.startsWith(
306                &topNode->mDicNodeState.mDicNodeStatePrevWord, newPrevWordStartIndex - 1)) {
307            // Node mismatch.
308            return false;
309        }
310        mDicNodeState.mDicNodeStateInput.truncate(inputCommitPoint);
311        mDicNodeState.mDicNodeStatePrevWord.truncate(newPrevWordStartIndex);
312        return true;
313    }
314
315    void outputResult(int *dest) const {
316        const uint16_t prevWordLength = mDicNodeState.mDicNodeStatePrevWord.getPrevWordLength();
317        const uint16_t currentDepth = getNodeCodePointCount();
318        DicNodeUtils::appendTwoWords(mDicNodeState.mDicNodeStatePrevWord.mPrevWord,
319                   prevWordLength, getOutputWordBuf(), currentDepth, dest);
320        DUMP_WORD_AND_SCORE("OUTPUT");
321    }
322
323    void outputSpacePositionsResult(int *spaceIndices) const {
324        mDicNodeState.mDicNodeStatePrevWord.outputSpacePositions(spaceIndices);
325    }
326
327    bool hasMultipleWords() const {
328        return mDicNodeState.mDicNodeStatePrevWord.getPrevWordCount() > 0;
329    }
330
331    int getProximityCorrectionCount() const {
332        return mDicNodeState.mDicNodeStateScoring.getProximityCorrectionCount();
333    }
334
335    int getEditCorrectionCount() const {
336        return mDicNodeState.mDicNodeStateScoring.getEditCorrectionCount();
337    }
338
339    // Used to prune nodes
340    float getNormalizedCompoundDistance() const {
341        return mDicNodeState.mDicNodeStateScoring.getNormalizedCompoundDistance();
342    }
343
344    // Used to prune nodes
345    float getNormalizedSpatialDistance() const {
346        return mDicNodeState.mDicNodeStateScoring.getSpatialDistance()
347                / static_cast<float>(getInputIndex(0) + 1);
348    }
349
350    // Used to prune nodes
351    float getCompoundDistance() const {
352        return mDicNodeState.mDicNodeStateScoring.getCompoundDistance();
353    }
354
355    // Used to prune nodes
356    float getCompoundDistance(const float languageWeight) const {
357        return mDicNodeState.mDicNodeStateScoring.getCompoundDistance(languageWeight);
358    }
359
360    // Used to commit input partially
361    int getPrevWordNodePos() const {
362        return mDicNodeState.mDicNodeStatePrevWord.getPrevWordNodePos();
363    }
364
365    AK_FORCE_INLINE const int *getOutputWordBuf() const {
366        return mDicNodeState.mDicNodeStateOutput.mCodePointsBuf;
367    }
368
369    int getPrevCodePointG(int pointerId) const {
370        return mDicNodeState.mDicNodeStateInput.getPrevCodePoint(pointerId);
371    }
372
373    // Whether the current codepoint can be an intentional omission, in which case the traversal
374    // algorithm will always check for a possible omission here.
375    bool canBeIntentionalOmission() const {
376        return CharUtils::isIntentionalOmissionCodePoint(getNodeCodePoint());
377    }
378
379    // Whether the omission is so frequent that it should incur zero cost.
380    bool isZeroCostOmission() const {
381        // TODO: do not hardcode and read from header
382        return (getNodeCodePoint() == KEYCODE_SINGLE_QUOTE);
383    }
384
385    // TODO: remove
386    float getTerminalDiffCostG(int path) const {
387        return mDicNodeState.mDicNodeStateInput.getTerminalDiffCost(path);
388    }
389
390    //////////////////////
391    // Temporary getter //
392    // TODO: Remove     //
393    //////////////////////
394    // TODO: Remove once touch path is merged into ProximityInfoState
395    // Note: Returned codepoint may be a digraph codepoint if the node is in a composite glyph.
396    int getNodeCodePoint() const {
397        const int codePoint = mDicNodeProperties.getNodeCodePoint();
398        const DigraphUtils::DigraphCodePointIndex digraphIndex =
399                mDicNodeState.mDicNodeStateScoring.getDigraphIndex();
400        if (digraphIndex == DigraphUtils::NOT_A_DIGRAPH_INDEX) {
401            return codePoint;
402        }
403        return DigraphUtils::getDigraphCodePointForIndex(codePoint, digraphIndex);
404    }
405
406    ////////////////////////////////
407    // Utils for cost calculation //
408    ////////////////////////////////
409    AK_FORCE_INLINE bool isSameNodeCodePoint(const DicNode *const dicNode) const {
410        return mDicNodeProperties.getNodeCodePoint()
411                == dicNode->mDicNodeProperties.getNodeCodePoint();
412    }
413
414    // TODO: remove
415    // TODO: rename getNextInputIndex
416    int16_t getInputIndex(int pointerId) const {
417        return mDicNodeState.mDicNodeStateInput.getInputIndex(pointerId);
418    }
419
420    ////////////////////////////////////
421    // Getter of features for scoring //
422    ////////////////////////////////////
423    float getSpatialDistanceForScoring() const {
424        return mDicNodeState.mDicNodeStateScoring.getSpatialDistance();
425    }
426
427    float getLanguageDistanceForScoring() const {
428        return mDicNodeState.mDicNodeStateScoring.getLanguageDistance();
429    }
430
431    float getLanguageDistanceRatePerWordForScoring() const {
432        const float langDist = getLanguageDistanceForScoring();
433        const float totalWordCount =
434                static_cast<float>(mDicNodeState.mDicNodeStatePrevWord.getPrevWordCount() + 1);
435        return langDist / totalWordCount;
436    }
437
438    float getRawLength() const {
439        return mDicNodeState.mDicNodeStateScoring.getRawLength();
440    }
441
442    bool isLessThanOneErrorForScoring() const {
443        return mDicNodeState.mDicNodeStateScoring.getEditCorrectionCount()
444                + mDicNodeState.mDicNodeStateScoring.getProximityCorrectionCount() <= 1;
445    }
446
447    DoubleLetterLevel getDoubleLetterLevel() const {
448        return mDicNodeState.mDicNodeStateScoring.getDoubleLetterLevel();
449    }
450
451    void setDoubleLetterLevel(DoubleLetterLevel doubleLetterLevel) {
452        mDicNodeState.mDicNodeStateScoring.setDoubleLetterLevel(doubleLetterLevel);
453    }
454
455    bool isInDigraph() const {
456        return mDicNodeState.mDicNodeStateScoring.getDigraphIndex()
457                != DigraphUtils::NOT_A_DIGRAPH_INDEX;
458    }
459
460    void advanceDigraphIndex() {
461        mDicNodeState.mDicNodeStateScoring.advanceDigraphIndex();
462    }
463
464    bool isExactMatch() const {
465        return mDicNodeState.mDicNodeStateScoring.isExactMatch();
466    }
467
468    bool isBlacklistedOrNotAWord() const {
469        return mDicNodeProperties.isBlacklistedOrNotAWord();
470    }
471
472    inline uint16_t getNodeCodePointCount() const {
473        return mDicNodeProperties.getDepth();
474    }
475
476    // Returns code point count including spaces
477    inline uint16_t getTotalNodeCodePointCount() const {
478        return getNodeCodePointCount() + mDicNodeState.mDicNodeStatePrevWord.getPrevWordLength();
479    }
480
481    AK_FORCE_INLINE void dump(const char *tag) const {
482#if DEBUG_DICT
483        DUMP_WORD_AND_SCORE(tag);
484#if DEBUG_DUMP_ERROR
485        mProfiler.dump();
486#endif
487#endif
488    }
489
490    void setReleaseListener(DicNodeReleaseListener *releaseListener) {
491        mReleaseListener = releaseListener;
492    }
493
494    AK_FORCE_INLINE bool compare(const DicNode *right) {
495        if (!isUsed() && !right->isUsed()) {
496            // Compare pointer values here for stable comparison
497            return this > right;
498        }
499        if (!isUsed()) {
500            return true;
501        }
502        if (!right->isUsed()) {
503            return false;
504        }
505        // Promote exact matches to prevent them from being pruned.
506        const bool leftExactMatch = isExactMatch();
507        const bool rightExactMatch = right->isExactMatch();
508        if (leftExactMatch != rightExactMatch) {
509            return leftExactMatch;
510        }
511        const float diff =
512                right->getNormalizedCompoundDistance() - getNormalizedCompoundDistance();
513        static const float MIN_DIFF = 0.000001f;
514        if (diff > MIN_DIFF) {
515            return true;
516        } else if (diff < -MIN_DIFF) {
517            return false;
518        }
519        const int depth = getNodeCodePointCount();
520        const int depthDiff = right->getNodeCodePointCount() - depth;
521        if (depthDiff != 0) {
522            return depthDiff > 0;
523        }
524        for (int i = 0; i < depth; ++i) {
525            const int codePoint = mDicNodeState.mDicNodeStateOutput.getCodePointAt(i);
526            const int rightCodePoint = right->mDicNodeState.mDicNodeStateOutput.getCodePointAt(i);
527            if (codePoint != rightCodePoint) {
528                return rightCodePoint > codePoint;
529            }
530        }
531        // Compare pointer values here for stable comparison
532        return this > right;
533    }
534
535 private:
536    DicNodeProperties mDicNodeProperties;
537    DicNodeState mDicNodeState;
538    // TODO: Remove
539    bool mIsCachedForNextSuggestion;
540    bool mIsUsed;
541    DicNodeReleaseListener *mReleaseListener;
542
543    AK_FORCE_INLINE int getTotalInputIndex() const {
544        int index = 0;
545        for (int i = 0; i < MAX_POINTER_COUNT_G; i++) {
546            index += mDicNodeState.mDicNodeStateInput.getInputIndex(i);
547        }
548        return index;
549    }
550
551    // Caveat: Must not be called outside Weighting
552    // This restriction is guaranteed by "friend"
553    AK_FORCE_INLINE void addCost(const float spatialCost, const float languageCost,
554            const bool doNormalization, const int inputSize, const ErrorType errorType) {
555        if (DEBUG_GEO_FULL) {
556            LOGI_SHOW_ADD_COST_PROP;
557        }
558        mDicNodeState.mDicNodeStateScoring.addCost(spatialCost, languageCost, doNormalization,
559                inputSize, getTotalInputIndex(), errorType);
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(DicNode_InputStateG *inputStateG) {
576        mDicNodeState.mDicNodeStateInput.updateInputIndexG(inputStateG->mPointerId,
577                inputStateG->mInputIndex, inputStateG->mPrevCodePoint,
578                inputStateG->mTerminalDiffCost, inputStateG->mRawLength);
579        mDicNodeState.mDicNodeStateScoring.addRawLength(inputStateG->mRawLength);
580        mDicNodeState.mDicNodeStateScoring.setDoubleLetterLevel(inputStateG->mDoubleLetterLevel);
581    }
582};
583} // namespace latinime
584#endif // LATINIME_DIC_NODE_H
585