Index: third_party/android_prediction/suggest/core/layout/proximity_info_state.cpp |
diff --git a/third_party/android_prediction/suggest/core/layout/proximity_info_state.cpp b/third_party/android_prediction/suggest/core/layout/proximity_info_state.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..d289919956471535b2d62dd038a4446888d9e2cd |
--- /dev/null |
+++ b/third_party/android_prediction/suggest/core/layout/proximity_info_state.cpp |
@@ -0,0 +1,306 @@ |
+/* |
+ * Copyright (C) 2012 The Android Open Source Project |
+ * |
+ * Licensed under the Apache License, Version 2.0 (the "License"); |
+ * you may not use this file except in compliance with the License. |
+ * You may obtain a copy of the License at |
+ * |
+ * http://www.apache.org/licenses/LICENSE-2.0 |
+ * |
+ * Unless required by applicable law or agreed to in writing, software |
+ * distributed under the License is distributed on an "AS IS" BASIS, |
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
+ * See the License for the specific language governing permissions and |
+ * limitations under the License. |
+ */ |
+ |
+#define LOG_TAG "LatinIME: proximity_info_state.cpp" |
+ |
+#include "third_party/android_prediction/suggest/core/layout/proximity_info_state.h" |
+ |
+#include <algorithm> |
+#include <cstring> // for memset() and memmove() |
+#include <sstream> // for debug prints |
+#include <unordered_map> |
+#include <vector> |
+ |
+#include "third_party/android_prediction/defines.h" |
+#include "third_party/android_prediction/suggest/core/layout/geometry_utils.h" |
+#include "third_party/android_prediction/suggest/core/layout/proximity_info.h" |
+#include "third_party/android_prediction/suggest/core/layout/proximity_info_state_utils.h" |
+#include "third_party/android_prediction/utils/char_utils.h" |
+ |
+namespace latinime { |
+ |
+int ProximityInfoState::getPrimaryOriginalCodePointAt(const int index) const { |
+ const int primaryCodePoint = getPrimaryCodePointAt(index); |
+ const int keyIndex = mProximityInfo->getKeyIndexOf(primaryCodePoint); |
+ return mProximityInfo->getOriginalCodePointOf(keyIndex); |
+} |
+ |
+// TODO: Remove the dependency of "isGeometric" |
+void ProximityInfoState::initInputParams(const int pointerId, const float maxPointToKeyLength, |
+ const ProximityInfo *proximityInfo, const int *const inputCodes, const int inputSize, |
+ const int *const xCoordinates, const int *const yCoordinates, const int *const times, |
+ const int *const pointerIds, const bool isGeometric) { |
+ ASSERT(isGeometric || (inputSize < MAX_WORD_LENGTH)); |
+ mIsContinuousSuggestionPossible = (mHasBeenUpdatedByGeometricInput != isGeometric) ? |
+ false : ProximityInfoStateUtils::checkAndReturnIsContinuousSuggestionPossible( |
+ inputSize, xCoordinates, yCoordinates, times, mSampledInputSize, |
+ &mSampledInputXs, &mSampledInputYs, &mSampledTimes, &mSampledInputIndice); |
+ if (DEBUG_DICT) { |
+ AKLOGI("isContinuousSuggestionPossible = %s", |
+ (mIsContinuousSuggestionPossible ? "true" : "false")); |
+ } |
+ |
+ mProximityInfo = proximityInfo; |
+ mHasTouchPositionCorrectionData = proximityInfo->hasTouchPositionCorrectionData(); |
+ mMostCommonKeyWidthSquare = proximityInfo->getMostCommonKeyWidthSquare(); |
+ mKeyCount = proximityInfo->getKeyCount(); |
+ mCellHeight = proximityInfo->getCellHeight(); |
+ mCellWidth = proximityInfo->getCellWidth(); |
+ mGridHeight = proximityInfo->getGridWidth(); |
+ mGridWidth = proximityInfo->getGridHeight(); |
+ |
+ memset(mInputProximities, 0, sizeof(mInputProximities)); |
+ |
+ if (!isGeometric && pointerId == 0) { |
+ mProximityInfo->initializeProximities(inputCodes, xCoordinates, yCoordinates, |
+ inputSize, mInputProximities); |
+ } |
+ |
+ /////////////////////// |
+ // Setup touch points |
+ int pushTouchPointStartIndex = 0; |
+ int lastSavedInputSize = 0; |
+ mMaxPointToKeyLength = maxPointToKeyLength; |
+ mSampledInputSize = 0; |
+ mMostProbableStringProbability = 0.0f; |
+ |
+ if (mIsContinuousSuggestionPossible && mSampledInputIndice.size() > 1) { |
+ // Just update difference. |
+ // Previous two points are never skipped. Thus, we pop 2 input point data here. |
+ pushTouchPointStartIndex = ProximityInfoStateUtils::trimLastTwoTouchPoints( |
+ &mSampledInputXs, &mSampledInputYs, &mSampledTimes, &mSampledLengthCache, |
+ &mSampledInputIndice); |
+ lastSavedInputSize = mSampledInputXs.size(); |
+ } else { |
+ // Clear all data. |
+ mSampledInputXs.clear(); |
+ mSampledInputYs.clear(); |
+ mSampledTimes.clear(); |
+ mSampledInputIndice.clear(); |
+ mSampledLengthCache.clear(); |
+ mSampledNormalizedSquaredLengthCache.clear(); |
+ mSampledSearchKeySets.clear(); |
+ mSpeedRates.clear(); |
+ mBeelineSpeedPercentiles.clear(); |
+ mCharProbabilities.clear(); |
+ mDirections.clear(); |
+ } |
+ |
+ if (DEBUG_GEO_FULL) { |
+ AKLOGI("Init ProximityInfoState: reused points = %d, last input size = %d", |
+ pushTouchPointStartIndex, lastSavedInputSize); |
+ } |
+ |
+ if (xCoordinates && yCoordinates) { |
+ mSampledInputSize = ProximityInfoStateUtils::updateTouchPoints(mProximityInfo, |
+ mMaxPointToKeyLength, mInputProximities, xCoordinates, yCoordinates, times, |
+ pointerIds, inputSize, isGeometric, pointerId, |
+ pushTouchPointStartIndex, &mSampledInputXs, &mSampledInputYs, &mSampledTimes, |
+ &mSampledLengthCache, &mSampledInputIndice); |
+ } |
+ |
+ if (mSampledInputSize > 0 && isGeometric) { |
+ mAverageSpeed = ProximityInfoStateUtils::refreshSpeedRates(inputSize, xCoordinates, |
+ yCoordinates, times, lastSavedInputSize, mSampledInputSize, &mSampledInputXs, |
+ &mSampledInputYs, &mSampledTimes, &mSampledLengthCache, &mSampledInputIndice, |
+ &mSpeedRates, &mDirections); |
+ ProximityInfoStateUtils::refreshBeelineSpeedRates(mProximityInfo->getMostCommonKeyWidth(), |
+ mAverageSpeed, inputSize, xCoordinates, yCoordinates, times, mSampledInputSize, |
+ &mSampledInputXs, &mSampledInputYs, &mSampledInputIndice, |
+ &mBeelineSpeedPercentiles); |
+ } |
+ |
+ if (mSampledInputSize > 0) { |
+ ProximityInfoStateUtils::initGeometricDistanceInfos(mProximityInfo, mSampledInputSize, |
+ lastSavedInputSize, isGeometric, &mSampledInputXs, &mSampledInputYs, |
+ &mSampledNormalizedSquaredLengthCache); |
+ if (isGeometric) { |
+ // updates probabilities of skipping or mapping each key for all points. |
+ ProximityInfoStateUtils::updateAlignPointProbabilities( |
+ mMaxPointToKeyLength, mProximityInfo->getMostCommonKeyWidth(), |
+ mProximityInfo->getKeyCount(), lastSavedInputSize, mSampledInputSize, |
+ &mSampledInputXs, &mSampledInputYs, &mSpeedRates, &mSampledLengthCache, |
+ &mSampledNormalizedSquaredLengthCache, mProximityInfo, &mCharProbabilities); |
+ ProximityInfoStateUtils::updateSampledSearchKeySets(mProximityInfo, |
+ mSampledInputSize, lastSavedInputSize, &mSampledLengthCache, |
+ &mCharProbabilities, &mSampledSearchKeySets, |
+ &mSampledSearchKeyVectors); |
+ mMostProbableStringProbability = ProximityInfoStateUtils::getMostProbableString( |
+ mProximityInfo, mSampledInputSize, &mCharProbabilities, mMostProbableString); |
+ |
+ } |
+ } |
+ |
+ if (DEBUG_SAMPLING_POINTS) { |
+ ProximityInfoStateUtils::dump(isGeometric, inputSize, xCoordinates, yCoordinates, |
+ mSampledInputSize, &mSampledInputXs, &mSampledInputYs, &mSampledTimes, &mSpeedRates, |
+ &mBeelineSpeedPercentiles); |
+ } |
+ // end |
+ /////////////////////// |
+ |
+ mTouchPositionCorrectionEnabled = mSampledInputSize > 0 && mHasTouchPositionCorrectionData |
+ && xCoordinates && yCoordinates; |
+ if (!isGeometric && pointerId == 0) { |
+ ProximityInfoStateUtils::initPrimaryInputWord( |
+ inputSize, mInputProximities, mPrimaryInputWord); |
+ } |
+ if (DEBUG_GEO_FULL) { |
+ AKLOGI("ProximityState init finished: %d points out of %d", mSampledInputSize, inputSize); |
+ } |
+ mHasBeenUpdatedByGeometricInput = isGeometric; |
+} |
+ |
+// This function basically converts from a length to an edit distance. Accordingly, it's obviously |
+// wrong to compare with mMaxPointToKeyLength. |
+float ProximityInfoState::getPointToKeyLength( |
+ const int inputIndex, const int codePoint) const { |
+ const int keyId = mProximityInfo->getKeyIndexOf(codePoint); |
+ if (keyId != NOT_AN_INDEX) { |
+ const int index = inputIndex * mProximityInfo->getKeyCount() + keyId; |
+ return std::min(mSampledNormalizedSquaredLengthCache[index], mMaxPointToKeyLength); |
+ } |
+ if (CharUtils::isIntentionalOmissionCodePoint(codePoint)) { |
+ return 0.0f; |
+ } |
+ // If the char is not a key on the keyboard then return the max length. |
+ return static_cast<float>(MAX_VALUE_FOR_WEIGHTING); |
+} |
+ |
+float ProximityInfoState::getPointToKeyByIdLength( |
+ const int inputIndex, const int keyId) const { |
+ return ProximityInfoStateUtils::getPointToKeyByIdLength(mMaxPointToKeyLength, |
+ &mSampledNormalizedSquaredLengthCache, mProximityInfo->getKeyCount(), inputIndex, |
+ keyId); |
+} |
+ |
+// In the following function, c is the current character of the dictionary word currently examined. |
+// currentChars is an array containing the keys close to the character the user actually typed at |
+// the same position. We want to see if c is in it: if so, then the word contains at that position |
+// a character close to what the user typed. |
+// What the user typed is actually the first character of the array. |
+// proximityIndex is a pointer to the variable where getProximityType returns the index of c |
+// in the proximity chars of the input index. |
+// Notice : accented characters do not have a proximity list, so they are alone in their list. The |
+// non-accented version of the character should be considered "close", but not the other keys close |
+// to the non-accented version. |
+ProximityType ProximityInfoState::getProximityType(const int index, const int codePoint, |
+ const bool checkProximityChars, int *proximityIndex) const { |
+ const int *currentCodePoints = getProximityCodePointsAt(index); |
+ const int firstCodePoint = currentCodePoints[0]; |
+ const int baseLowerC = CharUtils::toBaseLowerCase(codePoint); |
+ |
+ // The first char in the array is what user typed. If it matches right away, that means the |
+ // user typed that same char for this pos. |
+ if (firstCodePoint == baseLowerC || firstCodePoint == codePoint) { |
+ return MATCH_CHAR; |
+ } |
+ |
+ if (!checkProximityChars) return SUBSTITUTION_CHAR; |
+ |
+ // If the non-accented, lowercased version of that first character matches c, then we have a |
+ // non-accented version of the accented character the user typed. Treat it as a close char. |
+ if (CharUtils::toBaseLowerCase(firstCodePoint) == baseLowerC) { |
+ return PROXIMITY_CHAR; |
+ } |
+ |
+ // Not an exact nor an accent-alike match: search the list of close keys |
+ int j = 1; |
+ while (j < MAX_PROXIMITY_CHARS_SIZE |
+ && currentCodePoints[j] > ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE) { |
+ const bool matched = (currentCodePoints[j] == baseLowerC |
+ || currentCodePoints[j] == codePoint); |
+ if (matched) { |
+ if (proximityIndex) { |
+ *proximityIndex = j; |
+ } |
+ return PROXIMITY_CHAR; |
+ } |
+ ++j; |
+ } |
+ if (j < MAX_PROXIMITY_CHARS_SIZE |
+ && currentCodePoints[j] == ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE) { |
+ ++j; |
+ while (j < MAX_PROXIMITY_CHARS_SIZE |
+ && currentCodePoints[j] > ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE) { |
+ const bool matched = (currentCodePoints[j] == baseLowerC |
+ || currentCodePoints[j] == codePoint); |
+ if (matched) { |
+ if (proximityIndex) { |
+ *proximityIndex = j; |
+ } |
+ return ADDITIONAL_PROXIMITY_CHAR; |
+ } |
+ ++j; |
+ } |
+ } |
+ // Was not included, signal this as a substitution character. |
+ return SUBSTITUTION_CHAR; |
+} |
+ |
+ProximityType ProximityInfoState::getProximityTypeG(const int index, const int codePoint) const { |
+ if (!isUsed()) { |
+ return UNRELATED_CHAR; |
+ } |
+ const int sampledSearchKeyVectorsSize = static_cast<int>(mSampledSearchKeyVectors.size()); |
+ if (index < 0 || index >= sampledSearchKeyVectorsSize) { |
+ AKLOGE("getProximityTypeG() is called with an invalid index(%d). " |
+ "mSampledSearchKeyVectors.size() = %d, codePoint = %x.", index, |
+ sampledSearchKeyVectorsSize, codePoint); |
+ ASSERT(false); |
+ return UNRELATED_CHAR; |
+ } |
+ const int lowerCodePoint = CharUtils::toLowerCase(codePoint); |
+ const int baseLowerCodePoint = CharUtils::toBaseCodePoint(lowerCodePoint); |
+ for (int i = 0; i < static_cast<int>(mSampledSearchKeyVectors[index].size()); ++i) { |
+ if (mSampledSearchKeyVectors[index][i] == lowerCodePoint |
+ || mSampledSearchKeyVectors[index][i] == baseLowerCodePoint) { |
+ return MATCH_CHAR; |
+ } |
+ } |
+ return UNRELATED_CHAR; |
+} |
+ |
+bool ProximityInfoState::isKeyInSerchKeysAfterIndex(const int index, const int keyId) const { |
+ ASSERT(keyId >= 0 && index >= 0 && index < mSampledInputSize); |
+ return mSampledSearchKeySets[index].test(keyId); |
+} |
+ |
+float ProximityInfoState::getDirection(const int index0, const int index1) const { |
+ return ProximityInfoStateUtils::getDirection( |
+ &mSampledInputXs, &mSampledInputYs, index0, index1); |
+} |
+ |
+float ProximityInfoState::getMostProbableString(int *const codePointBuf) const { |
+ memmove(codePointBuf, mMostProbableString, sizeof(mMostProbableString)); |
+ return mMostProbableStringProbability; |
+} |
+ |
+bool ProximityInfoState::hasSpaceProximity(const int index) const { |
+ ASSERT(0 <= index && index < mSampledInputSize); |
+ return mProximityInfo->hasSpaceProximity(getInputX(index), getInputY(index)); |
+} |
+ |
+// Returns a probability of mapping index to keyIndex. |
+float ProximityInfoState::getProbability(const int index, const int keyIndex) const { |
+ ASSERT(0 <= index && index < mSampledInputSize); |
+ std::unordered_map<int, float>::const_iterator it = mCharProbabilities[index].find(keyIndex); |
+ if (it != mCharProbabilities[index].end()) { |
+ return it->second; |
+ } |
+ return static_cast<float>(MAX_VALUE_FOR_WEIGHTING); |
+} |
+} // namespace latinime |