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