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