1ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka/*
2ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka * Copyright (C) 2013 The Android Open Source Project
3ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka *
4ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka * Licensed under the Apache License, Version 2.0 (the "License");
5ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka * you may not use this file except in compliance with the License.
6ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka * You may obtain a copy of the License at
7ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka *
8ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka *      http://www.apache.org/licenses/LICENSE-2.0
9ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka *
10ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka * Unless required by applicable law or agreed to in writing, software
11ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka * distributed under the License is distributed on an "AS IS" BASIS,
12ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka * See the License for the specific language governing permissions and
14ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka * limitations under the License.
15ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka */
16ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka
1729432f843a8cd6ffb2be286104964592e80d77c9Ken Wakasa#include "suggest/core/layout/proximity_info_state_utils.h"
1829432f843a8cd6ffb2be286104964592e80d77c9Ken Wakasa
19ca0a0da8640d1469cb460120ff0aede2322c6802Ken Wakasa#include <algorithm>
20e2912d17e4dab75b81f4c9e41a539e491ac059caSatoshi Kataoka#include <cmath>
2120b6775acc957896bdb038dfd99794d6cd7cea5aSatoshi Kataoka#include <cstring> // for memset()
22d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka#include <sstream> // for debug prints
238ca9be17db2f1845c7c7a3b584507cf60c9ca53dKen Wakasa#include <unordered_map>
24ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka#include <vector>
25ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka
266c22439bf80da08576e86c1282afc5cfa431e235Ken Wakasa#include "defines.h"
2729432f843a8cd6ffb2be286104964592e80d77c9Ken Wakasa#include "suggest/core/layout/geometry_utils.h"
2826c806620c26e048918624367ee624526613b0d2Keisuke Kuroyanagi#include "suggest/core/layout/normal_distribution_2d.h"
2929432f843a8cd6ffb2be286104964592e80d77c9Ken Wakasa#include "suggest/core/layout/proximity_info.h"
3029432f843a8cd6ffb2be286104964592e80d77c9Ken Wakasa#include "suggest/core/layout/proximity_info_params.h"
31ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka
32ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataokanamespace latinime {
33d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka
3420b6775acc957896bdb038dfd99794d6cd7cea5aSatoshi Kataoka/* static */ int ProximityInfoStateUtils::trimLastTwoTouchPoints(std::vector<int> *sampledInputXs,
3520b6775acc957896bdb038dfd99794d6cd7cea5aSatoshi Kataoka        std::vector<int> *sampledInputYs, std::vector<int> *sampledInputTimes,
3620b6775acc957896bdb038dfd99794d6cd7cea5aSatoshi Kataoka        std::vector<int> *sampledLengthCache, std::vector<int> *sampledInputIndice) {
3720b6775acc957896bdb038dfd99794d6cd7cea5aSatoshi Kataoka    const int nextStartIndex = (*sampledInputIndice)[sampledInputIndice->size() - 2];
3820b6775acc957896bdb038dfd99794d6cd7cea5aSatoshi Kataoka    popInputData(sampledInputXs, sampledInputYs, sampledInputTimes, sampledLengthCache,
3920b6775acc957896bdb038dfd99794d6cd7cea5aSatoshi Kataoka            sampledInputIndice);
4020b6775acc957896bdb038dfd99794d6cd7cea5aSatoshi Kataoka    popInputData(sampledInputXs, sampledInputYs, sampledInputTimes, sampledLengthCache,
4120b6775acc957896bdb038dfd99794d6cd7cea5aSatoshi Kataoka            sampledInputIndice);
4220b6775acc957896bdb038dfd99794d6cd7cea5aSatoshi Kataoka    return nextStartIndex;
4320b6775acc957896bdb038dfd99794d6cd7cea5aSatoshi Kataoka}
4420b6775acc957896bdb038dfd99794d6cd7cea5aSatoshi Kataoka
4528c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa/* static */ int ProximityInfoStateUtils::updateTouchPoints(
46ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        const ProximityInfo *const proximityInfo, const int maxPointToKeyLength,
476c22439bf80da08576e86c1282afc5cfa431e235Ken Wakasa        const int *const inputProximities, const int *const inputXCoordinates,
486c22439bf80da08576e86c1282afc5cfa431e235Ken Wakasa        const int *const inputYCoordinates, const int *const times, const int *const pointerIds,
490052dbda762b1871c3214a6abeb5e89f11e091cdKeisuke Kuroynagi        const int inputSize, const bool isGeometric, const int pointerId,
500052dbda762b1871c3214a6abeb5e89f11e091cdKeisuke Kuroynagi        const int pushTouchPointStartIndex, std::vector<int> *sampledInputXs,
516c22439bf80da08576e86c1282afc5cfa431e235Ken Wakasa        std::vector<int> *sampledInputYs, std::vector<int> *sampledInputTimes,
526c22439bf80da08576e86c1282afc5cfa431e235Ken Wakasa        std::vector<int> *sampledLengthCache, std::vector<int> *sampledInputIndice) {
53ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    if (DEBUG_SAMPLING_POINTS) {
54ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        if (times) {
55ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka            for (int i = 0; i < inputSize; ++i) {
56ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka                AKLOGI("(%d) x %d, y %d, time %d",
57e2912d17e4dab75b81f4c9e41a539e491ac059caSatoshi Kataoka                        i, inputXCoordinates[i], inputYCoordinates[i], times[i]);
58ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka            }
59ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        }
60ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    }
61ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka#ifdef DO_ASSERT_TEST
62ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    if (times) {
63ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        for (int i = 0; i < inputSize; ++i) {
64ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka            if (i > 0) {
65e2912d17e4dab75b81f4c9e41a539e491ac059caSatoshi Kataoka                if (times[i] < times[i - 1]) {
66e9e4fa5af6a94dbd6e24e631cd4606d7e41c1f16Ken Wakasa                    AKLOGI("Invalid time sequence. %d, %d", times[i - 1], times[i]);
67e2912d17e4dab75b81f4c9e41a539e491ac059caSatoshi Kataoka                    ASSERT(false);
68e2912d17e4dab75b81f4c9e41a539e491ac059caSatoshi Kataoka                }
69ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka            }
70ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        }
71ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    }
72ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka#endif
73ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    const bool proximityOnly = !isGeometric
74ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka            && (inputXCoordinates[0] < 0 || inputYCoordinates[0] < 0);
75ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    int lastInputIndex = pushTouchPointStartIndex;
76ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    for (int i = lastInputIndex; i < inputSize; ++i) {
77ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        const int pid = pointerIds ? pointerIds[i] : 0;
78ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        if (pointerId == pid) {
79ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka            lastInputIndex = i;
80ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        }
81ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    }
82ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    if (DEBUG_GEO_FULL) {
83ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        AKLOGI("Init ProximityInfoState: last input index = %d", lastInputIndex);
84ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    }
85ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    // Working space to save near keys distances for current, prev and prevprev input point.
86ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    NearKeysDistanceMap nearKeysDistances[3];
87ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    // These pointers are swapped for each inputs points.
88ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    NearKeysDistanceMap *currentNearKeysDistances = &nearKeysDistances[0];
89ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    NearKeysDistanceMap *prevNearKeysDistances = &nearKeysDistances[1];
90ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    NearKeysDistanceMap *prevPrevNearKeysDistances = &nearKeysDistances[2];
91ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    // "sumAngle" is accumulated by each angle of input points. And when "sumAngle" exceeds
92ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    // the threshold we save that point, reset sumAngle. This aims to keep the figure of
93ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    // the curve.
94ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    float sumAngle = 0.0f;
95ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka
96ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    for (int i = pushTouchPointStartIndex; i <= lastInputIndex; ++i) {
97ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        // Assuming pointerId == 0 if pointerIds is null.
98ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        const int pid = pointerIds ? pointerIds[i] : 0;
99ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        if (DEBUG_GEO_FULL) {
100ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka            AKLOGI("Init ProximityInfoState: (%d)PID = %d", i, pid);
101ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        }
102ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        if (pointerId == pid) {
103ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka            const int c = isGeometric ?
104ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka                    NOT_A_COORDINATE : getPrimaryCodePointAt(inputProximities, i);
105ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka            const int x = proximityOnly ? NOT_A_COORDINATE : inputXCoordinates[i];
106ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka            const int y = proximityOnly ? NOT_A_COORDINATE : inputYCoordinates[i];
107ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka            const int time = times ? times[i] : -1;
108ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka
109ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka            if (i > 1) {
11029432f843a8cd6ffb2be286104964592e80d77c9Ken Wakasa                const float prevAngle = GeometryUtils::getAngle(
111ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka                        inputXCoordinates[i - 2], inputYCoordinates[i - 2],
112ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka                        inputXCoordinates[i - 1], inputYCoordinates[i - 1]);
11329432f843a8cd6ffb2be286104964592e80d77c9Ken Wakasa                const float currentAngle = GeometryUtils::getAngle(
11428c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa                        inputXCoordinates[i - 1], inputYCoordinates[i - 1], x, y);
11529432f843a8cd6ffb2be286104964592e80d77c9Ken Wakasa                sumAngle += GeometryUtils::getAngleDiff(prevAngle, currentAngle);
116ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka            }
117ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka
11828c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa            if (pushTouchPoint(proximityInfo, maxPointToKeyLength, i, c, x, y, time,
1190052dbda762b1871c3214a6abeb5e89f11e091cdKeisuke Kuroynagi                    isGeometric, isGeometric /* doSampling */, i == lastInputIndex,
120a1d84bcf8ffd031c135b6f3f8c94b6732071849bSatoshi Kataoka                    sumAngle, currentNearKeysDistances, prevNearKeysDistances,
121a1d84bcf8ffd031c135b6f3f8c94b6732071849bSatoshi Kataoka                    prevPrevNearKeysDistances, sampledInputXs, sampledInputYs, sampledInputTimes,
122a1d84bcf8ffd031c135b6f3f8c94b6732071849bSatoshi Kataoka                    sampledLengthCache, sampledInputIndice)) {
123ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka                // Previous point information was popped.
124ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka                NearKeysDistanceMap *tmp = prevNearKeysDistances;
125ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka                prevNearKeysDistances = currentNearKeysDistances;
126ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka                currentNearKeysDistances = tmp;
127ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka            } else {
128ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka                NearKeysDistanceMap *tmp = prevPrevNearKeysDistances;
129ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka                prevPrevNearKeysDistances = prevNearKeysDistances;
130ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka                prevNearKeysDistances = currentNearKeysDistances;
131ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka                currentNearKeysDistances = tmp;
132ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka                sumAngle = 0.0f;
133ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka            }
134ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        }
135ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    }
136ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    return sampledInputXs->size();
137ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka}
138ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka
139ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka/* static */ const int *ProximityInfoStateUtils::getProximityCodePointsAt(
140ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        const int *const inputProximities, const int index) {
1416c22439bf80da08576e86c1282afc5cfa431e235Ken Wakasa    return inputProximities + (index * MAX_PROXIMITY_CHARS_SIZE);
142ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka}
143ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka
1444920d370f2a9f14bcc14212738590c22bd3752d0Ken Wakasa/* static */ int ProximityInfoStateUtils::getPrimaryCodePointAt(const int *const inputProximities,
1454920d370f2a9f14bcc14212738590c22bd3752d0Ken Wakasa        const int index) {
146ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    return getProximityCodePointsAt(inputProximities, index)[0];
147ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka}
148ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka
1494920d370f2a9f14bcc14212738590c22bd3752d0Ken Wakasa/* static */ void ProximityInfoStateUtils::initPrimaryInputWord(const int inputSize,
1504920d370f2a9f14bcc14212738590c22bd3752d0Ken Wakasa        const int *const inputProximities, int *primaryInputWord) {
15120b6775acc957896bdb038dfd99794d6cd7cea5aSatoshi Kataoka    memset(primaryInputWord, 0, sizeof(primaryInputWord[0]) * MAX_WORD_LENGTH);
152d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka    for (int i = 0; i < inputSize; ++i) {
153d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka        primaryInputWord[i] = getPrimaryCodePointAt(inputProximities, i);
154d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka    }
155d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka}
156d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka
157d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka/* static */ float ProximityInfoStateUtils::calculateSquaredDistanceFromSweetSpotCenter(
158d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka        const ProximityInfo *const proximityInfo, const std::vector<int> *const sampledInputXs,
1594920d370f2a9f14bcc14212738590c22bd3752d0Ken Wakasa        const std::vector<int> *const sampledInputYs, const int keyIndex, const int inputIndex) {
160d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka    const float sweetSpotCenterX = proximityInfo->getSweetSpotCenterXAt(keyIndex);
161d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka    const float sweetSpotCenterY = proximityInfo->getSweetSpotCenterYAt(keyIndex);
162d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka    const float inputX = static_cast<float>((*sampledInputXs)[inputIndex]);
163d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka    const float inputY = static_cast<float>((*sampledInputYs)[inputIndex]);
16429432f843a8cd6ffb2be286104964592e80d77c9Ken Wakasa    return GeometryUtils::SQUARE_FLOAT(inputX - sweetSpotCenterX)
16529432f843a8cd6ffb2be286104964592e80d77c9Ken Wakasa            + GeometryUtils::SQUARE_FLOAT(inputY - sweetSpotCenterY);
166d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka}
167d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka
168d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka/* static */ float ProximityInfoStateUtils::calculateNormalizedSquaredDistance(
169d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka        const ProximityInfo *const proximityInfo, const std::vector<int> *const sampledInputXs,
1704920d370f2a9f14bcc14212738590c22bd3752d0Ken Wakasa        const std::vector<int> *const sampledInputYs, const int keyIndex, const int inputIndex) {
171d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka    if (keyIndex == NOT_AN_INDEX) {
172d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka        return ProximityInfoParams::NOT_A_DISTANCE_FLOAT;
173d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka    }
174d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka    if (!proximityInfo->hasSweetSpotData(keyIndex)) {
175d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka        return ProximityInfoParams::NOT_A_DISTANCE_FLOAT;
176d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka    }
177d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka    if (NOT_A_COORDINATE == (*sampledInputXs)[inputIndex]) {
178d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka        return ProximityInfoParams::NOT_A_DISTANCE_FLOAT;
179d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka    }
180d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka    const float squaredDistance = calculateSquaredDistanceFromSweetSpotCenter(proximityInfo,
181d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka            sampledInputXs, sampledInputYs, keyIndex, inputIndex);
18229432f843a8cd6ffb2be286104964592e80d77c9Ken Wakasa    const float squaredRadius = GeometryUtils::SQUARE_FLOAT(
18329432f843a8cd6ffb2be286104964592e80d77c9Ken Wakasa            proximityInfo->getSweetSpotRadiiAt(keyIndex));
184d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka    return squaredDistance / squaredRadius;
185d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka}
186d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka
187d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka/* static */ void ProximityInfoStateUtils::initGeometricDistanceInfos(
18828c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa        const ProximityInfo *const proximityInfo, const int sampledInputSize,
1890052dbda762b1871c3214a6abeb5e89f11e091cdKeisuke Kuroynagi        const int lastSavedInputSize, const bool isGeometric,
190a1d84bcf8ffd031c135b6f3f8c94b6732071849bSatoshi Kataoka        const std::vector<int> *const sampledInputXs,
191d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka        const std::vector<int> *const sampledInputYs,
192837f46dcb35a8f42a6bd5bc5fc6395d7386acb81Satoshi Kataoka        std::vector<float> *sampledNormalizedSquaredLengthCache) {
19328c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa    const int keyCount = proximityInfo->getKeyCount();
194837f46dcb35a8f42a6bd5bc5fc6395d7386acb81Satoshi Kataoka    sampledNormalizedSquaredLengthCache->resize(sampledInputSize * keyCount);
195d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka    for (int i = lastSavedInputSize; i < sampledInputSize; ++i) {
196d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka        for (int k = 0; k < keyCount; ++k) {
197d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka            const int index = i * keyCount + k;
198d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka            const int x = (*sampledInputXs)[i];
199d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka            const int y = (*sampledInputYs)[i];
200d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka            const float normalizedSquaredDistance =
201a1d84bcf8ffd031c135b6f3f8c94b6732071849bSatoshi Kataoka                    proximityInfo->getNormalizedSquaredDistanceFromCenterFloatG(
2020052dbda762b1871c3214a6abeb5e89f11e091cdKeisuke Kuroynagi                            k, x, y, isGeometric);
203837f46dcb35a8f42a6bd5bc5fc6395d7386acb81Satoshi Kataoka            (*sampledNormalizedSquaredLengthCache)[index] = normalizedSquaredDistance;
204d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka        }
205d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka    }
206d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka}
207d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka
208ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka/* static */ void ProximityInfoStateUtils::popInputData(std::vector<int> *sampledInputXs,
209ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        std::vector<int> *sampledInputYs, std::vector<int> *sampledInputTimes,
210ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        std::vector<int> *sampledLengthCache, std::vector<int> *sampledInputIndice) {
211ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    sampledInputXs->pop_back();
212ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    sampledInputYs->pop_back();
213ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    sampledInputTimes->pop_back();
214ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    sampledLengthCache->pop_back();
215ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    sampledInputIndice->pop_back();
216ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka}
217ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka
218ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka/* static */ float ProximityInfoStateUtils::refreshSpeedRates(const int inputSize,
219ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        const int *const xCoordinates, const int *const yCoordinates, const int *const times,
220ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        const int lastSavedInputSize, const int sampledInputSize,
2214920d370f2a9f14bcc14212738590c22bd3752d0Ken Wakasa        const std::vector<int> *const sampledInputXs, const std::vector<int> *const sampledInputYs,
222ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        const std::vector<int> *const sampledInputTimes,
223ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        const std::vector<int> *const sampledLengthCache,
224ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        const std::vector<int> *const sampledInputIndice, std::vector<float> *sampledSpeedRates,
225ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        std::vector<float> *sampledDirections) {
226ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    // Relative speed calculation.
227ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    const int sumDuration = sampledInputTimes->back() - sampledInputTimes->front();
228ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    const int sumLength = sampledLengthCache->back() - sampledLengthCache->front();
229ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    const float averageSpeed = static_cast<float>(sumLength) / static_cast<float>(sumDuration);
230ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    sampledSpeedRates->resize(sampledInputSize);
231ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    for (int i = lastSavedInputSize; i < sampledInputSize; ++i) {
232ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        const int index = (*sampledInputIndice)[i];
233ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        int length = 0;
234ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        int duration = 0;
235ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka
236ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        // Calculate velocity by using distances and durations of
23728c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa        // ProximityInfoParams::NUM_POINTS_FOR_SPEED_CALCULATION points for both forward and
23828c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa        // backward.
239865e6cf49764f3a411ee32861d927b15653ee398Keisuke Kuroyanagi        const int forwardNumPoints = std::min(inputSize - 1,
24028c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa                index + ProximityInfoParams::NUM_POINTS_FOR_SPEED_CALCULATION);
24128c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa        for (int j = index; j < forwardNumPoints; ++j) {
242ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka            if (i < sampledInputSize - 1 && j >= (*sampledInputIndice)[i + 1]) {
243ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka                break;
244ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka            }
24529432f843a8cd6ffb2be286104964592e80d77c9Ken Wakasa            length += GeometryUtils::getDistanceInt(xCoordinates[j], yCoordinates[j],
246ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka                    xCoordinates[j + 1], yCoordinates[j + 1]);
247ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka            duration += times[j + 1] - times[j];
248ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        }
249865e6cf49764f3a411ee32861d927b15653ee398Keisuke Kuroyanagi        const int backwardNumPoints = std::max(0,
25028c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa                index - ProximityInfoParams::NUM_POINTS_FOR_SPEED_CALCULATION);
25128c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa        for (int j = index - 1; j >= backwardNumPoints; --j) {
252ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka            if (i > 0 && j < (*sampledInputIndice)[i - 1]) {
253ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka                break;
254ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka            }
255d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka            // TODO: use mSampledLengthCache instead?
25629432f843a8cd6ffb2be286104964592e80d77c9Ken Wakasa            length += GeometryUtils::getDistanceInt(xCoordinates[j], yCoordinates[j],
257ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka                    xCoordinates[j + 1], yCoordinates[j + 1]);
258ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka            duration += times[j + 1] - times[j];
259ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        }
260ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        if (duration == 0 || sumDuration == 0) {
261ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka            // Cannot calculate speed; thus, it gives an average value (1.0);
262ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka            (*sampledSpeedRates)[i] = 1.0f;
263ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        } else {
264ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka            const float speed = static_cast<float>(length) / static_cast<float>(duration);
265ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka            (*sampledSpeedRates)[i] = speed / averageSpeed;
266ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        }
267ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    }
268ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka
269ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    // Direction calculation.
270ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    sampledDirections->resize(sampledInputSize - 1);
271865e6cf49764f3a411ee32861d927b15653ee398Keisuke Kuroyanagi    for (int i = std::max(0, lastSavedInputSize - 1); i < sampledInputSize - 1; ++i) {
272ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        (*sampledDirections)[i] = getDirection(sampledInputXs, sampledInputYs, i, i + 1);
273ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    }
274ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    return averageSpeed;
275ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka}
276ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka
277ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka/* static */ void ProximityInfoStateUtils::refreshBeelineSpeedRates(const int mostCommonKeyWidth,
278ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        const float averageSpeed, const int inputSize, const int *const xCoordinates,
279ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        const int *const yCoordinates, const int *times, const int sampledInputSize,
280ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        const std::vector<int> *const sampledInputXs,
281ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        const std::vector<int> *const sampledInputYs, const std::vector<int> *const inputIndice,
282ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        std::vector<int> *beelineSpeedPercentiles) {
283ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    if (DEBUG_SAMPLING_POINTS) {
284ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        AKLOGI("--- refresh beeline speed rates");
285ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    }
286ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    beelineSpeedPercentiles->resize(sampledInputSize);
287ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    for (int i = 0; i < sampledInputSize; ++i) {
288ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        (*beelineSpeedPercentiles)[i] = static_cast<int>(calculateBeelineSpeedRate(
289ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka                mostCommonKeyWidth, averageSpeed, i, inputSize, xCoordinates, yCoordinates, times,
290ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka                sampledInputSize, sampledInputXs, sampledInputYs, inputIndice) * MAX_PERCENTILE);
291ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    }
292ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka}
293ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka
294ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka/* static */float ProximityInfoStateUtils::getDirection(
295ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        const std::vector<int> *const sampledInputXs,
296ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        const std::vector<int> *const sampledInputYs, const int index0, const int index1) {
297ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    ASSERT(sampledInputXs && sampledInputYs);
298ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    const int sampledInputSize =sampledInputXs->size();
299ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    if (index0 < 0 || index0 > sampledInputSize - 1) {
300ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        return 0.0f;
301ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    }
302ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    if (index1 < 0 || index1 > sampledInputSize - 1) {
303ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        return 0.0f;
304ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    }
305ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    const int x1 = (*sampledInputXs)[index0];
306ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    const int y1 = (*sampledInputYs)[index0];
307ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    const int x2 = (*sampledInputXs)[index1];
308ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    const int y2 = (*sampledInputYs)[index1];
30929432f843a8cd6ffb2be286104964592e80d77c9Ken Wakasa    return GeometryUtils::getAngle(x1, y1, x2, y2);
310ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka}
311ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka
312ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka// Calculating point to key distance for all near keys and returning the distance between
313ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka// the given point and the nearest key position.
314ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka/* static */ float ProximityInfoStateUtils::updateNearKeysDistances(
315ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        const ProximityInfo *const proximityInfo, const float maxPointToKeyLength, const int x,
3160052dbda762b1871c3214a6abeb5e89f11e091cdKeisuke Kuroynagi        const int y, const bool isGeometric, NearKeysDistanceMap *const currentNearKeysDistances) {
317ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    currentNearKeysDistances->clear();
318ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    const int keyCount = proximityInfo->getKeyCount();
319ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    float nearestKeyDistance = maxPointToKeyLength;
320ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    for (int k = 0; k < keyCount; ++k) {
321a1d84bcf8ffd031c135b6f3f8c94b6732071849bSatoshi Kataoka        const float dist = proximityInfo->getNormalizedSquaredDistanceFromCenterFloatG(k, x, y,
3220052dbda762b1871c3214a6abeb5e89f11e091cdKeisuke Kuroynagi                isGeometric);
3234920d370f2a9f14bcc14212738590c22bd3752d0Ken Wakasa        if (dist < ProximityInfoParams::NEAR_KEY_THRESHOLD_FOR_DISTANCE) {
324ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka            currentNearKeysDistances->insert(std::pair<int, float>(k, dist));
325ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        }
326ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        if (nearestKeyDistance > dist) {
327ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka            nearestKeyDistance = dist;
328ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        }
329ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    }
330ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    return nearestKeyDistance;
331ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka}
332ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka
333ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka// Check if previous point is at local minimum position to near keys.
334ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka/* static */ bool ProximityInfoStateUtils::isPrevLocalMin(
335ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        const NearKeysDistanceMap *const currentNearKeysDistances,
336ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        const NearKeysDistanceMap *const prevNearKeysDistances,
337ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        const NearKeysDistanceMap *const prevPrevNearKeysDistances) {
338ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    for (NearKeysDistanceMap::const_iterator it = prevNearKeysDistances->begin();
339ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka            it != prevNearKeysDistances->end(); ++it) {
340ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        NearKeysDistanceMap::const_iterator itPP = prevPrevNearKeysDistances->find(it->first);
341ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        NearKeysDistanceMap::const_iterator itC = currentNearKeysDistances->find(it->first);
3424920d370f2a9f14bcc14212738590c22bd3752d0Ken Wakasa        const bool isPrevPrevNear = (itPP == prevPrevNearKeysDistances->end()
3434920d370f2a9f14bcc14212738590c22bd3752d0Ken Wakasa                || itPP->second > it->second + ProximityInfoParams::MARGIN_FOR_PREV_LOCAL_MIN);
3444920d370f2a9f14bcc14212738590c22bd3752d0Ken Wakasa        const bool isCurrentNear = (itC == currentNearKeysDistances->end()
3454920d370f2a9f14bcc14212738590c22bd3752d0Ken Wakasa                || itC->second > it->second + ProximityInfoParams::MARGIN_FOR_PREV_LOCAL_MIN);
3464920d370f2a9f14bcc14212738590c22bd3752d0Ken Wakasa        if (isPrevPrevNear && isCurrentNear) {
347ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka            return true;
348ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        }
349ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    }
350ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    return false;
351ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka}
352ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka
353ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka// Calculating a point score that indicates usefulness of the point.
354ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka/* static */ float ProximityInfoStateUtils::getPointScore(const int mostCommonKeyWidth,
355ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        const int x, const int y, const int time, const bool lastPoint, const float nearest,
356ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        const float sumAngle, const NearKeysDistanceMap *const currentNearKeysDistances,
357ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        const NearKeysDistanceMap *const prevNearKeysDistances,
358ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        const NearKeysDistanceMap *const prevPrevNearKeysDistances,
359ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        std::vector<int> *sampledInputXs, std::vector<int> *sampledInputYs) {
360ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    const size_t size = sampledInputXs->size();
361ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    // If there is only one point, add this point. Besides, if the previous point's distance map
362ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    // is empty, we re-compute nearby keys distances from the current point.
363ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    // Note that the current point is the first point in the incremental input that needs to
364ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    // be re-computed.
365ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    if (size <= 1 || prevNearKeysDistances->empty()) {
366ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        return 0.0f;
367ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    }
368ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka
369ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    const int baseSampleRate = mostCommonKeyWidth;
37029432f843a8cd6ffb2be286104964592e80d77c9Ken Wakasa    const int distPrev = GeometryUtils::getDistanceInt(sampledInputXs->back(),
37129432f843a8cd6ffb2be286104964592e80d77c9Ken Wakasa            sampledInputYs->back(), (*sampledInputXs)[size - 2],
37229432f843a8cd6ffb2be286104964592e80d77c9Ken Wakasa            (*sampledInputYs)[size - 2]) * ProximityInfoParams::DISTANCE_BASE_SCALE;
373ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    float score = 0.0f;
374ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka
375ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    // Location
376ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    if (!isPrevLocalMin(currentNearKeysDistances, prevNearKeysDistances,
377ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        prevPrevNearKeysDistances)) {
3784920d370f2a9f14bcc14212738590c22bd3752d0Ken Wakasa        score += ProximityInfoParams::NOT_LOCALMIN_DISTANCE_SCORE;
3794920d370f2a9f14bcc14212738590c22bd3752d0Ken Wakasa    } else if (nearest < ProximityInfoParams::NEAR_KEY_THRESHOLD_FOR_POINT_SCORE) {
380ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        // Promote points nearby keys
3814920d370f2a9f14bcc14212738590c22bd3752d0Ken Wakasa        score += ProximityInfoParams::LOCALMIN_DISTANCE_AND_NEAR_TO_KEY_SCORE;
382ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    }
383ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    // Angle
38429432f843a8cd6ffb2be286104964592e80d77c9Ken Wakasa    const float angle1 = GeometryUtils::getAngle(x, y, sampledInputXs->back(),
38529432f843a8cd6ffb2be286104964592e80d77c9Ken Wakasa            sampledInputYs->back());
38629432f843a8cd6ffb2be286104964592e80d77c9Ken Wakasa    const float angle2 = GeometryUtils::getAngle(sampledInputXs->back(), sampledInputYs->back(),
387ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka            (*sampledInputXs)[size - 2], (*sampledInputYs)[size - 2]);
38829432f843a8cd6ffb2be286104964592e80d77c9Ken Wakasa    const float angleDiff = GeometryUtils::getAngleDiff(angle1, angle2);
389ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka
390ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    // Save corner
3914920d370f2a9f14bcc14212738590c22bd3752d0Ken Wakasa    if (distPrev > baseSampleRate * ProximityInfoParams::CORNER_CHECK_DISTANCE_THRESHOLD_SCALE
3924920d370f2a9f14bcc14212738590c22bd3752d0Ken Wakasa            && (sumAngle > ProximityInfoParams::CORNER_SUM_ANGLE_THRESHOLD
3934920d370f2a9f14bcc14212738590c22bd3752d0Ken Wakasa                    || angleDiff > ProximityInfoParams::CORNER_ANGLE_THRESHOLD_FOR_POINT_SCORE)) {
3944920d370f2a9f14bcc14212738590c22bd3752d0Ken Wakasa        score += ProximityInfoParams::CORNER_SCORE;
395ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    }
396ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    return score;
397ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka}
398ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka
399ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka// Sampling touch point and pushing information to vectors.
400ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka// Returning if previous point is popped or not.
40128c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa/* static */ bool ProximityInfoStateUtils::pushTouchPoint(const ProximityInfo *const proximityInfo,
40228c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa        const int maxPointToKeyLength, const int inputIndex, const int nodeCodePoint, int x, int y,
4030052dbda762b1871c3214a6abeb5e89f11e091cdKeisuke Kuroynagi        const int time, const bool isGeometric, const bool doSampling,
404a1d84bcf8ffd031c135b6f3f8c94b6732071849bSatoshi Kataoka        const bool isLastPoint, const float sumAngle,
405ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        NearKeysDistanceMap *const currentNearKeysDistances,
406ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        const NearKeysDistanceMap *const prevNearKeysDistances,
407ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        const NearKeysDistanceMap *const prevPrevNearKeysDistances,
408ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        std::vector<int> *sampledInputXs, std::vector<int> *sampledInputYs,
409ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        std::vector<int> *sampledInputTimes, std::vector<int> *sampledLengthCache,
410ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        std::vector<int> *sampledInputIndice) {
41128c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa    const int mostCommonKeyWidth = proximityInfo->getMostCommonKeyWidth();
412ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka
413ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    size_t size = sampledInputXs->size();
414ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    bool popped = false;
4156c22439bf80da08576e86c1282afc5cfa431e235Ken Wakasa    if (nodeCodePoint < 0 && doSampling) {
416a1d84bcf8ffd031c135b6f3f8c94b6732071849bSatoshi Kataoka        const float nearest = updateNearKeysDistances(proximityInfo, maxPointToKeyLength, x, y,
4170052dbda762b1871c3214a6abeb5e89f11e091cdKeisuke Kuroynagi                isGeometric, currentNearKeysDistances);
418ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        const float score = getPointScore(mostCommonKeyWidth, x, y, time, isLastPoint, nearest,
419ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka                sumAngle, currentNearKeysDistances, prevNearKeysDistances,
420ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka                prevPrevNearKeysDistances, sampledInputXs, sampledInputYs);
421ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        if (score < 0) {
422ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka            // Pop previous point because it would be useless.
423ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka            popInputData(sampledInputXs, sampledInputYs, sampledInputTimes, sampledLengthCache,
424ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka                    sampledInputIndice);
425ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka            size = sampledInputXs->size();
426ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka            popped = true;
427ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        } else {
428ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka            popped = false;
429ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        }
430ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        // Check if the last point should be skipped.
431ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        if (isLastPoint && size > 0) {
43229432f843a8cd6ffb2be286104964592e80d77c9Ken Wakasa            if (GeometryUtils::getDistanceInt(x, y, sampledInputXs->back(), sampledInputYs->back())
43328c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa                    * ProximityInfoParams::LAST_POINT_SKIP_DISTANCE_SCALE < mostCommonKeyWidth) {
434ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka                // This point is not used because it's too close to the previous point.
435ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka                if (DEBUG_GEO_FULL) {
436ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka                    AKLOGI("p0: size = %zd, x = %d, y = %d, lx = %d, ly = %d, dist = %d, "
437e2912d17e4dab75b81f4c9e41a539e491ac059caSatoshi Kataoka                           "width = %d", size, x, y, sampledInputXs->back(),
43829432f843a8cd6ffb2be286104964592e80d77c9Ken Wakasa                           sampledInputYs->back(), GeometryUtils::getDistanceInt(
439e2912d17e4dab75b81f4c9e41a539e491ac059caSatoshi Kataoka                                   x, y, sampledInputXs->back(), sampledInputYs->back()),
44028c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa                           mostCommonKeyWidth
44128c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa                                   / ProximityInfoParams::LAST_POINT_SKIP_DISTANCE_SCALE);
442ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka                }
443ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka                return popped;
444ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka            }
445ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        }
446ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    }
447ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka
448ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    if (nodeCodePoint >= 0 && (x < 0 || y < 0)) {
449ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        const int keyId = proximityInfo->getKeyIndexOf(nodeCodePoint);
450ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        if (keyId >= 0) {
4510052dbda762b1871c3214a6abeb5e89f11e091cdKeisuke Kuroynagi            x = proximityInfo->getKeyCenterXOfKeyIdG(keyId, NOT_AN_INDEX, isGeometric);
4520052dbda762b1871c3214a6abeb5e89f11e091cdKeisuke Kuroynagi            y = proximityInfo->getKeyCenterYOfKeyIdG(keyId, NOT_AN_INDEX, isGeometric);
453ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        }
454ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    }
455ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka
456ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    // Pushing point information.
457ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    if (size > 0) {
458ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        sampledLengthCache->push_back(
45929432f843a8cd6ffb2be286104964592e80d77c9Ken Wakasa                sampledLengthCache->back() + GeometryUtils::getDistanceInt(
460ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka                        x, y, sampledInputXs->back(), sampledInputYs->back()));
461ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    } else {
462ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        sampledLengthCache->push_back(0);
463ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    }
464ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    sampledInputXs->push_back(x);
465ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    sampledInputYs->push_back(y);
466ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    sampledInputTimes->push_back(time);
467ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    sampledInputIndice->push_back(inputIndex);
468ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    if (DEBUG_GEO_FULL) {
469ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        AKLOGI("pushTouchPoint: x = %03d, y = %03d, time = %d, index = %d, popped ? %01d",
470ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka                x, y, time, inputIndex, popped);
471ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    }
472ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    return popped;
473ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka}
474ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka
475ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka/* static */ float ProximityInfoStateUtils::calculateBeelineSpeedRate(const int mostCommonKeyWidth,
476ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        const float averageSpeed, const int id, const int inputSize, const int *const xCoordinates,
477ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        const int *const yCoordinates, const int *times, const int sampledInputSize,
478ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        const std::vector<int> *const sampledInputXs,
479e2912d17e4dab75b81f4c9e41a539e491ac059caSatoshi Kataoka        const std::vector<int> *const sampledInputYs,
480e2912d17e4dab75b81f4c9e41a539e491ac059caSatoshi Kataoka        const std::vector<int> *const sampledInputIndices) {
481ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    if (sampledInputSize <= 0 || averageSpeed < 0.001f) {
482ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        if (DEBUG_SAMPLING_POINTS) {
483ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka            AKLOGI("--- invalid state: cancel. size = %d, ave = %f",
484e2912d17e4dab75b81f4c9e41a539e491ac059caSatoshi Kataoka                    sampledInputSize, averageSpeed);
485ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        }
486ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        return 1.0f;
487ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    }
488ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    const int lookupRadius = mostCommonKeyWidth
489ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka            * ProximityInfoParams::LOOKUP_RADIUS_PERCENTILE / MAX_PERCENTILE;
490ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    const int x0 = (*sampledInputXs)[id];
491ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    const int y0 = (*sampledInputYs)[id];
492e2912d17e4dab75b81f4c9e41a539e491ac059caSatoshi Kataoka    const int actualInputIndex = (*sampledInputIndices)[id];
493ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    int tempTime = 0;
494ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    int tempBeelineDistance = 0;
495ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    int start = actualInputIndex;
496ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    // lookup forward
497ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    while (start > 0 && tempBeelineDistance < lookupRadius) {
498ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        tempTime += times[start] - times[start - 1];
499ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        --start;
50029432f843a8cd6ffb2be286104964592e80d77c9Ken Wakasa        tempBeelineDistance = GeometryUtils::getDistanceInt(x0, y0, xCoordinates[start],
50129432f843a8cd6ffb2be286104964592e80d77c9Ken Wakasa                yCoordinates[start]);
502ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    }
503ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    // Exclusive unless this is an edge point
504ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    if (start > 0 && start < actualInputIndex) {
505ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        ++start;
506ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    }
507ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    tempTime= 0;
508ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    tempBeelineDistance = 0;
509ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    int end = actualInputIndex;
510ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    // lookup backward
511ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    while (end < (inputSize - 1) && tempBeelineDistance < lookupRadius) {
512ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        tempTime += times[end + 1] - times[end];
513ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        ++end;
51429432f843a8cd6ffb2be286104964592e80d77c9Ken Wakasa        tempBeelineDistance = GeometryUtils::getDistanceInt(x0, y0, xCoordinates[end],
51529432f843a8cd6ffb2be286104964592e80d77c9Ken Wakasa                yCoordinates[end]);
516ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    }
517ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    // Exclusive unless this is an edge point
518ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    if (end > actualInputIndex && end < (inputSize - 1)) {
519ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        --end;
520ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    }
521ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka
522ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    if (start >= end) {
523ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        if (DEBUG_DOUBLE_LETTER) {
524ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka            AKLOGI("--- double letter: start == end %d", start);
525ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        }
526ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        return 1.0f;
527ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    }
528ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka
529ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    const int x2 = xCoordinates[start];
530ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    const int y2 = yCoordinates[start];
531ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    const int x3 = xCoordinates[end];
532ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    const int y3 = yCoordinates[end];
53329432f843a8cd6ffb2be286104964592e80d77c9Ken Wakasa    const int beelineDistance = GeometryUtils::getDistanceInt(x2, y2, x3, y3);
534ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    int adjustedStartTime = times[start];
535ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    if (start == 0 && actualInputIndex == 0 && inputSize > 1) {
536ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        adjustedStartTime += ProximityInfoParams::FIRST_POINT_TIME_OFFSET_MILLIS;
537ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    }
538ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    int adjustedEndTime = times[end];
539ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    if (end == (inputSize - 1) && inputSize > 1) {
540ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        adjustedEndTime -= ProximityInfoParams::FIRST_POINT_TIME_OFFSET_MILLIS;
541ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    }
542ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    const int time = adjustedEndTime - adjustedStartTime;
543ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    if (time <= 0) {
544ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        return 1.0f;
545ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    }
546ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka
547ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    if (time >= ProximityInfoParams::STRONG_DOUBLE_LETTER_TIME_MILLIS){
548ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        return 0.0f;
549ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    }
550ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    if (DEBUG_DOUBLE_LETTER) {
551ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka        AKLOGI("--- (%d, %d) double letter: start = %d, end = %d, dist = %d, time = %d,"
552ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka                " speed = %f, ave = %f, val = %f, start time = %d, end time = %d",
553e2912d17e4dab75b81f4c9e41a539e491ac059caSatoshi Kataoka                id, (*sampledInputIndices)[id], start, end, beelineDistance, time,
554e2912d17e4dab75b81f4c9e41a539e491ac059caSatoshi Kataoka                (static_cast<float>(beelineDistance) / static_cast<float>(time)), averageSpeed,
555ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka                ((static_cast<float>(beelineDistance) / static_cast<float>(time))
556e2912d17e4dab75b81f4c9e41a539e491ac059caSatoshi Kataoka                        / averageSpeed), adjustedStartTime, adjustedEndTime);
557ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    }
558ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    // Offset 1%
559ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    // TODO: Detect double letter more smartly
560ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka    return 0.01f + static_cast<float>(beelineDistance) / static_cast<float>(time) / averageSpeed;
561ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka}
562d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka
563d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka/* static */ float ProximityInfoStateUtils::getPointAngle(
564d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        const std::vector<int> *const sampledInputXs,
565d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        const std::vector<int> *const sampledInputYs, const int index) {
566d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka    if (!sampledInputXs || !sampledInputYs) {
567d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        return 0.0f;
568d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka    }
569d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka    const int sampledInputSize = sampledInputXs->size();
570d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka    if (index <= 0 || index >= sampledInputSize - 1) {
571d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        return 0.0f;
572d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka    }
573d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka    const float previousDirection = getDirection(sampledInputXs, sampledInputYs, index - 1, index);
574d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka    const float nextDirection = getDirection(sampledInputXs, sampledInputYs, index, index + 1);
57529432f843a8cd6ffb2be286104964592e80d77c9Ken Wakasa    const float directionDiff = GeometryUtils::getAngleDiff(previousDirection, nextDirection);
576d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka    return directionDiff;
577d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka}
578d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka
579d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka/* static */ float ProximityInfoStateUtils::getPointsAngle(
580d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        const std::vector<int> *const sampledInputXs,
581d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        const std::vector<int> *const sampledInputYs,
582d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        const int index0, const int index1, const int index2) {
583d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka    if (!sampledInputXs || !sampledInputYs) {
584d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        return 0.0f;
585d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka    }
586d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka    const int sampledInputSize = sampledInputXs->size();
587d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka    if (index0 < 0 || index0 > sampledInputSize - 1) {
588d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        return 0.0f;
589d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka    }
590d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka    if (index1 < 0 || index1 > sampledInputSize - 1) {
591d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        return 0.0f;
592d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka    }
593d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka    if (index2 < 0 || index2 > sampledInputSize - 1) {
594d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        return 0.0f;
595d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka    }
596d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka    const float previousDirection = getDirection(sampledInputXs, sampledInputYs, index0, index1);
597d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka    const float nextDirection = getDirection(sampledInputXs, sampledInputYs, index1, index2);
59829432f843a8cd6ffb2be286104964592e80d77c9Ken Wakasa    return GeometryUtils::getAngleDiff(previousDirection, nextDirection);
599d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka}
600d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka
601d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka// This function basically converts from a length to an edit distance. Accordingly, it's obviously
602d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka// wrong to compare with mMaxPointToKeyLength.
603d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka/* static */ float ProximityInfoStateUtils::getPointToKeyByIdLength(const float maxPointToKeyLength,
604837f46dcb35a8f42a6bd5bc5fc6395d7386acb81Satoshi Kataoka        const std::vector<float> *const sampledNormalizedSquaredLengthCache, const int keyCount,
6052192d08b9cca6a40d834d6a5001d19b5845ed8a0Tom Ouyang        const int inputIndex, const int keyId) {
606d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka    if (keyId != NOT_AN_INDEX) {
607d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        const int index = inputIndex * keyCount + keyId;
608865e6cf49764f3a411ee32861d927b15653ee398Keisuke Kuroyanagi        return std::min((*sampledNormalizedSquaredLengthCache)[index], maxPointToKeyLength);
609d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka    }
610d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka    // If the char is not a key on the keyboard then return the max length.
611830ba67498c6da53b38212dd9ac5ba318a00de11Satoshi Kataoka    return static_cast<float>(MAX_VALUE_FOR_WEIGHTING);
612d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka}
613d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka
614d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka// Updates probabilities of aligning to some keys and skipping.
615d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka// Word suggestion should be based on this probabilities.
616d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka/* static */ void ProximityInfoStateUtils::updateAlignPointProbabilities(
617d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        const float maxPointToKeyLength, const int mostCommonKeyWidth, const int keyCount,
618d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        const int start, const int sampledInputSize, const std::vector<int> *const sampledInputXs,
619d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        const std::vector<int> *const sampledInputYs,
620d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        const std::vector<float> *const sampledSpeedRates,
621d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        const std::vector<int> *const sampledLengthCache,
622837f46dcb35a8f42a6bd5bc5fc6395d7386acb81Satoshi Kataoka        const std::vector<float> *const sampledNormalizedSquaredLengthCache,
62326c806620c26e048918624367ee624526613b0d2Keisuke Kuroyanagi        const ProximityInfo *const proximityInfo,
6240b1fa0c1c7572893365c019780357a817158e5eaKen Wakasa        std::vector<std::unordered_map<int, float>> *charProbabilities) {
625d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka    charProbabilities->resize(sampledInputSize);
626d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka    // Calculates probabilities of using a point as a correlated point with the character
627d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka    // for each point.
628d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka    for (int i = start; i < sampledInputSize; ++i) {
629d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        (*charProbabilities)[i].clear();
63028c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa        // First, calculates skip probability. Starts from MAX_SKIP_PROBABILITY.
631d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        // Note that all values that are multiplied to this probability should be in [0.0, 1.0];
63228c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa        float skipProbability = ProximityInfoParams::MAX_SKIP_PROBABILITY;
633d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka
634d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        const float currentAngle = getPointAngle(sampledInputXs, sampledInputYs, i);
635d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        const float speedRate = (*sampledSpeedRates)[i];
636d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka
637830ba67498c6da53b38212dd9ac5ba318a00de11Satoshi Kataoka        float nearestKeyDistance = static_cast<float>(MAX_VALUE_FOR_WEIGHTING);
638d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        for (int j = 0; j < keyCount; ++j) {
639bc9500fb14622f8b185e30a10a7c0b4c5a29930aKeisuke Kuroyanagi            const float distance = getPointToKeyByIdLength(
640bc9500fb14622f8b185e30a10a7c0b4c5a29930aKeisuke Kuroyanagi                    maxPointToKeyLength, sampledNormalizedSquaredLengthCache, keyCount, i, j);
641bc9500fb14622f8b185e30a10a7c0b4c5a29930aKeisuke Kuroyanagi            if (distance < nearestKeyDistance) {
642bc9500fb14622f8b185e30a10a7c0b4c5a29930aKeisuke Kuroyanagi                nearestKeyDistance = distance;
643d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka            }
644d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        }
645d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka
646d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        if (i == 0) {
647865e6cf49764f3a411ee32861d927b15653ee398Keisuke Kuroyanagi            skipProbability *= std::min(1.0f,
64828c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa                    nearestKeyDistance * ProximityInfoParams::NEAREST_DISTANCE_WEIGHT
64928c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa                            + ProximityInfoParams::NEAREST_DISTANCE_BIAS);
650d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka            // Promote the first point
65128c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa            skipProbability *= ProximityInfoParams::SKIP_FIRST_POINT_PROBABILITY;
652d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        } else if (i == sampledInputSize - 1) {
653865e6cf49764f3a411ee32861d927b15653ee398Keisuke Kuroyanagi            skipProbability *= std::min(1.0f,
65428c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa                    nearestKeyDistance * ProximityInfoParams::NEAREST_DISTANCE_WEIGHT_FOR_LAST
65528c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa                            + ProximityInfoParams::NEAREST_DISTANCE_BIAS_FOR_LAST);
656d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka            // Promote the last point
65728c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa            skipProbability *= ProximityInfoParams::SKIP_LAST_POINT_PROBABILITY;
658d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        } else {
659d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka            // If the current speed is relatively slower than adjacent keys, we promote this point.
66028c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa            if ((*sampledSpeedRates)[i - 1] - ProximityInfoParams::SPEED_MARGIN > speedRate
66128c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa                    && speedRate
66228c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa                            < (*sampledSpeedRates)[i + 1] - ProximityInfoParams::SPEED_MARGIN) {
66328c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa                if (currentAngle < ProximityInfoParams::CORNER_ANGLE_THRESHOLD) {
664865e6cf49764f3a411ee32861d927b15653ee398Keisuke Kuroyanagi                    skipProbability *= std::min(1.0f, speedRate
66528c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa                            * ProximityInfoParams::SLOW_STRAIGHT_WEIGHT_FOR_SKIP_PROBABILITY);
666d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka                } else {
667d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka                    // If the angle is small enough, we promote this point more. (e.g. pit vs put)
668865e6cf49764f3a411ee32861d927b15653ee398Keisuke Kuroyanagi                    skipProbability *= std::min(1.0f,
66928c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa                            speedRate * ProximityInfoParams::SPEED_WEIGHT_FOR_SKIP_PROBABILITY
67028c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa                                    + ProximityInfoParams::MIN_SPEED_RATE_FOR_SKIP_PROBABILITY);
671d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka                }
672d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka            }
673d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka
674865e6cf49764f3a411ee32861d927b15653ee398Keisuke Kuroyanagi            skipProbability *= std::min(1.0f,
67528c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa                    speedRate * nearestKeyDistance * ProximityInfoParams::NEAREST_DISTANCE_WEIGHT
67628c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa                            + ProximityInfoParams::NEAREST_DISTANCE_BIAS);
677d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka
678d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka            // Adjusts skip probability by a rate depending on angle.
679d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka            // ANGLE_RATE of skipProbability is adjusted by current angle.
68028c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa            skipProbability *= (M_PI_F - currentAngle) / M_PI_F * ProximityInfoParams::ANGLE_WEIGHT
68128c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa                    + (1.0f - ProximityInfoParams::ANGLE_WEIGHT);
68228c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa            if (currentAngle > ProximityInfoParams::DEEP_CORNER_ANGLE_THRESHOLD) {
68328c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa                skipProbability *= ProximityInfoParams::SKIP_DEEP_CORNER_PROBABILITY;
684d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka            }
685d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka            // We assume the angle of this point is the angle for point[i], point[i - 2]
686d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka            // and point[i - 3]. The reason why we don't use the angle for point[i], point[i - 1]
687d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka            // and point[i - 2] is this angle can be more affected by the noise.
688d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka            const float prevAngle = getPointsAngle(sampledInputXs, sampledInputYs, i, i - 2, i - 3);
68928c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa            if (i >= 3 && prevAngle < ProximityInfoParams::STRAIGHT_ANGLE_THRESHOLD
69028c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa                    && currentAngle > ProximityInfoParams::CORNER_ANGLE_THRESHOLD) {
69128c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa                skipProbability *= ProximityInfoParams::SKIP_CORNER_PROBABILITY;
692d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka            }
693d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        }
694d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka
69528c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa        // probabilities must be in [0.0, ProximityInfoParams::MAX_SKIP_PROBABILITY];
696d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        ASSERT(skipProbability >= 0.0f);
69728c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa        ASSERT(skipProbability <= ProximityInfoParams::MAX_SKIP_PROBABILITY);
698d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        (*charProbabilities)[i][NOT_AN_INDEX] = skipProbability;
699d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka
700d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        // Second, calculates key probabilities by dividing the rest probability
701d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        // (1.0f - skipProbability).
702d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        const float inputCharProbability = 1.0f - skipProbability;
703d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka
70426c806620c26e048918624367ee624526613b0d2Keisuke Kuroyanagi        const float speedMultipliedByAngleRate = std::min(speedRate * currentAngle / M_PI_F
7053e954347e3a7b381d7e94feb002e158f3bc69a32Jean Chalard                * ProximityInfoParams::SPEEDxANGLE_WEIGHT_FOR_STANDARD_DEVIATION,
7063e954347e3a7b381d7e94feb002e158f3bc69a32Jean Chalard                        ProximityInfoParams::MAX_SPEEDxANGLE_RATE_FOR_STANDARD_DEVIATION);
70726c806620c26e048918624367ee624526613b0d2Keisuke Kuroyanagi        const float speedMultipliedByNearestKeyDistanceRate = std::min(
70826c806620c26e048918624367ee624526613b0d2Keisuke Kuroyanagi                speedRate * nearestKeyDistance
70926c806620c26e048918624367ee624526613b0d2Keisuke Kuroyanagi                        * ProximityInfoParams::SPEEDxNEAREST_WEIGHT_FOR_STANDARD_DEVIATION,
71026c806620c26e048918624367ee624526613b0d2Keisuke Kuroyanagi                                ProximityInfoParams::MAX_SPEEDxNEAREST_RATE_FOR_STANDARD_DEVIATION);
71126c806620c26e048918624367ee624526613b0d2Keisuke Kuroyanagi        const float sigma = (speedMultipliedByAngleRate + speedMultipliedByNearestKeyDistanceRate
71226c806620c26e048918624367ee624526613b0d2Keisuke Kuroyanagi                + ProximityInfoParams::MIN_STANDARD_DEVIATION) * mostCommonKeyWidth;
71326c806620c26e048918624367ee624526613b0d2Keisuke Kuroyanagi        float theta = 0.0f;
71426c806620c26e048918624367ee624526613b0d2Keisuke Kuroyanagi        // TODO: Use different metrics to compute sigmas.
71526c806620c26e048918624367ee624526613b0d2Keisuke Kuroyanagi        float sigmaX = sigma;
71626c806620c26e048918624367ee624526613b0d2Keisuke Kuroyanagi        float sigmaY = sigma;
71726c806620c26e048918624367ee624526613b0d2Keisuke Kuroyanagi        if (i == 0 && i != sampledInputSize - 1) {
71826c806620c26e048918624367ee624526613b0d2Keisuke Kuroyanagi            // First point
71926c806620c26e048918624367ee624526613b0d2Keisuke Kuroyanagi            theta = getDirection(sampledInputXs, sampledInputYs, i + 1, i);
72026c806620c26e048918624367ee624526613b0d2Keisuke Kuroyanagi            sigmaX *= ProximityInfoParams::STANDARD_DEVIATION_X_WEIGHT_FOR_FIRST;
72126c806620c26e048918624367ee624526613b0d2Keisuke Kuroyanagi            sigmaY *= ProximityInfoParams::STANDARD_DEVIATION_Y_WEIGHT_FOR_FIRST;
72226c806620c26e048918624367ee624526613b0d2Keisuke Kuroyanagi        } else {
72326c806620c26e048918624367ee624526613b0d2Keisuke Kuroyanagi            if (i == sampledInputSize - 1) {
72426c806620c26e048918624367ee624526613b0d2Keisuke Kuroyanagi                // Last point
72526c806620c26e048918624367ee624526613b0d2Keisuke Kuroyanagi                sigmaX *= ProximityInfoParams::STANDARD_DEVIATION_X_WEIGHT_FOR_LAST;
72626c806620c26e048918624367ee624526613b0d2Keisuke Kuroyanagi                sigmaY *= ProximityInfoParams::STANDARD_DEVIATION_Y_WEIGHT_FOR_LAST;
72726c806620c26e048918624367ee624526613b0d2Keisuke Kuroyanagi            } else {
72826c806620c26e048918624367ee624526613b0d2Keisuke Kuroyanagi                sigmaX *= ProximityInfoParams::STANDARD_DEVIATION_X_WEIGHT;
72926c806620c26e048918624367ee624526613b0d2Keisuke Kuroyanagi                sigmaY *= ProximityInfoParams::STANDARD_DEVIATION_Y_WEIGHT;
73026c806620c26e048918624367ee624526613b0d2Keisuke Kuroyanagi            }
73126c806620c26e048918624367ee624526613b0d2Keisuke Kuroyanagi            theta = getDirection(sampledInputXs, sampledInputYs, i, i - 1);
73226c806620c26e048918624367ee624526613b0d2Keisuke Kuroyanagi        }
73326c806620c26e048918624367ee624526613b0d2Keisuke Kuroyanagi        NormalDistribution2D distribution((*sampledInputXs)[i], sigmaX, (*sampledInputYs)[i],
73426c806620c26e048918624367ee624526613b0d2Keisuke Kuroyanagi                sigmaY, theta);
735d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        // Summing up probability densities of all near keys.
736d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        float sumOfProbabilityDensities = 0.0f;
737d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        for (int j = 0; j < keyCount; ++j) {
738bc9500fb14622f8b185e30a10a7c0b4c5a29930aKeisuke Kuroyanagi            sumOfProbabilityDensities += distribution.getProbabilityDensity(
739bc9500fb14622f8b185e30a10a7c0b4c5a29930aKeisuke Kuroyanagi                    proximityInfo->getKeyCenterXOfKeyIdG(j,
740bc9500fb14622f8b185e30a10a7c0b4c5a29930aKeisuke Kuroyanagi                            NOT_A_COORDINATE /* referencePointX */, true /* isGeometric */),
741bc9500fb14622f8b185e30a10a7c0b4c5a29930aKeisuke Kuroyanagi                    proximityInfo->getKeyCenterYOfKeyIdG(j,
742bc9500fb14622f8b185e30a10a7c0b4c5a29930aKeisuke Kuroyanagi                            NOT_A_COORDINATE /* referencePointY */, true /* isGeometric */));
743d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        }
744d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka
745d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        // Split the probability of an input point to keys that are close to the input point.
746d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        for (int j = 0; j < keyCount; ++j) {
747bc9500fb14622f8b185e30a10a7c0b4c5a29930aKeisuke Kuroyanagi            const float probabilityDensity = distribution.getProbabilityDensity(
748bc9500fb14622f8b185e30a10a7c0b4c5a29930aKeisuke Kuroyanagi                    proximityInfo->getKeyCenterXOfKeyIdG(j,
749bc9500fb14622f8b185e30a10a7c0b4c5a29930aKeisuke Kuroyanagi                            NOT_A_COORDINATE /* referencePointX */, true /* isGeometric */),
750bc9500fb14622f8b185e30a10a7c0b4c5a29930aKeisuke Kuroyanagi                    proximityInfo->getKeyCenterYOfKeyIdG(j,
751bc9500fb14622f8b185e30a10a7c0b4c5a29930aKeisuke Kuroyanagi                            NOT_A_COORDINATE /* referencePointY */, true /* isGeometric */));
752bc9500fb14622f8b185e30a10a7c0b4c5a29930aKeisuke Kuroyanagi            const float probability = inputCharProbability * probabilityDensity
753bc9500fb14622f8b185e30a10a7c0b4c5a29930aKeisuke Kuroyanagi                    / sumOfProbabilityDensities;
754bc9500fb14622f8b185e30a10a7c0b4c5a29930aKeisuke Kuroyanagi            (*charProbabilities)[i][j] = probability;
755d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        }
756d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka    }
757d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka
758d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka    if (DEBUG_POINTS_PROBABILITY) {
759d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        for (int i = 0; i < sampledInputSize; ++i) {
760d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka            std::stringstream sstream;
761d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka            sstream << i << ", ";
762d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka            sstream << "(" << (*sampledInputXs)[i] << ", " << (*sampledInputYs)[i] << "), ";
763d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka            sstream << "Speed: "<< (*sampledSpeedRates)[i] << ", ";
764d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka            sstream << "Angle: "<< getPointAngle(sampledInputXs, sampledInputYs, i) << ", \n";
765d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka
7668ca9be17db2f1845c7c7a3b584507cf60c9ca53dKen Wakasa            for (std::unordered_map<int, float>::iterator it = (*charProbabilities)[i].begin();
767d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka                    it != (*charProbabilities)[i].end(); ++it) {
768d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka                if (it->first == NOT_AN_INDEX) {
769d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka                    sstream << it->first
770d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka                            << "(skip):"
771d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka                            << it->second
772d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka                            << "\n";
773d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka                } else {
774d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka                    sstream << it->first
775d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka                            << "("
776d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka                            //<< static_cast<char>(mProximityInfo->getCodePointOf(it->first))
777d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka                            << "):"
778d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka                            << it->second
779d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka                            << "\n";
780d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka                }
781d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka            }
782d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka            AKLOGI("%s", sstream.str().c_str());
783d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        }
784d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka    }
785d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka
786d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka    // Decrease key probabilities of points which don't have the highest probability of that key
787d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka    // among nearby points. Probabilities of the first point and the last point are not suppressed.
788865e6cf49764f3a411ee32861d927b15653ee398Keisuke Kuroyanagi    for (int i = std::max(start, 1); i < sampledInputSize; ++i) {
789d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        for (int j = i + 1; j < sampledInputSize; ++j) {
790d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka            if (!suppressCharProbabilities(
791d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka                    mostCommonKeyWidth, sampledInputSize, sampledLengthCache, i, j,
792d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka                    charProbabilities)) {
793d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka                break;
794d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka            }
795d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        }
796865e6cf49764f3a411ee32861d927b15653ee398Keisuke Kuroyanagi        for (int j = i - 1; j >= std::max(start, 0); --j) {
797d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka            if (!suppressCharProbabilities(
798d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka                    mostCommonKeyWidth, sampledInputSize, sampledLengthCache, i, j,
799d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka                    charProbabilities)) {
800d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka                break;
801d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka            }
802d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        }
803d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka    }
804d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka
805d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka    // Converting from raw probabilities to log probabilities to calculate spatial distance.
806d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka    for (int i = start; i < sampledInputSize; ++i) {
807d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        for (int j = 0; j < keyCount; ++j) {
8088ca9be17db2f1845c7c7a3b584507cf60c9ca53dKen Wakasa            std::unordered_map<int, float>::iterator it = (*charProbabilities)[i].find(j);
809d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka            if (it == (*charProbabilities)[i].end()){
810bc9500fb14622f8b185e30a10a7c0b4c5a29930aKeisuke Kuroyanagi                continue;
81128c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa            } else if(it->second < ProximityInfoParams::MIN_PROBABILITY) {
812d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka                // Erases from near keys vector because it has very low probability.
813d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka                (*charProbabilities)[i].erase(j);
814d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka            } else {
815d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka                it->second = -logf(it->second);
816d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka            }
817d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        }
818d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        (*charProbabilities)[i][NOT_AN_INDEX] = -logf((*charProbabilities)[i][NOT_AN_INDEX]);
819d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka    }
820d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka}
821d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka
822e5cdd21102e4e49b18c696261a084783eb6d7e7aSatoshi Kataoka/* static */ void ProximityInfoStateUtils::updateSampledSearchKeySets(
823e2912d17e4dab75b81f4c9e41a539e491ac059caSatoshi Kataoka        const ProximityInfo *const proximityInfo, const int sampledInputSize,
824bc9500fb14622f8b185e30a10a7c0b4c5a29930aKeisuke Kuroyanagi        const int lastSavedInputSize, const std::vector<int> *const sampledLengthCache,
8250b1fa0c1c7572893365c019780357a817158e5eaKen Wakasa        const std::vector<std::unordered_map<int, float>> *const charProbabilities,
826e5cdd21102e4e49b18c696261a084783eb6d7e7aSatoshi Kataoka        std::vector<NearKeycodesSet> *sampledSearchKeySets,
8270b1fa0c1c7572893365c019780357a817158e5eaKen Wakasa        std::vector<std::vector<int>> *sampledSearchKeyVectors) {
828e5cdd21102e4e49b18c696261a084783eb6d7e7aSatoshi Kataoka    sampledSearchKeySets->resize(sampledInputSize);
829e5cdd21102e4e49b18c696261a084783eb6d7e7aSatoshi Kataoka    sampledSearchKeyVectors->resize(sampledInputSize);
830e2912d17e4dab75b81f4c9e41a539e491ac059caSatoshi Kataoka    const int readForwordLength = static_cast<int>(
831e2912d17e4dab75b81f4c9e41a539e491ac059caSatoshi Kataoka            hypotf(proximityInfo->getKeyboardWidth(), proximityInfo->getKeyboardHeight())
832e2912d17e4dab75b81f4c9e41a539e491ac059caSatoshi Kataoka                    * ProximityInfoParams::SEARCH_KEY_RADIUS_RATIO);
833e2912d17e4dab75b81f4c9e41a539e491ac059caSatoshi Kataoka    for (int i = 0; i < sampledInputSize; ++i) {
834e2912d17e4dab75b81f4c9e41a539e491ac059caSatoshi Kataoka        if (i >= lastSavedInputSize) {
835e5cdd21102e4e49b18c696261a084783eb6d7e7aSatoshi Kataoka            (*sampledSearchKeySets)[i].reset();
836e2912d17e4dab75b81f4c9e41a539e491ac059caSatoshi Kataoka        }
837865e6cf49764f3a411ee32861d927b15653ee398Keisuke Kuroyanagi        for (int j = std::max(i, lastSavedInputSize); j < sampledInputSize; ++j) {
838e2912d17e4dab75b81f4c9e41a539e491ac059caSatoshi Kataoka            // TODO: Investigate if this is required. This may not fail.
839e2912d17e4dab75b81f4c9e41a539e491ac059caSatoshi Kataoka            if ((*sampledLengthCache)[j] - (*sampledLengthCache)[i] >= readForwordLength) {
840e2912d17e4dab75b81f4c9e41a539e491ac059caSatoshi Kataoka                break;
841e2912d17e4dab75b81f4c9e41a539e491ac059caSatoshi Kataoka            }
842bc9500fb14622f8b185e30a10a7c0b4c5a29930aKeisuke Kuroyanagi            for(const auto& charProbability : charProbabilities->at(j)) {
843bc9500fb14622f8b185e30a10a7c0b4c5a29930aKeisuke Kuroyanagi                if (charProbability.first == NOT_AN_INDEX) {
844bc9500fb14622f8b185e30a10a7c0b4c5a29930aKeisuke Kuroyanagi                    continue;
845bc9500fb14622f8b185e30a10a7c0b4c5a29930aKeisuke Kuroyanagi                }
846bc9500fb14622f8b185e30a10a7c0b4c5a29930aKeisuke Kuroyanagi                (*sampledSearchKeySets)[i].set(charProbability.first);
847bc9500fb14622f8b185e30a10a7c0b4c5a29930aKeisuke Kuroyanagi            }
848e5cdd21102e4e49b18c696261a084783eb6d7e7aSatoshi Kataoka        }
849e5cdd21102e4e49b18c696261a084783eb6d7e7aSatoshi Kataoka    }
850e5cdd21102e4e49b18c696261a084783eb6d7e7aSatoshi Kataoka    const int keyCount = proximityInfo->getKeyCount();
851e5cdd21102e4e49b18c696261a084783eb6d7e7aSatoshi Kataoka    for (int i = 0; i < sampledInputSize; ++i) {
852e5cdd21102e4e49b18c696261a084783eb6d7e7aSatoshi Kataoka        std::vector<int> *searchKeyVector = &(*sampledSearchKeyVectors)[i];
853e5cdd21102e4e49b18c696261a084783eb6d7e7aSatoshi Kataoka        searchKeyVector->clear();
854e5cdd21102e4e49b18c696261a084783eb6d7e7aSatoshi Kataoka        for (int j = 0; j < keyCount; ++j) {
855e5cdd21102e4e49b18c696261a084783eb6d7e7aSatoshi Kataoka            if ((*sampledSearchKeySets)[i].test(j)) {
856e5cdd21102e4e49b18c696261a084783eb6d7e7aSatoshi Kataoka                const int keyCodePoint = proximityInfo->getCodePointOf(j);
857e5cdd21102e4e49b18c696261a084783eb6d7e7aSatoshi Kataoka                if (std::find(searchKeyVector->begin(), searchKeyVector->end(), keyCodePoint)
858e5cdd21102e4e49b18c696261a084783eb6d7e7aSatoshi Kataoka                        == searchKeyVector->end()) {
859e5cdd21102e4e49b18c696261a084783eb6d7e7aSatoshi Kataoka                    searchKeyVector->push_back(keyCodePoint);
860e5cdd21102e4e49b18c696261a084783eb6d7e7aSatoshi Kataoka                }
861e5cdd21102e4e49b18c696261a084783eb6d7e7aSatoshi Kataoka            }
862e2912d17e4dab75b81f4c9e41a539e491ac059caSatoshi Kataoka        }
863e2912d17e4dab75b81f4c9e41a539e491ac059caSatoshi Kataoka    }
864e2912d17e4dab75b81f4c9e41a539e491ac059caSatoshi Kataoka}
865e2912d17e4dab75b81f4c9e41a539e491ac059caSatoshi Kataoka
866d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka// Decreases char probabilities of index0 by checking probabilities of a near point (index1) and
867d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka// increases char probabilities of index1 by checking probabilities of index0.
868d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka/* static */ bool ProximityInfoStateUtils::suppressCharProbabilities(const int mostCommonKeyWidth,
869d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        const int sampledInputSize, const std::vector<int> *const lengthCache,
870d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        const int index0, const int index1,
8710b1fa0c1c7572893365c019780357a817158e5eaKen Wakasa        std::vector<std::unordered_map<int, float>> *charProbabilities) {
872d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka    ASSERT(0 <= index0 && index0 < sampledInputSize);
873d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka    ASSERT(0 <= index1 && index1 < sampledInputSize);
874d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka    const float keyWidthFloat = static_cast<float>(mostCommonKeyWidth);
875d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka    const float diff = fabsf(static_cast<float>((*lengthCache)[index0] - (*lengthCache)[index1]));
87628c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa    if (diff > keyWidthFloat * ProximityInfoParams::SUPPRESSION_LENGTH_WEIGHT) {
877d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        return false;
878d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka    }
87928c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa    const float suppressionRate = ProximityInfoParams::MIN_SUPPRESSION_RATE
88028c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa            + diff / keyWidthFloat / ProximityInfoParams::SUPPRESSION_LENGTH_WEIGHT
88128c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa                    * ProximityInfoParams::SUPPRESSION_WEIGHT;
8828ca9be17db2f1845c7c7a3b584507cf60c9ca53dKen Wakasa    for (std::unordered_map<int, float>::iterator it = (*charProbabilities)[index0].begin();
883d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka            it != (*charProbabilities)[index0].end(); ++it) {
8848ca9be17db2f1845c7c7a3b584507cf60c9ca53dKen Wakasa        std::unordered_map<int, float>::iterator it2 = (*charProbabilities)[index1].find(it->first);
885d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        if (it2 != (*charProbabilities)[index1].end() && it->second < it2->second) {
886d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka            const float newProbability = it->second * suppressionRate;
887d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka            const float suppression = it->second - newProbability;
888d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka            it->second = newProbability;
889d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka            // mCharProbabilities[index0][NOT_AN_INDEX] is the probability of skipping this point.
890d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka            (*charProbabilities)[index0][NOT_AN_INDEX] += suppression;
891d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka
892d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka            // Add the probability of the same key nearby index1
893865e6cf49764f3a411ee32861d927b15653ee398Keisuke Kuroyanagi            const float probabilityGain = std::min(suppression
89428c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa                    * ProximityInfoParams::SUPPRESSION_WEIGHT_FOR_PROBABILITY_GAIN,
895d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka                    (*charProbabilities)[index1][NOT_AN_INDEX]
89628c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa                            * ProximityInfoParams::SKIP_PROBABALITY_WEIGHT_FOR_PROBABILITY_GAIN);
897d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka            it2->second += probabilityGain;
898d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka            (*charProbabilities)[index1][NOT_AN_INDEX] -= probabilityGain;
899d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka        }
900d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka    }
901d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka    return true;
902d4828d5053ac30476b884c177235be0cac982c92Satoshi Kataoka}
903d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka
904394b0bd345f33b1314613a433478fd0bb711e0f7Satoshi Kataoka/* static */ bool ProximityInfoStateUtils::checkAndReturnIsContinuousSuggestionPossible(
905394b0bd345f33b1314613a433478fd0bb711e0f7Satoshi Kataoka        const int inputSize, const int *const xCoordinates, const int *const yCoordinates,
906394b0bd345f33b1314613a433478fd0bb711e0f7Satoshi Kataoka        const int *const times, const int sampledInputSize,
907394b0bd345f33b1314613a433478fd0bb711e0f7Satoshi Kataoka        const std::vector<int> *const sampledInputXs, const std::vector<int> *const sampledInputYs,
9089d18c6dd38c4d5632a5d5a5c26f567b9f6f7f969Satoshi Kataoka        const std::vector<int> *const sampledTimes,
9099d18c6dd38c4d5632a5d5a5c26f567b9f6f7f969Satoshi Kataoka        const std::vector<int> *const sampledInputIndices) {
9109d18c6dd38c4d5632a5d5a5c26f567b9f6f7f969Satoshi Kataoka    if (inputSize < sampledInputSize) {
9119d18c6dd38c4d5632a5d5a5c26f567b9f6f7f969Satoshi Kataoka        return false;
9129d18c6dd38c4d5632a5d5a5c26f567b9f6f7f969Satoshi Kataoka    }
9139d18c6dd38c4d5632a5d5a5c26f567b9f6f7f969Satoshi Kataoka    for (int i = 0; i < sampledInputSize; ++i) {
9149d18c6dd38c4d5632a5d5a5c26f567b9f6f7f969Satoshi Kataoka        const int index = (*sampledInputIndices)[i];
9159d18c6dd38c4d5632a5d5a5c26f567b9f6f7f969Satoshi Kataoka        if (index >= inputSize) {
9169d18c6dd38c4d5632a5d5a5c26f567b9f6f7f969Satoshi Kataoka            return false;
9179d18c6dd38c4d5632a5d5a5c26f567b9f6f7f969Satoshi Kataoka        }
9189d18c6dd38c4d5632a5d5a5c26f567b9f6f7f969Satoshi Kataoka        if (xCoordinates[index] != (*sampledInputXs)[i]
9199d18c6dd38c4d5632a5d5a5c26f567b9f6f7f969Satoshi Kataoka                || yCoordinates[index] != (*sampledInputYs)[i]) {
9209d18c6dd38c4d5632a5d5a5c26f567b9f6f7f969Satoshi Kataoka            return false;
9219d18c6dd38c4d5632a5d5a5c26f567b9f6f7f969Satoshi Kataoka        }
9229d18c6dd38c4d5632a5d5a5c26f567b9f6f7f969Satoshi Kataoka        if (!times) {
9239d18c6dd38c4d5632a5d5a5c26f567b9f6f7f969Satoshi Kataoka            continue;
9249d18c6dd38c4d5632a5d5a5c26f567b9f6f7f969Satoshi Kataoka        }
9259d18c6dd38c4d5632a5d5a5c26f567b9f6f7f969Satoshi Kataoka        if (times[index] != (*sampledTimes)[i]) {
9269d18c6dd38c4d5632a5d5a5c26f567b9f6f7f969Satoshi Kataoka            return false;
9279d18c6dd38c4d5632a5d5a5c26f567b9f6f7f969Satoshi Kataoka        }
9289d18c6dd38c4d5632a5d5a5c26f567b9f6f7f969Satoshi Kataoka    }
9299d18c6dd38c4d5632a5d5a5c26f567b9f6f7f969Satoshi Kataoka    return true;
9309d18c6dd38c4d5632a5d5a5c26f567b9f6f7f969Satoshi Kataoka}
9319d18c6dd38c4d5632a5d5a5c26f567b9f6f7f969Satoshi Kataoka
93220b6775acc957896bdb038dfd99794d6cd7cea5aSatoshi Kataoka// Get a word that is detected by tracing the most probable string into codePointBuf and
93320b6775acc957896bdb038dfd99794d6cd7cea5aSatoshi Kataoka// returns probability of generating the word.
93420b6775acc957896bdb038dfd99794d6cd7cea5aSatoshi Kataoka/* static */ float ProximityInfoStateUtils::getMostProbableString(
93520b6775acc957896bdb038dfd99794d6cd7cea5aSatoshi Kataoka        const ProximityInfo *const proximityInfo, const int sampledInputSize,
9360b1fa0c1c7572893365c019780357a817158e5eaKen Wakasa        const std::vector<std::unordered_map<int, float>> *const charProbabilities,
93720b6775acc957896bdb038dfd99794d6cd7cea5aSatoshi Kataoka        int *const codePointBuf) {
938a8ce88bf447c7de1ec7c35130d7cec8be63633cfKen Wakasa    ASSERT(sampledInputSize >= 0);
93920b6775acc957896bdb038dfd99794d6cd7cea5aSatoshi Kataoka    memset(codePointBuf, 0, sizeof(codePointBuf[0]) * MAX_WORD_LENGTH);
94020b6775acc957896bdb038dfd99794d6cd7cea5aSatoshi Kataoka    int index = 0;
94120b6775acc957896bdb038dfd99794d6cd7cea5aSatoshi Kataoka    float sumLogProbability = 0.0f;
94220b6775acc957896bdb038dfd99794d6cd7cea5aSatoshi Kataoka    // TODO: Current implementation is greedy algorithm. DP would be efficient for many cases.
94320b6775acc957896bdb038dfd99794d6cd7cea5aSatoshi Kataoka    for (int i = 0; i < sampledInputSize && index < MAX_WORD_LENGTH - 1; ++i) {
944830ba67498c6da53b38212dd9ac5ba318a00de11Satoshi Kataoka        float minLogProbability = static_cast<float>(MAX_VALUE_FOR_WEIGHTING);
94520b6775acc957896bdb038dfd99794d6cd7cea5aSatoshi Kataoka        int character = NOT_AN_INDEX;
9468ca9be17db2f1845c7c7a3b584507cf60c9ca53dKen Wakasa        for (std::unordered_map<int, float>::const_iterator it = (*charProbabilities)[i].begin();
94720b6775acc957896bdb038dfd99794d6cd7cea5aSatoshi Kataoka                it != (*charProbabilities)[i].end(); ++it) {
94820b6775acc957896bdb038dfd99794d6cd7cea5aSatoshi Kataoka            const float logProbability = (it->first != NOT_AN_INDEX)
94928c008421cc5d97da8e470dbc934a2891daf9997Ken Wakasa                    ? it->second + ProximityInfoParams::DEMOTION_LOG_PROBABILITY : it->second;
95020b6775acc957896bdb038dfd99794d6cd7cea5aSatoshi Kataoka            if (logProbability < minLogProbability) {
95120b6775acc957896bdb038dfd99794d6cd7cea5aSatoshi Kataoka                minLogProbability = logProbability;
95220b6775acc957896bdb038dfd99794d6cd7cea5aSatoshi Kataoka                character = it->first;
95320b6775acc957896bdb038dfd99794d6cd7cea5aSatoshi Kataoka            }
95420b6775acc957896bdb038dfd99794d6cd7cea5aSatoshi Kataoka        }
95520b6775acc957896bdb038dfd99794d6cd7cea5aSatoshi Kataoka        if (character != NOT_AN_INDEX) {
95633e38096ad68164a4033eb5a77df75a532f3a7ecKeisuke Kuroyanagi            const int codePoint = proximityInfo->getCodePointOf(character);
95733e38096ad68164a4033eb5a77df75a532f3a7ecKeisuke Kuroyanagi            if (codePoint == NOT_A_CODE_POINT) {
95833e38096ad68164a4033eb5a77df75a532f3a7ecKeisuke Kuroyanagi                AKLOGE("Key index(%d) is not found. Cannot construct most probable string",
95933e38096ad68164a4033eb5a77df75a532f3a7ecKeisuke Kuroyanagi                        character);
96033e38096ad68164a4033eb5a77df75a532f3a7ecKeisuke Kuroyanagi                ASSERT(false);
96133e38096ad68164a4033eb5a77df75a532f3a7ecKeisuke Kuroyanagi                // Make the length zero, which means most probable string won't be used.
96233e38096ad68164a4033eb5a77df75a532f3a7ecKeisuke Kuroyanagi                index = 0;
96333e38096ad68164a4033eb5a77df75a532f3a7ecKeisuke Kuroyanagi                break;
96433e38096ad68164a4033eb5a77df75a532f3a7ecKeisuke Kuroyanagi            }
96533e38096ad68164a4033eb5a77df75a532f3a7ecKeisuke Kuroyanagi            codePointBuf[index] = codePoint;
96620b6775acc957896bdb038dfd99794d6cd7cea5aSatoshi Kataoka            index++;
96720b6775acc957896bdb038dfd99794d6cd7cea5aSatoshi Kataoka        }
96820b6775acc957896bdb038dfd99794d6cd7cea5aSatoshi Kataoka        sumLogProbability += minLogProbability;
96920b6775acc957896bdb038dfd99794d6cd7cea5aSatoshi Kataoka    }
97020b6775acc957896bdb038dfd99794d6cd7cea5aSatoshi Kataoka    codePointBuf[index] = '\0';
97120b6775acc957896bdb038dfd99794d6cd7cea5aSatoshi Kataoka    return sumLogProbability;
97220b6775acc957896bdb038dfd99794d6cd7cea5aSatoshi Kataoka}
97320b6775acc957896bdb038dfd99794d6cd7cea5aSatoshi Kataoka
974d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka/* static */ void ProximityInfoStateUtils::dump(const bool isGeometric, const int inputSize,
975d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka        const int *const inputXCoordinates, const int *const inputYCoordinates,
976d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka        const int sampledInputSize, const std::vector<int> *const sampledInputXs,
977d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka        const std::vector<int> *const sampledInputYs,
978e2912d17e4dab75b81f4c9e41a539e491ac059caSatoshi Kataoka        const std::vector<int> *const sampledTimes,
979d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka        const std::vector<float> *const sampledSpeedRates,
980d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka        const std::vector<int> *const sampledBeelineSpeedPercentiles) {
981d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka    if (DEBUG_GEO_FULL) {
982d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka        for (int i = 0; i < sampledInputSize; ++i) {
983e2912d17e4dab75b81f4c9e41a539e491ac059caSatoshi Kataoka            AKLOGI("Sampled(%d): x = %d, y = %d, time = %d", i, (*sampledInputXs)[i],
984e2912d17e4dab75b81f4c9e41a539e491ac059caSatoshi Kataoka                    (*sampledInputYs)[i], sampledTimes ? (*sampledTimes)[i] : -1);
985d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka        }
986d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka    }
987d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka
988d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka    std::stringstream originalX, originalY, sampledX, sampledY;
989d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka    for (int i = 0; i < inputSize; ++i) {
990d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka        originalX << inputXCoordinates[i];
991d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka        originalY << inputYCoordinates[i];
992d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka        if (i != inputSize - 1) {
993d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka            originalX << ";";
994d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka            originalY << ";";
995d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka        }
996d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka    }
997d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka    AKLOGI("===== sampled points =====");
998d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka    for (int i = 0; i < sampledInputSize; ++i) {
999d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka        if (isGeometric) {
1000d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka            AKLOGI("%d: x = %d, y = %d, time = %d, relative speed = %.4f, beeline speed = %d",
1001e2912d17e4dab75b81f4c9e41a539e491ac059caSatoshi Kataoka                    i, (*sampledInputXs)[i], (*sampledInputYs)[i], (*sampledTimes)[i],
1002e2912d17e4dab75b81f4c9e41a539e491ac059caSatoshi Kataoka                    (*sampledSpeedRates)[i], (*sampledBeelineSpeedPercentiles)[i]);
1003d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka        }
1004d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka        sampledX << (*sampledInputXs)[i];
1005d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka        sampledY << (*sampledInputYs)[i];
1006d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka        if (i != sampledInputSize - 1) {
1007d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka            sampledX << ";";
1008d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka            sampledY << ";";
1009d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka        }
1010d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka    }
1011d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka    AKLOGI("original points:\n%s, %s,\nsampled points:\n%s, %s,\n",
1012d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka            originalX.str().c_str(), originalY.str().c_str(), sampledX.str().c_str(),
1013d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka            sampledY.str().c_str());
1014d7a8fbf6a9ec8828d4b6d1c615a6c605bbe5b72eSatoshi Kataoka}
1015ee62b78c9675bddaf2437e0cf521f6115e1d9febSatoshi Kataoka} // namespace latinime
1016