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