proximity_info_state.cpp revision 0519fdd18ac4414b8298d3338db1c8ccce032170
13e8c58f68d53e6cc9dbf59201c7bdfb8ad04a1cdSatoshi Kataoka/*
23e8c58f68d53e6cc9dbf59201c7bdfb8ad04a1cdSatoshi Kataoka * Copyright (C) 2012 The Android Open Source Project
33e8c58f68d53e6cc9dbf59201c7bdfb8ad04a1cdSatoshi Kataoka *
43e8c58f68d53e6cc9dbf59201c7bdfb8ad04a1cdSatoshi Kataoka * Licensed under the Apache License, Version 2.0 (the "License");
53e8c58f68d53e6cc9dbf59201c7bdfb8ad04a1cdSatoshi Kataoka * you may not use this file except in compliance with the License.
63e8c58f68d53e6cc9dbf59201c7bdfb8ad04a1cdSatoshi Kataoka * You may obtain a copy of the License at
73e8c58f68d53e6cc9dbf59201c7bdfb8ad04a1cdSatoshi Kataoka *
83e8c58f68d53e6cc9dbf59201c7bdfb8ad04a1cdSatoshi Kataoka *      http://www.apache.org/licenses/LICENSE-2.0
93e8c58f68d53e6cc9dbf59201c7bdfb8ad04a1cdSatoshi Kataoka *
103e8c58f68d53e6cc9dbf59201c7bdfb8ad04a1cdSatoshi Kataoka * Unless required by applicable law or agreed to in writing, software
113e8c58f68d53e6cc9dbf59201c7bdfb8ad04a1cdSatoshi Kataoka * distributed under the License is distributed on an "AS IS" BASIS,
123e8c58f68d53e6cc9dbf59201c7bdfb8ad04a1cdSatoshi Kataoka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133e8c58f68d53e6cc9dbf59201c7bdfb8ad04a1cdSatoshi Kataoka * See the License for the specific language governing permissions and
143e8c58f68d53e6cc9dbf59201c7bdfb8ad04a1cdSatoshi Kataoka * limitations under the License.
153e8c58f68d53e6cc9dbf59201c7bdfb8ad04a1cdSatoshi Kataoka */
163e8c58f68d53e6cc9dbf59201c7bdfb8ad04a1cdSatoshi Kataoka
1777e8e81ad95cfc1eb8f8407fc872674b8d08bbe9Ken Wakasa#include <cstring> // for memset()
18806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi#include <sstream> // for debug prints
193e8c58f68d53e6cc9dbf59201c7bdfb8ad04a1cdSatoshi Kataoka
203e8c58f68d53e6cc9dbf59201c7bdfb8ad04a1cdSatoshi Kataoka#define LOG_TAG "LatinIME: proximity_info_state.cpp"
213e8c58f68d53e6cc9dbf59201c7bdfb8ad04a1cdSatoshi Kataoka
223e8c58f68d53e6cc9dbf59201c7bdfb8ad04a1cdSatoshi Kataoka#include "defines.h"
233e8c58f68d53e6cc9dbf59201c7bdfb8ad04a1cdSatoshi Kataoka#include "proximity_info.h"
243e8c58f68d53e6cc9dbf59201c7bdfb8ad04a1cdSatoshi Kataoka#include "proximity_info_state.h"
253e8c58f68d53e6cc9dbf59201c7bdfb8ad04a1cdSatoshi Kataoka
263e8c58f68d53e6cc9dbf59201c7bdfb8ad04a1cdSatoshi Kataokanamespace latinime {
27764dd712032d7b8012797b1116b523bef7b907f3Ken Wakasa
28764dd712032d7b8012797b1116b523bef7b907f3Ken Wakasaconst int ProximityInfoState::NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR_LOG_2 = 10;
29764dd712032d7b8012797b1116b523bef7b907f3Ken Wakasaconst int ProximityInfoState::NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR =
30764dd712032d7b8012797b1116b523bef7b907f3Ken Wakasa        1 << NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR_LOG_2;
31764dd712032d7b8012797b1116b523bef7b907f3Ken Wakasaconst float ProximityInfoState::NOT_A_DISTANCE_FLOAT = -1.0f;
32764dd712032d7b8012797b1116b523bef7b907f3Ken Wakasaconst int ProximityInfoState::NOT_A_CODE = -1;
336ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataokaconst int ProximityInfoState::LOOKUP_RADIUS_PERCENTILE = 50;
346ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataokaconst int ProximityInfoState::FIRST_POINT_TIME_OFFSET_MILLIS = 150;
356ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataokaconst int ProximityInfoState::STRONG_DOUBLE_LETTER_TIME_MILLIS = 600;
366ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataokaconst int ProximityInfoState::MIN_DOUBLE_LETTER_BEELINE_SPEED_PERCENTILE = 5;
37764dd712032d7b8012797b1116b523bef7b907f3Ken Wakasa
38233aad5e5c7567a97af30f38f50a65365f729dfeSatoshi Kataokavoid ProximityInfoState::initInputParams(const int pointerId, const float maxPointToKeyLength,
391e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa        const ProximityInfo *proximityInfo, const int *const inputCodes, const int inputSize,
40687a244703a02323ebd64433cbaead5def499861Satoshi Kataoka        const int *const xCoordinates, const int *const yCoordinates, const int *const times,
41687a244703a02323ebd64433cbaead5def499861Satoshi Kataoka        const int *const pointerIds, const bool isGeometric) {
42fe4f1ce26ca41ef51b5245d70e93b502f76262a5Satoshi Kataoka    mIsContinuationPossible = checkAndReturnIsContinuationPossible(
43fe4f1ce26ca41ef51b5245d70e93b502f76262a5Satoshi Kataoka            inputSize, xCoordinates, yCoordinates, times, isGeometric);
44096f35ff4b5413906e2a339663baf16e5dabaf64Keisuke Kuroyanagi
454a3db7057f77dc85311fb1f94934b5a004ab613eSatoshi Kataoka    mProximityInfo = proximityInfo;
464a3db7057f77dc85311fb1f94934b5a004ab613eSatoshi Kataoka    mHasTouchPositionCorrectionData = proximityInfo->hasTouchPositionCorrectionData();
474a3db7057f77dc85311fb1f94934b5a004ab613eSatoshi Kataoka    mMostCommonKeyWidthSquare = proximityInfo->getMostCommonKeyWidthSquare();
484a3db7057f77dc85311fb1f94934b5a004ab613eSatoshi Kataoka    mLocaleStr = proximityInfo->getLocaleStr();
494a3db7057f77dc85311fb1f94934b5a004ab613eSatoshi Kataoka    mKeyCount = proximityInfo->getKeyCount();
504a3db7057f77dc85311fb1f94934b5a004ab613eSatoshi Kataoka    mCellHeight = proximityInfo->getCellHeight();
514a3db7057f77dc85311fb1f94934b5a004ab613eSatoshi Kataoka    mCellWidth = proximityInfo->getCellWidth();
524a3db7057f77dc85311fb1f94934b5a004ab613eSatoshi Kataoka    mGridHeight = proximityInfo->getGridWidth();
534a3db7057f77dc85311fb1f94934b5a004ab613eSatoshi Kataoka    mGridWidth = proximityInfo->getGridHeight();
543e8c58f68d53e6cc9dbf59201c7bdfb8ad04a1cdSatoshi Kataoka
5508f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka    memset(mInputCodes, 0, sizeof(mInputCodes));
563e8c58f68d53e6cc9dbf59201c7bdfb8ad04a1cdSatoshi Kataoka
5708f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka    if (!isGeometric && pointerId == 0) {
5808f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka        // Initialize
5908f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka        // - mInputCodes
6008f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka        // - mNormalizedSquaredDistances
6108f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka        // TODO: Merge
62687a244703a02323ebd64433cbaead5def499861Satoshi Kataoka        for (int i = 0; i < inputSize; ++i) {
631e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa            const int primaryKey = inputCodes[i];
6408f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka            const int x = xCoordinates[i];
6508f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka            const int y = yCoordinates[i];
6608f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka            int *proximities = &mInputCodes[i * MAX_PROXIMITY_CHARS_SIZE_INTERNAL];
6708f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka            mProximityInfo->calculateNearbyKeyCodes(x, y, primaryKey, proximities);
6808f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka        }
6908f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka
7008f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka        if (DEBUG_PROXIMITY_CHARS) {
7108f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka            for (int i = 0; i < inputSize; ++i) {
7208f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka                AKLOGI("---");
7308f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka                for (int j = 0; j < MAX_PROXIMITY_CHARS_SIZE_INTERNAL; ++j) {
7408f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka                    int icc = mInputCodes[i * MAX_PROXIMITY_CHARS_SIZE_INTERNAL + j];
7508f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka                    int icfjc = inputCodes[i * MAX_PROXIMITY_CHARS_SIZE_INTERNAL + j];
7608f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka                    icc += 0;
7708f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka                    icfjc += 0;
7808f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka                    AKLOGI("--- (%d)%c,%c", i, icc, icfjc); AKLOGI("--- A<%d>,B<%d>", icc, icfjc);
7908f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka                }
803e8c58f68d53e6cc9dbf59201c7bdfb8ad04a1cdSatoshi Kataoka            }
813e8c58f68d53e6cc9dbf59201c7bdfb8ad04a1cdSatoshi Kataoka        }
823e8c58f68d53e6cc9dbf59201c7bdfb8ad04a1cdSatoshi Kataoka    }
83687a244703a02323ebd64433cbaead5def499861Satoshi Kataoka
84687a244703a02323ebd64433cbaead5def499861Satoshi Kataoka    ///////////////////////
85687a244703a02323ebd64433cbaead5def499861Satoshi Kataoka    // Setup touch points
86096f35ff4b5413906e2a339663baf16e5dabaf64Keisuke Kuroyanagi    int pushTouchPointStartIndex = 0;
87096f35ff4b5413906e2a339663baf16e5dabaf64Keisuke Kuroyanagi    int lastSavedInputSize = 0;
8808f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka    mMaxPointToKeyLength = maxPointToKeyLength;
89096f35ff4b5413906e2a339663baf16e5dabaf64Keisuke Kuroyanagi    if (mIsContinuationPossible && mInputIndice.size() > 1) {
90096f35ff4b5413906e2a339663baf16e5dabaf64Keisuke Kuroyanagi        // Just update difference.
91096f35ff4b5413906e2a339663baf16e5dabaf64Keisuke Kuroyanagi        // Two points prior is never skipped. Thus, we pop 2 input point data here.
92096f35ff4b5413906e2a339663baf16e5dabaf64Keisuke Kuroyanagi        pushTouchPointStartIndex = mInputIndice[mInputIndice.size() - 2];
93096f35ff4b5413906e2a339663baf16e5dabaf64Keisuke Kuroyanagi        popInputData();
94096f35ff4b5413906e2a339663baf16e5dabaf64Keisuke Kuroyanagi        popInputData();
95feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka        lastSavedInputSize = mSampledInputXs.size();
96096f35ff4b5413906e2a339663baf16e5dabaf64Keisuke Kuroyanagi    } else {
97096f35ff4b5413906e2a339663baf16e5dabaf64Keisuke Kuroyanagi        // Clear all data.
98feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka        mSampledInputXs.clear();
99feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka        mSampledInputYs.clear();
100096f35ff4b5413906e2a339663baf16e5dabaf64Keisuke Kuroyanagi        mTimes.clear();
101096f35ff4b5413906e2a339663baf16e5dabaf64Keisuke Kuroyanagi        mInputIndice.clear();
102096f35ff4b5413906e2a339663baf16e5dabaf64Keisuke Kuroyanagi        mLengthCache.clear();
103096f35ff4b5413906e2a339663baf16e5dabaf64Keisuke Kuroyanagi        mDistanceCache.clear();
104096f35ff4b5413906e2a339663baf16e5dabaf64Keisuke Kuroyanagi        mNearKeysVector.clear();
105ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        mSearchKeysVector.clear();
1069af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka        mSpeedRates.clear();
1076ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka        mBeelineSpeedPercentiles.clear();
108806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi        mCharProbabilities.clear();
1091e06a4d8e9e71188ed685282155ea52a48ddc050Keisuke Kuroyanagi        mDirections.clear();
110096f35ff4b5413906e2a339663baf16e5dabaf64Keisuke Kuroyanagi    }
1119182daf98c0d1db897e82b77c2196b7f9aad6f01Satoshi Kataoka    if (DEBUG_GEO_FULL) {
1129182daf98c0d1db897e82b77c2196b7f9aad6f01Satoshi Kataoka        AKLOGI("Init ProximityInfoState: reused points =  %d, last input size = %d",
1139182daf98c0d1db897e82b77c2196b7f9aad6f01Satoshi Kataoka                pushTouchPointStartIndex, lastSavedInputSize);
1149182daf98c0d1db897e82b77c2196b7f9aad6f01Satoshi Kataoka    }
115feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    mSampledInputSize = 0;
116d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi
117687a244703a02323ebd64433cbaead5def499861Satoshi Kataoka    if (xCoordinates && yCoordinates) {
1189af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka        if (DEBUG_SAMPLING_POINTS) {
1199af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka            if (isGeometric) {
1209af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka                for (int i = 0; i < inputSize; ++i) {
1219af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka                    AKLOGI("(%d) x %d, y %d, time %d",
1229af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka                            i, xCoordinates[i], yCoordinates[i], times[i]);
1239af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka                }
1249af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka            }
1259af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka        }
126687a244703a02323ebd64433cbaead5def499861Satoshi Kataoka        const bool proximityOnly = !isGeometric && (xCoordinates[0] < 0 || yCoordinates[0] < 0);
127096f35ff4b5413906e2a339663baf16e5dabaf64Keisuke Kuroyanagi        int lastInputIndex = pushTouchPointStartIndex;
128096f35ff4b5413906e2a339663baf16e5dabaf64Keisuke Kuroyanagi        for (int i = lastInputIndex; i < inputSize; ++i) {
129d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi            const int pid = pointerIds ? pointerIds[i] : 0;
130d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi            if (pointerId == pid) {
131d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi                lastInputIndex = i;
132d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi            }
133d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi        }
1349182daf98c0d1db897e82b77c2196b7f9aad6f01Satoshi Kataoka        if (DEBUG_GEO_FULL) {
1359182daf98c0d1db897e82b77c2196b7f9aad6f01Satoshi Kataoka            AKLOGI("Init ProximityInfoState: last input index = %d", lastInputIndex);
1369182daf98c0d1db897e82b77c2196b7f9aad6f01Satoshi Kataoka        }
137d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi        // Working space to save near keys distances for current, prev and prevprev input point.
138d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi        NearKeysDistanceMap nearKeysDistances[3];
139d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi        // These pointers are swapped for each inputs points.
140d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi        NearKeysDistanceMap *currentNearKeysDistances = &nearKeysDistances[0];
141d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi        NearKeysDistanceMap *prevNearKeysDistances = &nearKeysDistances[1];
142d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi        NearKeysDistanceMap *prevPrevNearKeysDistances = &nearKeysDistances[2];
143ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        // "sumAngle" is accumulated by each angle of input points. And when "sumAngle" exceeds
144ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        // the threshold we save that point, reset sumAngle. This aims to keep the figure of
145ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        // the curve.
146ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        float sumAngle = 0.0f;
147d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi
148096f35ff4b5413906e2a339663baf16e5dabaf64Keisuke Kuroyanagi        for (int i = pushTouchPointStartIndex; i <= lastInputIndex; ++i) {
149687a244703a02323ebd64433cbaead5def499861Satoshi Kataoka            // Assuming pointerId == 0 if pointerIds is null.
150687a244703a02323ebd64433cbaead5def499861Satoshi Kataoka            const int pid = pointerIds ? pointerIds[i] : 0;
1519182daf98c0d1db897e82b77c2196b7f9aad6f01Satoshi Kataoka            if (DEBUG_GEO_FULL) {
1529182daf98c0d1db897e82b77c2196b7f9aad6f01Satoshi Kataoka                AKLOGI("Init ProximityInfoState: (%d)PID = %d", i, pid);
1539182daf98c0d1db897e82b77c2196b7f9aad6f01Satoshi Kataoka            }
154687a244703a02323ebd64433cbaead5def499861Satoshi Kataoka            if (pointerId == pid) {
1551e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa                const int c = isGeometric ? NOT_A_COORDINATE : getPrimaryCodePointAt(i);
156687a244703a02323ebd64433cbaead5def499861Satoshi Kataoka                const int x = proximityOnly ? NOT_A_COORDINATE : xCoordinates[i];
157687a244703a02323ebd64433cbaead5def499861Satoshi Kataoka                const int y = proximityOnly ? NOT_A_COORDINATE : yCoordinates[i];
158687a244703a02323ebd64433cbaead5def499861Satoshi Kataoka                const int time = times ? times[i] : -1;
159ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi
160ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                if (i > 1) {
161ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                    const float prevAngle = getAngle(xCoordinates[i - 2], yCoordinates[i - 2],
162ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                            xCoordinates[i - 1], yCoordinates[i - 1]);
163ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                    const float currentAngle =
164ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                            getAngle(xCoordinates[i - 1], yCoordinates[i - 1], x, y);
165ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                    sumAngle += getAngleDiff(prevAngle, currentAngle);
166ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                }
167ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi
168952ec4977d772607140773ae7d8868f86a7e0097Satoshi Kataoka                if (pushTouchPoint(i, c, x, y, time, isGeometric /* do sampling */,
169ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                        i == lastInputIndex, sumAngle, currentNearKeysDistances,
170ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                        prevNearKeysDistances, prevPrevNearKeysDistances)) {
171d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi                    // Previous point information was popped.
172d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi                    NearKeysDistanceMap *tmp = prevNearKeysDistances;
173d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi                    prevNearKeysDistances = currentNearKeysDistances;
174d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi                    currentNearKeysDistances = tmp;
175d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi                } else {
176d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi                    NearKeysDistanceMap *tmp = prevPrevNearKeysDistances;
177d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi                    prevPrevNearKeysDistances = prevNearKeysDistances;
178d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi                    prevNearKeysDistances = currentNearKeysDistances;
179d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi                    currentNearKeysDistances = tmp;
180ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                    sumAngle = 0.0f;
18137b153e205c9672b299b47e97921fee2462a78bbSatoshi Kataoka                }
182687a244703a02323ebd64433cbaead5def499861Satoshi Kataoka            }
183687a244703a02323ebd64433cbaead5def499861Satoshi Kataoka        }
184feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka        mSampledInputSize = mSampledInputXs.size();
1853e8c58f68d53e6cc9dbf59201c7bdfb8ad04a1cdSatoshi Kataoka    }
186687a244703a02323ebd64433cbaead5def499861Satoshi Kataoka
187feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    if (mSampledInputSize > 0 && isGeometric) {
1889af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka        refreshSpeedRates(inputSize, xCoordinates, yCoordinates, times, lastSavedInputSize);
1899af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka        refreshBeelineSpeedRates(inputSize, xCoordinates, yCoordinates, times);
19028661069591fd1d6a8e25981aaade2e5d8b20b9aKeisuke Kuroyanagi    }
19128661069591fd1d6a8e25981aaade2e5d8b20b9aKeisuke Kuroyanagi
192ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    if (DEBUG_GEO_FULL) {
193feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka        for (int i = 0; i < mSampledInputSize; ++i) {
194feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka            AKLOGI("Sampled(%d): x = %d, y = %d, time = %d", i, mSampledInputXs[i],
195feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka                    mSampledInputYs[i], mTimes[i]);
196ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        }
197ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    }
198ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi
199feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    if (mSampledInputSize > 0) {
200687a244703a02323ebd64433cbaead5def499861Satoshi Kataoka        const int keyCount = mProximityInfo->getKeyCount();
201feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka        mNearKeysVector.resize(mSampledInputSize);
202feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka        mSearchKeysVector.resize(mSampledInputSize);
203feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka        mDistanceCache.resize(mSampledInputSize * keyCount);
204feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka        for (int i = lastSavedInputSize; i < mSampledInputSize; ++i) {
20595a49a527ac9c2c97cfcc758bd6f0d58fb4ad9c0Keisuke Kuroyanagi            mNearKeysVector[i].reset();
206ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi            mSearchKeysVector[i].reset();
20795a49a527ac9c2c97cfcc758bd6f0d58fb4ad9c0Keisuke Kuroyanagi            static const float NEAR_KEY_NORMALIZED_SQUARED_THRESHOLD = 4.0f;
208687a244703a02323ebd64433cbaead5def499861Satoshi Kataoka            for (int k = 0; k < keyCount; ++k) {
209687a244703a02323ebd64433cbaead5def499861Satoshi Kataoka                const int index = i * keyCount + k;
210feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka                const int x = mSampledInputXs[i];
211feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka                const int y = mSampledInputYs[i];
21295a49a527ac9c2c97cfcc758bd6f0d58fb4ad9c0Keisuke Kuroyanagi                const float normalizedSquaredDistance =
2130edab9d2fcc30667c79aa9221dbb27f042d8b455Satoshi Kataoka                        mProximityInfo->getNormalizedSquaredDistanceFromCenterFloatG(k, x, y);
21495a49a527ac9c2c97cfcc758bd6f0d58fb4ad9c0Keisuke Kuroyanagi                mDistanceCache[index] = normalizedSquaredDistance;
21595a49a527ac9c2c97cfcc758bd6f0d58fb4ad9c0Keisuke Kuroyanagi                if (normalizedSquaredDistance < NEAR_KEY_NORMALIZED_SQUARED_THRESHOLD) {
216ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                    mNearKeysVector[i][k] = true;
21795a49a527ac9c2c97cfcc758bd6f0d58fb4ad9c0Keisuke Kuroyanagi                }
21895a49a527ac9c2c97cfcc758bd6f0d58fb4ad9c0Keisuke Kuroyanagi            }
21995a49a527ac9c2c97cfcc758bd6f0d58fb4ad9c0Keisuke Kuroyanagi        }
220ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        if (isGeometric) {
221ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi            // updates probabilities of skipping or mapping each key for all points.
222ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi            updateAlignPointProbabilities(lastSavedInputSize);
223ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi
224ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi            static const float READ_FORWORD_LENGTH_SCALE = 0.95f;
225ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi            const int readForwordLength = static_cast<int>(
226ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                    hypotf(mProximityInfo->getKeyboardWidth(), mProximityInfo->getKeyboardHeight())
227ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                            * READ_FORWORD_LENGTH_SCALE);
228feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka            for (int i = 0; i < mSampledInputSize; ++i) {
229ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                if (i >= lastSavedInputSize) {
230ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                    mSearchKeysVector[i].reset();
231ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                }
232feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka                for (int j = max(i, lastSavedInputSize); j < mSampledInputSize; ++j) {
233ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                    if (mLengthCache[j] - mLengthCache[i] >= readForwordLength) {
234ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                        break;
235ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                    }
236ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                    mSearchKeysVector[i] |= mNearKeysVector[j];
23795a49a527ac9c2c97cfcc758bd6f0d58fb4ad9c0Keisuke Kuroyanagi                }
238687a244703a02323ebd64433cbaead5def499861Satoshi Kataoka            }
239687a244703a02323ebd64433cbaead5def499861Satoshi Kataoka        }
240687a244703a02323ebd64433cbaead5def499861Satoshi Kataoka    }
24108f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka
242806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi    if (DEBUG_SAMPLING_POINTS) {
243806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi        std::stringstream originalX, originalY, sampledX, sampledY;
244806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi        for (int i = 0; i < inputSize; ++i) {
245806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi            originalX << xCoordinates[i];
246806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi            originalY << yCoordinates[i];
247806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi            if (i != inputSize - 1) {
248806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi                originalX << ";";
249806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi                originalY << ";";
250806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi            }
251806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi        }
2529af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka        AKLOGI("===== sampled points =====");
253feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka        for (int i = 0; i < mSampledInputSize; ++i) {
2549af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka            if (isGeometric) {
2556ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka                AKLOGI("%d: x = %d, y = %d, time = %d, relative speed = %.4f, beeline speed = %d",
2569af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka                        i, mSampledInputXs[i], mSampledInputYs[i], mTimes[i], mSpeedRates[i],
2576ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka                        getBeelineSpeedPercentile(i));
2589af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka            }
259feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka            sampledX << mSampledInputXs[i];
260feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka            sampledY << mSampledInputYs[i];
261feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka            if (i != mSampledInputSize - 1) {
262806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi                sampledX << ";";
263806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi                sampledY << ";";
264806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi            }
265806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi        }
266f25cc4405e329447425e52c3c5514a8802097fb8Satoshi Kataoka        AKLOGI("original points:\n%s, %s,\nsampled points:\n%s, %s,\n",
267f25cc4405e329447425e52c3c5514a8802097fb8Satoshi Kataoka                originalX.str().c_str(), originalY.str().c_str(), sampledX.str().c_str(),
268f25cc4405e329447425e52c3c5514a8802097fb8Satoshi Kataoka                sampledY.str().c_str());
269806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi    }
270687a244703a02323ebd64433cbaead5def499861Satoshi Kataoka    // end
271687a244703a02323ebd64433cbaead5def499861Satoshi Kataoka    ///////////////////////
272687a244703a02323ebd64433cbaead5def499861Satoshi Kataoka
27308f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka    memset(mNormalizedSquaredDistances, NOT_A_DISTANCE, sizeof(mNormalizedSquaredDistances));
27408f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka    memset(mPrimaryInputWord, 0, sizeof(mPrimaryInputWord));
275feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    mTouchPositionCorrectionEnabled = mSampledInputSize > 0 && mHasTouchPositionCorrectionData
2760edab9d2fcc30667c79aa9221dbb27f042d8b455Satoshi Kataoka            && xCoordinates && yCoordinates;
27708f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka    if (!isGeometric && pointerId == 0) {
27808f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka        for (int i = 0; i < inputSize; ++i) {
2791e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa            mPrimaryInputWord[i] = getPrimaryCodePointAt(i);
2803e8c58f68d53e6cc9dbf59201c7bdfb8ad04a1cdSatoshi Kataoka        }
28108f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka
282feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka        for (int i = 0; i < mSampledInputSize && mTouchPositionCorrectionEnabled; ++i) {
2831e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa            const int *proximityCodePoints = getProximityCodePointsAt(i);
2841e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa            const int primaryKey = proximityCodePoints[0];
28508f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka            const int x = xCoordinates[i];
28608f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka            const int y = yCoordinates[i];
2873e8c58f68d53e6cc9dbf59201c7bdfb8ad04a1cdSatoshi Kataoka            if (DEBUG_PROXIMITY_CHARS) {
28808f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka                int a = x + y + primaryKey;
28908f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka                a += 0;
29008f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka                AKLOGI("--- Primary = %c, x = %d, y = %d", primaryKey, x, y);
29108f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka            }
2921e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa            for (int j = 0; j < MAX_PROXIMITY_CHARS_SIZE_INTERNAL && proximityCodePoints[j] > 0;
2931e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa                    ++j) {
2941e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa                const int currentCodePoint = proximityCodePoints[j];
29508f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka                const float squaredDistance =
29608f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka                        hasInputCoordinates() ? calculateNormalizedSquaredDistance(
2971e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa                                mProximityInfo->getKeyIndexOf(currentCodePoint), i) :
29808f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka                                NOT_A_DISTANCE_FLOAT;
29908f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka                if (squaredDistance >= 0.0f) {
30008f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka                    mNormalizedSquaredDistances[i * MAX_PROXIMITY_CHARS_SIZE_INTERNAL + j] =
30108f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka                            (int) (squaredDistance * NORMALIZED_SQUARED_DISTANCE_SCALING_FACTOR);
30208f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka                } else {
30308f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka                    mNormalizedSquaredDistances[i * MAX_PROXIMITY_CHARS_SIZE_INTERNAL + j] =
30408f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka                            (j == 0) ? EQUIVALENT_CHAR_WITHOUT_DISTANCE_INFO :
30508f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka                                    PROXIMITY_CHAR_WITHOUT_DISTANCE_INFO;
30608f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka                }
30708f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka                if (DEBUG_PROXIMITY_CHARS) {
3081e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa                    AKLOGI("--- Proximity (%d) = %c", j, currentCodePoint);
30908f00cf55f2e083c1ed254a32495b622c9ad9862Satoshi Kataoka                }
3103e8c58f68d53e6cc9dbf59201c7bdfb8ad04a1cdSatoshi Kataoka            }
3113e8c58f68d53e6cc9dbf59201c7bdfb8ad04a1cdSatoshi Kataoka        }
3123e8c58f68d53e6cc9dbf59201c7bdfb8ad04a1cdSatoshi Kataoka    }
313952ec4977d772607140773ae7d8868f86a7e0097Satoshi Kataoka
314952ec4977d772607140773ae7d8868f86a7e0097Satoshi Kataoka    if (DEBUG_GEO_FULL) {
315feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka        AKLOGI("ProximityState init finished: %d points out of %d", mSampledInputSize, inputSize);
316feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    }
317feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka}
318feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka
3199af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataokavoid ProximityInfoState::refreshSpeedRates(const int inputSize, const int *const xCoordinates,
320feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka        const int *const yCoordinates, const int *const times, const int lastSavedInputSize) {
321feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    // Relative speed calculation.
322feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    const int sumDuration = mTimes.back() - mTimes.front();
323feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    const int sumLength = mLengthCache.back() - mLengthCache.front();
3249af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka    mAverageSpeed = static_cast<float>(sumLength) / static_cast<float>(sumDuration);
3259af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka    mSpeedRates.resize(mSampledInputSize);
326feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    for (int i = lastSavedInputSize; i < mSampledInputSize; ++i) {
327feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka        const int index = mInputIndice[i];
328feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka        int length = 0;
329feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka        int duration = 0;
330feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka
331feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka        // Calculate velocity by using distances and durations of
332feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka        // NUM_POINTS_FOR_SPEED_CALCULATION points for both forward and backward.
333feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka        static const int NUM_POINTS_FOR_SPEED_CALCULATION = 2;
334feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka        for (int j = index; j < min(inputSize - 1, index + NUM_POINTS_FOR_SPEED_CALCULATION);
335feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka                ++j) {
336feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka            if (i < mSampledInputSize - 1 && j >= mInputIndice[i + 1]) {
337feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka                break;
338feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka            }
339feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka            length += getDistanceInt(xCoordinates[j], yCoordinates[j],
340feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka                    xCoordinates[j + 1], yCoordinates[j + 1]);
341feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka            duration += times[j + 1] - times[j];
342feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka        }
343feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka        for (int j = index - 1; j >= max(0, index - NUM_POINTS_FOR_SPEED_CALCULATION); --j) {
344feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka            if (i > 0 && j < mInputIndice[i - 1]) {
345feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka                break;
346feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka            }
3479af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka            // TODO: use mLengthCache instead?
348feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka            length += getDistanceInt(xCoordinates[j], yCoordinates[j],
349feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka                    xCoordinates[j + 1], yCoordinates[j + 1]);
350feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka            duration += times[j + 1] - times[j];
351feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka        }
352feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka        if (duration == 0 || sumDuration == 0) {
353feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka            // Cannot calculate speed; thus, it gives an average value (1.0);
3549af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka            mSpeedRates[i] = 1.0f;
355feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka        } else {
356feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka            const float speed = static_cast<float>(length) / static_cast<float>(duration);
3579af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka            mSpeedRates[i] = speed / mAverageSpeed;
358feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka        }
359feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    }
360feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka
361feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    // Direction calculation.
362feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    mDirections.resize(mSampledInputSize - 1);
363feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    for (int i = max(0, lastSavedInputSize - 1); i < mSampledInputSize - 1; ++i) {
364feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka        mDirections[i] = getDirection(i, i + 1);
365952ec4977d772607140773ae7d8868f86a7e0097Satoshi Kataoka    }
3663e8c58f68d53e6cc9dbf59201c7bdfb8ad04a1cdSatoshi Kataoka}
3674a3db7057f77dc85311fb1f94934b5a004ab613eSatoshi Kataoka
3686ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataokastatic const int MAX_PERCENTILE = 100;
3699af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataokavoid ProximityInfoState::refreshBeelineSpeedRates(const int inputSize,
3709af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka        const int *const xCoordinates, const int *const yCoordinates, const int * times) {
3716ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka    if (DEBUG_SAMPLING_POINTS){
3726ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka        AKLOGI("--- refresh beeline speed rates");
3736ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka    }
3746ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka    mBeelineSpeedPercentiles.resize(mSampledInputSize);
3759af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka    for (int i = 0; i < mSampledInputSize; ++i) {
3766ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka        mBeelineSpeedPercentiles[i] = static_cast<int>(calculateBeelineSpeedRate(
3776ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka                i, inputSize, xCoordinates, yCoordinates, times) * MAX_PERCENTILE);
3789af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka    }
3799af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka}
3809af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka
3819af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataokafloat ProximityInfoState::calculateBeelineSpeedRate(
3829af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka        const int id, const int inputSize, const int *const xCoordinates,
3839af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka        const int *const yCoordinates, const int * times) const {
3846ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka    if (mSampledInputSize <= 0 || mAverageSpeed < 0.001f) {
3856ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka        if (DEBUG_SAMPLING_POINTS){
3866ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka            AKLOGI("--- invalid state: cancel. size = %d, ave = %f",
3876ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka                    mSampledInputSize, mAverageSpeed);
3886ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka        }
3899af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka        return 1.0f;
3909af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka    }
3919af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka    const int lookupRadius =
3929af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka            mProximityInfo->getMostCommonKeyWidth() * LOOKUP_RADIUS_PERCENTILE / MAX_PERCENTILE;
3939af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka    const int x0 = mSampledInputXs[id];
3949af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka    const int y0 = mSampledInputYs[id];
3956ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka    const int actualInputIndex = mInputIndice[id];
3969af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka    int tempTime = 0;
3979af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka    int tempBeelineDistance = 0;
3986ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka    int start = actualInputIndex;
3999af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka    // lookup forward
4006ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka    while (start > 0 && tempBeelineDistance < lookupRadius) {
4019af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka        tempTime += times[start] - times[start - 1];
4029af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka        --start;
4039af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka        tempBeelineDistance = getDistanceInt(x0, y0, xCoordinates[start], yCoordinates[start]);
4049af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka    }
4056ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka    // Exclusive unless this is an edge point
4066ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka    if (start > 0 && start < actualInputIndex) {
4076ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka        ++start;
4086ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka    }
4099af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka    tempTime= 0;
4109af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka    tempBeelineDistance = 0;
4116ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka    int end = actualInputIndex;
4129af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka    // lookup backward
4136ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka    while (end < (inputSize - 1) && tempBeelineDistance < lookupRadius) {
4149af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka        tempTime += times[end + 1] - times[end];
4159af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka        ++end;
4166ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka        tempBeelineDistance = getDistanceInt(x0, y0, xCoordinates[end], yCoordinates[end]);
4176ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka    }
4186ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka    // Exclusive unless this is an edge point
4196ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka    if (end > actualInputIndex && end < (inputSize - 1)) {
4206ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka        --end;
4219af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka    }
4229af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka
4236ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka    if (start >= end) {
4246ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka        if (DEBUG_DOUBLE_LETTER) {
4256ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka            AKLOGI("--- double letter: start == end %d", start);
4266ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka        }
4279af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka        return 1.0f;
4289af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka    }
4299af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka
4309af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka    const int x2 = xCoordinates[start];
4319af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka    const int y2 = yCoordinates[start];
4329af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka    const int x3 = xCoordinates[end];
4339af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka    const int y3 = yCoordinates[end];
4349af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka    const int beelineDistance = getDistanceInt(x2, y2, x3, y3);
4356ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka    int adjustedStartTime = times[start];
4366ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka    if (start == 0 && actualInputIndex == 0 && inputSize > 1) {
4376ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka        adjustedStartTime += FIRST_POINT_TIME_OFFSET_MILLIS;
4386ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka    }
4396ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka    int adjustedEndTime = times[end];
4406ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka    if (end == (inputSize - 1) && inputSize > 1) {
4416ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka        adjustedEndTime -= FIRST_POINT_TIME_OFFSET_MILLIS;
4426ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka    }
4436ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka    const int time = adjustedEndTime - adjustedStartTime;
4449af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka    if (time <= 0) {
4459af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka        return 1.0f;
4469af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka    }
4476ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka
4486ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka    if (time >= STRONG_DOUBLE_LETTER_TIME_MILLIS){
4496ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka        return 0.0f;
4506ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka    }
4516ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka    if (DEBUG_DOUBLE_LETTER) {
4526ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka        AKLOGI("--- (%d, %d) double letter: start = %d, end = %d, dist = %d, time = %d, speed = %f,"
4536ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka                " ave = %f, val = %f, start time = %d, end time = %d",
4546ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka                id, mInputIndice[id], start, end, beelineDistance, time,
4556ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka                (static_cast<float>(beelineDistance) / static_cast<float>(time)), mAverageSpeed,
4566ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka                ((static_cast<float>(beelineDistance) / static_cast<float>(time)) / mAverageSpeed),
4576ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka                adjustedStartTime, adjustedEndTime);
4586ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka    }
4596ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka    // Offset 1%
4606ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka    // TODO: Detect double letter more smartly
4616ae8dd4343445d2df4444388b605d2aa930fa2a0Satoshi Kataoka    return 0.01f + static_cast<float>(beelineDistance) / static_cast<float>(time) / mAverageSpeed;
4629af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka}
4639af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka
464096f35ff4b5413906e2a339663baf16e5dabaf64Keisuke Kuroyanagibool ProximityInfoState::checkAndReturnIsContinuationPossible(const int inputSize,
465fe4f1ce26ca41ef51b5245d70e93b502f76262a5Satoshi Kataoka        const int *const xCoordinates, const int *const yCoordinates, const int *const times,
466fe4f1ce26ca41ef51b5245d70e93b502f76262a5Satoshi Kataoka        const bool isGeometric) const {
467fe4f1ce26ca41ef51b5245d70e93b502f76262a5Satoshi Kataoka    if (isGeometric) {
468fe4f1ce26ca41ef51b5245d70e93b502f76262a5Satoshi Kataoka        for (int i = 0; i < mSampledInputSize; ++i) {
469fe4f1ce26ca41ef51b5245d70e93b502f76262a5Satoshi Kataoka            const int index = mInputIndice[i];
470fe4f1ce26ca41ef51b5245d70e93b502f76262a5Satoshi Kataoka            if (index > inputSize || xCoordinates[index] != mSampledInputXs[i] ||
471fe4f1ce26ca41ef51b5245d70e93b502f76262a5Satoshi Kataoka                    yCoordinates[index] != mSampledInputYs[i] || times[index] != mTimes[i]) {
472fe4f1ce26ca41ef51b5245d70e93b502f76262a5Satoshi Kataoka                return false;
473fe4f1ce26ca41ef51b5245d70e93b502f76262a5Satoshi Kataoka            }
474fe4f1ce26ca41ef51b5245d70e93b502f76262a5Satoshi Kataoka        }
475fe4f1ce26ca41ef51b5245d70e93b502f76262a5Satoshi Kataoka    } else {
476fe4f1ce26ca41ef51b5245d70e93b502f76262a5Satoshi Kataoka        if (inputSize < mSampledInputSize) {
477fe4f1ce26ca41ef51b5245d70e93b502f76262a5Satoshi Kataoka            // Assuming the cache is invalid if the previous input size is larger than the new one.
478096f35ff4b5413906e2a339663baf16e5dabaf64Keisuke Kuroyanagi            return false;
479096f35ff4b5413906e2a339663baf16e5dabaf64Keisuke Kuroyanagi        }
480fe4f1ce26ca41ef51b5245d70e93b502f76262a5Satoshi Kataoka        for (int i = 0; i < mSampledInputSize && i < MAX_WORD_LENGTH_INTERNAL; ++i) {
481fe4f1ce26ca41ef51b5245d70e93b502f76262a5Satoshi Kataoka            if (xCoordinates[i] != mSampledInputXs[i]
482fe4f1ce26ca41ef51b5245d70e93b502f76262a5Satoshi Kataoka                    || yCoordinates[i] != mSampledInputYs[i]) {
483fe4f1ce26ca41ef51b5245d70e93b502f76262a5Satoshi Kataoka                return false;
484fe4f1ce26ca41ef51b5245d70e93b502f76262a5Satoshi Kataoka            }
485fe4f1ce26ca41ef51b5245d70e93b502f76262a5Satoshi Kataoka        }
486096f35ff4b5413906e2a339663baf16e5dabaf64Keisuke Kuroyanagi    }
487096f35ff4b5413906e2a339663baf16e5dabaf64Keisuke Kuroyanagi    return true;
488096f35ff4b5413906e2a339663baf16e5dabaf64Keisuke Kuroyanagi}
489096f35ff4b5413906e2a339663baf16e5dabaf64Keisuke Kuroyanagi
490d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi// Calculating point to key distance for all near keys and returning the distance between
491d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi// the given point and the nearest key position.
492d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagifloat ProximityInfoState::updateNearKeysDistances(const int x, const int y,
493d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi        NearKeysDistanceMap *const currentNearKeysDistances) {
494ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    static const float NEAR_KEY_THRESHOLD = 2.0f;
495d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi
496d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi    currentNearKeysDistances->clear();
497d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi    const int keyCount = mProximityInfo->getKeyCount();
498d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi    float nearestKeyDistance = mMaxPointToKeyLength;
499d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi    for (int k = 0; k < keyCount; ++k) {
5000edab9d2fcc30667c79aa9221dbb27f042d8b455Satoshi Kataoka        const float dist = mProximityInfo->getNormalizedSquaredDistanceFromCenterFloatG(k, x, y);
501d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi        if (dist < NEAR_KEY_THRESHOLD) {
502d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi            currentNearKeysDistances->insert(std::pair<int, float>(k, dist));
503687a244703a02323ebd64433cbaead5def499861Satoshi Kataoka        }
504d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi        if (nearestKeyDistance > dist) {
505d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi            nearestKeyDistance = dist;
506d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi        }
507d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi    }
508d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi    return nearestKeyDistance;
509d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi}
510d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi
511d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi// Check if previous point is at local minimum position to near keys.
512d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagibool ProximityInfoState::isPrevLocalMin(const NearKeysDistanceMap *const currentNearKeysDistances,
513d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi        const NearKeysDistanceMap *const prevNearKeysDistances,
514d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi        const NearKeysDistanceMap *const prevPrevNearKeysDistances) const {
515ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    static const float MARGIN = 0.01f;
516d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi
517d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi    for (NearKeysDistanceMap::const_iterator it = prevNearKeysDistances->begin();
518d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi        it != prevNearKeysDistances->end(); ++it) {
519d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi        NearKeysDistanceMap::const_iterator itPP = prevPrevNearKeysDistances->find(it->first);
520d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi        NearKeysDistanceMap::const_iterator itC = currentNearKeysDistances->find(it->first);
521d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi        if ((itPP == prevPrevNearKeysDistances->end() || itPP->second > it->second + MARGIN)
522d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi                && (itC == currentNearKeysDistances->end() || itC->second > it->second + MARGIN)) {
523d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi            return true;
524d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi        }
525d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi    }
526d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi    return false;
527d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi}
528d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi
529d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi// Calculating a point score that indicates usefulness of the point.
530d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagifloat ProximityInfoState::getPointScore(
531d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi        const int x, const int y, const int time, const bool lastPoint, const float nearest,
532ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        const float sumAngle, const NearKeysDistanceMap *const currentNearKeysDistances,
533d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi        const NearKeysDistanceMap *const prevNearKeysDistances,
534d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi        const NearKeysDistanceMap *const prevPrevNearKeysDistances) const {
53503dc8fe141fa9f6fa5fe8362d9d35b3f3c954b87Ken Wakasa    static const int DISTANCE_BASE_SCALE = 100;
536ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    static const float NEAR_KEY_THRESHOLD = 0.6f;
537ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    static const int CORNER_CHECK_DISTANCE_THRESHOLD_SCALE = 25;
538806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi    static const float NOT_LOCALMIN_DISTANCE_SCORE = -1.0f;
539ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    static const float LOCALMIN_DISTANCE_AND_NEAR_TO_KEY_SCORE = 1.0f;
540ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    static const float CORNER_ANGLE_THRESHOLD = M_PI_F * 2.0f / 3.0f;
541ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    static const float CORNER_SUM_ANGLE_THRESHOLD = M_PI_F / 4.0f;
542aba26e4f6355bf2cf59ffc5a31fc4b09041f5bc0Keisuke Kuroyanagi    static const float CORNER_SCORE = 1.0f;
543d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi
544feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    const size_t size = mSampledInputXs.size();
545ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    // If there is only one point, add this point. Besides, if the previous point's distance map
546ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    // is empty, we re-compute nearby keys distances from the current point.
547ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    // Note that the current point is the first point in the incremental input that needs to
548ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    // be re-computed.
549ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    if (size <= 1 || prevNearKeysDistances->empty()) {
55003dc8fe141fa9f6fa5fe8362d9d35b3f3c954b87Ken Wakasa        return 0.0f;
551687a244703a02323ebd64433cbaead5def499861Satoshi Kataoka    }
552ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi
55303dc8fe141fa9f6fa5fe8362d9d35b3f3c954b87Ken Wakasa    const int baseSampleRate = mProximityInfo->getMostCommonKeyWidth();
554feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    const int distPrev = getDistanceInt(mSampledInputXs.back(), mSampledInputYs.back(),
555feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka            mSampledInputXs[size - 2], mSampledInputYs[size - 2]) * DISTANCE_BASE_SCALE;
556d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi    float score = 0.0f;
557d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi
558d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi    // Location
559ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    if (!isPrevLocalMin(currentNearKeysDistances, prevNearKeysDistances,
560ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        prevPrevNearKeysDistances)) {
561ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        score += NOT_LOCALMIN_DISTANCE_SCORE;
562ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    } else if (nearest < NEAR_KEY_THRESHOLD) {
563ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        // Promote points nearby keys
564ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        score += LOCALMIN_DISTANCE_AND_NEAR_TO_KEY_SCORE;
565d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi    }
566d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi    // Angle
567feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    const float angle1 = getAngle(x, y, mSampledInputXs.back(), mSampledInputYs.back());
568feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    const float angle2 = getAngle(mSampledInputXs.back(), mSampledInputYs.back(),
569feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka            mSampledInputXs[size - 2], mSampledInputYs[size - 2]);
570aba26e4f6355bf2cf59ffc5a31fc4b09041f5bc0Keisuke Kuroyanagi    const float angleDiff = getAngleDiff(angle1, angle2);
571ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi
572aba26e4f6355bf2cf59ffc5a31fc4b09041f5bc0Keisuke Kuroyanagi    // Save corner
573aba26e4f6355bf2cf59ffc5a31fc4b09041f5bc0Keisuke Kuroyanagi    if (distPrev > baseSampleRate * CORNER_CHECK_DISTANCE_THRESHOLD_SCALE
574ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi            && (sumAngle > CORNER_SUM_ANGLE_THRESHOLD || angleDiff > CORNER_ANGLE_THRESHOLD)) {
575aba26e4f6355bf2cf59ffc5a31fc4b09041f5bc0Keisuke Kuroyanagi        score += CORNER_SCORE;
576d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi    }
577d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi    return score;
578d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi}
579d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi
580d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi// Sampling touch point and pushing information to vectors.
581d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi// Returning if previous point is popped or not.
5821e61493c50082264caaef862df02b1ccc84dc396Ken Wakasabool ProximityInfoState::pushTouchPoint(const int inputIndex, const int nodeCodePoint, int x, int y,
583ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        const int time, const bool sample, const bool isLastPoint, const float sumAngle,
584d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi        NearKeysDistanceMap *const currentNearKeysDistances,
585d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi        const NearKeysDistanceMap *const prevNearKeysDistances,
586d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi        const NearKeysDistanceMap *const prevPrevNearKeysDistances) {
587806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi    static const int LAST_POINT_SKIP_DISTANCE_SCALE = 4;
588d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi
589feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    size_t size = mSampledInputXs.size();
590d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi    bool popped = false;
5911e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa    if (nodeCodePoint < 0 && sample) {
592d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi        const float nearest = updateNearKeysDistances(x, y, currentNearKeysDistances);
593ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        const float score = getPointScore(x, y, time, isLastPoint, nearest, sumAngle,
594d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi                currentNearKeysDistances, prevNearKeysDistances, prevPrevNearKeysDistances);
595d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi        if (score < 0) {
596d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi            // Pop previous point because it would be useless.
597096f35ff4b5413906e2a339663baf16e5dabaf64Keisuke Kuroyanagi            popInputData();
598feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka            size = mSampledInputXs.size();
599d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi            popped = true;
600d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi        } else {
601d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi            popped = false;
602d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi        }
603d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi        // Check if the last point should be skipped.
604806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi        if (isLastPoint && size > 0) {
605feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka            if (getDistanceInt(x, y, mSampledInputXs.back(), mSampledInputYs.back())
606ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                    * LAST_POINT_SKIP_DISTANCE_SCALE < mProximityInfo->getMostCommonKeyWidth()) {
607806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi                // This point is not used because it's too close to the previous point.
6089182daf98c0d1db897e82b77c2196b7f9aad6f01Satoshi Kataoka                if (DEBUG_GEO_FULL) {
609806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi                    AKLOGI("p0: size = %zd, x = %d, y = %d, lx = %d, ly = %d, dist = %d, "
610feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka                           "width = %d", size, x, y, mSampledInputXs.back(), mSampledInputYs.back(),
611feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka                           getDistanceInt(x, y, mSampledInputXs.back(), mSampledInputYs.back()),
6129182daf98c0d1db897e82b77c2196b7f9aad6f01Satoshi Kataoka                           mProximityInfo->getMostCommonKeyWidth()
613806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi                                   / LAST_POINT_SKIP_DISTANCE_SCALE);
6149182daf98c0d1db897e82b77c2196b7f9aad6f01Satoshi Kataoka                }
615d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi                return popped;
616d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi            }
617d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi        }
618d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi    }
619d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi
6201e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa    if (nodeCodePoint >= 0 && (x < 0 || y < 0)) {
6211e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa        const int keyId = mProximityInfo->getKeyIndexOf(nodeCodePoint);
622687a244703a02323ebd64433cbaead5def499861Satoshi Kataoka        if (keyId >= 0) {
623f2789819bd005b5b0581e8439601b5501306327dKen Wakasa            x = mProximityInfo->getKeyCenterXOfKeyIdG(keyId);
624f2789819bd005b5b0581e8439601b5501306327dKen Wakasa            y = mProximityInfo->getKeyCenterYOfKeyIdG(keyId);
625687a244703a02323ebd64433cbaead5def499861Satoshi Kataoka        }
626687a244703a02323ebd64433cbaead5def499861Satoshi Kataoka    }
627d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi
628d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi    // Pushing point information.
629d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi    if (size > 0) {
630d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi        mLengthCache.push_back(
631feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka                mLengthCache.back() + getDistanceInt(
632feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka                        x, y, mSampledInputXs.back(), mSampledInputYs.back()));
633d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi    } else {
634d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi        mLengthCache.push_back(0);
635d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi    }
636feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    mSampledInputXs.push_back(x);
637feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    mSampledInputYs.push_back(y);
638687a244703a02323ebd64433cbaead5def499861Satoshi Kataoka    mTimes.push_back(time);
639096f35ff4b5413906e2a339663baf16e5dabaf64Keisuke Kuroyanagi    mInputIndice.push_back(inputIndex);
640952ec4977d772607140773ae7d8868f86a7e0097Satoshi Kataoka    if (DEBUG_GEO_FULL) {
641952ec4977d772607140773ae7d8868f86a7e0097Satoshi Kataoka        AKLOGI("pushTouchPoint: x = %03d, y = %03d, time = %d, index = %d, popped ? %01d",
642952ec4977d772607140773ae7d8868f86a7e0097Satoshi Kataoka                x, y, time, inputIndex, popped);
643952ec4977d772607140773ae7d8868f86a7e0097Satoshi Kataoka    }
644d9c10b19793b011f862e3dd31883f746044431d7Keisuke Kuroyanagi    return popped;
645687a244703a02323ebd64433cbaead5def499861Satoshi Kataoka}
646687a244703a02323ebd64433cbaead5def499861Satoshi Kataoka
6474a3db7057f77dc85311fb1f94934b5a004ab613eSatoshi Kataokafloat ProximityInfoState::calculateNormalizedSquaredDistance(
6484a3db7057f77dc85311fb1f94934b5a004ab613eSatoshi Kataoka        const int keyIndex, const int inputIndex) const {
6494a3db7057f77dc85311fb1f94934b5a004ab613eSatoshi Kataoka    if (keyIndex == NOT_AN_INDEX) {
6504a3db7057f77dc85311fb1f94934b5a004ab613eSatoshi Kataoka        return NOT_A_DISTANCE_FLOAT;
6514a3db7057f77dc85311fb1f94934b5a004ab613eSatoshi Kataoka    }
6524a3db7057f77dc85311fb1f94934b5a004ab613eSatoshi Kataoka    if (!mProximityInfo->hasSweetSpotData(keyIndex)) {
6534a3db7057f77dc85311fb1f94934b5a004ab613eSatoshi Kataoka        return NOT_A_DISTANCE_FLOAT;
6544a3db7057f77dc85311fb1f94934b5a004ab613eSatoshi Kataoka    }
655feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    if (NOT_A_COORDINATE == mSampledInputXs[inputIndex]) {
6564a3db7057f77dc85311fb1f94934b5a004ab613eSatoshi Kataoka        return NOT_A_DISTANCE_FLOAT;
6574a3db7057f77dc85311fb1f94934b5a004ab613eSatoshi Kataoka    }
6584a3db7057f77dc85311fb1f94934b5a004ab613eSatoshi Kataoka    const float squaredDistance = calculateSquaredDistanceFromSweetSpotCenter(
6594a3db7057f77dc85311fb1f94934b5a004ab613eSatoshi Kataoka            keyIndex, inputIndex);
6604a3db7057f77dc85311fb1f94934b5a004ab613eSatoshi Kataoka    const float squaredRadius = square(mProximityInfo->getSweetSpotRadiiAt(keyIndex));
6614a3db7057f77dc85311fb1f94934b5a004ab613eSatoshi Kataoka    return squaredDistance / squaredRadius;
6624a3db7057f77dc85311fb1f94934b5a004ab613eSatoshi Kataoka}
6634a3db7057f77dc85311fb1f94934b5a004ab613eSatoshi Kataoka
664687a244703a02323ebd64433cbaead5def499861Satoshi Kataokaint ProximityInfoState::getDuration(const int index) const {
665feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    if (index >= 0 && index < mSampledInputSize - 1) {
666a811938d40070b96557df0f2a36ba8daa561fdd4Tom Ouyang        return mTimes[index + 1] - mTimes[index];
667687a244703a02323ebd64433cbaead5def499861Satoshi Kataoka    }
66837b153e205c9672b299b47e97921fee2462a78bbSatoshi Kataoka    return 0;
669687a244703a02323ebd64433cbaead5def499861Satoshi Kataoka}
670687a244703a02323ebd64433cbaead5def499861Satoshi Kataoka
6710519fdd18ac4414b8298d3338db1c8ccce032170Satoshi Kataoka// TODO: Remove the "scale" parameter
6720519fdd18ac4414b8298d3338db1c8ccce032170Satoshi Kataoka// This function basically converts from a length to an edit distance. Accordingly, it's obviously
6730519fdd18ac4414b8298d3338db1c8ccce032170Satoshi Kataoka// wrong to compare with mMaxPointToKeyLength.
6740519fdd18ac4414b8298d3338db1c8ccce032170Satoshi Kataokafloat ProximityInfoState::getPointToKeyLength(
6750519fdd18ac4414b8298d3338db1c8ccce032170Satoshi Kataoka        const int inputIndex, const int codePoint, const float scale) const {
676ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    const int keyId = mProximityInfo->getKeyIndexOf(codePoint);
677ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    if (keyId != NOT_AN_INDEX) {
678ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        const int index = inputIndex * mProximityInfo->getKeyCount() + keyId;
6790519fdd18ac4414b8298d3338db1c8ccce032170Satoshi Kataoka        return min(mDistanceCache[index] * scale, mMaxPointToKeyLength);
680ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    }
6811e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa    if (isSkippableCodePoint(codePoint)) {
682806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi        return 0.0f;
683806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi    }
684ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    // If the char is not a key on the keyboard then return the max length.
685ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    return MAX_POINT_TO_KEY_LENGTH;
686806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi}
687806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi
6880519fdd18ac4414b8298d3338db1c8ccce032170Satoshi Kataokafloat ProximityInfoState::getPointToKeyLength(const int inputIndex, const int codePoint) const {
6890519fdd18ac4414b8298d3338db1c8ccce032170Satoshi Kataoka    return getPointToKeyLength(inputIndex, codePoint, 1.0f);
6900519fdd18ac4414b8298d3338db1c8ccce032170Satoshi Kataoka}
6910519fdd18ac4414b8298d3338db1c8ccce032170Satoshi Kataoka
6920519fdd18ac4414b8298d3338db1c8ccce032170Satoshi Kataoka// TODO: Remove the "scale" parameter
6930519fdd18ac4414b8298d3338db1c8ccce032170Satoshi Kataoka// This function basically converts from a length to an edit distance. Accordingly, it's obviously
6940519fdd18ac4414b8298d3338db1c8ccce032170Satoshi Kataoka// wrong to compare with mMaxPointToKeyLength.
6950519fdd18ac4414b8298d3338db1c8ccce032170Satoshi Kataokafloat ProximityInfoState::getPointToKeyByIdLength(
6960519fdd18ac4414b8298d3338db1c8ccce032170Satoshi Kataoka        const int inputIndex, const int keyId, const float scale) const {
6978c220a0aa2c5139a3b12af20e68c420f6402294aTom Ouyang    if (keyId != NOT_AN_INDEX) {
698687a244703a02323ebd64433cbaead5def499861Satoshi Kataoka        const int index = inputIndex * mProximityInfo->getKeyCount() + keyId;
6990519fdd18ac4414b8298d3338db1c8ccce032170Satoshi Kataoka        return min(mDistanceCache[index] * scale, mMaxPointToKeyLength);
700cde005c05ec6b552ec26740b578be12c7d24013bSatoshi Kataoka    }
7018c220a0aa2c5139a3b12af20e68c420f6402294aTom Ouyang    // If the char is not a key on the keyboard then return the max length.
702806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi    return static_cast<float>(MAX_POINT_TO_KEY_LENGTH);
703687a244703a02323ebd64433cbaead5def499861Satoshi Kataoka}
704687a244703a02323ebd64433cbaead5def499861Satoshi Kataoka
7050519fdd18ac4414b8298d3338db1c8ccce032170Satoshi Kataokafloat ProximityInfoState::getPointToKeyByIdLength(const int inputIndex, const int keyId) const {
7060519fdd18ac4414b8298d3338db1c8ccce032170Satoshi Kataoka    return getPointToKeyByIdLength(inputIndex, keyId, 1.0f);
7070519fdd18ac4414b8298d3338db1c8ccce032170Satoshi Kataoka}
7080519fdd18ac4414b8298d3338db1c8ccce032170Satoshi Kataoka
7092c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa// In the following function, c is the current character of the dictionary word currently examined.
7102c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa// currentChars is an array containing the keys close to the character the user actually typed at
7112c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa// the same position. We want to see if c is in it: if so, then the word contains at that position
7122c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa// a character close to what the user typed.
7132c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa// What the user typed is actually the first character of the array.
7142c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa// proximityIndex is a pointer to the variable where getMatchedProximityId returns the index of c
7152c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa// in the proximity chars of the input index.
7162c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa// Notice : accented characters do not have a proximity list, so they are alone in their list. The
7172c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa// non-accented version of the character should be considered "close", but not the other keys close
7182c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa// to the non-accented version.
7192c2f3a90d8115777adbe9ffd597f344aede84276Ken WakasaProximityType ProximityInfoState::getMatchedProximityId(const int index, const int c,
7202c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa        const bool checkProximityChars, int *proximityIndex) const {
7212c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa    const int *currentCodePoints = getProximityCodePointsAt(index);
7222c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa    const int firstCodePoint = currentCodePoints[0];
7232c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa    const int baseLowerC = toBaseLowerCase(c);
7242c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa
7252c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa    // The first char in the array is what user typed. If it matches right away, that means the
7262c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa    // user typed that same char for this pos.
7272c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa    if (firstCodePoint == baseLowerC || firstCodePoint == c) {
7282c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa        return EQUIVALENT_CHAR;
7292c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa    }
7302c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa
7312c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa    if (!checkProximityChars) return UNRELATED_CHAR;
7322c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa
7332c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa    // If the non-accented, lowercased version of that first character matches c, then we have a
7342c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa    // non-accented version of the accented character the user typed. Treat it as a close char.
7352c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa    if (toBaseLowerCase(firstCodePoint) == baseLowerC) {
7362c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa        return NEAR_PROXIMITY_CHAR;
7372c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa    }
7382c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa
7392c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa    // Not an exact nor an accent-alike match: search the list of close keys
7402c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa    int j = 1;
7412c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa    while (j < MAX_PROXIMITY_CHARS_SIZE_INTERNAL
7422c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa            && currentCodePoints[j] > ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE) {
7432c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa        const bool matched = (currentCodePoints[j] == baseLowerC || currentCodePoints[j] == c);
7442c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa        if (matched) {
7452c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa            if (proximityIndex) {
7462c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa                *proximityIndex = j;
7472c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa            }
7482c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa            return NEAR_PROXIMITY_CHAR;
7492c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa        }
7502c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa        ++j;
7512c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa    }
7522c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa    if (j < MAX_PROXIMITY_CHARS_SIZE_INTERNAL
7532c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa            && currentCodePoints[j] == ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE) {
7542c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa        ++j;
7552c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa        while (j < MAX_PROXIMITY_CHARS_SIZE_INTERNAL
7562c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa                && currentCodePoints[j] > ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE) {
7572c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa            const bool matched = (currentCodePoints[j] == baseLowerC || currentCodePoints[j] == c);
7582c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa            if (matched) {
7592c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa                if (proximityIndex) {
7602c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa                    *proximityIndex = j;
7612c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa                }
7622c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa                return ADDITIONAL_PROXIMITY_CHAR;
7632c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa            }
7642c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa            ++j;
7652c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa        }
7662c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa    }
7672c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa    // Was not included, signal this as an unrelated character.
7682c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa    return UNRELATED_CHAR;
7692c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa}
7702c2f3a90d8115777adbe9ffd597f344aede84276Ken Wakasa
7713811a28ddc07201930e0bbd2e1d01045b59af308Keisuke Kuroyanagiint ProximityInfoState::getSpaceY() const {
772b02ee3d67a1884b6ff59cc16c29a476845c0694fKen Wakasa    const int keyId = mProximityInfo->getKeyIndexOf(KEYCODE_SPACE);
773f2789819bd005b5b0581e8439601b5501306327dKen Wakasa    return mProximityInfo->getKeyCenterYOfKeyIdG(keyId);
774687a244703a02323ebd64433cbaead5def499861Satoshi Kataoka}
775687a244703a02323ebd64433cbaead5def499861Satoshi Kataoka
7764a3db7057f77dc85311fb1f94934b5a004ab613eSatoshi Kataokafloat ProximityInfoState::calculateSquaredDistanceFromSweetSpotCenter(
7774a3db7057f77dc85311fb1f94934b5a004ab613eSatoshi Kataoka        const int keyIndex, const int inputIndex) const {
7784a3db7057f77dc85311fb1f94934b5a004ab613eSatoshi Kataoka    const float sweetSpotCenterX = mProximityInfo->getSweetSpotCenterXAt(keyIndex);
7794a3db7057f77dc85311fb1f94934b5a004ab613eSatoshi Kataoka    const float sweetSpotCenterY = mProximityInfo->getSweetSpotCenterYAt(keyIndex);
780feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    const float inputX = static_cast<float>(mSampledInputXs[inputIndex]);
781feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    const float inputY = static_cast<float>(mSampledInputYs[inputIndex]);
7824a3db7057f77dc85311fb1f94934b5a004ab613eSatoshi Kataoka    return square(inputX - sweetSpotCenterX) + square(inputY - sweetSpotCenterY);
7834a3db7057f77dc85311fb1f94934b5a004ab613eSatoshi Kataoka}
78495a49a527ac9c2c97cfcc758bd6f0d58fb4ad9c0Keisuke Kuroyanagi
78595a49a527ac9c2c97cfcc758bd6f0d58fb4ad9c0Keisuke Kuroyanagi// Puts possible characters into filter and returns new filter size.
786082507e1da56c6cefe575ec3d6a334e9b717e3faKen Wakasaint ProximityInfoState::getAllPossibleChars(
787082507e1da56c6cefe575ec3d6a334e9b717e3faKen Wakasa        const size_t index, int *const filter, const int filterSize) const {
788feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    if (index >= mSampledInputXs.size()) {
78995a49a527ac9c2c97cfcc758bd6f0d58fb4ad9c0Keisuke Kuroyanagi        return filterSize;
79095a49a527ac9c2c97cfcc758bd6f0d58fb4ad9c0Keisuke Kuroyanagi    }
79141f12ee27b269033fe818f7d52e81ba948a046c3Keisuke Kuroyanagi    int newFilterSize = filterSize;
792ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    const int keyCount = mProximityInfo->getKeyCount();
793ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    for (int j = 0; j < keyCount; ++j) {
794ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        if (mSearchKeysVector[index].test(j)) {
795082507e1da56c6cefe575ec3d6a334e9b717e3faKen Wakasa            const int keyCodePoint = mProximityInfo->getCodePointOf(j);
79695a49a527ac9c2c97cfcc758bd6f0d58fb4ad9c0Keisuke Kuroyanagi            bool insert = true;
79795a49a527ac9c2c97cfcc758bd6f0d58fb4ad9c0Keisuke Kuroyanagi            // TODO: Avoid linear search
79895a49a527ac9c2c97cfcc758bd6f0d58fb4ad9c0Keisuke Kuroyanagi            for (int k = 0; k < filterSize; ++k) {
79995a49a527ac9c2c97cfcc758bd6f0d58fb4ad9c0Keisuke Kuroyanagi                if (filter[k] == keyCodePoint) {
80095a49a527ac9c2c97cfcc758bd6f0d58fb4ad9c0Keisuke Kuroyanagi                    insert = false;
80195a49a527ac9c2c97cfcc758bd6f0d58fb4ad9c0Keisuke Kuroyanagi                    break;
80295a49a527ac9c2c97cfcc758bd6f0d58fb4ad9c0Keisuke Kuroyanagi                }
80395a49a527ac9c2c97cfcc758bd6f0d58fb4ad9c0Keisuke Kuroyanagi            }
80495a49a527ac9c2c97cfcc758bd6f0d58fb4ad9c0Keisuke Kuroyanagi            if (insert) {
80541f12ee27b269033fe818f7d52e81ba948a046c3Keisuke Kuroyanagi                filter[newFilterSize++] = keyCodePoint;
80695a49a527ac9c2c97cfcc758bd6f0d58fb4ad9c0Keisuke Kuroyanagi            }
80795a49a527ac9c2c97cfcc758bd6f0d58fb4ad9c0Keisuke Kuroyanagi        }
80895a49a527ac9c2c97cfcc758bd6f0d58fb4ad9c0Keisuke Kuroyanagi    }
80941f12ee27b269033fe818f7d52e81ba948a046c3Keisuke Kuroyanagi    return newFilterSize;
81095a49a527ac9c2c97cfcc758bd6f0d58fb4ad9c0Keisuke Kuroyanagi}
8113811a28ddc07201930e0bbd2e1d01045b59af308Keisuke Kuroyanagi
812ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagibool ProximityInfoState::isKeyInSerchKeysAfterIndex(const int index, const int keyId) const {
813ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    ASSERT(keyId >= 0);
814feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    ASSERT(index >= 0 && index < mSampledInputSize);
815ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    return mSearchKeysVector[index].test(keyId);
816ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi}
817ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi
818096f35ff4b5413906e2a339663baf16e5dabaf64Keisuke Kuroyanagivoid ProximityInfoState::popInputData() {
819feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    mSampledInputXs.pop_back();
820feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    mSampledInputYs.pop_back();
821096f35ff4b5413906e2a339663baf16e5dabaf64Keisuke Kuroyanagi    mTimes.pop_back();
822096f35ff4b5413906e2a339663baf16e5dabaf64Keisuke Kuroyanagi    mLengthCache.pop_back();
823096f35ff4b5413906e2a339663baf16e5dabaf64Keisuke Kuroyanagi    mInputIndice.pop_back();
824096f35ff4b5413906e2a339663baf16e5dabaf64Keisuke Kuroyanagi}
825096f35ff4b5413906e2a339663baf16e5dabaf64Keisuke Kuroyanagi
826ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagifloat ProximityInfoState::getDirection(const int index0, const int index1) const {
827feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    if (index0 < 0 || index0 > mSampledInputSize - 1) {
828ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        return 0.0f;
829ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    }
830feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    if (index1 < 0 || index1 > mSampledInputSize - 1) {
831ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        return 0.0f;
832ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    }
833feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    const int x1 = mSampledInputXs[index0];
834feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    const int y1 = mSampledInputYs[index0];
835feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    const int x2 = mSampledInputXs[index1];
836feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    const int y2 = mSampledInputYs[index1];
837ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    return getAngle(x1, y1, x2, y2);
838ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi}
839ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi
840806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagifloat ProximityInfoState::getPointAngle(const int index) const {
841feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    if (index <= 0 || index >= mSampledInputSize - 1) {
842806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi        return 0.0f;
843806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi    }
844ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    const float previousDirection = getDirection(index - 1, index);
845ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    const float nextDirection = getDirection(index, index + 1);
846806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi    const float directionDiff = getAngleDiff(previousDirection, nextDirection);
847806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi    return directionDiff;
848806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi}
849806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi
850806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagifloat ProximityInfoState::getPointsAngle(
851806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi        const int index0, const int index1, const int index2) const {
852feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    if (index0 < 0 || index0 > mSampledInputSize - 1) {
853806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi        return 0.0f;
854806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi    }
855feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    if (index1 < 0 || index1 > mSampledInputSize - 1) {
856806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi        return 0.0f;
857806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi    }
858feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    if (index2 < 0 || index2 > mSampledInputSize - 1) {
859806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi        return 0.0f;
860806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi    }
861ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    const float previousDirection = getDirection(index0, index1);
862ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    const float nextDirection = getDirection(index1, index2);
863ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    return getAngleDiff(previousDirection, nextDirection);
864ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi}
865ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi
866ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagifloat ProximityInfoState::getLineToKeyDistance(
867ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        const int from, const int to, const int keyId, const bool extend) const {
868feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    if (from < 0 || from > mSampledInputSize - 1) {
869ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        return 0.0f;
870ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    }
871feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    if (to < 0 || to > mSampledInputSize - 1) {
872ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        return 0.0f;
873ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    }
874feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    const int x0 = mSampledInputXs[from];
875feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    const int y0 = mSampledInputYs[from];
876feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    const int x1 = mSampledInputXs[to];
877feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    const int y1 = mSampledInputYs[to];
878ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi
879ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    const int keyX = mProximityInfo->getKeyCenterXOfKeyIdG(keyId);
880ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    const int keyY = mProximityInfo->getKeyCenterYOfKeyIdG(keyId);
881ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi
882ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    return pointToLineSegSquaredDistanceFloat(keyX, keyY, x0, y0, x1, y1, extend);
883806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi}
884806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi
885806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi// Updates probabilities of aligning to some keys and skipping.
886806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi// Word suggestion should be based on this probabilities.
887ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagivoid ProximityInfoState::updateAlignPointProbabilities(const int start) {
888ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    static const float MIN_PROBABILITY = 0.000001f;
889ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    static const float MAX_SKIP_PROBABILITY = 0.95f;
890806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi    static const float SKIP_FIRST_POINT_PROBABILITY = 0.01f;
891806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi    static const float SKIP_LAST_POINT_PROBABILITY = 0.1f;
892ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    static const float MIN_SPEED_RATE_FOR_SKIP_PROBABILITY = 0.15f;
893ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    static const float SPEED_WEIGHT_FOR_SKIP_PROBABILITY = 0.9f;
894ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    static const float SLOW_STRAIGHT_WEIGHT_FOR_SKIP_PROBABILITY = 0.6f;
895ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    static const float NEAREST_DISTANCE_WEIGHT = 0.5f;
896ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    static const float NEAREST_DISTANCE_BIAS = 0.5f;
897ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    static const float NEAREST_DISTANCE_WEIGHT_FOR_LAST = 0.6f;
898ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    static const float NEAREST_DISTANCE_BIAS_FOR_LAST = 0.4f;
899ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi
900ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    static const float ANGLE_WEIGHT = 0.90f;
901ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    static const float DEEP_CORNER_ANGLE_THRESHOLD = M_PI_F * 60.0f / 180.0f;
902ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    static const float SKIP_DEEP_CORNER_PROBABILITY = 0.1f;
903ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    static const float CORNER_ANGLE_THRESHOLD = M_PI_F * 30.0f / 180.0f;
904806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi    static const float STRAIGHT_ANGLE_THRESHOLD = M_PI_F * 15.0f / 180.0f;
905ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    static const float SKIP_CORNER_PROBABILITY = 0.4f;
906ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    static const float SPEED_MARGIN = 0.1f;
907806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi    static const float CENTER_VALUE_OF_NORMALIZED_DISTRIBUTION = 0.0f;
908806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi
909ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    const int keyCount = mProximityInfo->getKeyCount();
910feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    mCharProbabilities.resize(mSampledInputSize);
911806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi    // Calculates probabilities of using a point as a correlated point with the character
912806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi    // for each point.
913feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    for (int i = start; i < mSampledInputSize; ++i) {
914ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        mCharProbabilities[i].clear();
915ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        // First, calculates skip probability. Starts form MIN_SKIP_PROBABILITY.
916806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi        // Note that all values that are multiplied to this probability should be in [0.0, 1.0];
917ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        float skipProbability = MAX_SKIP_PROBABILITY;
918ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi
919ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        const float currentAngle = getPointAngle(i);
9209af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka        const float speedRate = getSpeedRate(i);
921ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi
922ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        float nearestKeyDistance = static_cast<float>(MAX_POINT_TO_KEY_LENGTH);
923ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        for (int j = 0; j < keyCount; ++j) {
924ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi            if (mNearKeysVector[i].test(j)) {
925ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                const float distance = getPointToKeyByIdLength(i, j);
926ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                if (distance < nearestKeyDistance) {
927ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                    nearestKeyDistance = distance;
928ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                }
929ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi            }
930ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        }
931806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi
932806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi        if (i == 0) {
933ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi            skipProbability *= min(1.0f, nearestKeyDistance * NEAREST_DISTANCE_WEIGHT
934ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                    + NEAREST_DISTANCE_BIAS);
935ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi            // Promote the first point
936806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi            skipProbability *= SKIP_FIRST_POINT_PROBABILITY;
937feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka        } else if (i == mSampledInputSize - 1) {
938ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi            skipProbability *= min(1.0f, nearestKeyDistance * NEAREST_DISTANCE_WEIGHT_FOR_LAST
939ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                    + NEAREST_DISTANCE_BIAS_FOR_LAST);
940ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi            // Promote the last point
941806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi            skipProbability *= SKIP_LAST_POINT_PROBABILITY;
942806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi        } else {
943ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi            // If the current speed is relatively slower than adjacent keys, we promote this point.
9449af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka            if (getSpeedRate(i - 1) - SPEED_MARGIN > speedRate
9459af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka                    && speedRate < getSpeedRate(i + 1) - SPEED_MARGIN) {
946ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                if (currentAngle < CORNER_ANGLE_THRESHOLD) {
9479af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka                    skipProbability *= min(1.0f, speedRate
948ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                            * SLOW_STRAIGHT_WEIGHT_FOR_SKIP_PROBABILITY);
949ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                } else {
950ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                    // If the angle is small enough, we promote this point more. (e.g. pit vs put)
9519af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka                    skipProbability *= min(1.0f, speedRate * SPEED_WEIGHT_FOR_SKIP_PROBABILITY
952ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                            + MIN_SPEED_RATE_FOR_SKIP_PROBABILITY);
953ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                }
954ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi            }
955ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi
9569af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka            skipProbability *= min(1.0f, speedRate * nearestKeyDistance *
957ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                    NEAREST_DISTANCE_WEIGHT + NEAREST_DISTANCE_BIAS);
958806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi
959806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi            // Adjusts skip probability by a rate depending on angle.
960806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi            // ANGLE_RATE of skipProbability is adjusted by current angle.
961ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi            skipProbability *= (M_PI_F - currentAngle) / M_PI_F * ANGLE_WEIGHT
962ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                    + (1.0f - ANGLE_WEIGHT);
963806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi            if (currentAngle > DEEP_CORNER_ANGLE_THRESHOLD) {
964806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi                skipProbability *= SKIP_DEEP_CORNER_PROBABILITY;
965806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi            }
966ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi            // We assume the angle of this point is the angle for point[i], point[i - 2]
967ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi            // and point[i - 3]. The reason why we don't use the angle for point[i], point[i - 1]
968ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi            // and point[i - 2] is this angle can be more affected by the noise.
969ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi            const float prevAngle = getPointsAngle(i, i - 2, i - 3);
970ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi            if (i >= 3 && prevAngle < STRAIGHT_ANGLE_THRESHOLD
971ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                    && currentAngle > CORNER_ANGLE_THRESHOLD) {
972806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi                skipProbability *= SKIP_CORNER_PROBABILITY;
973806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi            }
974806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi        }
975806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi
976ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        // probabilities must be in [0.0, MAX_SKIP_PROBABILITY];
977806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi        ASSERT(skipProbability >= 0.0f);
978ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        ASSERT(skipProbability <= MAX_SKIP_PROBABILITY);
979806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi        mCharProbabilities[i][NOT_AN_INDEX] = skipProbability;
980ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi
981806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi        // Second, calculates key probabilities by dividing the rest probability
982806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi        // (1.0f - skipProbability).
983806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi        const float inputCharProbability = 1.0f - skipProbability;
984ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi
985ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        // TODO: The variance is critical for accuracy; thus, adjusting these parameter by machine
986ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        // learning or something would be efficient.
987ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        static const float SPEEDxANGLE_WEIGHT_FOR_STANDARD_DIVIATION = 0.3f;
988ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        static const float MAX_SPEEDxANGLE_RATE_FOR_STANDERD_DIVIATION = 0.25f;
989ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        static const float SPEEDxNEAREST_WEIGHT_FOR_STANDARD_DIVIATION = 0.5f;
990ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        static const float MAX_SPEEDxNEAREST_RATE_FOR_STANDERD_DIVIATION = 0.15f;
991ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        static const float MIN_STANDERD_DIVIATION = 0.37f;
992ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi
9939af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka        const float speedxAngleRate = min(speedRate * currentAngle / M_PI_F
994ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                * SPEEDxANGLE_WEIGHT_FOR_STANDARD_DIVIATION,
995ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                        MAX_SPEEDxANGLE_RATE_FOR_STANDERD_DIVIATION);
9969af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka        const float speedxNearestKeyDistanceRate = min(speedRate * nearestKeyDistance
997ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                * SPEEDxNEAREST_WEIGHT_FOR_STANDARD_DIVIATION,
998ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                        MAX_SPEEDxNEAREST_RATE_FOR_STANDERD_DIVIATION);
999ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        const float sigma = speedxAngleRate + speedxNearestKeyDistanceRate + MIN_STANDERD_DIVIATION;
1000ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi
1001806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi        NormalDistribution distribution(CENTER_VALUE_OF_NORMALIZED_DISTRIBUTION, sigma);
1002ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        static const float PREV_DISTANCE_WEIGHT = 0.5f;
1003ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        static const float NEXT_DISTANCE_WEIGHT = 0.6f;
1004ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        // Summing up probability densities of all near keys.
1005ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        float sumOfProbabilityDensities = 0.0f;
1006ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        for (int j = 0; j < keyCount; ++j) {
1007806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi            if (mNearKeysVector[i].test(j)) {
1008ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                float distance = sqrtf(getPointToKeyByIdLength(i, j));
1009feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka                if (i == 0 && i != mSampledInputSize - 1) {
1010ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                    // For the first point, weighted average of distances from first point and the
1011ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                    // next point to the key is used as a point to key distance.
1012ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                    const float nextDistance = sqrtf(getPointToKeyByIdLength(i + 1, j));
1013ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                    if (nextDistance < distance) {
1014ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                        // The distance of the first point tends to bigger than continuing
1015ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                        // points because the first touch by the user can be sloppy.
1016ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                        // So we promote the first point if the distance of that point is larger
1017ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                        // than the distance of the next point.
1018ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                        distance = (distance + nextDistance * NEXT_DISTANCE_WEIGHT)
1019ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                                / (1.0f + NEXT_DISTANCE_WEIGHT);
1020ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                    }
1021feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka                } else if (i != 0 && i == mSampledInputSize - 1) {
1022ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                    // For the first point, weighted average of distances from last point and
1023ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                    // the previous point to the key is used as a point to key distance.
1024ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                    const float previousDistance = sqrtf(getPointToKeyByIdLength(i - 1, j));
1025ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                    if (previousDistance < distance) {
1026ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                        // The distance of the last point tends to bigger than continuing points
1027ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                        // because the last touch by the user can be sloppy. So we promote the
1028ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                        // last point if the distance of that point is larger than the distance of
1029ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                        // the previous point.
1030ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                        distance = (distance + previousDistance * PREV_DISTANCE_WEIGHT)
1031ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                                / (1.0f + PREV_DISTANCE_WEIGHT);
1032ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                    }
1033ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                }
1034ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                // TODO: Promote the first point when the extended line from the next input is near
1035ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                // from a key. Also, promote the last point as well.
1036ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                sumOfProbabilityDensities += distribution.getProbabilityDensity(distance);
1037806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi            }
1038806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi        }
1039ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi
1040ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        // Split the probability of an input point to keys that are close to the input point.
1041ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        for (int j = 0; j < keyCount; ++j) {
1042806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi            if (mNearKeysVector[i].test(j)) {
1043ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                float distance = sqrtf(getPointToKeyByIdLength(i, j));
1044feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka                if (i == 0 && i != mSampledInputSize - 1) {
1045ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                    // For the first point, weighted average of distances from the first point and
1046ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                    // the next point to the key is used as a point to key distance.
1047ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                    const float prevDistance = sqrtf(getPointToKeyByIdLength(i + 1, j));
1048ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                    if (prevDistance < distance) {
1049ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                        distance = (distance + prevDistance * NEXT_DISTANCE_WEIGHT)
1050ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                                / (1.0f + NEXT_DISTANCE_WEIGHT);
1051ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                    }
1052feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka                } else if (i != 0 && i == mSampledInputSize - 1) {
1053ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                    // For the first point, weighted average of distances from last point and
1054ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                    // the previous point to the key is used as a point to key distance.
1055ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                    const float prevDistance = sqrtf(getPointToKeyByIdLength(i - 1, j));
1056ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                    if (prevDistance < distance) {
1057ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                        distance = (distance + prevDistance * PREV_DISTANCE_WEIGHT)
1058ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                                / (1.0f + PREV_DISTANCE_WEIGHT);
1059ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                    }
1060806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi                }
1061ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                const float probabilityDensity = distribution.getProbabilityDensity(distance);
1062ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                const float probability = inputCharProbability * probabilityDensity
1063ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                        / sumOfProbabilityDensities;
1064ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                mCharProbabilities[i][j] = probability;
1065806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi            }
1066806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi        }
1067806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi    }
1068806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi
1069ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi
1070ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    if (DEBUG_POINTS_PROBABILITY) {
1071feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka        for (int i = 0; i < mSampledInputSize; ++i) {
1072ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi            std::stringstream sstream;
1073ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi            sstream << i << ", ";
1074feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka            sstream << "(" << mSampledInputXs[i] << ", " << mSampledInputYs[i] << "), ";
10759af533538ea749d7c930bb3125fa4d3e4feb8478Satoshi Kataoka            sstream << "Speed: "<< getSpeedRate(i) << ", ";
1076ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi            sstream << "Angle: "<< getPointAngle(i) << ", \n";
1077ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi
1078ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi            for (hash_map_compat<int, float>::iterator it = mCharProbabilities[i].begin();
1079ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                    it != mCharProbabilities[i].end(); ++it) {
1080ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                if (it->first == NOT_AN_INDEX) {
1081ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                    sstream << it->first
1082ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                            << "(skip):"
1083ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                            << it->second
1084ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                            << "\n";
1085ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                } else {
1086ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                    sstream << it->first
1087ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                            << "("
1088ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                            << static_cast<char>(mProximityInfo->getCodePointOf(it->first))
1089ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                            << "):"
1090ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                            << it->second
1091ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                            << "\n";
1092ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                }
1093ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi            }
1094ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi            AKLOGI("%s", sstream.str().c_str());
1095ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        }
1096ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    }
1097ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi
1098806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi    // Decrease key probabilities of points which don't have the highest probability of that key
1099806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi    // among nearby points. Probabilities of the first point and the last point are not suppressed.
1100feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    for (int i = max(start, 1); i < mSampledInputSize; ++i) {
1101feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka        for (int j = i + 1; j < mSampledInputSize; ++j) {
1102ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi            if (!suppressCharProbabilities(i, j)) {
1103806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi                break;
1104806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi            }
1105806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi        }
1106ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        for (int j = i - 1; j >= max(start, 0); --j) {
1107ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi            if (!suppressCharProbabilities(i, j)) {
1108806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi                break;
1109806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi            }
1110806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi        }
1111806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi    }
1112806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi
1113ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    // Converting from raw probabilities to log probabilities to calculate spatial distance.
1114feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    for (int i = start; i < mSampledInputSize; ++i) {
1115ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        for (int j = 0; j < keyCount; ++j) {
1116ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi            hash_map_compat<int, float>::iterator it = mCharProbabilities[i].find(j);
1117ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi            if (it == mCharProbabilities[i].end()){
1118ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                mNearKeysVector[i].reset(j);
1119ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi            } else if(it->second < MIN_PROBABILITY) {
1120ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                // Erases from near keys vector because it has very low probability.
1121ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                mNearKeysVector[i].reset(j);
1122ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                mCharProbabilities[i].erase(j);
1123ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi            } else {
1124ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                it->second = -logf(it->second);
1125806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi            }
1126806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi        }
1127ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        mCharProbabilities[i][NOT_AN_INDEX] = -logf(mCharProbabilities[i][NOT_AN_INDEX]);
1128806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi    }
1129806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi}
1130806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi
1131ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi// Decreases char probabilities of index0 by checking probabilities of a near point (index1) and
1132ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi// increases char probabilities of index1 by checking probabilities of index0.
1133806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagibool ProximityInfoState::suppressCharProbabilities(const int index0, const int index1) {
1134feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    ASSERT(0 <= index0 && index0 < mSampledInputSize);
1135feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    ASSERT(0 <= index1 && index1 < mSampledInputSize);
1136ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi
1137806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi    static const float SUPPRESSION_LENGTH_WEIGHT = 1.5f;
1138ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    static const float MIN_SUPPRESSION_RATE = 0.1f;
1139ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    static const float SUPPRESSION_WEIGHT = 0.5f;
1140ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    static const float SUPPRESSION_WEIGHT_FOR_PROBABILITY_GAIN = 0.1f;
1141ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    static const float SKIP_PROBABALITY_WEIGHT_FOR_PROBABILITY_GAIN = 0.3f;
1142ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi
1143806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi    const float keyWidthFloat = static_cast<float>(mProximityInfo->getMostCommonKeyWidth());
1144806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi    const float diff = fabsf(static_cast<float>(mLengthCache[index0] - mLengthCache[index1]));
1145806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi    if (diff > keyWidthFloat * SUPPRESSION_LENGTH_WEIGHT) {
1146806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi        return false;
1147806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi    }
1148ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    const float suppressionRate = MIN_SUPPRESSION_RATE
1149ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi            + diff / keyWidthFloat / SUPPRESSION_LENGTH_WEIGHT * SUPPRESSION_WEIGHT;
1150806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi    for (hash_map_compat<int, float>::iterator it = mCharProbabilities[index0].begin();
1151806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi            it != mCharProbabilities[index0].end(); ++it) {
1152ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        hash_map_compat<int, float>::iterator it2 =  mCharProbabilities[index1].find(it->first);
1153806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi        if (it2 != mCharProbabilities[index1].end() && it->second < it2->second) {
1154806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi            const float newProbability = it->second * suppressionRate;
1155ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi            const float suppression = it->second - newProbability;
1156806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi            it->second = newProbability;
1157ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi            // mCharProbabilities[index0][NOT_AN_INDEX] is the probability of skipping this point.
1158ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi            mCharProbabilities[index0][NOT_AN_INDEX] += suppression;
1159ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi
1160ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi            // Add the probability of the same key nearby index1
1161ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi            const float probabilityGain = min(suppression * SUPPRESSION_WEIGHT_FOR_PROBABILITY_GAIN,
1162ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                    mCharProbabilities[index1][NOT_AN_INDEX]
1163ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                            * SKIP_PROBABALITY_WEIGHT_FOR_PROBABILITY_GAIN);
1164ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi            it2->second += probabilityGain;
1165ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi            mCharProbabilities[index1][NOT_AN_INDEX] -= probabilityGain;
1166806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi        }
1167806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi    }
1168806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi    return true;
1169806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi}
1170806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi
1171a21187188fd17c2ce133fca8512106327212da5fSatoshi Kataoka// Get a word that is detected by tracing the most probable char sequence into codePointBuf and
11721e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa// returns probability of generating the word.
1173a21187188fd17c2ce133fca8512106327212da5fSatoshi Kataokafloat ProximityInfoState::getMostProbableCharSequence(int *const codePointBuf) const {
1174350309aeb81ad1924af4d2e6d0bceaa6f98e4821Keisuke Kuroyanagi    static const float DEMOTION_LOG_PROBABILITY = 0.3f;
1175ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    int index = 0;
1176ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    float sumLogProbability = 0.0f;
1177806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi    // TODO: Current implementation is greedy algorithm. DP would be efficient for many cases.
1178feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    for (int i = 0; i < mSampledInputSize && index < MAX_WORD_LENGTH_INTERNAL - 1; ++i) {
1179ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        float minLogProbability = static_cast<float>(MAX_POINT_TO_KEY_LENGTH);
1180ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        int character = NOT_AN_INDEX;
1181806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi        for (hash_map_compat<int, float>::const_iterator it = mCharProbabilities[i].begin();
1182806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi                it != mCharProbabilities[i].end(); ++it) {
1183ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi            const float logProbability = (it->first != NOT_AN_INDEX)
1184350309aeb81ad1924af4d2e6d0bceaa6f98e4821Keisuke Kuroyanagi                    ? it->second + DEMOTION_LOG_PROBABILITY : it->second;
1185ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi            if (logProbability < minLogProbability) {
1186ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                minLogProbability = logProbability;
1187ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi                character = it->first;
1188806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi            }
1189806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi        }
1190ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        if (character != NOT_AN_INDEX) {
11911e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa            codePointBuf[index] = mProximityInfo->getCodePointOf(character);
1192806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi            index++;
1193806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi        }
1194ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        sumLogProbability += minLogProbability;
1195806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi    }
11961e61493c50082264caaef862df02b1ccc84dc396Ken Wakasa    codePointBuf[index] = '\0';
1197ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    return sumLogProbability;
1198ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi}
1199ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi
1200ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi// Returns a probability of mapping index to keyIndex.
1201ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagifloat ProximityInfoState::getProbability(const int index, const int keyIndex) const {
1202feec20a692c9ffdecf7855a45531a12f129086caSatoshi Kataoka    ASSERT(0 <= index && index < mSampledInputSize);
1203ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    hash_map_compat<int, float>::const_iterator it = mCharProbabilities[index].find(keyIndex);
1204ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    if (it != mCharProbabilities[index].end()) {
1205ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi        return it->second;
1206ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    }
1207ff74cc3e5e75fc0c6b9ffaa5e68d879775dc6115Keisuke Kuroyanagi    return static_cast<float>(MAX_POINT_TO_KEY_LENGTH);
1208806eba452423e5e5971ef096dfae3fed180db665Keisuke Kuroyanagi}
12093e8c58f68d53e6cc9dbf59201c7bdfb8ad04a1cdSatoshi Kataoka} // namespace latinime
1210