aboutsummaryrefslogtreecommitdiffstats
path: root/native/jni/src
diff options
context:
space:
mode:
Diffstat (limited to 'native/jni/src')
-rw-r--r--native/jni/src/char_utils.cpp7
-rw-r--r--native/jni/src/correction.cpp2
-rw-r--r--native/jni/src/correction.h1
-rw-r--r--native/jni/src/defines.h10
-rw-r--r--native/jni/src/dictionary.cpp7
-rw-r--r--native/jni/src/dictionary.h4
-rw-r--r--native/jni/src/geometry_utils.h19
-rw-r--r--native/jni/src/proximity_info_state.cpp376
-rw-r--r--native/jni/src/proximity_info_state.h18
-rw-r--r--native/jni/src/unigram_dictionary.cpp24
-rw-r--r--native/jni/src/unigram_dictionary.h5
11 files changed, 381 insertions, 92 deletions
diff --git a/native/jni/src/char_utils.cpp b/native/jni/src/char_utils.cpp
index d0547a982..9d27cadad 100644
--- a/native/jni/src/char_utils.cpp
+++ b/native/jni/src/char_utils.cpp
@@ -17,6 +17,7 @@
#include <cstdlib>
#include "char_utils.h"
+#include "defines.h"
namespace latinime {
@@ -33,7 +34,7 @@ struct LatinCapitalSmallPair {
//
// unsigned short c, cc, ccc, ccc2;
// for (c = 0; c < 0xFFFF ; c++) {
-// if (c < sizeof(BASE_CHARS) / sizeof(BASE_CHARS[0])) {
+// if (c < NELEMS(BASE_CHARS)) {
// cc = BASE_CHARS[c];
// } else {
// cc = c;
@@ -894,9 +895,7 @@ static int compare_pair_capital(const void *a, const void *b) {
unsigned short latin_tolower(const unsigned short c) {
struct LatinCapitalSmallPair *p =
static_cast<struct LatinCapitalSmallPair *>(bsearch(&c, SORTED_CHAR_MAP,
- sizeof(SORTED_CHAR_MAP) / sizeof(SORTED_CHAR_MAP[0]),
- sizeof(SORTED_CHAR_MAP[0]),
- compare_pair_capital));
+ NELEMS(SORTED_CHAR_MAP), sizeof(SORTED_CHAR_MAP[0]), compare_pair_capital));
return p ? p->small : c;
}
} // namespace latinime
diff --git a/native/jni/src/correction.cpp b/native/jni/src/correction.cpp
index 524abe9a1..d57b0e370 100644
--- a/native/jni/src/correction.cpp
+++ b/native/jni/src/correction.cpp
@@ -1118,7 +1118,7 @@ float Correction::RankingAlgorithm::calcNormalizedScore(const unsigned short *be
const int distance = editDistance(before, beforeLength, after, afterLength);
int spaceCount = 0;
for (int i = 0; i < afterLength; ++i) {
- if (after[i] == CODE_SPACE) {
+ if (after[i] == KEYCODE_SPACE) {
++spaceCount;
}
}
diff --git a/native/jni/src/correction.h b/native/jni/src/correction.h
index f016d5453..a099853e6 100644
--- a/native/jni/src/correction.h
+++ b/native/jni/src/correction.h
@@ -116,7 +116,6 @@ class Correction {
static int editDistance(const unsigned short *before,
const int beforeLength, const unsigned short *after, const int afterLength);
private:
- static const int CODE_SPACE = ' ';
static const int MAX_INITIAL_SCORE = 255;
};
diff --git a/native/jni/src/defines.h b/native/jni/src/defines.h
index ea0f0ef70..942068a49 100644
--- a/native/jni/src/defines.h
+++ b/native/jni/src/defines.h
@@ -219,6 +219,8 @@ static inline void prof_out(void) {
#define DEBUG_CORRECTION false
#define DEBUG_CORRECTION_FREQ false
#define DEBUG_WORDS_PRIORITY_QUEUE false
+#define DEBUG_SAMPLING_POINTS true
+#define DEBUG_POINTS_PROBABILITY true
#ifdef FLAG_FULL_DBG
#define DEBUG_GEO_FULL true
@@ -239,6 +241,8 @@ static inline void prof_out(void) {
#define DEBUG_CORRECTION false
#define DEBUG_CORRECTION_FREQ false
#define DEBUG_WORDS_PRIORITY_QUEUE false
+#define DEBUG_SAMPLING_POINTS false
+#define DEBUG_POINTS_PROBABILITY false
#define DEBUG_GEO_FULL false
@@ -344,8 +348,8 @@ static inline void prof_out(void) {
#define MULTIPLE_WORDS_DEMOTION_RATE 80
#define MIN_INPUT_LENGTH_FOR_THREE_OR_MORE_WORDS_CORRECTION 6
-#define TWO_WORDS_CORRECTION_WITH_OTHER_ERROR_THRESHOLD 0.35
-#define START_TWO_WORDS_CORRECTION_THRESHOLD 0.185
+#define TWO_WORDS_CORRECTION_WITH_OTHER_ERROR_THRESHOLD 0.35f
+#define START_TWO_WORDS_CORRECTION_THRESHOLD 0.185f
/* heuristic... This should be changed if we change the unit of the frequency. */
#define SUPPRESS_SHORT_MULTIPLE_WORDS_THRESHOLD_FREQ (MAX_FREQ * 58 / 100)
@@ -392,6 +396,8 @@ static inline void prof_out(void) {
template<typename T> inline T min(T a, T b) { return a < b ? a : b; }
template<typename T> inline T max(T a, T b) { return a > b ? a : b; }
+#define NELEMS(x) (sizeof(x) / sizeof((x)[0]))
+
// The ratio of neutral area radius to sweet spot radius.
#define NEUTRAL_AREA_RADIUS_RATIO 1.3f
diff --git a/native/jni/src/dictionary.cpp b/native/jni/src/dictionary.cpp
index 2fbe83e86..81789ccfc 100644
--- a/native/jni/src/dictionary.cpp
+++ b/native/jni/src/dictionary.cpp
@@ -30,13 +30,12 @@ namespace latinime {
// TODO: Change the type of all keyCodes to uint32_t
Dictionary::Dictionary(void *dict, int dictSize, int mmapFd, int dictBufAdjust,
- int typedLetterMultiplier, int fullWordMultiplier, int maxWordLength, int maxWords,
- int maxPredictions)
+ int fullWordMultiplier, int maxWordLength, int maxWords, int maxPredictions)
: mDict(static_cast<unsigned char *>(dict)),
mOffsetDict((static_cast<unsigned char *>(dict)) + BinaryFormat::getHeaderSize(mDict)),
mDictSize(dictSize), mMmapFd(mmapFd), mDictBufAdjust(dictBufAdjust),
- mUnigramDictionary(new UnigramDictionary(mOffsetDict, typedLetterMultiplier,
- fullWordMultiplier, maxWordLength, maxWords, BinaryFormat::getFlags(mDict))),
+ mUnigramDictionary(new UnigramDictionary(mOffsetDict, fullWordMultiplier, maxWordLength,
+ maxWords, BinaryFormat::getFlags(mDict))),
mBigramDictionary(new BigramDictionary(mOffsetDict, maxWordLength, maxPredictions)),
mGestureDecoder(new GestureDecoderWrapper(maxWordLength, maxWords)) {
if (DEBUG_DICT) {
diff --git a/native/jni/src/dictionary.h b/native/jni/src/dictionary.h
index a1358890d..120ca5f7f 100644
--- a/native/jni/src/dictionary.h
+++ b/native/jni/src/dictionary.h
@@ -41,8 +41,8 @@ class Dictionary {
const static int KIND_SHORTCUT = 7; // A shortcut
const static int KIND_PREDICTION = 8; // A prediction (== a suggestion with no input)
- Dictionary(void *dict, int dictSize, int mmapFd, int dictBufAdjust, int typedLetterMultipler,
- int fullWordMultiplier, int maxWordLength, int maxWords, int maxPredictions);
+ Dictionary(void *dict, int dictSize, int mmapFd, int dictBufAdjust, int fullWordMultiplier,
+ int maxWordLength, int maxWords, int maxPredictions);
int getSuggestions(ProximityInfo *proximityInfo, void *traverseSession, int *xcoordinates,
int *ycoordinates, int *times, int *pointerIds, int *codes, int codesSize,
diff --git a/native/jni/src/geometry_utils.h b/native/jni/src/geometry_utils.h
index 31359e19d..734fbefda 100644
--- a/native/jni/src/geometry_utils.h
+++ b/native/jni/src/geometry_utils.h
@@ -85,5 +85,24 @@ static inline float pointToLineSegSquaredDistanceFloat(
}
return getSquaredDistanceFloat(x, y, projectionX, projectionY);
}
+
+// Normal distribution N(u, sigma^2).
+struct NormalDistribution {
+ NormalDistribution(const float u, const float sigma)
+ : mU(u), mSigma(sigma),
+ mPreComputedNonExpPart(1.0f / sqrtf(2.0f * M_PI_F * sigma * sigma)),
+ mPreComputedExponentPart(-1.0f / (2.0f * sigma * sigma)) {}
+
+ float getProbabilityDensity(const float x) {
+ const float shiftedX = x - mU;
+ return mPreComputedNonExpPart * expf(mPreComputedExponentPart * SQUARE_FLOAT(shiftedX));
+ }
+private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(NormalDistribution);
+ float mU; // mean value
+ float mSigma; // standard deviation
+ float mPreComputedNonExpPart; // = 1 / sqrt(2 * PI * sigma^2)
+ float mPreComputedExponentPart; // = -1 / (2 * sigma^2)
+};
} // namespace latinime
#endif // LATINIME_GEOMETRY_UTILS_H
diff --git a/native/jni/src/proximity_info_state.cpp b/native/jni/src/proximity_info_state.cpp
index 392ec8194..90e3671eb 100644
--- a/native/jni/src/proximity_info_state.cpp
+++ b/native/jni/src/proximity_info_state.cpp
@@ -15,6 +15,7 @@
*/
#include <cstring> // for memset()
+#include <sstream> // for debug prints
#include <stdint.h>
#define LOG_TAG "LatinIME: proximity_info_state.cpp"
@@ -105,6 +106,7 @@ void ProximityInfoState::initInputParams(const int pointerId, const float maxPoi
mDistanceCache.clear();
mNearKeysVector.clear();
mRelativeSpeeds.clear();
+ mCharProbabilities.clear();
}
if (DEBUG_GEO_FULL) {
AKLOGI("Init ProximityInfoState: reused points = %d, last input size = %d",
@@ -161,34 +163,42 @@ void ProximityInfoState::initInputParams(const int pointerId, const float maxPoi
}
if (mInputSize > 0 && isGeometric) {
- int sumDuration = mTimes.back() - mTimes.front();
- int sumLength = mLengthCache.back() - mLengthCache.front();
- float averageSpeed = static_cast<float>(sumLength) / static_cast<float>(sumDuration);
+ const int sumDuration = mTimes.back() - mTimes.front();
+ const int sumLength = mLengthCache.back() - mLengthCache.front();
+ const float averageSpeed = static_cast<float>(sumLength) / static_cast<float>(sumDuration);
mRelativeSpeeds.resize(mInputSize);
for (int i = lastSavedInputSize; i < mInputSize; ++i) {
const int index = mInputIndice[i];
int length = 0;
int duration = 0;
- if (index == 0 && index < inputSize - 1) {
- length = getDistanceInt(xCoordinates[index], yCoordinates[index],
- xCoordinates[index + 1], yCoordinates[index + 1]);
- duration = times[index + 1] - times[index];
- } else if (index == inputSize - 1 && index > 0) {
- length = getDistanceInt(xCoordinates[index - 1], yCoordinates[index - 1],
- xCoordinates[index], yCoordinates[index]);
- duration = times[index] - times[index - 1];
- } else if (0 < index && index < inputSize - 1) {
- length = getDistanceInt(xCoordinates[index - 1], yCoordinates[index - 1],
- xCoordinates[index], yCoordinates[index])
- + getDistanceInt(xCoordinates[index], yCoordinates[index],
- xCoordinates[index + 1], yCoordinates[index + 1]);
- duration = times[index + 1] - times[index - 1];
+
+ // Calculate velocity by using distances and durations of
+ // NUM_POINTS_FOR_SPEED_CALCULATION points for both forward and backward.
+ static const int NUM_POINTS_FOR_SPEED_CALCULATION = 1;
+ for (int j = index; j < min(inputSize - 1, index + NUM_POINTS_FOR_SPEED_CALCULATION);
+ ++j) {
+ if (i < mInputSize - 1 && j >= mInputIndice[i + 1]) {
+ break;
+ }
+ length += getDistanceInt(xCoordinates[j], yCoordinates[j],
+ xCoordinates[j + 1], yCoordinates[j + 1]);
+ duration += times[j + 1] - times[j];
+ }
+ for (int j = index - 1; j >= max(0, index - NUM_POINTS_FOR_SPEED_CALCULATION); --j) {
+ if (i > 0 && j < mInputIndice[i - 1]) {
+ break;
+ }
+ length += getDistanceInt(xCoordinates[j], yCoordinates[j],
+ xCoordinates[j + 1], yCoordinates[j + 1]);
+ duration += times[j + 1] - times[j];
+ }
+ if (duration == 0 || sumDuration == 0) {
+ // Cannot calculate speed; thus, it gives an average value (1.0);
+ mRelativeSpeeds[i] = 1.0f;
} else {
- length = 0;
- duration = 1;
+ const float speed = static_cast<float>(length) / static_cast<float>(duration);
+ mRelativeSpeeds[i] = speed / averageSpeed;
}
- const float speed = static_cast<float>(length) / static_cast<float>(duration);
- mRelativeSpeeds[i] = speed / averageSpeed;
}
}
@@ -215,7 +225,7 @@ void ProximityInfoState::initInputParams(const int pointerId, const float maxPoi
static const float READ_FORWORD_LENGTH_SCALE = 0.95f;
const int readForwordLength = static_cast<int>(
hypotf(mProximityInfo->getKeyboardWidth(), mProximityInfo->getKeyboardHeight())
- * READ_FORWORD_LENGTH_SCALE);
+ * READ_FORWORD_LENGTH_SCALE);
for (int i = 0; i < mInputSize; ++i) {
if (DEBUG_GEO_FULL) {
AKLOGI("Sampled(%d): x = %d, y = %d, time = %d", i, mInputXs[i], mInputYs[i],
@@ -230,6 +240,27 @@ void ProximityInfoState::initInputParams(const int pointerId, const float maxPoi
}
}
+ if (DEBUG_SAMPLING_POINTS) {
+ std::stringstream originalX, originalY, sampledX, sampledY;
+ for (int i = 0; i < inputSize; ++i) {
+ originalX << xCoordinates[i];
+ originalY << yCoordinates[i];
+ if (i != inputSize - 1) {
+ originalX << ";";
+ originalY << ";";
+ }
+ }
+ for (int i = 0; i < mInputSize; ++i) {
+ sampledX << mInputXs[i];
+ sampledY << mInputYs[i];
+ if (i != mInputSize - 1) {
+ sampledX << ";";
+ sampledY << ";";
+ }
+ }
+ AKLOGI("\n%s, %s,\n%s, %s,\n", originalX.str().c_str(), originalY.str().c_str(),
+ sampledX.str().c_str(), sampledY.str().c_str());
+ }
// end
///////////////////////
@@ -276,6 +307,10 @@ void ProximityInfoState::initInputParams(const int pointerId, const float maxPoi
if (DEBUG_GEO_FULL) {
AKLOGI("ProximityState init finished: %d points out of %d", mInputSize, inputSize);
}
+ if (isGeometric && mInputSize > 0) {
+ // updates probabilities of skipping or mapping each key for all points.
+ updateAlignPointProbabilities();
+ }
}
bool ProximityInfoState::checkAndReturnIsContinuationPossible(const int inputSize,
@@ -294,7 +329,7 @@ bool ProximityInfoState::checkAndReturnIsContinuationPossible(const int inputSiz
// the given point and the nearest key position.
float ProximityInfoState::updateNearKeysDistances(const int x, const int y,
NearKeysDistanceMap *const currentNearKeysDistances) {
- static const float NEAR_KEY_THRESHOLD = 4.0f;
+ static const float NEAR_KEY_THRESHOLD = 1.7f;
currentNearKeysDistances->clear();
const int keyCount = mProximityInfo->getKeyCount();
@@ -315,7 +350,7 @@ float ProximityInfoState::updateNearKeysDistances(const int x, const int y,
bool ProximityInfoState::isPrevLocalMin(const NearKeysDistanceMap *const currentNearKeysDistances,
const NearKeysDistanceMap *const prevNearKeysDistances,
const NearKeysDistanceMap *const prevPrevNearKeysDistances) const {
- static const float MARGIN = 0.01f;
+ static const float MARGIN = 0.03f;
for (NearKeysDistanceMap::const_iterator it = prevNearKeysDistances->begin();
it != prevNearKeysDistances->end(); ++it) {
@@ -336,18 +371,20 @@ float ProximityInfoState::getPointScore(
const NearKeysDistanceMap *const prevNearKeysDistances,
const NearKeysDistanceMap *const prevPrevNearKeysDistances) const {
static const int DISTANCE_BASE_SCALE = 100;
- static const int SAVE_DISTANCE_SCALE = 200;
- static const int SKIP_DISTANCE_SCALE = 25;
- static const int CHECK_LOCALMIN_DISTANCE_THRESHOLD_SCALE = 40;
- static const int STRAIGHT_SKIP_DISTANCE_THRESHOLD_SCALE = 50;
- static const int CORNER_CHECK_DISTANCE_THRESHOLD_SCALE = 27;
+ static const int SAVE_DISTANCE_SCALE = 500;
+ static const int SKIP_DISTANCE_SCALE = 10;
+ static const float NEAR_KEY_THRESHOLD = 1.0f;
+ static const int CHECK_LOCALMIN_DISTANCE_THRESHOLD_SCALE = 100;
+ static const int STRAIGHT_SKIP_DISTANCE_THRESHOLD_SCALE = 200;
+ static const int CORNER_CHECK_DISTANCE_THRESHOLD_SCALE = 20;
static const float SAVE_DISTANCE_SCORE = 2.0f;
static const float SKIP_DISTANCE_SCORE = -1.0f;
- static const float CHECK_LOCALMIN_DISTANCE_SCORE = -1.0f;
+ static const float NOT_LOCALMIN_DISTANCE_SCORE = -1.0f;
+ static const float LOCALMIN_DISTANCE_AND_NEAR_TO_KEY_SCORE = 2.0f;
static const float STRAIGHT_ANGLE_THRESHOLD = M_PI_F / 36.0f;
static const float STRAIGHT_SKIP_NEAREST_DISTANCE_THRESHOLD = 0.5f;
static const float STRAIGHT_SKIP_SCORE = -1.0f;
- static const float CORNER_ANGLE_THRESHOLD = M_PI_F / 2.0f;
+ static const float CORNER_ANGLE_THRESHOLD = M_PI_F / 6.0f;
static const float CORNER_SCORE = 1.0f;
const std::size_t size = mInputXs.size();
@@ -373,7 +410,10 @@ float ProximityInfoState::getPointScore(
if (distPrev < baseSampleRate * CHECK_LOCALMIN_DISTANCE_THRESHOLD_SCALE) {
if (!isPrevLocalMin(currentNearKeysDistances, prevNearKeysDistances,
prevPrevNearKeysDistances)) {
- score += CHECK_LOCALMIN_DISTANCE_SCORE;
+ score += NOT_LOCALMIN_DISTANCE_SCORE;
+ } else if (nearest < NEAR_KEY_THRESHOLD) {
+ // Promote points nearby keys
+ score += LOCALMIN_DISTANCE_AND_NEAR_TO_KEY_SCORE;
}
}
// Angle
@@ -402,7 +442,8 @@ bool ProximityInfoState::pushTouchPoint(const int inputIndex, const int nodeChar
NearKeysDistanceMap *const currentNearKeysDistances,
const NearKeysDistanceMap *const prevNearKeysDistances,
const NearKeysDistanceMap *const prevPrevNearKeysDistances) {
- static const float LAST_POINT_SKIP_DISTANCE_SCALE = 0.25f;
+ static const int LAST_POINT_SKIP_DISTANCE_SCALE = 4;
+ static const int LAST_AND_NOT_NEAREST_POINT_SKIP_DISTANCE_SCALE = 2;
size_t size = mInputXs.size();
bool popped = false;
@@ -419,33 +460,38 @@ bool ProximityInfoState::pushTouchPoint(const int inputIndex, const int nodeChar
popped = false;
}
// Check if the last point should be skipped.
- if (isLastPoint) {
- if (size > 0 && getDistanceFloat(x, y, mInputXs.back(), mInputYs.back())
- < mProximityInfo->getMostCommonKeyWidth() * LAST_POINT_SKIP_DISTANCE_SCALE) {
+ if (isLastPoint && size > 0) {
+ const int lastPointsDistance = getDistanceInt(x, y, mInputXs.back(), mInputYs.back());
+ if (lastPointsDistance * LAST_POINT_SKIP_DISTANCE_SCALE
+ < mProximityInfo->getMostCommonKeyWidth()) {
+ // This point is not used because it's too close to the previous point.
if (DEBUG_GEO_FULL) {
- AKLOGI("p0: size = %zd, x = %d, y = %d, lx = %d, ly = %d, dist = %f, "
- "width = %f", size, x, y, mInputXs.back(), mInputYs.back(),
- getDistanceFloat(x, y, mInputXs.back(), mInputYs.back()),
+ AKLOGI("p0: size = %zd, x = %d, y = %d, lx = %d, ly = %d, dist = %d, "
+ "width = %d", size, x, y, mInputXs.back(), mInputYs.back(),
+ getDistanceInt(x, y, mInputXs.back(), mInputYs.back()),
mProximityInfo->getMostCommonKeyWidth()
- * LAST_POINT_SKIP_DISTANCE_SCALE);
+ / LAST_POINT_SKIP_DISTANCE_SCALE);
}
return popped;
- } else if (size > 1) {
- int minChar = 0;
- float minDist = mMaxPointToKeyLength;
+ } else if (lastPointsDistance * LAST_AND_NOT_NEAREST_POINT_SKIP_DISTANCE_SCALE
+ < mProximityInfo->getMostCommonKeyWidth()) {
+ int nearestChar = 0;
+ float nearestCharDistance = mMaxPointToKeyLength;
for (NearKeysDistanceMap::const_iterator it = currentNearKeysDistances->begin();
it != currentNearKeysDistances->end(); ++it) {
- if (minDist > it->second) {
- minChar = it->first;
- minDist = it->second;
+ if (nearestCharDistance > it->second) {
+ nearestChar = it->first;
+ nearestCharDistance = it->second;
}
}
NearKeysDistanceMap::const_iterator itPP =
- prevNearKeysDistances->find(minChar);
- if (itPP != prevNearKeysDistances->end() && minDist > itPP->second) {
+ prevNearKeysDistances->find(nearestChar);
+ if (itPP != prevNearKeysDistances->end() && nearestCharDistance > itPP->second) {
+ // The nearest key of the penultimate point is same as the nearest key of the
+ // last point. So, we don't need to use the last point.
if (DEBUG_GEO_FULL) {
AKLOGI("p1: char = %c, minDist = %f, prevNear key minDist = %f",
- minChar, itPP->second, minDist);
+ nearestChar, itPP->second, nearestCharDistance);
}
return popped;
}
@@ -503,22 +549,25 @@ int ProximityInfoState::getDuration(const int index) const {
return 0;
}
-float ProximityInfoState::getPointToKeyLength(const int inputIndex, const int codePoint,
- const float scale) const {
+float ProximityInfoState::getPointToKeyLength(const int inputIndex, const int codePoint) const {
+ if (isSkippableChar(codePoint)) {
+ return 0.0f;
+ }
const int keyId = mProximityInfo->getKeyIndexOf(codePoint);
+ return getPointToKeyByIdLength(inputIndex, keyId);
+}
+
+float ProximityInfoState::getPointToKeyByIdLength(const int inputIndex, const int keyId) const {
if (keyId != NOT_AN_INDEX) {
const int index = inputIndex * mProximityInfo->getKeyCount() + keyId;
- return min(mDistanceCache[index] * scale, mMaxPointToKeyLength);
- }
- if (isSkippableChar(codePoint)) {
- return 0.0f;
+ return min(mDistanceCache[index], mMaxPointToKeyLength);
}
// If the char is not a key on the keyboard then return the max length.
- return MAX_POINT_TO_KEY_LENGTH;
+ return static_cast<float>(MAX_POINT_TO_KEY_LENGTH);
}
int ProximityInfoState::getSpaceY() const {
- const int keyId = mProximityInfo->getKeyIndexOf(' ');
+ const int keyId = mProximityInfo->getKeyIndexOf(KEYCODE_SPACE);
return mProximityInfo->getKeyCenterYOfKeyIdG(keyId);
}
@@ -565,4 +614,217 @@ void ProximityInfoState::popInputData() {
mInputIndice.pop_back();
}
+float ProximityInfoState::getPointAngle(const int index) const {
+ if (index <= 0 || index >= mInputSize - 1) {
+ return 0.0f;
+ }
+ const int x = mInputXs[index];
+ const int y = mInputYs[index];
+ const int nextX = mInputXs[index + 1];
+ const int nextY = mInputYs[index + 1];
+ const int previousX = mInputXs[index - 1];
+ const int previousY = mInputYs[index - 1];
+ const float previousDirection = getAngle(previousX, previousY, x, y);
+ const float nextDirection = getAngle(x, y, nextX, nextY);
+ const float directionDiff = getAngleDiff(previousDirection, nextDirection);
+ return directionDiff;
+}
+
+float ProximityInfoState::getPointsAngle(
+ const int index0, const int index1, const int index2) const {
+ if (index0 < 0 || index0 > mInputSize - 1) {
+ return 0.0f;
+ }
+ if (index1 < 0 || index1 > mInputSize - 1) {
+ return 0.0f;
+ }
+ if (index2 < 0 || index2 > mInputSize - 1) {
+ return 0.0f;
+ }
+ const int x0 = mInputXs[index0];
+ const int y0 = mInputYs[index0];
+ const int x1 = mInputXs[index1];
+ const int y1 = mInputYs[index1];
+ const int x2 = mInputXs[index2];
+ const int y2 = mInputYs[index2];
+ const float previousDirection = getAngle(x0, y0, x1, y1);
+ const float nextDirection = getAngle(x1, y1, x2, y2);
+ const float directionDiff = getAngleDiff(previousDirection, nextDirection);
+ return directionDiff;
+}
+
+// Updates probabilities of aligning to some keys and skipping.
+// Word suggestion should be based on this probabilities.
+void ProximityInfoState::updateAlignPointProbabilities() {
+ static const float MIN_PROBABILITY = 0.00001f;
+ static const float SKIP_FIRST_POINT_PROBABILITY = 0.01f;
+ static const float SKIP_LAST_POINT_PROBABILITY = 0.1f;
+ static const float ANGLE_RATE = 0.8f;
+ static const float DEEP_CORNER_ANGLE_THRESHOLD = M_PI_F * 0.5f;
+ static const float SKIP_DEEP_CORNER_PROBABILITY = 0.3f;
+ static const float CORNER_ANGLE_THRESHOLD = M_PI_F * 35.0f / 180.0f;
+ static const float STRAIGHT_ANGLE_THRESHOLD = M_PI_F * 15.0f / 180.0f;
+ static const float SKIP_CORNER_PROBABILITY = 0.5f;
+ static const float SLOW_STRAIGHT_WEIGHT = 0.8f;
+ static const float CENTER_VALUE_OF_NORMALIZED_DISTRIBUTION = 0.0f;
+
+ mCharProbabilities.resize(mInputSize);
+ // Calculates probabilities of using a point as a correlated point with the character
+ // for each point.
+ for (int i = 0; i < mInputSize; ++i) {
+ // First, calculates skip probability. Starts form 100%.
+ // Note that all values that are multiplied to this probability should be in [0.0, 1.0];
+ float skipProbability = 1.0f;
+ const float speed = getRelativeSpeed(i);
+
+ // Adjusts skip probability by a rate depending on speed.
+ skipProbability *= min(1.0f, speed);
+ if (i == 0) {
+ skipProbability *= SKIP_FIRST_POINT_PROBABILITY;
+ } else if (i == mInputSize - 1) {
+ skipProbability *= SKIP_LAST_POINT_PROBABILITY;
+ } else {
+ const float currentAngle = getPointAngle(i);
+
+ // Adjusts skip probability by a rate depending on angle.
+ // ANGLE_RATE of skipProbability is adjusted by current angle.
+ skipProbability *= max((M_PI_F - currentAngle) / M_PI_F, 0.0f) * ANGLE_RATE +
+ (1.0f - ANGLE_RATE);
+ if (currentAngle > DEEP_CORNER_ANGLE_THRESHOLD) {
+ skipProbability *= SKIP_DEEP_CORNER_PROBABILITY;
+ }
+ const float prevAngle = getPointsAngle(i, i - 1, i - 2);
+ if (prevAngle < STRAIGHT_ANGLE_THRESHOLD && currentAngle > CORNER_ANGLE_THRESHOLD) {
+ skipProbability *= SKIP_CORNER_PROBABILITY;
+ }
+ if (currentAngle < STRAIGHT_ANGLE_THRESHOLD) {
+ // Adjusts skip probability by speed.
+ skipProbability *= min(1.0f, speed * SLOW_STRAIGHT_WEIGHT);
+ }
+ }
+
+ // probabilities must be in [0.0, 1.0];
+ ASSERT(skipProbability >= 0.0f);
+ ASSERT(skipProbability <= 1.0f);
+
+ mCharProbabilities[i][NOT_AN_INDEX] = skipProbability;
+ // Second, calculates key probabilities by dividing the rest probability
+ // (1.0f - skipProbability).
+ const float inputCharProbability = 1.0f - skipProbability;
+ // Summing up probability densities of all near keys.
+ float sumOfProbabilityDensityOfNearKeys = 0.0f;
+ const float sigma = speed;
+ NormalDistribution distribution(CENTER_VALUE_OF_NORMALIZED_DISTRIBUTION, sigma);
+ for (int j = 0; j < mProximityInfo->getKeyCount(); ++j) {
+ if (mNearKeysVector[i].test(j)) {
+ const float distance = sqrtf(getPointToKeyByIdLength(i, j));
+ sumOfProbabilityDensityOfNearKeys += distribution.getProbabilityDensity(distance);
+ }
+ }
+ for (int j = 0; j < mProximityInfo->getKeyCount(); ++j) {
+ if (mNearKeysVector[i].test(j)) {
+ const float distance = sqrtf(getPointToKeyByIdLength(i, j));
+ const float probabilityDessity = distribution.getProbabilityDensity(distance);
+ // inputCharProbability divided to the probability for each near key.
+ const float probability = inputCharProbability * probabilityDessity
+ / sumOfProbabilityDensityOfNearKeys;
+ if (probability > MIN_PROBABILITY) {
+ mCharProbabilities[i][j] = probability;
+ }
+ }
+ }
+ }
+
+ // Decrease key probabilities of points which don't have the highest probability of that key
+ // among nearby points. Probabilities of the first point and the last point are not suppressed.
+ for (int i = 1; i < mInputSize - 1; ++i) {
+ // forward
+ for (int j = i + 1; j < mInputSize; ++j) {
+ if (suppressCharProbabilities(i, j)) {
+ break;
+ }
+ }
+ // backward
+ for (int j = i - 1; j >= 0; --j) {
+ if (suppressCharProbabilities(i, j)) {
+ break;
+ }
+ }
+ }
+
+ if (DEBUG_POINTS_PROBABILITY) {
+ for (int i = 0; i < mInputSize; ++i) {
+ std::stringstream sstream;
+ sstream << i << ", ";
+ for (hash_map_compat<int, float>::iterator it = mCharProbabilities[i].begin();
+ it != mCharProbabilities[i].end(); ++it) {
+ sstream << it->first
+ << "("
+ << static_cast<char>(mProximityInfo->getCodePointOf(it->first))
+ << "):"
+ << it->second
+ << ", ";
+ }
+ AKLOGI("%s", sstream.str().c_str());
+ }
+ }
+}
+
+// Decreases char probabilities of index0 by checking probabilities of a near point (index1).
+bool ProximityInfoState::suppressCharProbabilities(const int index0, const int index1) {
+ ASSERT(0 <= index0 && index0 < mInputSize);
+ ASSERT(0 <= index1 && index1 < mInputSize);
+ static const float SUPPRESSION_LENGTH_WEIGHT = 1.5f;
+ const float keyWidthFloat = static_cast<float>(mProximityInfo->getMostCommonKeyWidth());
+ const float diff = fabsf(static_cast<float>(mLengthCache[index0] - mLengthCache[index1]));
+ if (diff > keyWidthFloat * SUPPRESSION_LENGTH_WEIGHT) {
+ return false;
+ }
+ // Summing up decreased amount of probabilities from 0%.
+ float sumOfAdjustedProbabilities = 0.0f;
+ const float suppressionRate = diff / keyWidthFloat / SUPPRESSION_LENGTH_WEIGHT;
+ for (hash_map_compat<int, float>::iterator it = mCharProbabilities[index0].begin();
+ it != mCharProbabilities[index0].end(); ++it) {
+ hash_map_compat<int, float>::const_iterator it2 =
+ mCharProbabilities[index1].find(it->first);
+ if (it2 != mCharProbabilities[index1].end() && it->second < it2->second) {
+ const float newProbability = it->second * suppressionRate;
+ sumOfAdjustedProbabilities += it->second - newProbability;
+ it->second = newProbability;
+ }
+ }
+ // All decreased amount of probabilities are added to the probability of skipping.
+ mCharProbabilities[index0][NOT_AN_INDEX] += sumOfAdjustedProbabilities;
+ return true;
+}
+
+// Get a word that is detected by tracing highest probability sequence into charBuf and returns
+// probability of generating the word.
+float ProximityInfoState::getHighestProbabilitySequence(uint16_t *const charBuf) const {
+ int buf[mInputSize];
+ // Maximum probabilities of each point are multiplied to 100%.
+ float probability = 1.0f;
+ // TODO: Current implementation is greedy algorithm. DP would be efficient for many cases.
+ for (int i = 0; i < mInputSize; ++i) {
+ float maxProbability = 0.0f;
+ for (hash_map_compat<int, float>::const_iterator it = mCharProbabilities[i].begin();
+ it != mCharProbabilities[i].end(); ++it) {
+ if (it->second > maxProbability) {
+ maxProbability = it->second;
+ buf[i] = it->first;
+ }
+ }
+ probability *= maxProbability;
+ }
+ int index = 0;
+ for (int i = 0; i < mInputSize && index < MAX_WORD_LENGTH_INTERNAL - 1; ++i) {
+ if (buf[i] != NOT_AN_INDEX) {
+ charBuf[index] = mProximityInfo->getCodePointOf(buf[i]);
+ index++;
+ }
+ }
+ charBuf[index] = '\0';
+ return probability;
+}
+
} // namespace latinime
diff --git a/native/jni/src/proximity_info_state.h b/native/jni/src/proximity_info_state.h
index c1ec76c38..7286e5ed5 100644
--- a/native/jni/src/proximity_info_state.h
+++ b/native/jni/src/proximity_info_state.h
@@ -55,8 +55,8 @@ class ProximityInfoState {
mHasTouchPositionCorrectionData(false), mMostCommonKeyWidthSquare(0), mLocaleStr(),
mKeyCount(0), mCellHeight(0), mCellWidth(0), mGridHeight(0), mGridWidth(0),
mIsContinuationPossible(false), mInputXs(), mInputYs(), mTimes(), mInputIndice(),
- mDistanceCache(), mLengthCache(), mRelativeSpeeds(), mNearKeysVector(),
- mTouchPositionCorrectionEnabled(false), mInputSize(0) {
+ mDistanceCache(), mLengthCache(), mRelativeSpeeds(), mCharProbabilities(),
+ mNearKeysVector(), mTouchPositionCorrectionEnabled(false), mInputSize(0) {
memset(mInputCodes, 0, sizeof(mInputCodes));
memset(mNormalizedSquaredDistances, 0, sizeof(mNormalizedSquaredDistances));
memset(mPrimaryInputWord, 0, sizeof(mPrimaryInputWord));
@@ -213,7 +213,9 @@ class ProximityInfoState {
return mIsContinuationPossible;
}
- float getPointToKeyLength(const int inputIndex, const int charCode, const float scale) const;
+ float getPointToKeyLength(const int inputIndex, const int charCode) const;
+
+ float getPointToKeyByIdLength(const int inputIndex, const int keyId) const;
int getSpaceY() const;
@@ -223,6 +225,12 @@ class ProximityInfoState {
float getRelativeSpeed(const int index) const {
return mRelativeSpeeds[index];
}
+
+ float getPointAngle(const int index) const;
+ // Returns angle of three points. x, y, and z are indices.
+ float getPointsAngle(const int index0, const int index1, const int index2) const;
+
+ float getHighestProbabilitySequence(uint16_t *const charBuf) const;
private:
DISALLOW_COPY_AND_ASSIGN(ProximityInfoState);
typedef hash_map_compat<int, float> NearKeysDistanceMap;
@@ -265,6 +273,8 @@ class ProximityInfoState {
bool checkAndReturnIsContinuationPossible(const int inputSize, const int *const xCoordinates,
const int *const yCoordinates, const int *const times);
void popInputData();
+ void updateAlignPointProbabilities();
+ bool suppressCharProbabilities(const int index1, const int index2);
// const
const ProximityInfo *mProximityInfo;
@@ -286,6 +296,8 @@ class ProximityInfoState {
std::vector<float> mDistanceCache;
std::vector<int> mLengthCache;
std::vector<float> mRelativeSpeeds;
+ // probabilities of skipping or mapping to a key for each point.
+ std::vector<hash_map_compat<int, float> > mCharProbabilities;
std::vector<NearKeycodesSet> mNearKeysVector;
bool mTouchPositionCorrectionEnabled;
int32_t mInputCodes[MAX_PROXIMITY_CHARS_SIZE_INTERNAL * MAX_WORD_LENGTH_INTERNAL];
diff --git a/native/jni/src/unigram_dictionary.cpp b/native/jni/src/unigram_dictionary.cpp
index 49d044fbc..3b485a055 100644
--- a/native/jni/src/unigram_dictionary.cpp
+++ b/native/jni/src/unigram_dictionary.cpp
@@ -41,14 +41,12 @@ const UnigramDictionary::digraph_t UnigramDictionary::FRENCH_LIGATURES_DIGRAPHS[
{ 'o', 'e', 0x0153 } }; // U+0153 : LATIN SMALL LIGATURE OE
// TODO: check the header
-UnigramDictionary::UnigramDictionary(const uint8_t *const streamStart, int typedLetterMultiplier,
- int fullWordMultiplier, int maxWordLength, int maxWords, const unsigned int flags)
- : DICT_ROOT(streamStart), MAX_WORD_LENGTH(maxWordLength), MAX_WORDS(maxWords),
- TYPED_LETTER_MULTIPLIER(typedLetterMultiplier), FULL_WORD_MULTIPLIER(fullWordMultiplier),
- // TODO : remove this variable.
- ROOT_POS(0),
- BYTES_IN_ONE_CHAR(sizeof(int)),
- MAX_DIGRAPH_SEARCH_DEPTH(DEFAULT_MAX_DIGRAPH_SEARCH_DEPTH), FLAGS(flags) {
+UnigramDictionary::UnigramDictionary(const uint8_t *const streamStart, int fullWordMultiplier,
+ int maxWordLength, int maxWords, const unsigned int flags)
+ : DICT_ROOT(streamStart), MAX_WORD_LENGTH(maxWordLength), MAX_WORDS(maxWords),
+ FULL_WORD_MULTIPLIER(fullWordMultiplier), // TODO : remove this variable.
+ ROOT_POS(0), BYTES_IN_ONE_CHAR(sizeof(int)),
+ MAX_DIGRAPH_SEARCH_DEPTH(DEFAULT_MAX_DIGRAPH_SEARCH_DEPTH), FLAGS(flags) {
if (DEBUG_DICT) {
AKLOGI("UnigramDictionary - constructor");
}
@@ -188,8 +186,7 @@ int UnigramDictionary::getSuggestions(ProximityInfo *proximityInfo, const int *x
getWordWithDigraphSuggestionsRec(proximityInfo, xcoordinates, ycoordinates, codesBuffer,
xCoordinatesBuffer, yCoordinatesBuffer, codesSize, bigramMap, bigramFilter,
useFullEditDistance, codes, codesSize, 0, codesBuffer, &masterCorrection,
- &queuePool, GERMAN_UMLAUT_DIGRAPHS,
- sizeof(GERMAN_UMLAUT_DIGRAPHS) / sizeof(GERMAN_UMLAUT_DIGRAPHS[0]));
+ &queuePool, GERMAN_UMLAUT_DIGRAPHS, NELEMS(GERMAN_UMLAUT_DIGRAPHS));
} else if (BinaryFormat::REQUIRES_FRENCH_LIGATURES_PROCESSING & FLAGS) {
int codesBuffer[getCodesBufferSize(codes, codesSize)];
int xCoordinatesBuffer[codesSize];
@@ -197,8 +194,7 @@ int UnigramDictionary::getSuggestions(ProximityInfo *proximityInfo, const int *x
getWordWithDigraphSuggestionsRec(proximityInfo, xcoordinates, ycoordinates, codesBuffer,
xCoordinatesBuffer, yCoordinatesBuffer, codesSize, bigramMap, bigramFilter,
useFullEditDistance, codes, codesSize, 0, codesBuffer, &masterCorrection,
- &queuePool, FRENCH_LIGATURES_DIGRAPHS,
- sizeof(FRENCH_LIGATURES_DIGRAPHS) / sizeof(FRENCH_LIGATURES_DIGRAPHS[0]));
+ &queuePool, FRENCH_LIGATURES_DIGRAPHS, NELEMS(FRENCH_LIGATURES_DIGRAPHS));
} else { // Normal processing
getWordSuggestions(proximityInfo, xcoordinates, ycoordinates, codes, codesSize,
bigramMap, bigramFilter, useFullEditDistance, &masterCorrection, &queuePool);
@@ -314,8 +310,6 @@ void UnigramDictionary::initSuggestions(ProximityInfo *proximityInfo, const int
correction->initCorrection(proximityInfo, inputSize, maxDepth);
}
-static const char SPACE = ' ';
-
void UnigramDictionary::getOneWordSuggestions(ProximityInfo *proximityInfo,
const int *xcoordinates, const int *ycoordinates, const int *codes,
const std::map<int, int> *bigramMap, const uint8_t *bigramFilter,
@@ -570,7 +564,7 @@ int UnigramDictionary::getSubStringSuggestion(
if (outputWordStartPos + nextWordLength >= MAX_WORD_LENGTH) {
return FLAG_MULTIPLE_SUGGEST_SKIP;
}
- outputWord[tempOutputWordLength] = SPACE;
+ outputWord[tempOutputWordLength] = KEYCODE_SPACE;
if (outputWordLength) {
++*outputWordLength;
}
diff --git a/native/jni/src/unigram_dictionary.h b/native/jni/src/unigram_dictionary.h
index 57129bb07..244d78d8c 100644
--- a/native/jni/src/unigram_dictionary.h
+++ b/native/jni/src/unigram_dictionary.h
@@ -39,8 +39,8 @@ class UnigramDictionary {
static const int FLAG_MULTIPLE_SUGGEST_ABORT = 0;
static const int FLAG_MULTIPLE_SUGGEST_SKIP = 1;
static const int FLAG_MULTIPLE_SUGGEST_CONTINUE = 2;
- UnigramDictionary(const uint8_t *const streamStart, int typedLetterMultipler,
- int fullWordMultiplier, int maxWordLength, int maxWords, const unsigned int flags);
+ UnigramDictionary(const uint8_t *const streamStart, int fullWordMultiplier, int maxWordLength,
+ int maxWords, const unsigned int flags);
int getFrequency(const int32_t *const inWord, const int length) const;
int getBigramPosition(int pos, unsigned short *word, int offset, int length) const;
int getSuggestions(ProximityInfo *proximityInfo, const int *xcoordinates,
@@ -115,7 +115,6 @@ class UnigramDictionary {
const uint8_t *const DICT_ROOT;
const int MAX_WORD_LENGTH;
const int MAX_WORDS;
- const int TYPED_LETTER_MULTIPLIER;
const int FULL_WORD_MULTIPLIER;
const int ROOT_POS;
const unsigned int BYTES_IN_ONE_CHAR;