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, %5f,", header, \ 42 getSpatialDistanceForScoring(), getLanguageDistanceForScoring(), \ 43 getNormalizedCompoundDistance(), getRawLength(), prevWordCharBuf, charBuf, \ 44 getInputIndex(0), getNormalizedCompoundDistanceAfterFirstWord()); \ 45 } while (0) 46 #else 47 #define LOGI_SHOW_ADD_COST_PROP 48 #define DUMP_WORD_AND_SCORE(header) 49 #endif 50 51 namespace latinime { 52 53 // This struct is purely a bucket to return values. No instances of this struct should be kept. 54 struct 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 69 class 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_DICT_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_DICT_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.getSecondWordFirstInputIndex(), 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_DICT_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 shouldBeFilteredBySafetyNetForBigram() 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 // "Total" in this context (and other methods in this class) means the whole suggestion. When 325 // this represents a multi-word suggestion, the referenced PtNode (in mDicNodeState) is only 326 // the one that corresponds to the last word of the suggestion, and all the previous words 327 // are concatenated together in mPrevWord - which contains a space at the end. 328 int getTotalNodeSpaceCount() const { 329 if (isFirstWord()) return 0; 330 return CharUtils::getSpaceCount(mDicNodeState.mDicNodeStatePrevWord.mPrevWord, 331 mDicNodeState.mDicNodeStatePrevWord.getPrevWordLength()); 332 } 333 334 int getSecondWordFirstInputIndex(const ProximityInfoState *const pInfoState) const { 335 const int inputIndex = mDicNodeState.mDicNodeStatePrevWord.getSecondWordFirstInputIndex(); 336 if (inputIndex == NOT_AN_INDEX) { 337 return NOT_AN_INDEX; 338 } else { 339 return pInfoState->getInputIndexOfSampledPoint(inputIndex); 340 } 341 } 342 343 bool hasMultipleWords() const { 344 return mDicNodeState.mDicNodeStatePrevWord.getPrevWordCount() > 0; 345 } 346 347 int getProximityCorrectionCount() const { 348 return mDicNodeState.mDicNodeStateScoring.getProximityCorrectionCount(); 349 } 350 351 int getEditCorrectionCount() const { 352 return mDicNodeState.mDicNodeStateScoring.getEditCorrectionCount(); 353 } 354 355 // Used to prune nodes 356 float getNormalizedCompoundDistance() const { 357 return mDicNodeState.mDicNodeStateScoring.getNormalizedCompoundDistance(); 358 } 359 360 // Used to prune nodes 361 float getNormalizedSpatialDistance() const { 362 return mDicNodeState.mDicNodeStateScoring.getSpatialDistance() 363 / static_cast<float>(getInputIndex(0) + 1); 364 } 365 366 // Used to prune nodes 367 float getCompoundDistance() const { 368 return mDicNodeState.mDicNodeStateScoring.getCompoundDistance(); 369 } 370 371 // Used to prune nodes 372 float getCompoundDistance(const float languageWeight) const { 373 return mDicNodeState.mDicNodeStateScoring.getCompoundDistance(languageWeight); 374 } 375 376 // Used to commit input partially 377 int getPrevWordNodePos() const { 378 return mDicNodeState.mDicNodeStatePrevWord.getPrevWordNodePos(); 379 } 380 381 AK_FORCE_INLINE const int *getOutputWordBuf() const { 382 return mDicNodeState.mDicNodeStateOutput.mCodePointsBuf; 383 } 384 385 int getPrevCodePointG(int pointerId) const { 386 return mDicNodeState.mDicNodeStateInput.getPrevCodePoint(pointerId); 387 } 388 389 // Whether the current codepoint can be an intentional omission, in which case the traversal 390 // algorithm will always check for a possible omission here. 391 bool canBeIntentionalOmission() const { 392 return CharUtils::isIntentionalOmissionCodePoint(getNodeCodePoint()); 393 } 394 395 // Whether the omission is so frequent that it should incur zero cost. 396 bool isZeroCostOmission() const { 397 // TODO: do not hardcode and read from header 398 return (getNodeCodePoint() == KEYCODE_SINGLE_QUOTE); 399 } 400 401 // TODO: remove 402 float getTerminalDiffCostG(int path) const { 403 return mDicNodeState.mDicNodeStateInput.getTerminalDiffCost(path); 404 } 405 406 ////////////////////// 407 // Temporary getter // 408 // TODO: Remove // 409 ////////////////////// 410 // TODO: Remove once touch path is merged into ProximityInfoState 411 // Note: Returned codepoint may be a digraph codepoint if the node is in a composite glyph. 412 int getNodeCodePoint() const { 413 const int codePoint = mDicNodeProperties.getNodeCodePoint(); 414 const DigraphUtils::DigraphCodePointIndex digraphIndex = 415 mDicNodeState.mDicNodeStateScoring.getDigraphIndex(); 416 if (digraphIndex == DigraphUtils::NOT_A_DIGRAPH_INDEX) { 417 return codePoint; 418 } 419 return DigraphUtils::getDigraphCodePointForIndex(codePoint, digraphIndex); 420 } 421 422 //////////////////////////////// 423 // Utils for cost calculation // 424 //////////////////////////////// 425 AK_FORCE_INLINE bool isSameNodeCodePoint(const DicNode *const dicNode) const { 426 return mDicNodeProperties.getNodeCodePoint() 427 == dicNode->mDicNodeProperties.getNodeCodePoint(); 428 } 429 430 // TODO: remove 431 // TODO: rename getNextInputIndex 432 int16_t getInputIndex(int pointerId) const { 433 return mDicNodeState.mDicNodeStateInput.getInputIndex(pointerId); 434 } 435 436 //////////////////////////////////// 437 // Getter of features for scoring // 438 //////////////////////////////////// 439 float getSpatialDistanceForScoring() const { 440 return mDicNodeState.mDicNodeStateScoring.getSpatialDistance(); 441 } 442 443 float getLanguageDistanceForScoring() const { 444 return mDicNodeState.mDicNodeStateScoring.getLanguageDistance(); 445 } 446 447 // For space-aware gestures, we store the normalized distance at the char index 448 // that ends the first word of the suggestion. We call this the distance after 449 // first word. 450 float getNormalizedCompoundDistanceAfterFirstWord() const { 451 return mDicNodeState.mDicNodeStateScoring.getNormalizedCompoundDistanceAfterFirstWord(); 452 } 453 454 float getLanguageDistanceRatePerWordForScoring() const { 455 const float langDist = getLanguageDistanceForScoring(); 456 const float totalWordCount = 457 static_cast<float>(mDicNodeState.mDicNodeStatePrevWord.getPrevWordCount() + 1); 458 return langDist / totalWordCount; 459 } 460 461 float getRawLength() const { 462 return mDicNodeState.mDicNodeStateScoring.getRawLength(); 463 } 464 465 bool isLessThanOneErrorForScoring() const { 466 return mDicNodeState.mDicNodeStateScoring.getEditCorrectionCount() 467 + mDicNodeState.mDicNodeStateScoring.getProximityCorrectionCount() <= 1; 468 } 469 470 DoubleLetterLevel getDoubleLetterLevel() const { 471 return mDicNodeState.mDicNodeStateScoring.getDoubleLetterLevel(); 472 } 473 474 void setDoubleLetterLevel(DoubleLetterLevel doubleLetterLevel) { 475 mDicNodeState.mDicNodeStateScoring.setDoubleLetterLevel(doubleLetterLevel); 476 } 477 478 bool isInDigraph() const { 479 return mDicNodeState.mDicNodeStateScoring.getDigraphIndex() 480 != DigraphUtils::NOT_A_DIGRAPH_INDEX; 481 } 482 483 void advanceDigraphIndex() { 484 mDicNodeState.mDicNodeStateScoring.advanceDigraphIndex(); 485 } 486 487 bool isExactMatch() const { 488 return mDicNodeState.mDicNodeStateScoring.isExactMatch(); 489 } 490 491 bool isBlacklistedOrNotAWord() const { 492 return mDicNodeProperties.isBlacklistedOrNotAWord(); 493 } 494 495 inline uint16_t getNodeCodePointCount() const { 496 return mDicNodeProperties.getDepth(); 497 } 498 499 // Returns code point count including spaces 500 inline uint16_t getTotalNodeCodePointCount() const { 501 return getNodeCodePointCount() + mDicNodeState.mDicNodeStatePrevWord.getPrevWordLength(); 502 } 503 504 AK_FORCE_INLINE void dump(const char *tag) const { 505 #if DEBUG_DICT 506 DUMP_WORD_AND_SCORE(tag); 507 #if DEBUG_DUMP_ERROR 508 mProfiler.dump(); 509 #endif 510 #endif 511 } 512 513 void setReleaseListener(DicNodeReleaseListener *releaseListener) { 514 mReleaseListener = releaseListener; 515 } 516 517 AK_FORCE_INLINE bool compare(const DicNode *right) { 518 if (!isUsed() && !right->isUsed()) { 519 // Compare pointer values here for stable comparison 520 return this > right; 521 } 522 if (!isUsed()) { 523 return true; 524 } 525 if (!right->isUsed()) { 526 return false; 527 } 528 // Promote exact matches to prevent them from being pruned. 529 const bool leftExactMatch = isExactMatch(); 530 const bool rightExactMatch = right->isExactMatch(); 531 if (leftExactMatch != rightExactMatch) { 532 return leftExactMatch; 533 } 534 const float diff = 535 right->getNormalizedCompoundDistance() - getNormalizedCompoundDistance(); 536 static const float MIN_DIFF = 0.000001f; 537 if (diff > MIN_DIFF) { 538 return true; 539 } else if (diff < -MIN_DIFF) { 540 return false; 541 } 542 const int depth = getNodeCodePointCount(); 543 const int depthDiff = right->getNodeCodePointCount() - depth; 544 if (depthDiff != 0) { 545 return depthDiff > 0; 546 } 547 for (int i = 0; i < depth; ++i) { 548 const int codePoint = mDicNodeState.mDicNodeStateOutput.getCodePointAt(i); 549 const int rightCodePoint = right->mDicNodeState.mDicNodeStateOutput.getCodePointAt(i); 550 if (codePoint != rightCodePoint) { 551 return rightCodePoint > codePoint; 552 } 553 } 554 // Compare pointer values here for stable comparison 555 return this > right; 556 } 557 558 private: 559 DicNodeProperties mDicNodeProperties; 560 DicNodeState mDicNodeState; 561 // TODO: Remove 562 bool mIsCachedForNextSuggestion; 563 bool mIsUsed; 564 DicNodeReleaseListener *mReleaseListener; 565 566 AK_FORCE_INLINE int getTotalInputIndex() const { 567 int index = 0; 568 for (int i = 0; i < MAX_POINTER_COUNT_G; i++) { 569 index += mDicNodeState.mDicNodeStateInput.getInputIndex(i); 570 } 571 return index; 572 } 573 574 // Caveat: Must not be called outside Weighting 575 // This restriction is guaranteed by "friend" 576 AK_FORCE_INLINE void addCost(const float spatialCost, const float languageCost, 577 const bool doNormalization, const int inputSize, const ErrorType errorType) { 578 if (DEBUG_GEO_FULL) { 579 LOGI_SHOW_ADD_COST_PROP; 580 } 581 mDicNodeState.mDicNodeStateScoring.addCost(spatialCost, languageCost, doNormalization, 582 inputSize, getTotalInputIndex(), errorType); 583 } 584 585 // Saves the current normalized compound distance for space-aware gestures. 586 // See getNormalizedCompoundDistanceAfterFirstWord for details. 587 AK_FORCE_INLINE void saveNormalizedCompoundDistanceAfterFirstWordIfNoneYet() { 588 mDicNodeState.mDicNodeStateScoring.saveNormalizedCompoundDistanceAfterFirstWordIfNoneYet(); 589 } 590 591 // Caveat: Must not be called outside Weighting 592 // This restriction is guaranteed by "friend" 593 AK_FORCE_INLINE void forwardInputIndex(const int pointerId, const int count, 594 const bool overwritesPrevCodePointByNodeCodePoint) { 595 if (count == 0) { 596 return; 597 } 598 mDicNodeState.mDicNodeStateInput.forwardInputIndex(pointerId, count); 599 if (overwritesPrevCodePointByNodeCodePoint) { 600 mDicNodeState.mDicNodeStateInput.setPrevCodePoint(0, getNodeCodePoint()); 601 } 602 } 603 604 AK_FORCE_INLINE void updateInputIndexG(const DicNode_InputStateG *const inputStateG) { 605 if (mDicNodeState.mDicNodeStatePrevWord.getPrevWordCount() == 1 && isFirstLetter()) { 606 mDicNodeState.mDicNodeStatePrevWord.setSecondWordFirstInputIndex( 607 inputStateG->mInputIndex); 608 } 609 mDicNodeState.mDicNodeStateInput.updateInputIndexG(inputStateG->mPointerId, 610 inputStateG->mInputIndex, inputStateG->mPrevCodePoint, 611 inputStateG->mTerminalDiffCost, inputStateG->mRawLength); 612 mDicNodeState.mDicNodeStateScoring.addRawLength(inputStateG->mRawLength); 613 mDicNodeState.mDicNodeStateScoring.setDoubleLetterLevel(inputStateG->mDoubleLetterLevel); 614 } 615 }; 616 } // namespace latinime 617 #endif // LATINIME_DIC_NODE_H 618