1 /* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "suggest/core/layout/proximity_info_state_utils.h" 18 19 #include <cmath> 20 #include <cstring> // for memset() 21 #include <sstream> // for debug prints 22 #include <vector> 23 24 #include "defines.h" 25 #include "suggest/core/layout/geometry_utils.h" 26 #include "suggest/core/layout/proximity_info.h" 27 #include "suggest/core/layout/proximity_info_params.h" 28 29 namespace latinime { 30 31 /* static */ int ProximityInfoStateUtils::trimLastTwoTouchPoints(std::vector<int> *sampledInputXs, 32 std::vector<int> *sampledInputYs, std::vector<int> *sampledInputTimes, 33 std::vector<int> *sampledLengthCache, std::vector<int> *sampledInputIndice) { 34 const int nextStartIndex = (*sampledInputIndice)[sampledInputIndice->size() - 2]; 35 popInputData(sampledInputXs, sampledInputYs, sampledInputTimes, sampledLengthCache, 36 sampledInputIndice); 37 popInputData(sampledInputXs, sampledInputYs, sampledInputTimes, sampledLengthCache, 38 sampledInputIndice); 39 return nextStartIndex; 40 } 41 42 /* static */ int ProximityInfoStateUtils::updateTouchPoints( 43 const ProximityInfo *const proximityInfo, const int maxPointToKeyLength, 44 const int *const inputProximities, const int *const inputXCoordinates, 45 const int *const inputYCoordinates, const int *const times, const int *const pointerIds, 46 const int inputSize, const bool isGeometric, const int pointerId, 47 const int pushTouchPointStartIndex, std::vector<int> *sampledInputXs, 48 std::vector<int> *sampledInputYs, std::vector<int> *sampledInputTimes, 49 std::vector<int> *sampledLengthCache, std::vector<int> *sampledInputIndice) { 50 if (DEBUG_SAMPLING_POINTS) { 51 if (times) { 52 for (int i = 0; i < inputSize; ++i) { 53 AKLOGI("(%d) x %d, y %d, time %d", 54 i, inputXCoordinates[i], inputYCoordinates[i], times[i]); 55 } 56 } 57 } 58 #ifdef DO_ASSERT_TEST 59 if (times) { 60 for (int i = 0; i < inputSize; ++i) { 61 if (i > 0) { 62 if (times[i] < times[i - 1]) { 63 AKLOGI("Invalid time sequence. %d, %d", times[i - 1], times[i]); 64 ASSERT(false); 65 } 66 } 67 } 68 } 69 #endif 70 const bool proximityOnly = !isGeometric 71 && (inputXCoordinates[0] < 0 || inputYCoordinates[0] < 0); 72 int lastInputIndex = pushTouchPointStartIndex; 73 for (int i = lastInputIndex; i < inputSize; ++i) { 74 const int pid = pointerIds ? pointerIds[i] : 0; 75 if (pointerId == pid) { 76 lastInputIndex = i; 77 } 78 } 79 if (DEBUG_GEO_FULL) { 80 AKLOGI("Init ProximityInfoState: last input index = %d", lastInputIndex); 81 } 82 // Working space to save near keys distances for current, prev and prevprev input point. 83 NearKeysDistanceMap nearKeysDistances[3]; 84 // These pointers are swapped for each inputs points. 85 NearKeysDistanceMap *currentNearKeysDistances = &nearKeysDistances[0]; 86 NearKeysDistanceMap *prevNearKeysDistances = &nearKeysDistances[1]; 87 NearKeysDistanceMap *prevPrevNearKeysDistances = &nearKeysDistances[2]; 88 // "sumAngle" is accumulated by each angle of input points. And when "sumAngle" exceeds 89 // the threshold we save that point, reset sumAngle. This aims to keep the figure of 90 // the curve. 91 float sumAngle = 0.0f; 92 93 for (int i = pushTouchPointStartIndex; i <= lastInputIndex; ++i) { 94 // Assuming pointerId == 0 if pointerIds is null. 95 const int pid = pointerIds ? pointerIds[i] : 0; 96 if (DEBUG_GEO_FULL) { 97 AKLOGI("Init ProximityInfoState: (%d)PID = %d", i, pid); 98 } 99 if (pointerId == pid) { 100 const int c = isGeometric ? 101 NOT_A_COORDINATE : getPrimaryCodePointAt(inputProximities, i); 102 const int x = proximityOnly ? NOT_A_COORDINATE : inputXCoordinates[i]; 103 const int y = proximityOnly ? NOT_A_COORDINATE : inputYCoordinates[i]; 104 const int time = times ? times[i] : -1; 105 106 if (i > 1) { 107 const float prevAngle = GeometryUtils::getAngle( 108 inputXCoordinates[i - 2], inputYCoordinates[i - 2], 109 inputXCoordinates[i - 1], inputYCoordinates[i - 1]); 110 const float currentAngle = GeometryUtils::getAngle( 111 inputXCoordinates[i - 1], inputYCoordinates[i - 1], x, y); 112 sumAngle += GeometryUtils::getAngleDiff(prevAngle, currentAngle); 113 } 114 115 if (pushTouchPoint(proximityInfo, maxPointToKeyLength, i, c, x, y, time, 116 isGeometric, isGeometric /* doSampling */, i == lastInputIndex, 117 sumAngle, currentNearKeysDistances, prevNearKeysDistances, 118 prevPrevNearKeysDistances, sampledInputXs, sampledInputYs, sampledInputTimes, 119 sampledLengthCache, sampledInputIndice)) { 120 // Previous point information was popped. 121 NearKeysDistanceMap *tmp = prevNearKeysDistances; 122 prevNearKeysDistances = currentNearKeysDistances; 123 currentNearKeysDistances = tmp; 124 } else { 125 NearKeysDistanceMap *tmp = prevPrevNearKeysDistances; 126 prevPrevNearKeysDistances = prevNearKeysDistances; 127 prevNearKeysDistances = currentNearKeysDistances; 128 currentNearKeysDistances = tmp; 129 sumAngle = 0.0f; 130 } 131 } 132 } 133 return sampledInputXs->size(); 134 } 135 136 /* static */ const int *ProximityInfoStateUtils::getProximityCodePointsAt( 137 const int *const inputProximities, const int index) { 138 return inputProximities + (index * MAX_PROXIMITY_CHARS_SIZE); 139 } 140 141 /* static */ int ProximityInfoStateUtils::getPrimaryCodePointAt(const int *const inputProximities, 142 const int index) { 143 return getProximityCodePointsAt(inputProximities, index)[0]; 144 } 145 146 /* static */ void ProximityInfoStateUtils::initPrimaryInputWord(const int inputSize, 147 const int *const inputProximities, int *primaryInputWord) { 148 memset(primaryInputWord, 0, sizeof(primaryInputWord[0]) * MAX_WORD_LENGTH); 149 for (int i = 0; i < inputSize; ++i) { 150 primaryInputWord[i] = getPrimaryCodePointAt(inputProximities, i); 151 } 152 } 153 154 /* static */ float ProximityInfoStateUtils::calculateSquaredDistanceFromSweetSpotCenter( 155 const ProximityInfo *const proximityInfo, const std::vector<int> *const sampledInputXs, 156 const std::vector<int> *const sampledInputYs, const int keyIndex, const int inputIndex) { 157 const float sweetSpotCenterX = proximityInfo->getSweetSpotCenterXAt(keyIndex); 158 const float sweetSpotCenterY = proximityInfo->getSweetSpotCenterYAt(keyIndex); 159 const float inputX = static_cast<float>((*sampledInputXs)[inputIndex]); 160 const float inputY = static_cast<float>((*sampledInputYs)[inputIndex]); 161 return GeometryUtils::SQUARE_FLOAT(inputX - sweetSpotCenterX) 162 + GeometryUtils::SQUARE_FLOAT(inputY - sweetSpotCenterY); 163 } 164 165 /* static */ float ProximityInfoStateUtils::calculateNormalizedSquaredDistance( 166 const ProximityInfo *const proximityInfo, const std::vector<int> *const sampledInputXs, 167 const std::vector<int> *const sampledInputYs, const int keyIndex, const int inputIndex) { 168 if (keyIndex == NOT_AN_INDEX) { 169 return ProximityInfoParams::NOT_A_DISTANCE_FLOAT; 170 } 171 if (!proximityInfo->hasSweetSpotData(keyIndex)) { 172 return ProximityInfoParams::NOT_A_DISTANCE_FLOAT; 173 } 174 if (NOT_A_COORDINATE == (*sampledInputXs)[inputIndex]) { 175 return ProximityInfoParams::NOT_A_DISTANCE_FLOAT; 176 } 177 const float squaredDistance = calculateSquaredDistanceFromSweetSpotCenter(proximityInfo, 178 sampledInputXs, sampledInputYs, keyIndex, inputIndex); 179 const float squaredRadius = GeometryUtils::SQUARE_FLOAT( 180 proximityInfo->getSweetSpotRadiiAt(keyIndex)); 181 return squaredDistance / squaredRadius; 182 } 183 184 /* static */ void ProximityInfoStateUtils::initGeometricDistanceInfos( 185 const ProximityInfo *const proximityInfo, const int sampledInputSize, 186 const int lastSavedInputSize, const bool isGeometric, 187 const std::vector<int> *const sampledInputXs, 188 const std::vector<int> *const sampledInputYs, 189 std::vector<NearKeycodesSet> *sampledNearKeySets, 190 std::vector<float> *sampledNormalizedSquaredLengthCache) { 191 sampledNearKeySets->resize(sampledInputSize); 192 const int keyCount = proximityInfo->getKeyCount(); 193 sampledNormalizedSquaredLengthCache->resize(sampledInputSize * keyCount); 194 for (int i = lastSavedInputSize; i < sampledInputSize; ++i) { 195 (*sampledNearKeySets)[i].reset(); 196 for (int k = 0; k < keyCount; ++k) { 197 const int index = i * keyCount + k; 198 const int x = (*sampledInputXs)[i]; 199 const int y = (*sampledInputYs)[i]; 200 const float normalizedSquaredDistance = 201 proximityInfo->getNormalizedSquaredDistanceFromCenterFloatG( 202 k, x, y, isGeometric); 203 (*sampledNormalizedSquaredLengthCache)[index] = normalizedSquaredDistance; 204 if (normalizedSquaredDistance 205 < ProximityInfoParams::NEAR_KEY_NORMALIZED_SQUARED_THRESHOLD) { 206 (*sampledNearKeySets)[i][k] = true; 207 } 208 } 209 } 210 } 211 212 /* static */ void ProximityInfoStateUtils::popInputData(std::vector<int> *sampledInputXs, 213 std::vector<int> *sampledInputYs, std::vector<int> *sampledInputTimes, 214 std::vector<int> *sampledLengthCache, std::vector<int> *sampledInputIndice) { 215 sampledInputXs->pop_back(); 216 sampledInputYs->pop_back(); 217 sampledInputTimes->pop_back(); 218 sampledLengthCache->pop_back(); 219 sampledInputIndice->pop_back(); 220 } 221 222 /* static */ float ProximityInfoStateUtils::refreshSpeedRates(const int inputSize, 223 const int *const xCoordinates, const int *const yCoordinates, const int *const times, 224 const int lastSavedInputSize, const int sampledInputSize, 225 const std::vector<int> *const sampledInputXs, const std::vector<int> *const sampledInputYs, 226 const std::vector<int> *const sampledInputTimes, 227 const std::vector<int> *const sampledLengthCache, 228 const std::vector<int> *const sampledInputIndice, std::vector<float> *sampledSpeedRates, 229 std::vector<float> *sampledDirections) { 230 // Relative speed calculation. 231 const int sumDuration = sampledInputTimes->back() - sampledInputTimes->front(); 232 const int sumLength = sampledLengthCache->back() - sampledLengthCache->front(); 233 const float averageSpeed = static_cast<float>(sumLength) / static_cast<float>(sumDuration); 234 sampledSpeedRates->resize(sampledInputSize); 235 for (int i = lastSavedInputSize; i < sampledInputSize; ++i) { 236 const int index = (*sampledInputIndice)[i]; 237 int length = 0; 238 int duration = 0; 239 240 // Calculate velocity by using distances and durations of 241 // ProximityInfoParams::NUM_POINTS_FOR_SPEED_CALCULATION points for both forward and 242 // backward. 243 const int forwardNumPoints = min(inputSize - 1, 244 index + ProximityInfoParams::NUM_POINTS_FOR_SPEED_CALCULATION); 245 for (int j = index; j < forwardNumPoints; ++j) { 246 if (i < sampledInputSize - 1 && j >= (*sampledInputIndice)[i + 1]) { 247 break; 248 } 249 length += GeometryUtils::getDistanceInt(xCoordinates[j], yCoordinates[j], 250 xCoordinates[j + 1], yCoordinates[j + 1]); 251 duration += times[j + 1] - times[j]; 252 } 253 const int backwardNumPoints = max(0, 254 index - ProximityInfoParams::NUM_POINTS_FOR_SPEED_CALCULATION); 255 for (int j = index - 1; j >= backwardNumPoints; --j) { 256 if (i > 0 && j < (*sampledInputIndice)[i - 1]) { 257 break; 258 } 259 // TODO: use mSampledLengthCache instead? 260 length += GeometryUtils::getDistanceInt(xCoordinates[j], yCoordinates[j], 261 xCoordinates[j + 1], yCoordinates[j + 1]); 262 duration += times[j + 1] - times[j]; 263 } 264 if (duration == 0 || sumDuration == 0) { 265 // Cannot calculate speed; thus, it gives an average value (1.0); 266 (*sampledSpeedRates)[i] = 1.0f; 267 } else { 268 const float speed = static_cast<float>(length) / static_cast<float>(duration); 269 (*sampledSpeedRates)[i] = speed / averageSpeed; 270 } 271 } 272 273 // Direction calculation. 274 sampledDirections->resize(sampledInputSize - 1); 275 for (int i = max(0, lastSavedInputSize - 1); i < sampledInputSize - 1; ++i) { 276 (*sampledDirections)[i] = getDirection(sampledInputXs, sampledInputYs, i, i + 1); 277 } 278 return averageSpeed; 279 } 280 281 /* static */ void ProximityInfoStateUtils::refreshBeelineSpeedRates(const int mostCommonKeyWidth, 282 const float averageSpeed, const int inputSize, const int *const xCoordinates, 283 const int *const yCoordinates, const int *times, const int sampledInputSize, 284 const std::vector<int> *const sampledInputXs, 285 const std::vector<int> *const sampledInputYs, const std::vector<int> *const inputIndice, 286 std::vector<int> *beelineSpeedPercentiles) { 287 if (DEBUG_SAMPLING_POINTS) { 288 AKLOGI("--- refresh beeline speed rates"); 289 } 290 beelineSpeedPercentiles->resize(sampledInputSize); 291 for (int i = 0; i < sampledInputSize; ++i) { 292 (*beelineSpeedPercentiles)[i] = static_cast<int>(calculateBeelineSpeedRate( 293 mostCommonKeyWidth, averageSpeed, i, inputSize, xCoordinates, yCoordinates, times, 294 sampledInputSize, sampledInputXs, sampledInputYs, inputIndice) * MAX_PERCENTILE); 295 } 296 } 297 298 /* static */float ProximityInfoStateUtils::getDirection( 299 const std::vector<int> *const sampledInputXs, 300 const std::vector<int> *const sampledInputYs, const int index0, const int index1) { 301 ASSERT(sampledInputXs && sampledInputYs); 302 const int sampledInputSize =sampledInputXs->size(); 303 if (index0 < 0 || index0 > sampledInputSize - 1) { 304 return 0.0f; 305 } 306 if (index1 < 0 || index1 > sampledInputSize - 1) { 307 return 0.0f; 308 } 309 const int x1 = (*sampledInputXs)[index0]; 310 const int y1 = (*sampledInputYs)[index0]; 311 const int x2 = (*sampledInputXs)[index1]; 312 const int y2 = (*sampledInputYs)[index1]; 313 return GeometryUtils::getAngle(x1, y1, x2, y2); 314 } 315 316 // Calculating point to key distance for all near keys and returning the distance between 317 // the given point and the nearest key position. 318 /* static */ float ProximityInfoStateUtils::updateNearKeysDistances( 319 const ProximityInfo *const proximityInfo, const float maxPointToKeyLength, const int x, 320 const int y, const bool isGeometric, NearKeysDistanceMap *const currentNearKeysDistances) { 321 currentNearKeysDistances->clear(); 322 const int keyCount = proximityInfo->getKeyCount(); 323 float nearestKeyDistance = maxPointToKeyLength; 324 for (int k = 0; k < keyCount; ++k) { 325 const float dist = proximityInfo->getNormalizedSquaredDistanceFromCenterFloatG(k, x, y, 326 isGeometric); 327 if (dist < ProximityInfoParams::NEAR_KEY_THRESHOLD_FOR_DISTANCE) { 328 currentNearKeysDistances->insert(std::pair<int, float>(k, dist)); 329 } 330 if (nearestKeyDistance > dist) { 331 nearestKeyDistance = dist; 332 } 333 } 334 return nearestKeyDistance; 335 } 336 337 // Check if previous point is at local minimum position to near keys. 338 /* static */ bool ProximityInfoStateUtils::isPrevLocalMin( 339 const NearKeysDistanceMap *const currentNearKeysDistances, 340 const NearKeysDistanceMap *const prevNearKeysDistances, 341 const NearKeysDistanceMap *const prevPrevNearKeysDistances) { 342 for (NearKeysDistanceMap::const_iterator it = prevNearKeysDistances->begin(); 343 it != prevNearKeysDistances->end(); ++it) { 344 NearKeysDistanceMap::const_iterator itPP = prevPrevNearKeysDistances->find(it->first); 345 NearKeysDistanceMap::const_iterator itC = currentNearKeysDistances->find(it->first); 346 const bool isPrevPrevNear = (itPP == prevPrevNearKeysDistances->end() 347 || itPP->second > it->second + ProximityInfoParams::MARGIN_FOR_PREV_LOCAL_MIN); 348 const bool isCurrentNear = (itC == currentNearKeysDistances->end() 349 || itC->second > it->second + ProximityInfoParams::MARGIN_FOR_PREV_LOCAL_MIN); 350 if (isPrevPrevNear && isCurrentNear) { 351 return true; 352 } 353 } 354 return false; 355 } 356 357 // Calculating a point score that indicates usefulness of the point. 358 /* static */ float ProximityInfoStateUtils::getPointScore(const int mostCommonKeyWidth, 359 const int x, const int y, const int time, const bool lastPoint, const float nearest, 360 const float sumAngle, const NearKeysDistanceMap *const currentNearKeysDistances, 361 const NearKeysDistanceMap *const prevNearKeysDistances, 362 const NearKeysDistanceMap *const prevPrevNearKeysDistances, 363 std::vector<int> *sampledInputXs, std::vector<int> *sampledInputYs) { 364 const size_t size = sampledInputXs->size(); 365 // If there is only one point, add this point. Besides, if the previous point's distance map 366 // is empty, we re-compute nearby keys distances from the current point. 367 // Note that the current point is the first point in the incremental input that needs to 368 // be re-computed. 369 if (size <= 1 || prevNearKeysDistances->empty()) { 370 return 0.0f; 371 } 372 373 const int baseSampleRate = mostCommonKeyWidth; 374 const int distPrev = GeometryUtils::getDistanceInt(sampledInputXs->back(), 375 sampledInputYs->back(), (*sampledInputXs)[size - 2], 376 (*sampledInputYs)[size - 2]) * ProximityInfoParams::DISTANCE_BASE_SCALE; 377 float score = 0.0f; 378 379 // Location 380 if (!isPrevLocalMin(currentNearKeysDistances, prevNearKeysDistances, 381 prevPrevNearKeysDistances)) { 382 score += ProximityInfoParams::NOT_LOCALMIN_DISTANCE_SCORE; 383 } else if (nearest < ProximityInfoParams::NEAR_KEY_THRESHOLD_FOR_POINT_SCORE) { 384 // Promote points nearby keys 385 score += ProximityInfoParams::LOCALMIN_DISTANCE_AND_NEAR_TO_KEY_SCORE; 386 } 387 // Angle 388 const float angle1 = GeometryUtils::getAngle(x, y, sampledInputXs->back(), 389 sampledInputYs->back()); 390 const float angle2 = GeometryUtils::getAngle(sampledInputXs->back(), sampledInputYs->back(), 391 (*sampledInputXs)[size - 2], (*sampledInputYs)[size - 2]); 392 const float angleDiff = GeometryUtils::getAngleDiff(angle1, angle2); 393 394 // Save corner 395 if (distPrev > baseSampleRate * ProximityInfoParams::CORNER_CHECK_DISTANCE_THRESHOLD_SCALE 396 && (sumAngle > ProximityInfoParams::CORNER_SUM_ANGLE_THRESHOLD 397 || angleDiff > ProximityInfoParams::CORNER_ANGLE_THRESHOLD_FOR_POINT_SCORE)) { 398 score += ProximityInfoParams::CORNER_SCORE; 399 } 400 return score; 401 } 402 403 // Sampling touch point and pushing information to vectors. 404 // Returning if previous point is popped or not. 405 /* static */ bool ProximityInfoStateUtils::pushTouchPoint(const ProximityInfo *const proximityInfo, 406 const int maxPointToKeyLength, const int inputIndex, const int nodeCodePoint, int x, int y, 407 const int time, const bool isGeometric, const bool doSampling, 408 const bool isLastPoint, const float sumAngle, 409 NearKeysDistanceMap *const currentNearKeysDistances, 410 const NearKeysDistanceMap *const prevNearKeysDistances, 411 const NearKeysDistanceMap *const prevPrevNearKeysDistances, 412 std::vector<int> *sampledInputXs, std::vector<int> *sampledInputYs, 413 std::vector<int> *sampledInputTimes, std::vector<int> *sampledLengthCache, 414 std::vector<int> *sampledInputIndice) { 415 const int mostCommonKeyWidth = proximityInfo->getMostCommonKeyWidth(); 416 417 size_t size = sampledInputXs->size(); 418 bool popped = false; 419 if (nodeCodePoint < 0 && doSampling) { 420 const float nearest = updateNearKeysDistances(proximityInfo, maxPointToKeyLength, x, y, 421 isGeometric, currentNearKeysDistances); 422 const float score = getPointScore(mostCommonKeyWidth, x, y, time, isLastPoint, nearest, 423 sumAngle, currentNearKeysDistances, prevNearKeysDistances, 424 prevPrevNearKeysDistances, sampledInputXs, sampledInputYs); 425 if (score < 0) { 426 // Pop previous point because it would be useless. 427 popInputData(sampledInputXs, sampledInputYs, sampledInputTimes, sampledLengthCache, 428 sampledInputIndice); 429 size = sampledInputXs->size(); 430 popped = true; 431 } else { 432 popped = false; 433 } 434 // Check if the last point should be skipped. 435 if (isLastPoint && size > 0) { 436 if (GeometryUtils::getDistanceInt(x, y, sampledInputXs->back(), sampledInputYs->back()) 437 * ProximityInfoParams::LAST_POINT_SKIP_DISTANCE_SCALE < mostCommonKeyWidth) { 438 // This point is not used because it's too close to the previous point. 439 if (DEBUG_GEO_FULL) { 440 AKLOGI("p0: size = %zd, x = %d, y = %d, lx = %d, ly = %d, dist = %d, " 441 "width = %d", size, x, y, sampledInputXs->back(), 442 sampledInputYs->back(), GeometryUtils::getDistanceInt( 443 x, y, sampledInputXs->back(), sampledInputYs->back()), 444 mostCommonKeyWidth 445 / ProximityInfoParams::LAST_POINT_SKIP_DISTANCE_SCALE); 446 } 447 return popped; 448 } 449 } 450 } 451 452 if (nodeCodePoint >= 0 && (x < 0 || y < 0)) { 453 const int keyId = proximityInfo->getKeyIndexOf(nodeCodePoint); 454 if (keyId >= 0) { 455 x = proximityInfo->getKeyCenterXOfKeyIdG(keyId, NOT_AN_INDEX, isGeometric); 456 y = proximityInfo->getKeyCenterYOfKeyIdG(keyId, NOT_AN_INDEX, isGeometric); 457 } 458 } 459 460 // Pushing point information. 461 if (size > 0) { 462 sampledLengthCache->push_back( 463 sampledLengthCache->back() + GeometryUtils::getDistanceInt( 464 x, y, sampledInputXs->back(), sampledInputYs->back())); 465 } else { 466 sampledLengthCache->push_back(0); 467 } 468 sampledInputXs->push_back(x); 469 sampledInputYs->push_back(y); 470 sampledInputTimes->push_back(time); 471 sampledInputIndice->push_back(inputIndex); 472 if (DEBUG_GEO_FULL) { 473 AKLOGI("pushTouchPoint: x = %03d, y = %03d, time = %d, index = %d, popped ? %01d", 474 x, y, time, inputIndex, popped); 475 } 476 return popped; 477 } 478 479 /* static */ float ProximityInfoStateUtils::calculateBeelineSpeedRate(const int mostCommonKeyWidth, 480 const float averageSpeed, const int id, const int inputSize, const int *const xCoordinates, 481 const int *const yCoordinates, const int *times, const int sampledInputSize, 482 const std::vector<int> *const sampledInputXs, 483 const std::vector<int> *const sampledInputYs, 484 const std::vector<int> *const sampledInputIndices) { 485 if (sampledInputSize <= 0 || averageSpeed < 0.001f) { 486 if (DEBUG_SAMPLING_POINTS) { 487 AKLOGI("--- invalid state: cancel. size = %d, ave = %f", 488 sampledInputSize, averageSpeed); 489 } 490 return 1.0f; 491 } 492 const int lookupRadius = mostCommonKeyWidth 493 * ProximityInfoParams::LOOKUP_RADIUS_PERCENTILE / MAX_PERCENTILE; 494 const int x0 = (*sampledInputXs)[id]; 495 const int y0 = (*sampledInputYs)[id]; 496 const int actualInputIndex = (*sampledInputIndices)[id]; 497 int tempTime = 0; 498 int tempBeelineDistance = 0; 499 int start = actualInputIndex; 500 // lookup forward 501 while (start > 0 && tempBeelineDistance < lookupRadius) { 502 tempTime += times[start] - times[start - 1]; 503 --start; 504 tempBeelineDistance = GeometryUtils::getDistanceInt(x0, y0, xCoordinates[start], 505 yCoordinates[start]); 506 } 507 // Exclusive unless this is an edge point 508 if (start > 0 && start < actualInputIndex) { 509 ++start; 510 } 511 tempTime= 0; 512 tempBeelineDistance = 0; 513 int end = actualInputIndex; 514 // lookup backward 515 while (end < (inputSize - 1) && tempBeelineDistance < lookupRadius) { 516 tempTime += times[end + 1] - times[end]; 517 ++end; 518 tempBeelineDistance = GeometryUtils::getDistanceInt(x0, y0, xCoordinates[end], 519 yCoordinates[end]); 520 } 521 // Exclusive unless this is an edge point 522 if (end > actualInputIndex && end < (inputSize - 1)) { 523 --end; 524 } 525 526 if (start >= end) { 527 if (DEBUG_DOUBLE_LETTER) { 528 AKLOGI("--- double letter: start == end %d", start); 529 } 530 return 1.0f; 531 } 532 533 const int x2 = xCoordinates[start]; 534 const int y2 = yCoordinates[start]; 535 const int x3 = xCoordinates[end]; 536 const int y3 = yCoordinates[end]; 537 const int beelineDistance = GeometryUtils::getDistanceInt(x2, y2, x3, y3); 538 int adjustedStartTime = times[start]; 539 if (start == 0 && actualInputIndex == 0 && inputSize > 1) { 540 adjustedStartTime += ProximityInfoParams::FIRST_POINT_TIME_OFFSET_MILLIS; 541 } 542 int adjustedEndTime = times[end]; 543 if (end == (inputSize - 1) && inputSize > 1) { 544 adjustedEndTime -= ProximityInfoParams::FIRST_POINT_TIME_OFFSET_MILLIS; 545 } 546 const int time = adjustedEndTime - adjustedStartTime; 547 if (time <= 0) { 548 return 1.0f; 549 } 550 551 if (time >= ProximityInfoParams::STRONG_DOUBLE_LETTER_TIME_MILLIS){ 552 return 0.0f; 553 } 554 if (DEBUG_DOUBLE_LETTER) { 555 AKLOGI("--- (%d, %d) double letter: start = %d, end = %d, dist = %d, time = %d," 556 " speed = %f, ave = %f, val = %f, start time = %d, end time = %d", 557 id, (*sampledInputIndices)[id], start, end, beelineDistance, time, 558 (static_cast<float>(beelineDistance) / static_cast<float>(time)), averageSpeed, 559 ((static_cast<float>(beelineDistance) / static_cast<float>(time)) 560 / averageSpeed), adjustedStartTime, adjustedEndTime); 561 } 562 // Offset 1% 563 // TODO: Detect double letter more smartly 564 return 0.01f + static_cast<float>(beelineDistance) / static_cast<float>(time) / averageSpeed; 565 } 566 567 /* static */ float ProximityInfoStateUtils::getPointAngle( 568 const std::vector<int> *const sampledInputXs, 569 const std::vector<int> *const sampledInputYs, const int index) { 570 if (!sampledInputXs || !sampledInputYs) { 571 return 0.0f; 572 } 573 const int sampledInputSize = sampledInputXs->size(); 574 if (index <= 0 || index >= sampledInputSize - 1) { 575 return 0.0f; 576 } 577 const float previousDirection = getDirection(sampledInputXs, sampledInputYs, index - 1, index); 578 const float nextDirection = getDirection(sampledInputXs, sampledInputYs, index, index + 1); 579 const float directionDiff = GeometryUtils::getAngleDiff(previousDirection, nextDirection); 580 return directionDiff; 581 } 582 583 /* static */ float ProximityInfoStateUtils::getPointsAngle( 584 const std::vector<int> *const sampledInputXs, 585 const std::vector<int> *const sampledInputYs, 586 const int index0, const int index1, const int index2) { 587 if (!sampledInputXs || !sampledInputYs) { 588 return 0.0f; 589 } 590 const int sampledInputSize = sampledInputXs->size(); 591 if (index0 < 0 || index0 > sampledInputSize - 1) { 592 return 0.0f; 593 } 594 if (index1 < 0 || index1 > sampledInputSize - 1) { 595 return 0.0f; 596 } 597 if (index2 < 0 || index2 > sampledInputSize - 1) { 598 return 0.0f; 599 } 600 const float previousDirection = getDirection(sampledInputXs, sampledInputYs, index0, index1); 601 const float nextDirection = getDirection(sampledInputXs, sampledInputYs, index1, index2); 602 return GeometryUtils::getAngleDiff(previousDirection, nextDirection); 603 } 604 605 // This function basically converts from a length to an edit distance. Accordingly, it's obviously 606 // wrong to compare with mMaxPointToKeyLength. 607 /* static */ float ProximityInfoStateUtils::getPointToKeyByIdLength(const float maxPointToKeyLength, 608 const std::vector<float> *const sampledNormalizedSquaredLengthCache, const int keyCount, 609 const int inputIndex, const int keyId) { 610 if (keyId != NOT_AN_INDEX) { 611 const int index = inputIndex * keyCount + keyId; 612 return min((*sampledNormalizedSquaredLengthCache)[index], maxPointToKeyLength); 613 } 614 // If the char is not a key on the keyboard then return the max length. 615 return static_cast<float>(MAX_VALUE_FOR_WEIGHTING); 616 } 617 618 // Updates probabilities of aligning to some keys and skipping. 619 // Word suggestion should be based on this probabilities. 620 /* static */ void ProximityInfoStateUtils::updateAlignPointProbabilities( 621 const float maxPointToKeyLength, const int mostCommonKeyWidth, const int keyCount, 622 const int start, const int sampledInputSize, const std::vector<int> *const sampledInputXs, 623 const std::vector<int> *const sampledInputYs, 624 const std::vector<float> *const sampledSpeedRates, 625 const std::vector<int> *const sampledLengthCache, 626 const std::vector<float> *const sampledNormalizedSquaredLengthCache, 627 std::vector<NearKeycodesSet> *sampledNearKeySets, 628 std::vector<hash_map_compat<int, float> > *charProbabilities) { 629 charProbabilities->resize(sampledInputSize); 630 // Calculates probabilities of using a point as a correlated point with the character 631 // for each point. 632 for (int i = start; i < sampledInputSize; ++i) { 633 (*charProbabilities)[i].clear(); 634 // First, calculates skip probability. Starts from MAX_SKIP_PROBABILITY. 635 // Note that all values that are multiplied to this probability should be in [0.0, 1.0]; 636 float skipProbability = ProximityInfoParams::MAX_SKIP_PROBABILITY; 637 638 const float currentAngle = getPointAngle(sampledInputXs, sampledInputYs, i); 639 const float speedRate = (*sampledSpeedRates)[i]; 640 641 float nearestKeyDistance = static_cast<float>(MAX_VALUE_FOR_WEIGHTING); 642 for (int j = 0; j < keyCount; ++j) { 643 if ((*sampledNearKeySets)[i].test(j)) { 644 const float distance = getPointToKeyByIdLength( 645 maxPointToKeyLength, sampledNormalizedSquaredLengthCache, keyCount, i, j); 646 if (distance < nearestKeyDistance) { 647 nearestKeyDistance = distance; 648 } 649 } 650 } 651 652 if (i == 0) { 653 skipProbability *= min(1.0f, 654 nearestKeyDistance * ProximityInfoParams::NEAREST_DISTANCE_WEIGHT 655 + ProximityInfoParams::NEAREST_DISTANCE_BIAS); 656 // Promote the first point 657 skipProbability *= ProximityInfoParams::SKIP_FIRST_POINT_PROBABILITY; 658 } else if (i == sampledInputSize - 1) { 659 skipProbability *= min(1.0f, 660 nearestKeyDistance * ProximityInfoParams::NEAREST_DISTANCE_WEIGHT_FOR_LAST 661 + ProximityInfoParams::NEAREST_DISTANCE_BIAS_FOR_LAST); 662 // Promote the last point 663 skipProbability *= ProximityInfoParams::SKIP_LAST_POINT_PROBABILITY; 664 } else { 665 // If the current speed is relatively slower than adjacent keys, we promote this point. 666 if ((*sampledSpeedRates)[i - 1] - ProximityInfoParams::SPEED_MARGIN > speedRate 667 && speedRate 668 < (*sampledSpeedRates)[i + 1] - ProximityInfoParams::SPEED_MARGIN) { 669 if (currentAngle < ProximityInfoParams::CORNER_ANGLE_THRESHOLD) { 670 skipProbability *= min(1.0f, speedRate 671 * ProximityInfoParams::SLOW_STRAIGHT_WEIGHT_FOR_SKIP_PROBABILITY); 672 } else { 673 // If the angle is small enough, we promote this point more. (e.g. pit vs put) 674 skipProbability *= min(1.0f, 675 speedRate * ProximityInfoParams::SPEED_WEIGHT_FOR_SKIP_PROBABILITY 676 + ProximityInfoParams::MIN_SPEED_RATE_FOR_SKIP_PROBABILITY); 677 } 678 } 679 680 skipProbability *= min(1.0f, 681 speedRate * nearestKeyDistance * ProximityInfoParams::NEAREST_DISTANCE_WEIGHT 682 + ProximityInfoParams::NEAREST_DISTANCE_BIAS); 683 684 // Adjusts skip probability by a rate depending on angle. 685 // ANGLE_RATE of skipProbability is adjusted by current angle. 686 skipProbability *= (M_PI_F - currentAngle) / M_PI_F * ProximityInfoParams::ANGLE_WEIGHT 687 + (1.0f - ProximityInfoParams::ANGLE_WEIGHT); 688 if (currentAngle > ProximityInfoParams::DEEP_CORNER_ANGLE_THRESHOLD) { 689 skipProbability *= ProximityInfoParams::SKIP_DEEP_CORNER_PROBABILITY; 690 } 691 // We assume the angle of this point is the angle for point[i], point[i - 2] 692 // and point[i - 3]. The reason why we don't use the angle for point[i], point[i - 1] 693 // and point[i - 2] is this angle can be more affected by the noise. 694 const float prevAngle = getPointsAngle(sampledInputXs, sampledInputYs, i, i - 2, i - 3); 695 if (i >= 3 && prevAngle < ProximityInfoParams::STRAIGHT_ANGLE_THRESHOLD 696 && currentAngle > ProximityInfoParams::CORNER_ANGLE_THRESHOLD) { 697 skipProbability *= ProximityInfoParams::SKIP_CORNER_PROBABILITY; 698 } 699 } 700 701 // probabilities must be in [0.0, ProximityInfoParams::MAX_SKIP_PROBABILITY]; 702 ASSERT(skipProbability >= 0.0f); 703 ASSERT(skipProbability <= ProximityInfoParams::MAX_SKIP_PROBABILITY); 704 (*charProbabilities)[i][NOT_AN_INDEX] = skipProbability; 705 706 // Second, calculates key probabilities by dividing the rest probability 707 // (1.0f - skipProbability). 708 const float inputCharProbability = 1.0f - skipProbability; 709 710 const float speedxAngleRate = min(speedRate * currentAngle / M_PI_F 711 * ProximityInfoParams::SPEEDxANGLE_WEIGHT_FOR_STANDARD_DEVIATION, 712 ProximityInfoParams::MAX_SPEEDxANGLE_RATE_FOR_STANDARD_DEVIATION); 713 const float speedxNearestKeyDistanceRate = min(speedRate * nearestKeyDistance 714 * ProximityInfoParams::SPEEDxNEAREST_WEIGHT_FOR_STANDARD_DEVIATION, 715 ProximityInfoParams::MAX_SPEEDxNEAREST_RATE_FOR_STANDARD_DEVIATION); 716 const float sigma = speedxAngleRate + speedxNearestKeyDistanceRate 717 + ProximityInfoParams::MIN_STANDARD_DEVIATION; 718 719 ProximityInfoUtils::NormalDistribution 720 distribution(ProximityInfoParams::CENTER_VALUE_OF_NORMALIZED_DISTRIBUTION, sigma); 721 // Summing up probability densities of all near keys. 722 float sumOfProbabilityDensities = 0.0f; 723 for (int j = 0; j < keyCount; ++j) { 724 if ((*sampledNearKeySets)[i].test(j)) { 725 float distance = sqrtf(getPointToKeyByIdLength( 726 maxPointToKeyLength, sampledNormalizedSquaredLengthCache, keyCount, i, j)); 727 if (i == 0 && i != sampledInputSize - 1) { 728 // For the first point, weighted average of distances from first point and the 729 // next point to the key is used as a point to key distance. 730 const float nextDistance = sqrtf(getPointToKeyByIdLength( 731 maxPointToKeyLength, sampledNormalizedSquaredLengthCache, keyCount, 732 i + 1, j)); 733 if (nextDistance < distance) { 734 // The distance of the first point tends to bigger than continuing 735 // points because the first touch by the user can be sloppy. 736 // So we promote the first point if the distance of that point is larger 737 // than the distance of the next point. 738 distance = (distance 739 + nextDistance * ProximityInfoParams::NEXT_DISTANCE_WEIGHT) 740 / (1.0f + ProximityInfoParams::NEXT_DISTANCE_WEIGHT); 741 } 742 } else if (i != 0 && i == sampledInputSize - 1) { 743 // For the first point, weighted average of distances from last point and 744 // the previous point to the key is used as a point to key distance. 745 const float previousDistance = sqrtf(getPointToKeyByIdLength( 746 maxPointToKeyLength, sampledNormalizedSquaredLengthCache, keyCount, 747 i - 1, j)); 748 if (previousDistance < distance) { 749 // The distance of the last point tends to bigger than continuing points 750 // because the last touch by the user can be sloppy. So we promote the 751 // last point if the distance of that point is larger than the distance of 752 // the previous point. 753 distance = (distance 754 + previousDistance * ProximityInfoParams::PREV_DISTANCE_WEIGHT) 755 / (1.0f + ProximityInfoParams::PREV_DISTANCE_WEIGHT); 756 } 757 } 758 // TODO: Promote the first point when the extended line from the next input is near 759 // from a key. Also, promote the last point as well. 760 sumOfProbabilityDensities += distribution.getProbabilityDensity(distance); 761 } 762 } 763 764 // Split the probability of an input point to keys that are close to the input point. 765 for (int j = 0; j < keyCount; ++j) { 766 if ((*sampledNearKeySets)[i].test(j)) { 767 float distance = sqrtf(getPointToKeyByIdLength( 768 maxPointToKeyLength, sampledNormalizedSquaredLengthCache, keyCount, i, j)); 769 if (i == 0 && i != sampledInputSize - 1) { 770 // For the first point, weighted average of distances from the first point and 771 // the next point to the key is used as a point to key distance. 772 const float prevDistance = sqrtf(getPointToKeyByIdLength( 773 maxPointToKeyLength, sampledNormalizedSquaredLengthCache, keyCount, 774 i + 1, j)); 775 if (prevDistance < distance) { 776 distance = (distance 777 + prevDistance * ProximityInfoParams::NEXT_DISTANCE_WEIGHT) 778 / (1.0f + ProximityInfoParams::NEXT_DISTANCE_WEIGHT); 779 } 780 } else if (i != 0 && i == sampledInputSize - 1) { 781 // For the first point, weighted average of distances from last point and 782 // the previous point to the key is used as a point to key distance. 783 const float prevDistance = sqrtf(getPointToKeyByIdLength( 784 maxPointToKeyLength, sampledNormalizedSquaredLengthCache, keyCount, 785 i - 1, j)); 786 if (prevDistance < distance) { 787 distance = (distance 788 + prevDistance * ProximityInfoParams::PREV_DISTANCE_WEIGHT) 789 / (1.0f + ProximityInfoParams::PREV_DISTANCE_WEIGHT); 790 } 791 } 792 const float probabilityDensity = distribution.getProbabilityDensity(distance); 793 const float probability = inputCharProbability * probabilityDensity 794 / sumOfProbabilityDensities; 795 (*charProbabilities)[i][j] = probability; 796 } 797 } 798 } 799 800 if (DEBUG_POINTS_PROBABILITY) { 801 for (int i = 0; i < sampledInputSize; ++i) { 802 std::stringstream sstream; 803 sstream << i << ", "; 804 sstream << "(" << (*sampledInputXs)[i] << ", " << (*sampledInputYs)[i] << "), "; 805 sstream << "Speed: "<< (*sampledSpeedRates)[i] << ", "; 806 sstream << "Angle: "<< getPointAngle(sampledInputXs, sampledInputYs, i) << ", \n"; 807 808 for (hash_map_compat<int, float>::iterator it = (*charProbabilities)[i].begin(); 809 it != (*charProbabilities)[i].end(); ++it) { 810 if (it->first == NOT_AN_INDEX) { 811 sstream << it->first 812 << "(skip):" 813 << it->second 814 << "\n"; 815 } else { 816 sstream << it->first 817 << "(" 818 //<< static_cast<char>(mProximityInfo->getCodePointOf(it->first)) 819 << "):" 820 << it->second 821 << "\n"; 822 } 823 } 824 AKLOGI("%s", sstream.str().c_str()); 825 } 826 } 827 828 // Decrease key probabilities of points which don't have the highest probability of that key 829 // among nearby points. Probabilities of the first point and the last point are not suppressed. 830 for (int i = max(start, 1); i < sampledInputSize; ++i) { 831 for (int j = i + 1; j < sampledInputSize; ++j) { 832 if (!suppressCharProbabilities( 833 mostCommonKeyWidth, sampledInputSize, sampledLengthCache, i, j, 834 charProbabilities)) { 835 break; 836 } 837 } 838 for (int j = i - 1; j >= max(start, 0); --j) { 839 if (!suppressCharProbabilities( 840 mostCommonKeyWidth, sampledInputSize, sampledLengthCache, i, j, 841 charProbabilities)) { 842 break; 843 } 844 } 845 } 846 847 // Converting from raw probabilities to log probabilities to calculate spatial distance. 848 for (int i = start; i < sampledInputSize; ++i) { 849 for (int j = 0; j < keyCount; ++j) { 850 hash_map_compat<int, float>::iterator it = (*charProbabilities)[i].find(j); 851 if (it == (*charProbabilities)[i].end()){ 852 (*sampledNearKeySets)[i].reset(j); 853 } else if(it->second < ProximityInfoParams::MIN_PROBABILITY) { 854 // Erases from near keys vector because it has very low probability. 855 (*sampledNearKeySets)[i].reset(j); 856 (*charProbabilities)[i].erase(j); 857 } else { 858 it->second = -logf(it->second); 859 } 860 } 861 (*charProbabilities)[i][NOT_AN_INDEX] = -logf((*charProbabilities)[i][NOT_AN_INDEX]); 862 } 863 } 864 865 /* static */ void ProximityInfoStateUtils::updateSampledSearchKeySets( 866 const ProximityInfo *const proximityInfo, const int sampledInputSize, 867 const int lastSavedInputSize, 868 const std::vector<int> *const sampledLengthCache, 869 const std::vector<NearKeycodesSet> *const sampledNearKeySets, 870 std::vector<NearKeycodesSet> *sampledSearchKeySets, 871 std::vector<std::vector<int> > *sampledSearchKeyVectors) { 872 sampledSearchKeySets->resize(sampledInputSize); 873 sampledSearchKeyVectors->resize(sampledInputSize); 874 const int readForwordLength = static_cast<int>( 875 hypotf(proximityInfo->getKeyboardWidth(), proximityInfo->getKeyboardHeight()) 876 * ProximityInfoParams::SEARCH_KEY_RADIUS_RATIO); 877 for (int i = 0; i < sampledInputSize; ++i) { 878 if (i >= lastSavedInputSize) { 879 (*sampledSearchKeySets)[i].reset(); 880 } 881 for (int j = max(i, lastSavedInputSize); j < sampledInputSize; ++j) { 882 // TODO: Investigate if this is required. This may not fail. 883 if ((*sampledLengthCache)[j] - (*sampledLengthCache)[i] >= readForwordLength) { 884 break; 885 } 886 (*sampledSearchKeySets)[i] |= (*sampledNearKeySets)[j]; 887 } 888 } 889 const int keyCount = proximityInfo->getKeyCount(); 890 for (int i = 0; i < sampledInputSize; ++i) { 891 std::vector<int> *searchKeyVector = &(*sampledSearchKeyVectors)[i]; 892 searchKeyVector->clear(); 893 for (int j = 0; j < keyCount; ++j) { 894 if ((*sampledSearchKeySets)[i].test(j)) { 895 const int keyCodePoint = proximityInfo->getCodePointOf(j); 896 if (std::find(searchKeyVector->begin(), searchKeyVector->end(), keyCodePoint) 897 == searchKeyVector->end()) { 898 searchKeyVector->push_back(keyCodePoint); 899 } 900 } 901 } 902 } 903 } 904 905 // Decreases char probabilities of index0 by checking probabilities of a near point (index1) and 906 // increases char probabilities of index1 by checking probabilities of index0. 907 /* static */ bool ProximityInfoStateUtils::suppressCharProbabilities(const int mostCommonKeyWidth, 908 const int sampledInputSize, const std::vector<int> *const lengthCache, 909 const int index0, const int index1, 910 std::vector<hash_map_compat<int, float> > *charProbabilities) { 911 ASSERT(0 <= index0 && index0 < sampledInputSize); 912 ASSERT(0 <= index1 && index1 < sampledInputSize); 913 const float keyWidthFloat = static_cast<float>(mostCommonKeyWidth); 914 const float diff = fabsf(static_cast<float>((*lengthCache)[index0] - (*lengthCache)[index1])); 915 if (diff > keyWidthFloat * ProximityInfoParams::SUPPRESSION_LENGTH_WEIGHT) { 916 return false; 917 } 918 const float suppressionRate = ProximityInfoParams::MIN_SUPPRESSION_RATE 919 + diff / keyWidthFloat / ProximityInfoParams::SUPPRESSION_LENGTH_WEIGHT 920 * ProximityInfoParams::SUPPRESSION_WEIGHT; 921 for (hash_map_compat<int, float>::iterator it = (*charProbabilities)[index0].begin(); 922 it != (*charProbabilities)[index0].end(); ++it) { 923 hash_map_compat<int, float>::iterator it2 = (*charProbabilities)[index1].find(it->first); 924 if (it2 != (*charProbabilities)[index1].end() && it->second < it2->second) { 925 const float newProbability = it->second * suppressionRate; 926 const float suppression = it->second - newProbability; 927 it->second = newProbability; 928 // mCharProbabilities[index0][NOT_AN_INDEX] is the probability of skipping this point. 929 (*charProbabilities)[index0][NOT_AN_INDEX] += suppression; 930 931 // Add the probability of the same key nearby index1 932 const float probabilityGain = min(suppression 933 * ProximityInfoParams::SUPPRESSION_WEIGHT_FOR_PROBABILITY_GAIN, 934 (*charProbabilities)[index1][NOT_AN_INDEX] 935 * ProximityInfoParams::SKIP_PROBABALITY_WEIGHT_FOR_PROBABILITY_GAIN); 936 it2->second += probabilityGain; 937 (*charProbabilities)[index1][NOT_AN_INDEX] -= probabilityGain; 938 } 939 } 940 return true; 941 } 942 943 /* static */ bool ProximityInfoStateUtils::checkAndReturnIsContinuousSuggestionPossible( 944 const int inputSize, const int *const xCoordinates, const int *const yCoordinates, 945 const int *const times, const int sampledInputSize, 946 const std::vector<int> *const sampledInputXs, const std::vector<int> *const sampledInputYs, 947 const std::vector<int> *const sampledTimes, 948 const std::vector<int> *const sampledInputIndices) { 949 if (inputSize < sampledInputSize) { 950 return false; 951 } 952 for (int i = 0; i < sampledInputSize; ++i) { 953 const int index = (*sampledInputIndices)[i]; 954 if (index >= inputSize) { 955 return false; 956 } 957 if (xCoordinates[index] != (*sampledInputXs)[i] 958 || yCoordinates[index] != (*sampledInputYs)[i]) { 959 return false; 960 } 961 if (!times) { 962 continue; 963 } 964 if (times[index] != (*sampledTimes)[i]) { 965 return false; 966 } 967 } 968 return true; 969 } 970 971 // Get a word that is detected by tracing the most probable string into codePointBuf and 972 // returns probability of generating the word. 973 /* static */ float ProximityInfoStateUtils::getMostProbableString( 974 const ProximityInfo *const proximityInfo, const int sampledInputSize, 975 const std::vector<hash_map_compat<int, float> > *const charProbabilities, 976 int *const codePointBuf) { 977 ASSERT(sampledInputSize >= 0); 978 memset(codePointBuf, 0, sizeof(codePointBuf[0]) * MAX_WORD_LENGTH); 979 int index = 0; 980 float sumLogProbability = 0.0f; 981 // TODO: Current implementation is greedy algorithm. DP would be efficient for many cases. 982 for (int i = 0; i < sampledInputSize && index < MAX_WORD_LENGTH - 1; ++i) { 983 float minLogProbability = static_cast<float>(MAX_VALUE_FOR_WEIGHTING); 984 int character = NOT_AN_INDEX; 985 for (hash_map_compat<int, float>::const_iterator it = (*charProbabilities)[i].begin(); 986 it != (*charProbabilities)[i].end(); ++it) { 987 const float logProbability = (it->first != NOT_AN_INDEX) 988 ? it->second + ProximityInfoParams::DEMOTION_LOG_PROBABILITY : it->second; 989 if (logProbability < minLogProbability) { 990 minLogProbability = logProbability; 991 character = it->first; 992 } 993 } 994 if (character != NOT_AN_INDEX) { 995 codePointBuf[index] = proximityInfo->getCodePointOf(character); 996 index++; 997 } 998 sumLogProbability += minLogProbability; 999 } 1000 codePointBuf[index] = '\0'; 1001 return sumLogProbability; 1002 } 1003 1004 /* static */ void ProximityInfoStateUtils::dump(const bool isGeometric, const int inputSize, 1005 const int *const inputXCoordinates, const int *const inputYCoordinates, 1006 const int sampledInputSize, const std::vector<int> *const sampledInputXs, 1007 const std::vector<int> *const sampledInputYs, 1008 const std::vector<int> *const sampledTimes, 1009 const std::vector<float> *const sampledSpeedRates, 1010 const std::vector<int> *const sampledBeelineSpeedPercentiles) { 1011 if (DEBUG_GEO_FULL) { 1012 for (int i = 0; i < sampledInputSize; ++i) { 1013 AKLOGI("Sampled(%d): x = %d, y = %d, time = %d", i, (*sampledInputXs)[i], 1014 (*sampledInputYs)[i], sampledTimes ? (*sampledTimes)[i] : -1); 1015 } 1016 } 1017 1018 std::stringstream originalX, originalY, sampledX, sampledY; 1019 for (int i = 0; i < inputSize; ++i) { 1020 originalX << inputXCoordinates[i]; 1021 originalY << inputYCoordinates[i]; 1022 if (i != inputSize - 1) { 1023 originalX << ";"; 1024 originalY << ";"; 1025 } 1026 } 1027 AKLOGI("===== sampled points ====="); 1028 for (int i = 0; i < sampledInputSize; ++i) { 1029 if (isGeometric) { 1030 AKLOGI("%d: x = %d, y = %d, time = %d, relative speed = %.4f, beeline speed = %d", 1031 i, (*sampledInputXs)[i], (*sampledInputYs)[i], (*sampledTimes)[i], 1032 (*sampledSpeedRates)[i], (*sampledBeelineSpeedPercentiles)[i]); 1033 } 1034 sampledX << (*sampledInputXs)[i]; 1035 sampledY << (*sampledInputYs)[i]; 1036 if (i != sampledInputSize - 1) { 1037 sampledX << ";"; 1038 sampledY << ";"; 1039 } 1040 } 1041 AKLOGI("original points:\n%s, %s,\nsampled points:\n%s, %s,\n", 1042 originalX.str().c_str(), originalY.str().c_str(), sampledX.str().c_str(), 1043 sampledY.str().c_str()); 1044 } 1045 } // namespace latinime 1046