1 /*
2  * Copyright (C) 2013 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_TYPING_TRAVERSAL_H
18 #define LATINIME_TYPING_TRAVERSAL_H
19 
20 #include <cstdint>
21 
22 #include "defines.h"
23 #include "suggest/core/dicnode/dic_node.h"
24 #include "suggest/core/dicnode/dic_node_vector.h"
25 #include "suggest/core/layout/proximity_info_state.h"
26 #include "suggest/core/layout/proximity_info_utils.h"
27 #include "suggest/core/policy/traversal.h"
28 #include "suggest/core/session/dic_traverse_session.h"
29 #include "suggest/core/suggest_options.h"
30 #include "suggest/policyimpl/typing/scoring_params.h"
31 #include "utils/char_utils.h"
32 
33 namespace latinime {
34 class TypingTraversal : public Traversal {
35  public:
getInstance()36     static const TypingTraversal *getInstance() { return &sInstance; }
37 
getMaxPointerCount()38     AK_FORCE_INLINE int getMaxPointerCount() const {
39         return MAX_POINTER_COUNT;
40     }
41 
allowsErrorCorrections(const DicNode * const dicNode)42     AK_FORCE_INLINE bool allowsErrorCorrections(const DicNode *const dicNode) const {
43         return dicNode->getNormalizedSpatialDistance()
44                 < ScoringParams::NORMALIZED_SPATIAL_DISTANCE_THRESHOLD_FOR_EDIT;
45     }
46 
isOmission(const DicTraverseSession * const traverseSession,const DicNode * const dicNode,const DicNode * const childDicNode,const bool allowsErrorCorrections)47     AK_FORCE_INLINE bool isOmission(const DicTraverseSession *const traverseSession,
48             const DicNode *const dicNode, const DicNode *const childDicNode,
49             const bool allowsErrorCorrections) const {
50         if (!CORRECT_OMISSION) {
51             return false;
52         }
53         // Note: Always consider intentional omissions (like apostrophes) since they are common.
54         const bool canConsiderOmission =
55                 allowsErrorCorrections || childDicNode->canBeIntentionalOmission();
56         if (!canConsiderOmission) {
57             return false;
58         }
59         const int inputSize = traverseSession->getInputSize();
60         // TODO: Don't refer to isCompletion?
61         if (dicNode->isCompletion(inputSize)) {
62             return false;
63         }
64         if (dicNode->canBeIntentionalOmission()) {
65             return true;
66         }
67         const int point0Index = dicNode->getInputIndex(0);
68         const int currentBaseLowerCodePoint =
69                 CharUtils::toBaseLowerCase(childDicNode->getNodeCodePoint());
70         const int typedBaseLowerCodePoint =
71                 CharUtils::toBaseLowerCase(traverseSession->getProximityInfoState(0)
72                         ->getPrimaryCodePointAt(point0Index));
73         return (currentBaseLowerCodePoint != typedBaseLowerCodePoint);
74     }
75 
isSpaceSubstitutionTerminal(const DicTraverseSession * const traverseSession,const DicNode * const dicNode)76     AK_FORCE_INLINE bool isSpaceSubstitutionTerminal(
77             const DicTraverseSession *const traverseSession, const DicNode *const dicNode) const {
78         if (!CORRECT_NEW_WORD_SPACE_SUBSTITUTION) {
79             return false;
80         }
81         if (traverseSession->getSuggestOptions()->weightForLocale()
82                 < ScoringParams::LOCALE_WEIGHT_THRESHOLD_FOR_SPACE_SUBSTITUTION) {
83             // Space substitution is heavy, so we skip doing it if the weight for this language
84             // is low because we anticipate the suggestions out of this dictionary are not for
85             // the language the user intends to type in.
86             return false;
87         }
88         if (!canDoLookAheadCorrection(traverseSession, dicNode)) {
89             return false;
90         }
91         const int point0Index = dicNode->getInputIndex(0);
92         return dicNode->isTerminalDicNode()
93                 && traverseSession->getProximityInfoState(0)->
94                         hasSpaceProximity(point0Index);
95     }
96 
isSpaceOmissionTerminal(const DicTraverseSession * const traverseSession,const DicNode * const dicNode)97     AK_FORCE_INLINE bool isSpaceOmissionTerminal(
98             const DicTraverseSession *const traverseSession, const DicNode *const dicNode) const {
99         if (!CORRECT_NEW_WORD_SPACE_OMISSION) {
100             return false;
101         }
102         if (traverseSession->getSuggestOptions()->weightForLocale()
103                 < ScoringParams::LOCALE_WEIGHT_THRESHOLD_FOR_SPACE_OMISSION) {
104             // Space omission is heavy, so we skip doing it if the weight for this language
105             // is low because we anticipate the suggestions out of this dictionary are not for
106             // the language the user intends to type in.
107             return false;
108         }
109         const int inputSize = traverseSession->getInputSize();
110         // TODO: Don't refer to isCompletion?
111         if (dicNode->isCompletion(inputSize)) {
112             return false;
113         }
114         if (!dicNode->isTerminalDicNode()) {
115             return false;
116         }
117         const int16_t pointIndex = dicNode->getInputIndex(0);
118         return pointIndex <= inputSize && !dicNode->isTotalInputSizeExceedingLimit()
119                 && !dicNode->shouldBeFilteredBySafetyNetForBigram();
120     }
121 
shouldDepthLevelCache(const DicTraverseSession * const traverseSession)122     AK_FORCE_INLINE bool shouldDepthLevelCache(
123             const DicTraverseSession *const traverseSession) const {
124         const int inputSize = traverseSession->getInputSize();
125         return traverseSession->isCacheBorderForTyping(inputSize);
126     }
127 
shouldNodeLevelCache(const DicTraverseSession * const traverseSession,const DicNode * const dicNode)128     AK_FORCE_INLINE bool shouldNodeLevelCache(
129             const DicTraverseSession *const traverseSession, const DicNode *const dicNode) const {
130         return false;
131     }
132 
canDoLookAheadCorrection(const DicTraverseSession * const traverseSession,const DicNode * const dicNode)133     AK_FORCE_INLINE bool canDoLookAheadCorrection(
134             const DicTraverseSession *const traverseSession, const DicNode *const dicNode) const {
135         const int inputSize = traverseSession->getInputSize();
136         return dicNode->canDoLookAheadCorrection(inputSize);
137     }
138 
getProximityType(const DicTraverseSession * const traverseSession,const DicNode * const dicNode,const DicNode * const childDicNode)139     AK_FORCE_INLINE ProximityType getProximityType(
140             const DicTraverseSession *const traverseSession, const DicNode *const dicNode,
141             const DicNode *const childDicNode) const {
142         return traverseSession->getProximityInfoState(0)->getProximityType(
143                 dicNode->getInputIndex(0), childDicNode->getNodeCodePoint(),
144                 true /* checkProximityChars */);
145     }
146 
needsToTraverseAllUserInput()147     AK_FORCE_INLINE bool needsToTraverseAllUserInput() const {
148         return true;
149     }
150 
getMaxSpatialDistance()151     AK_FORCE_INLINE float getMaxSpatialDistance() const {
152         return ScoringParams::MAX_SPATIAL_DISTANCE;
153     }
154 
getDefaultExpandDicNodeSize()155     AK_FORCE_INLINE int getDefaultExpandDicNodeSize() const {
156         return DicNodeVector::DEFAULT_NODES_SIZE_FOR_OPTIMIZATION;
157     }
158 
getMaxCacheSize(const int inputSize,const float weightForLocale)159     AK_FORCE_INLINE int getMaxCacheSize(const int inputSize, const float weightForLocale) const {
160         if (inputSize <= 1) {
161             return ScoringParams::MAX_CACHE_DIC_NODE_SIZE_FOR_SINGLE_POINT;
162         }
163         if (weightForLocale < ScoringParams::LOCALE_WEIGHT_THRESHOLD_FOR_SMALL_CACHE_SIZE) {
164             return ScoringParams::MAX_CACHE_DIC_NODE_SIZE_FOR_LOW_PROBABILITY_LOCALE;
165         }
166         return ScoringParams::MAX_CACHE_DIC_NODE_SIZE;
167     }
168 
getTerminalCacheSize()169     AK_FORCE_INLINE int getTerminalCacheSize() const {
170         return MAX_RESULTS;
171     }
172 
isPossibleOmissionChildNode(const DicTraverseSession * const traverseSession,const DicNode * const parentDicNode,const DicNode * const dicNode)173     AK_FORCE_INLINE bool isPossibleOmissionChildNode(
174             const DicTraverseSession *const traverseSession, const DicNode *const parentDicNode,
175             const DicNode *const dicNode) const {
176         const ProximityType proximityType =
177                 getProximityType(traverseSession, parentDicNode, dicNode);
178         if (!ProximityInfoUtils::isMatchOrProximityChar(proximityType)) {
179             return false;
180         }
181         return true;
182     }
183 
isGoodToTraverseNextWord(const DicNode * const dicNode,const int probability)184     AK_FORCE_INLINE bool isGoodToTraverseNextWord(const DicNode *const dicNode,
185             const int probability) const {
186         if (probability < ScoringParams::THRESHOLD_NEXT_WORD_PROBABILITY) {
187             return false;
188         }
189         const bool shortCappedWord = dicNode->getNodeCodePointCount()
190                 < ScoringParams::THRESHOLD_SHORT_WORD_LENGTH && dicNode->isFirstCharUppercase();
191         return !shortCappedWord
192                 || probability >= ScoringParams::THRESHOLD_NEXT_WORD_PROBABILITY_FOR_CAPPED;
193     }
194 
195  private:
196     DISALLOW_COPY_AND_ASSIGN(TypingTraversal);
197     static const bool CORRECT_OMISSION;
198     static const bool CORRECT_NEW_WORD_SPACE_SUBSTITUTION;
199     static const bool CORRECT_NEW_WORD_SPACE_OMISSION;
200     static const TypingTraversal sInstance;
201 
TypingTraversal()202     TypingTraversal() {}
~TypingTraversal()203     ~TypingTraversal() {}
204 };
205 } // namespace latinime
206 #endif // LATINIME_TYPING_TRAVERSAL_H
207