proximity_info_state.cpp revision 2fa3693c264a4c150ac307d9bb7f6f8f18cc4ffc
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#define LOG_TAG "LatinIME: proximity_info_state.cpp"
18
19#include "suggest/core/layout/proximity_info_state.h"
20
21#include <cstring> // for memset() and memcpy()
22#include <sstream> // for debug prints
23#include <vector>
24
25#include "defines.h"
26#include "suggest/core/layout/geometry_utils.h"
27#include "suggest/core/layout/proximity_info.h"
28#include "suggest/core/layout/proximity_info_state_utils.h"
29#include "utils/char_utils.h"
30
31namespace latinime {
32
33int ProximityInfoState::getPrimaryOriginalCodePointAt(const int index) const {
34    const int primaryCodePoint = getPrimaryCodePointAt(index);
35    const int keyIndex = mProximityInfo->getKeyIndexOf(primaryCodePoint);
36    return mProximityInfo->getOriginalCodePointOf(keyIndex);
37}
38
39// TODO: Remove the dependency of "isGeometric"
40void ProximityInfoState::initInputParams(const int pointerId, const float maxPointToKeyLength,
41        const ProximityInfo *proximityInfo, const int *const inputCodes, const int inputSize,
42        const int *const xCoordinates, const int *const yCoordinates, const int *const times,
43        const int *const pointerIds, const bool isGeometric) {
44    ASSERT(isGeometric || (inputSize < MAX_WORD_LENGTH));
45    mIsContinuousSuggestionPossible = (mHasBeenUpdatedByGeometricInput != isGeometric) ?
46            false : ProximityInfoStateUtils::checkAndReturnIsContinuousSuggestionPossible(
47                    inputSize, xCoordinates, yCoordinates, times, mSampledInputSize,
48                    &mSampledInputXs, &mSampledInputYs, &mSampledTimes, &mSampledInputIndice);
49    if (DEBUG_DICT) {
50        AKLOGI("isContinuousSuggestionPossible = %s",
51                (mIsContinuousSuggestionPossible ? "true" : "false"));
52    }
53
54    mProximityInfo = proximityInfo;
55    mHasTouchPositionCorrectionData = proximityInfo->hasTouchPositionCorrectionData();
56    mMostCommonKeyWidthSquare = proximityInfo->getMostCommonKeyWidthSquare();
57    mKeyCount = proximityInfo->getKeyCount();
58    mCellHeight = proximityInfo->getCellHeight();
59    mCellWidth = proximityInfo->getCellWidth();
60    mGridHeight = proximityInfo->getGridWidth();
61    mGridWidth = proximityInfo->getGridHeight();
62
63    memset(mInputProximities, 0, sizeof(mInputProximities));
64
65    if (!isGeometric && pointerId == 0) {
66        mProximityInfo->initializeProximities(inputCodes, xCoordinates, yCoordinates,
67                inputSize, mInputProximities);
68    }
69
70    ///////////////////////
71    // Setup touch points
72    int pushTouchPointStartIndex = 0;
73    int lastSavedInputSize = 0;
74    mMaxPointToKeyLength = maxPointToKeyLength;
75    mSampledInputSize = 0;
76    mMostProbableStringProbability = 0.0f;
77
78    if (mIsContinuousSuggestionPossible && mSampledInputIndice.size() > 1) {
79        // Just update difference.
80        // Previous two points are never skipped. Thus, we pop 2 input point data here.
81        pushTouchPointStartIndex = ProximityInfoStateUtils::trimLastTwoTouchPoints(
82                &mSampledInputXs, &mSampledInputYs, &mSampledTimes, &mSampledLengthCache,
83                &mSampledInputIndice);
84        lastSavedInputSize = mSampledInputXs.size();
85    } else {
86        // Clear all data.
87        mSampledInputXs.clear();
88        mSampledInputYs.clear();
89        mSampledTimes.clear();
90        mSampledInputIndice.clear();
91        mSampledLengthCache.clear();
92        mSampledNormalizedSquaredLengthCache.clear();
93        mSampledNearKeySets.clear();
94        mSampledSearchKeySets.clear();
95        mSpeedRates.clear();
96        mBeelineSpeedPercentiles.clear();
97        mCharProbabilities.clear();
98        mDirections.clear();
99    }
100
101    if (DEBUG_GEO_FULL) {
102        AKLOGI("Init ProximityInfoState: reused points =  %d, last input size = %d",
103                pushTouchPointStartIndex, lastSavedInputSize);
104    }
105
106    if (xCoordinates && yCoordinates) {
107        mSampledInputSize = ProximityInfoStateUtils::updateTouchPoints(mProximityInfo,
108                mMaxPointToKeyLength, mInputProximities, xCoordinates, yCoordinates, times,
109                pointerIds, inputSize, isGeometric, pointerId,
110                pushTouchPointStartIndex, &mSampledInputXs, &mSampledInputYs, &mSampledTimes,
111                &mSampledLengthCache, &mSampledInputIndice);
112    }
113
114    if (mSampledInputSize > 0 && isGeometric) {
115        mAverageSpeed = ProximityInfoStateUtils::refreshSpeedRates(inputSize, xCoordinates,
116                yCoordinates, times, lastSavedInputSize, mSampledInputSize, &mSampledInputXs,
117                &mSampledInputYs, &mSampledTimes, &mSampledLengthCache, &mSampledInputIndice,
118                &mSpeedRates, &mDirections);
119        ProximityInfoStateUtils::refreshBeelineSpeedRates(mProximityInfo->getMostCommonKeyWidth(),
120                mAverageSpeed, inputSize, xCoordinates, yCoordinates, times, mSampledInputSize,
121                &mSampledInputXs, &mSampledInputYs, &mSampledInputIndice,
122                &mBeelineSpeedPercentiles);
123    }
124
125    if (mSampledInputSize > 0) {
126        ProximityInfoStateUtils::initGeometricDistanceInfos(mProximityInfo, mSampledInputSize,
127                lastSavedInputSize, isGeometric, &mSampledInputXs, &mSampledInputYs,
128                &mSampledNearKeySets, &mSampledNormalizedSquaredLengthCache);
129        if (isGeometric) {
130            // updates probabilities of skipping or mapping each key for all points.
131            ProximityInfoStateUtils::updateAlignPointProbabilities(
132                    mMaxPointToKeyLength, mProximityInfo->getMostCommonKeyWidth(),
133                    mProximityInfo->getKeyCount(), lastSavedInputSize, mSampledInputSize,
134                    &mSampledInputXs, &mSampledInputYs, &mSpeedRates, &mSampledLengthCache,
135                    &mSampledNormalizedSquaredLengthCache, &mSampledNearKeySets,
136                    &mCharProbabilities);
137            ProximityInfoStateUtils::updateSampledSearchKeySets(mProximityInfo,
138                    mSampledInputSize, lastSavedInputSize, &mSampledLengthCache,
139                    &mSampledNearKeySets, &mSampledSearchKeySets,
140                    &mSampledSearchKeyVectors);
141            mMostProbableStringProbability = ProximityInfoStateUtils::getMostProbableString(
142                    mProximityInfo, mSampledInputSize, &mCharProbabilities, mMostProbableString);
143
144        }
145    }
146
147    if (DEBUG_SAMPLING_POINTS) {
148        ProximityInfoStateUtils::dump(isGeometric, inputSize, xCoordinates, yCoordinates,
149                mSampledInputSize, &mSampledInputXs, &mSampledInputYs, &mSampledTimes, &mSpeedRates,
150                &mBeelineSpeedPercentiles);
151    }
152    // end
153    ///////////////////////
154
155    mTouchPositionCorrectionEnabled = mSampledInputSize > 0 && mHasTouchPositionCorrectionData
156            && xCoordinates && yCoordinates;
157    if (!isGeometric && pointerId == 0) {
158        ProximityInfoStateUtils::initPrimaryInputWord(
159                inputSize, mInputProximities, mPrimaryInputWord);
160    }
161    if (DEBUG_GEO_FULL) {
162        AKLOGI("ProximityState init finished: %d points out of %d", mSampledInputSize, inputSize);
163    }
164    mHasBeenUpdatedByGeometricInput = isGeometric;
165}
166
167// This function basically converts from a length to an edit distance. Accordingly, it's obviously
168// wrong to compare with mMaxPointToKeyLength.
169float ProximityInfoState::getPointToKeyLength(
170        const int inputIndex, const int codePoint) const {
171    const int keyId = mProximityInfo->getKeyIndexOf(codePoint);
172    if (keyId != NOT_AN_INDEX) {
173        const int index = inputIndex * mProximityInfo->getKeyCount() + keyId;
174        return min(mSampledNormalizedSquaredLengthCache[index], mMaxPointToKeyLength);
175    }
176    if (CharUtils::isIntentionalOmissionCodePoint(codePoint)) {
177        return 0.0f;
178    }
179    // If the char is not a key on the keyboard then return the max length.
180    return static_cast<float>(MAX_VALUE_FOR_WEIGHTING);
181}
182
183float ProximityInfoState::getPointToKeyByIdLength(
184        const int inputIndex, const int keyId) const {
185    return ProximityInfoStateUtils::getPointToKeyByIdLength(mMaxPointToKeyLength,
186            &mSampledNormalizedSquaredLengthCache, mProximityInfo->getKeyCount(), inputIndex,
187            keyId);
188}
189
190// In the following function, c is the current character of the dictionary word currently examined.
191// currentChars is an array containing the keys close to the character the user actually typed at
192// the same position. We want to see if c is in it: if so, then the word contains at that position
193// a character close to what the user typed.
194// What the user typed is actually the first character of the array.
195// proximityIndex is a pointer to the variable where getProximityType returns the index of c
196// in the proximity chars of the input index.
197// Notice : accented characters do not have a proximity list, so they are alone in their list. The
198// non-accented version of the character should be considered "close", but not the other keys close
199// to the non-accented version.
200ProximityType ProximityInfoState::getProximityType(const int index, const int codePoint,
201        const bool checkProximityChars, int *proximityIndex) const {
202    const int *currentCodePoints = getProximityCodePointsAt(index);
203    const int firstCodePoint = currentCodePoints[0];
204    const int baseLowerC = CharUtils::toBaseLowerCase(codePoint);
205
206    // The first char in the array is what user typed. If it matches right away, that means the
207    // user typed that same char for this pos.
208    if (firstCodePoint == baseLowerC || firstCodePoint == codePoint) {
209        return MATCH_CHAR;
210    }
211
212    if (!checkProximityChars) return SUBSTITUTION_CHAR;
213
214    // If the non-accented, lowercased version of that first character matches c, then we have a
215    // non-accented version of the accented character the user typed. Treat it as a close char.
216    if (CharUtils::toBaseLowerCase(firstCodePoint) == baseLowerC) {
217        return PROXIMITY_CHAR;
218    }
219
220    // Not an exact nor an accent-alike match: search the list of close keys
221    int j = 1;
222    while (j < MAX_PROXIMITY_CHARS_SIZE
223            && currentCodePoints[j] > ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE) {
224        const bool matched = (currentCodePoints[j] == baseLowerC
225                || currentCodePoints[j] == codePoint);
226        if (matched) {
227            if (proximityIndex) {
228                *proximityIndex = j;
229            }
230            return PROXIMITY_CHAR;
231        }
232        ++j;
233    }
234    if (j < MAX_PROXIMITY_CHARS_SIZE
235            && currentCodePoints[j] == ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE) {
236        ++j;
237        while (j < MAX_PROXIMITY_CHARS_SIZE
238                && currentCodePoints[j] > ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE) {
239            const bool matched = (currentCodePoints[j] == baseLowerC
240                    || currentCodePoints[j] == codePoint);
241            if (matched) {
242                if (proximityIndex) {
243                    *proximityIndex = j;
244                }
245                return ADDITIONAL_PROXIMITY_CHAR;
246            }
247            ++j;
248        }
249    }
250    // Was not included, signal this as a substitution character.
251    return SUBSTITUTION_CHAR;
252}
253
254ProximityType ProximityInfoState::getProximityTypeG(const int index, const int codePoint) const {
255    if (!isUsed()) {
256        return UNRELATED_CHAR;
257    }
258    const int lowerCodePoint = CharUtils::toLowerCase(codePoint);
259    const int baseLowerCodePoint = CharUtils::toBaseCodePoint(lowerCodePoint);
260    for (int i = 0; i < static_cast<int>(mSampledSearchKeyVectors[index].size()); ++i) {
261        if (mSampledSearchKeyVectors[index][i] == lowerCodePoint
262                || mSampledSearchKeyVectors[index][i] == baseLowerCodePoint) {
263            return MATCH_CHAR;
264        }
265    }
266    return UNRELATED_CHAR;
267}
268
269bool ProximityInfoState::isKeyInSerchKeysAfterIndex(const int index, const int keyId) const {
270    ASSERT(keyId >= 0 && index >= 0 && index < mSampledInputSize);
271    return mSampledSearchKeySets[index].test(keyId);
272}
273
274float ProximityInfoState::getDirection(const int index0, const int index1) const {
275    return ProximityInfoStateUtils::getDirection(
276            &mSampledInputXs, &mSampledInputYs, index0, index1);
277}
278
279float ProximityInfoState::getMostProbableString(int *const codePointBuf) const {
280    memcpy(codePointBuf, mMostProbableString, sizeof(mMostProbableString));
281    return mMostProbableStringProbability;
282}
283
284bool ProximityInfoState::hasSpaceProximity(const int index) const {
285    ASSERT(0 <= index && index < mSampledInputSize);
286    return mProximityInfo->hasSpaceProximity(getInputX(index), getInputY(index));
287}
288
289// Returns a probability of mapping index to keyIndex.
290float ProximityInfoState::getProbability(const int index, const int keyIndex) const {
291    ASSERT(0 <= index && index < mSampledInputSize);
292    hash_map_compat<int, float>::const_iterator it = mCharProbabilities[index].find(keyIndex);
293    if (it != mCharProbabilities[index].end()) {
294        return it->second;
295    }
296    return static_cast<float>(MAX_VALUE_FOR_WEIGHTING);
297}
298} // namespace latinime
299