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