1a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka/*
2a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka * Copyright (C) 2013 The Android Open Source Project
3a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka *
4a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka * Licensed under the Apache License, Version 2.0 (the "License");
5a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka * you may not use this file except in compliance with the License.
6a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka * You may obtain a copy of the License at
7a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka *
8a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka *      http://www.apache.org/licenses/LICENSE-2.0
9a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka *
10a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka * Unless required by applicable law or agreed to in writing, software
11a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka * distributed under the License is distributed on an "AS IS" BASIS,
12a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka * See the License for the specific language governing permissions and
14a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka * limitations under the License.
15a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka */
16a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka
17a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka#ifndef LATINIME_TYPING_TRAVERSAL_H
18a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka#define LATINIME_TYPING_TRAVERSAL_H
19a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka
20a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka#include <stdint.h>
21a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka
22a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka#include "defines.h"
23b68e73448104714e8f12f89a1e00fb10b5fd14c4Ken Wakasa#include "suggest/core/dicnode/dic_node.h"
24b68e73448104714e8f12f89a1e00fb10b5fd14c4Ken Wakasa#include "suggest/core/dicnode/dic_node_vector.h"
2529432f843a8cd6ffb2be286104964592e80d77c9Ken Wakasa#include "suggest/core/layout/proximity_info_state.h"
267a06a792871c38517264fcb63b80a9c09bfe4766Keisuke Kuroynagi#include "suggest/core/layout/proximity_info_utils.h"
27b68e73448104714e8f12f89a1e00fb10b5fd14c4Ken Wakasa#include "suggest/core/policy/traversal.h"
28b68e73448104714e8f12f89a1e00fb10b5fd14c4Ken Wakasa#include "suggest/core/session/dic_traverse_session.h"
29b68e73448104714e8f12f89a1e00fb10b5fd14c4Ken Wakasa#include "suggest/policyimpl/typing/scoring_params.h"
30addea83bad5751308fef508d79c6989b8872f050Ken Wakasa#include "utils/char_utils.h"
31a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka
32a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataokanamespace latinime {
33a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataokaclass TypingTraversal : public Traversal {
34a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka public:
35a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka    static const TypingTraversal *getInstance() { return &sInstance; }
36a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka
37a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka    AK_FORCE_INLINE int getMaxPointerCount() const {
38a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        return MAX_POINTER_COUNT;
39a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka    }
40a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka
41a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka    AK_FORCE_INLINE bool allowsErrorCorrections(const DicNode *const dicNode) const {
42a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        return dicNode->getNormalizedSpatialDistance()
438da0c932925f605b9dd815387753dfab19beb873Tom Ouyang                < ScoringParams::NORMALIZED_SPATIAL_DISTANCE_THRESHOLD_FOR_EDIT;
44a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka    }
45a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka
46a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka    AK_FORCE_INLINE bool isOmission(const DicTraverseSession *const traverseSession,
47fd02ec10f0a0374096e88fa30a0e126d6ff11c72Tom Ouyang            const DicNode *const dicNode, const DicNode *const childDicNode,
48fd02ec10f0a0374096e88fa30a0e126d6ff11c72Tom Ouyang            const bool allowsErrorCorrections) const {
49a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        if (!CORRECT_OMISSION) {
50a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka            return false;
51a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        }
52fd02ec10f0a0374096e88fa30a0e126d6ff11c72Tom Ouyang        // Note: Always consider intentional omissions (like apostrophes) since they are common.
53fd02ec10f0a0374096e88fa30a0e126d6ff11c72Tom Ouyang        const bool canConsiderOmission =
54fd02ec10f0a0374096e88fa30a0e126d6ff11c72Tom Ouyang                allowsErrorCorrections || childDicNode->canBeIntentionalOmission();
55fd02ec10f0a0374096e88fa30a0e126d6ff11c72Tom Ouyang        if (!canConsiderOmission) {
56fd02ec10f0a0374096e88fa30a0e126d6ff11c72Tom Ouyang            return false;
57fd02ec10f0a0374096e88fa30a0e126d6ff11c72Tom Ouyang        }
58a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        const int inputSize = traverseSession->getInputSize();
59a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        // TODO: Don't refer to isCompletion?
60a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        if (dicNode->isCompletion(inputSize)) {
61a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka            return false;
62a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        }
63a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        if (dicNode->canBeIntentionalOmission()) {
64a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka            return true;
65a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        }
66a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        const int point0Index = dicNode->getInputIndex(0);
67a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        const int currentBaseLowerCodePoint =
68464d3ba43257da34ab165da8ba0af11e928aae5cKen Wakasa                CharUtils::toBaseLowerCase(childDicNode->getNodeCodePoint());
69a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        const int typedBaseLowerCodePoint =
70464d3ba43257da34ab165da8ba0af11e928aae5cKen Wakasa                CharUtils::toBaseLowerCase(traverseSession->getProximityInfoState(0)
71a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka                        ->getPrimaryCodePointAt(point0Index));
72a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        return (currentBaseLowerCodePoint != typedBaseLowerCodePoint);
73a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka    }
74a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka
75a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka    AK_FORCE_INLINE bool isSpaceSubstitutionTerminal(
76a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka            const DicTraverseSession *const traverseSession, const DicNode *const dicNode) const {
77252412d7eb4573f91588b06b0fe49ef9f0ac38acSatoshi Kataoka        if (!CORRECT_NEW_WORD_SPACE_SUBSTITUTION) {
78a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka            return false;
79a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        }
80a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        if (!canDoLookAheadCorrection(traverseSession, dicNode)) {
81a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka            return false;
82a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        }
83a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        const int point0Index = dicNode->getInputIndex(0);
84a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        return dicNode->isTerminalWordNode()
85a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka                && traverseSession->getProximityInfoState(0)->
86a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka                        hasSpaceProximity(point0Index);
87a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka    }
88a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka
89a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka    AK_FORCE_INLINE bool isSpaceOmissionTerminal(
90a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka            const DicTraverseSession *const traverseSession, const DicNode *const dicNode) const {
91252412d7eb4573f91588b06b0fe49ef9f0ac38acSatoshi Kataoka        if (!CORRECT_NEW_WORD_SPACE_OMISSION) {
92a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka            return false;
93a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        }
94a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        const int inputSize = traverseSession->getInputSize();
95a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        // TODO: Don't refer to isCompletion?
96a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        if (dicNode->isCompletion(inputSize)) {
97a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka            return false;
98a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        }
99a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        if (!dicNode->isTerminalWordNode()) {
100a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka            return false;
101a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        }
102a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        const int16_t pointIndex = dicNode->getInputIndex(0);
103a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        return pointIndex <= inputSize && !dicNode->isTotalInputSizeExceedingLimit()
1043e954347e3a7b381d7e94feb002e158f3bc69a32Jean Chalard                && !dicNode->shouldBeFilteredBySafetyNetForBigram();
105a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka    }
106a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka
107a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka    AK_FORCE_INLINE bool shouldDepthLevelCache(
108a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka            const DicTraverseSession *const traverseSession) const {
109a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        const int inputSize = traverseSession->getInputSize();
110a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        return traverseSession->isCacheBorderForTyping(inputSize);
111a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka    }
112a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka
113a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka    AK_FORCE_INLINE bool shouldNodeLevelCache(
114a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka            const DicTraverseSession *const traverseSession, const DicNode *const dicNode) const {
115a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        return false;
116a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka    }
117a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka
118a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka    AK_FORCE_INLINE bool canDoLookAheadCorrection(
119a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka            const DicTraverseSession *const traverseSession, const DicNode *const dicNode) const {
120a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        const int inputSize = traverseSession->getInputSize();
121a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        return dicNode->canDoLookAheadCorrection(inputSize);
122a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka    }
123a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka
124a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka    AK_FORCE_INLINE ProximityType getProximityType(
125a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka            const DicTraverseSession *const traverseSession, const DicNode *const dicNode,
126a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka            const DicNode *const childDicNode) const {
127a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        return traverseSession->getProximityInfoState(0)->getProximityType(
128a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka                dicNode->getInputIndex(0), childDicNode->getNodeCodePoint(),
129a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka                true /* checkProximityChars */);
130a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka    }
131a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka
132a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka    AK_FORCE_INLINE bool needsToTraverseAllUserInput() const {
133a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        return true;
134a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka    }
135a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka
136a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka    AK_FORCE_INLINE float getMaxSpatialDistance() const {
137a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        return ScoringParams::MAX_SPATIAL_DISTANCE;
138a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka    }
139a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka
140a829188f54c0fd2e26192d98d9e56e033d8f91aaKeisuke Kuroynagi    AK_FORCE_INLINE bool autoCorrectsToMultiWordSuggestionIfTop() const {
141a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        return true;
142a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka    }
143a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka
144a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka    AK_FORCE_INLINE int getDefaultExpandDicNodeSize() const {
145a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        return DicNodeVector::DEFAULT_NODES_SIZE_FOR_OPTIMIZATION;
146a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka    }
147a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka
148a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka    AK_FORCE_INLINE bool sameAsTyped(
149a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka            const DicTraverseSession *const traverseSession, const DicNode *const dicNode) const {
150a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        return traverseSession->getProximityInfoState(0)->sameAsTyped(
15109858cbde49c086dd1d4d3050b57f0ea1774158bKeisuke Kuroynagi                dicNode->getOutputWordBuf(), dicNode->getNodeCodePointCount());
152a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka    }
153a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka
1544447b14b785652e36adca329f5cddf986bfd14faKeisuke Kuroynagi    AK_FORCE_INLINE int getMaxCacheSize(const int inputSize) const {
1554447b14b785652e36adca329f5cddf986bfd14faKeisuke Kuroynagi        return (inputSize <= 1) ? ScoringParams::MAX_CACHE_DIC_NODE_SIZE_FOR_SINGLE_POINT
1564447b14b785652e36adca329f5cddf986bfd14faKeisuke Kuroynagi                : ScoringParams::MAX_CACHE_DIC_NODE_SIZE;
157a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka    }
158a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka
159a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka    AK_FORCE_INLINE bool isPossibleOmissionChildNode(
160a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka            const DicTraverseSession *const traverseSession, const DicNode *const parentDicNode,
161a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka            const DicNode *const dicNode) const {
162a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        const ProximityType proximityType =
163a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka                getProximityType(traverseSession, parentDicNode, dicNode);
1647a06a792871c38517264fcb63b80a9c09bfe4766Keisuke Kuroynagi        if (!ProximityInfoUtils::isMatchOrProximityChar(proximityType)) {
165a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka            return false;
166a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        }
167a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        return true;
168a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka    }
169a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka
170a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka    AK_FORCE_INLINE bool isGoodToTraverseNextWord(const DicNode *const dicNode) const {
171a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        const int probability = dicNode->getProbability();
172a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        if (probability < ScoringParams::THRESHOLD_NEXT_WORD_PROBABILITY) {
173a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka            return false;
174a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        }
175a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        const int c = dicNode->getOutputWordBuf()[0];
17609858cbde49c086dd1d4d3050b57f0ea1774158bKeisuke Kuroynagi        const bool shortCappedWord = dicNode->getNodeCodePointCount()
177464d3ba43257da34ab165da8ba0af11e928aae5cKen Wakasa                < ScoringParams::THRESHOLD_SHORT_WORD_LENGTH && CharUtils::isAsciiUpper(c);
178a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka        return !shortCappedWord
179a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka                || probability >= ScoringParams::THRESHOLD_NEXT_WORD_PROBABILITY_FOR_CAPPED;
180a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka    }
181a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka
182a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka private:
183a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka    DISALLOW_COPY_AND_ASSIGN(TypingTraversal);
184a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka    static const bool CORRECT_OMISSION;
185252412d7eb4573f91588b06b0fe49ef9f0ac38acSatoshi Kataoka    static const bool CORRECT_NEW_WORD_SPACE_SUBSTITUTION;
186252412d7eb4573f91588b06b0fe49ef9f0ac38acSatoshi Kataoka    static const bool CORRECT_NEW_WORD_SPACE_OMISSION;
187a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka    static const TypingTraversal sInstance;
188a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka
189a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka    TypingTraversal() {}
190a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka    ~TypingTraversal() {}
191a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka};
192a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka} // namespace latinime
193a6a416519603d2e65416dd8f9507913b7e4fd0a0Satoshi Kataoka#endif // LATINIME_TYPING_TRAVERSAL_H
194