Home | History | Annotate | Download | only in layout
      1 /*
      2  * Copyright (C) 2012 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 #define LOG_TAG "LatinIME: proximity_info_state.cpp"
     18 
     19 #include "suggest/core/layout/proximity_info_state.h"
     20 
     21 #include <algorithm>
     22 #include <cstring> // for memset() and memmove()
     23 #include <sstream> // for debug prints
     24 #include <unordered_map>
     25 #include <vector>
     26 
     27 #include "defines.h"
     28 #include "suggest/core/layout/geometry_utils.h"
     29 #include "suggest/core/layout/proximity_info.h"
     30 #include "suggest/core/layout/proximity_info_state_utils.h"
     31 #include "utils/char_utils.h"
     32 
     33 namespace latinime {
     34 
     35 int ProximityInfoState::getPrimaryOriginalCodePointAt(const int index) const {
     36     const int primaryCodePoint = getPrimaryCodePointAt(index);
     37     const int keyIndex = mProximityInfo->getKeyIndexOf(primaryCodePoint);
     38     return mProximityInfo->getOriginalCodePointOf(keyIndex);
     39 }
     40 
     41 // TODO: Remove the dependency of "isGeometric"
     42 void ProximityInfoState::initInputParams(const int pointerId, const float maxPointToKeyLength,
     43         const ProximityInfo *proximityInfo, const int *const inputCodes, const int inputSize,
     44         const int *const xCoordinates, const int *const yCoordinates, const int *const times,
     45         const int *const pointerIds, const bool isGeometric, const std::vector<int> *locale) {
     46     ASSERT(isGeometric || (inputSize < MAX_WORD_LENGTH));
     47     mIsContinuousSuggestionPossible = (mHasBeenUpdatedByGeometricInput != isGeometric) ?
     48             false : ProximityInfoStateUtils::checkAndReturnIsContinuousSuggestionPossible(
     49                     inputSize, xCoordinates, yCoordinates, times, mSampledInputSize,
     50                     &mSampledInputXs, &mSampledInputYs, &mSampledTimes, &mSampledInputIndice);
     51     if (DEBUG_DICT) {
     52         AKLOGI("isContinuousSuggestionPossible = %s",
     53                 (mIsContinuousSuggestionPossible ? "true" : "false"));
     54     }
     55 
     56     mProximityInfo = proximityInfo;
     57     mHasTouchPositionCorrectionData = proximityInfo->hasTouchPositionCorrectionData();
     58     mMostCommonKeyWidthSquare = proximityInfo->getMostCommonKeyWidthSquare();
     59     mKeyCount = proximityInfo->getKeyCount();
     60     mCellHeight = proximityInfo->getCellHeight();
     61     mCellWidth = proximityInfo->getCellWidth();
     62     mGridHeight = proximityInfo->getGridWidth();
     63     mGridWidth = proximityInfo->getGridHeight();
     64 
     65     memset(mInputProximities, 0, sizeof(mInputProximities));
     66 
     67     if (!isGeometric && pointerId == 0) {
     68         mProximityInfo->initializeProximities(inputCodes, xCoordinates, yCoordinates,
     69                 inputSize, mInputProximities, locale);
     70     }
     71 
     72     ///////////////////////
     73     // Setup touch points
     74     int pushTouchPointStartIndex = 0;
     75     int lastSavedInputSize = 0;
     76     mMaxPointToKeyLength = maxPointToKeyLength;
     77     mSampledInputSize = 0;
     78     mMostProbableStringProbability = 0.0f;
     79 
     80     if (mIsContinuousSuggestionPossible && mSampledInputIndice.size() > 1) {
     81         // Just update difference.
     82         // Previous two points are never skipped. Thus, we pop 2 input point data here.
     83         pushTouchPointStartIndex = ProximityInfoStateUtils::trimLastTwoTouchPoints(
     84                 &mSampledInputXs, &mSampledInputYs, &mSampledTimes, &mSampledLengthCache,
     85                 &mSampledInputIndice);
     86         lastSavedInputSize = mSampledInputXs.size();
     87     } else {
     88         // Clear all data.
     89         mSampledInputXs.clear();
     90         mSampledInputYs.clear();
     91         mSampledTimes.clear();
     92         mSampledInputIndice.clear();
     93         mSampledLengthCache.clear();
     94         mSampledNormalizedSquaredLengthCache.clear();
     95         mSampledSearchKeySets.clear();
     96         mSpeedRates.clear();
     97         mBeelineSpeedPercentiles.clear();
     98         mCharProbabilities.clear();
     99         mDirections.clear();
    100     }
    101 
    102     if (DEBUG_GEO_FULL) {
    103         AKLOGI("Init ProximityInfoState: reused points =  %d, last input size = %d",
    104                 pushTouchPointStartIndex, lastSavedInputSize);
    105     }
    106 
    107     if (xCoordinates && yCoordinates) {
    108         mSampledInputSize = ProximityInfoStateUtils::updateTouchPoints(mProximityInfo,
    109                 mMaxPointToKeyLength, mInputProximities, xCoordinates, yCoordinates, times,
    110                 pointerIds, inputSize, isGeometric, pointerId,
    111                 pushTouchPointStartIndex, &mSampledInputXs, &mSampledInputYs, &mSampledTimes,
    112                 &mSampledLengthCache, &mSampledInputIndice);
    113     }
    114 
    115     if (mSampledInputSize > 0 && isGeometric) {
    116         mAverageSpeed = ProximityInfoStateUtils::refreshSpeedRates(inputSize, xCoordinates,
    117                 yCoordinates, times, lastSavedInputSize, mSampledInputSize, &mSampledInputXs,
    118                 &mSampledInputYs, &mSampledTimes, &mSampledLengthCache, &mSampledInputIndice,
    119                 &mSpeedRates, &mDirections);
    120         ProximityInfoStateUtils::refreshBeelineSpeedRates(mProximityInfo->getMostCommonKeyWidth(),
    121                 mAverageSpeed, inputSize, xCoordinates, yCoordinates, times, mSampledInputSize,
    122                 &mSampledInputXs, &mSampledInputYs, &mSampledInputIndice,
    123                 &mBeelineSpeedPercentiles);
    124     }
    125 
    126     if (mSampledInputSize > 0) {
    127         ProximityInfoStateUtils::initGeometricDistanceInfos(mProximityInfo, mSampledInputSize,
    128                 lastSavedInputSize, isGeometric, &mSampledInputXs, &mSampledInputYs,
    129                 &mSampledNormalizedSquaredLengthCache);
    130         if (isGeometric) {
    131             // updates probabilities of skipping or mapping each key for all points.
    132             ProximityInfoStateUtils::updateAlignPointProbabilities(
    133                     mMaxPointToKeyLength, mProximityInfo->getMostCommonKeyWidth(),
    134                     mProximityInfo->getKeyCount(), lastSavedInputSize, mSampledInputSize,
    135                     &mSampledInputXs, &mSampledInputYs, &mSpeedRates, &mSampledLengthCache,
    136                     &mSampledNormalizedSquaredLengthCache, mProximityInfo, &mCharProbabilities);
    137             ProximityInfoStateUtils::updateSampledSearchKeySets(mProximityInfo,
    138                     mSampledInputSize, lastSavedInputSize, &mSampledLengthCache,
    139                     &mCharProbabilities, &mSampledSearchKeySets,
    140                     &mSampledSearchKeyVectors);
    141             mMostProbableStringProbability = ProximityInfoStateUtils::getMostProbableString(
    142                     mProximityInfo, mSampledInputSize, &mCharProbabilities, mMostProbableString);
    143 
    144         }
    145     }
    146 
    147     if (DEBUG_SAMPLING_POINTS) {
    148         ProximityInfoStateUtils::dump(isGeometric, inputSize, xCoordinates, yCoordinates,
    149                 mSampledInputSize, &mSampledInputXs, &mSampledInputYs, &mSampledTimes, &mSpeedRates,
    150                 &mBeelineSpeedPercentiles);
    151     }
    152     // end
    153     ///////////////////////
    154 
    155     mTouchPositionCorrectionEnabled = mSampledInputSize > 0 && mHasTouchPositionCorrectionData
    156             && xCoordinates && yCoordinates;
    157     if (!isGeometric && pointerId == 0) {
    158         ProximityInfoStateUtils::initPrimaryInputWord(
    159                 inputSize, mInputProximities, mPrimaryInputWord);
    160     }
    161     if (DEBUG_GEO_FULL) {
    162         AKLOGI("ProximityState init finished: %d points out of %d", mSampledInputSize, inputSize);
    163     }
    164     mHasBeenUpdatedByGeometricInput = isGeometric;
    165 }
    166 
    167 // This function basically converts from a length to an edit distance. Accordingly, it's obviously
    168 // wrong to compare with mMaxPointToKeyLength.
    169 float ProximityInfoState::getPointToKeyLength(
    170         const int inputIndex, const int codePoint) const {
    171     const int keyId = mProximityInfo->getKeyIndexOf(codePoint);
    172     if (keyId != NOT_AN_INDEX) {
    173         const int index = inputIndex * mProximityInfo->getKeyCount() + keyId;
    174         return std::min(mSampledNormalizedSquaredLengthCache[index], mMaxPointToKeyLength);
    175     }
    176     if (CharUtils::isIntentionalOmissionCodePoint(codePoint)) {
    177         return 0.0f;
    178     }
    179     // If the char is not a key on the keyboard then return the max length.
    180     return static_cast<float>(MAX_VALUE_FOR_WEIGHTING);
    181 }
    182 
    183 float ProximityInfoState::getPointToKeyByIdLength(
    184         const int inputIndex, const int keyId) const {
    185     return ProximityInfoStateUtils::getPointToKeyByIdLength(mMaxPointToKeyLength,
    186             &mSampledNormalizedSquaredLengthCache, mProximityInfo->getKeyCount(), inputIndex,
    187             keyId);
    188 }
    189 
    190 // In the following function, c is the current character of the dictionary word currently examined.
    191 // currentChars is an array containing the keys close to the character the user actually typed at
    192 // the same position. We want to see if c is in it: if so, then the word contains at that position
    193 // a character close to what the user typed.
    194 // What the user typed is actually the first character of the array.
    195 // proximityIndex is a pointer to the variable where getProximityType returns the index of c
    196 // in the proximity chars of the input index.
    197 // Notice : accented characters do not have a proximity list, so they are alone in their list. The
    198 // non-accented version of the character should be considered "close", but not the other keys close
    199 // to the non-accented version.
    200 ProximityType ProximityInfoState::getProximityType(const int index, const int codePoint,
    201         const bool checkProximityChars, int *proximityIndex) const {
    202     const int *currentCodePoints = getProximityCodePointsAt(index);
    203     const int firstCodePoint = currentCodePoints[0];
    204     const int baseLowerC = CharUtils::toBaseLowerCase(codePoint);
    205 
    206     // The first char in the array is what user typed. If it matches right away, that means the
    207     // user typed that same char for this pos.
    208     if (firstCodePoint == baseLowerC || firstCodePoint == codePoint) {
    209         return MATCH_CHAR;
    210     }
    211 
    212     if (!checkProximityChars) return SUBSTITUTION_CHAR;
    213 
    214     // If the non-accented, lowercased version of that first character matches c, then we have a
    215     // non-accented version of the accented character the user typed. Treat it as a close char.
    216     if (CharUtils::toBaseLowerCase(firstCodePoint) == baseLowerC) {
    217         return PROXIMITY_CHAR;
    218     }
    219 
    220     // Not an exact nor an accent-alike match: search the list of close keys
    221     int j = 1;
    222     while (j < MAX_PROXIMITY_CHARS_SIZE
    223             && currentCodePoints[j] > ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE) {
    224         const bool matched = (currentCodePoints[j] == baseLowerC
    225                 || currentCodePoints[j] == codePoint);
    226         if (matched) {
    227             if (proximityIndex) {
    228                 *proximityIndex = j;
    229             }
    230             return PROXIMITY_CHAR;
    231         }
    232         ++j;
    233     }
    234     if (j < MAX_PROXIMITY_CHARS_SIZE
    235             && currentCodePoints[j] == ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE) {
    236         ++j;
    237         while (j < MAX_PROXIMITY_CHARS_SIZE
    238                 && currentCodePoints[j] > ADDITIONAL_PROXIMITY_CHAR_DELIMITER_CODE) {
    239             const bool matched = (currentCodePoints[j] == baseLowerC
    240                     || currentCodePoints[j] == codePoint);
    241             if (matched) {
    242                 if (proximityIndex) {
    243                     *proximityIndex = j;
    244                 }
    245                 return ADDITIONAL_PROXIMITY_CHAR;
    246             }
    247             ++j;
    248         }
    249     }
    250     // Was not included, signal this as a substitution character.
    251     return SUBSTITUTION_CHAR;
    252 }
    253 
    254 ProximityType ProximityInfoState::getProximityTypeG(const int index, const int codePoint) const {
    255     if (!isUsed()) {
    256         return UNRELATED_CHAR;
    257     }
    258     const int sampledSearchKeyVectorsSize = static_cast<int>(mSampledSearchKeyVectors.size());
    259     if (index < 0 || index >= sampledSearchKeyVectorsSize) {
    260         AKLOGE("getProximityTypeG() is called with an invalid index(%d). "
    261                 "mSampledSearchKeyVectors.size() = %d, codePoint = %x.", index,
    262                 sampledSearchKeyVectorsSize, codePoint);
    263         ASSERT(false);
    264         return UNRELATED_CHAR;
    265     }
    266     const int lowerCodePoint = CharUtils::toLowerCase(codePoint);
    267     const int baseLowerCodePoint = CharUtils::toBaseCodePoint(lowerCodePoint);
    268     for (int i = 0; i < static_cast<int>(mSampledSearchKeyVectors[index].size()); ++i) {
    269         if (mSampledSearchKeyVectors[index][i] == lowerCodePoint
    270                 || mSampledSearchKeyVectors[index][i] == baseLowerCodePoint) {
    271             return MATCH_CHAR;
    272         }
    273     }
    274     return UNRELATED_CHAR;
    275 }
    276 
    277 bool ProximityInfoState::isKeyInSerchKeysAfterIndex(const int index, const int keyId) const {
    278     ASSERT(keyId >= 0 && index >= 0 && index < mSampledInputSize);
    279     return mSampledSearchKeySets[index].test(keyId);
    280 }
    281 
    282 float ProximityInfoState::getDirection(const int index0, const int index1) const {
    283     return ProximityInfoStateUtils::getDirection(
    284             &mSampledInputXs, &mSampledInputYs, index0, index1);
    285 }
    286 
    287 float ProximityInfoState::getMostProbableString(int *const codePointBuf) const {
    288     memmove(codePointBuf, mMostProbableString, sizeof(mMostProbableString));
    289     return mMostProbableStringProbability;
    290 }
    291 
    292 bool ProximityInfoState::hasSpaceProximity(const int index) const {
    293     ASSERT(0 <= index && index < mSampledInputSize);
    294     return mProximityInfo->hasSpaceProximity(getInputX(index), getInputY(index));
    295 }
    296 
    297 // Returns a probability of mapping index to keyIndex.
    298 float ProximityInfoState::getProbability(const int index, const int keyIndex) const {
    299     ASSERT(0 <= index && index < mSampledInputSize);
    300     std::unordered_map<int, float>::const_iterator it = mCharProbabilities[index].find(keyIndex);
    301     if (it != mCharProbabilities[index].end()) {
    302         return it->second;
    303     }
    304     return static_cast<float>(MAX_VALUE_FOR_WEIGHTING);
    305 }
    306 } // namespace latinime
    307