LatinIME: Implement gesture typing
Co-authored-by: nift4 <nift4@protonmail.com>
Change-Id: I11344e95e32566507f937d6b72b6212e3cba2220
Signed-off-by: Mohammad Hasan Keramat J <ikeramat@protonmail.com>
diff --git a/native/jni/Android.bp b/native/jni/Android.bp
index ab14632..4d2ec8b 100644
--- a/native/jni/Android.bp
+++ b/native/jni/Android.bp
@@ -81,7 +81,6 @@
"src/suggest/core/session/dic_traverse_session.cpp",
"src/suggest/core/result/suggestion_results.cpp",
"src/suggest/core/result/suggestions_output_utils.cpp",
- "src/suggest/policyimpl/gesture/gesture_suggest_policy_factory.cpp",
"src/suggest/policyimpl/typing/scoring_params.cpp",
"src/suggest/policyimpl/typing/typing_scoring.cpp",
"src/suggest/policyimpl/typing/typing_suggest_policy.cpp",
@@ -93,6 +92,13 @@
"src/utils/log_utils.cpp",
"src/utils/time_keeper.cpp",
+ // Gesture
+ "src/suggest/policyimpl/gesture/gesture_scoring.cpp",
+ "src/suggest/policyimpl/gesture/gesture_suggest_policy.cpp",
+ "src/suggest/policyimpl/gesture/gesture_traversal.cpp",
+ "src/suggest/policyimpl/gesture/gesture_weighting.cpp",
+ "src/suggest/policyimpl/gesture/scoring_params_g.cpp",
+
// BACKWARD_V402
"src/dictionary/structure/backward/v402/ver4_dict_buffers.cpp",
"src/dictionary/structure/backward/v402/ver4_dict_constants.cpp",
diff --git a/native/jni/src/defines.h b/native/jni/src/defines.h
index 1531b6c..8685452 100644
--- a/native/jni/src/defines.h
+++ b/native/jni/src/defines.h
@@ -338,5 +338,6 @@
CT_NEW_WORD_SPACE_OMISSION,
// Create new word with space substitution
CT_NEW_WORD_SPACE_SUBSTITUTION,
+ CT_SKIP,
} CorrectionType;
#endif // LATINIME_DEFINES_H
diff --git a/native/jni/src/suggest/core/policy/traversal.h b/native/jni/src/suggest/core/policy/traversal.h
index 5b6616d..42fb40e 100644
--- a/native/jni/src/suggest/core/policy/traversal.h
+++ b/native/jni/src/suggest/core/policy/traversal.h
@@ -30,6 +30,8 @@
virtual bool isOmission(const DicTraverseSession *const traverseSession,
const DicNode *const dicNode, const DicNode *const childDicNode,
const bool allowsErrorCorrections) const = 0;
+ virtual bool isSkip(const DicTraverseSession *const traverseSession,
+ const DicNode *const dicNode) const = 0;
virtual bool isSpaceSubstitutionTerminal(const DicTraverseSession *const traverseSession,
const DicNode *const dicNode) const = 0;
virtual bool isSpaceOmissionTerminal(const DicTraverseSession *const traverseSession,
diff --git a/native/jni/src/suggest/core/policy/weighting.cpp b/native/jni/src/suggest/core/policy/weighting.cpp
index 450203d..368db04 100644
--- a/native/jni/src/suggest/core/policy/weighting.cpp
+++ b/native/jni/src/suggest/core/policy/weighting.cpp
@@ -134,6 +134,8 @@
return weighting->getInsertionCost(traverseSession, parentDicNode, dicNode);
case CT_TRANSPOSITION:
return weighting->getTranspositionCost(traverseSession, parentDicNode, dicNode);
+ case CT_SKIP:
+ return weighting->getSkipCost(traverseSession, dicNode);
default:
return 0.0f;
}
@@ -199,8 +201,67 @@
return 2; /* look ahead + skip the current char */
case CT_TRANSPOSITION:
return 2; /* look ahead + skip the current char */
+ case CT_SKIP:
+ return 1;
default:
return 0;
}
}
+
+
+ /* static */ void Weighting::addCostAndForwardInputIndexGesture(const Weighting *const weighting,
+ const CorrectionType correctionType, const DicTraverseSession *const traverseSession,
+ const DicNode *const parentDicNode, DicNode *const dicNode,
+ MultiBigramMap *const multiBigramMap) {
+ const int inputSize = traverseSession->getInputSize();
+ DicNode_InputStateG inputStateG;
+ inputStateG.mNeedsToUpdateInputStateG = false; // Don't use input info by default
+ const float spatialCost = Weighting::getSpatialCost(weighting, correctionType,
+ traverseSession, parentDicNode, dicNode, &inputStateG);
+ const float languageCost = Weighting::getLanguageCostGesture(weighting, correctionType,
+ traverseSession, parentDicNode, dicNode, multiBigramMap);
+ const ErrorTypeUtils::ErrorType errorType = weighting->getErrorType(correctionType,
+ traverseSession, parentDicNode, dicNode);
+ if (inputStateG.mNeedsToUpdateInputStateG) {
+ dicNode->updateInputIndexG(&inputStateG);
+ } else {
+ dicNode->forwardInputIndex(0 /*pointerId*/, getForwardInputCount(correctionType), false);
+ }
+ dicNode->addCost(spatialCost, languageCost, weighting->needsToNormalizeCompoundDistance(),
+ inputSize, errorType);
+ }
+
+ /* static */ float Weighting::getLanguageCostGesture(const Weighting *const weighting,
+ const CorrectionType correctionType, const DicTraverseSession *const traverseSession,
+ const DicNode *const parentDicNode, const DicNode *const dicNode,
+ MultiBigramMap *const multiBigramMap) {
+ switch(correctionType) {
+ case CT_OMISSION:
+ return 0.0f;
+ case CT_SUBSTITUTION:
+ return 0.0f;
+ case CT_NEW_WORD_SPACE_OMISSION:
+ return 0;
+ case CT_MATCH:
+ return 0.0f;
+ case CT_COMPLETION:
+ return 0.0f;
+ case CT_TERMINAL: {
+ const float languageImprobability =
+ DicNodeUtils::getBigramNodeImprobability(
+ traverseSession->getDictionaryStructurePolicy(), dicNode, multiBigramMap);
+ return weighting->getTerminalLanguageCost(traverseSession, dicNode, languageImprobability);
+ }
+ case CT_TERMINAL_INSERTION:
+ return 0.0f;
+ case CT_NEW_WORD_SPACE_SUBSTITUTION:
+ return 0;
+ case CT_INSERTION:
+ return 0.0f;
+ case CT_TRANSPOSITION:
+ return 0.0f;
+ default:
+ return 0.0f;
+ }
+ }
} // namespace latinime
diff --git a/native/jni/src/suggest/core/policy/weighting.h b/native/jni/src/suggest/core/policy/weighting.h
index 863c4ea..dc437b3 100644
--- a/native/jni/src/suggest/core/policy/weighting.h
+++ b/native/jni/src/suggest/core/policy/weighting.h
@@ -34,6 +34,11 @@
const DicTraverseSession *const traverseSession,
const DicNode *const parentDicNode, DicNode *const dicNode,
MultiBigramMap *const multiBigramMap);
+ static void addCostAndForwardInputIndexGesture(const Weighting *const weighting,
+ const CorrectionType correctionType,
+ const DicTraverseSession *const traverseSession,
+ const DicNode *const parentDicNode, DicNode *const dicNode,
+ MultiBigramMap *const multiBigramMap);
protected:
virtual float getTerminalSpatialCost(const DicTraverseSession *const traverseSession,
@@ -85,6 +90,9 @@
virtual float getSpaceSubstitutionCost(const DicTraverseSession *const traverseSession,
const DicNode *const dicNode) const = 0;
+ virtual float getSkipCost(const DicTraverseSession *const traverseSession,
+ const DicNode *const dicNode) const = 0;
+
virtual ErrorTypeUtils::ErrorType getErrorType(const CorrectionType correctionType,
const DicTraverseSession *const traverseSession,
const DicNode *const parentDicNode, const DicNode *const dicNode) const = 0;
@@ -103,6 +111,10 @@
const CorrectionType correctionType, const DicTraverseSession *const traverseSession,
const DicNode *const parentDicNode, const DicNode *const dicNode,
MultiBigramMap *const multiBigramMap);
+ static float getLanguageCostGesture(const Weighting *const weighting,
+ const CorrectionType correctionType, const DicTraverseSession *const traverseSession,
+ const DicNode *const parentDicNode, const DicNode *const dicNode,
+ MultiBigramMap *const multiBigramMap);
// TODO: Move to TypingWeighting and GestureWeighting?
static int getForwardInputCount(const CorrectionType correctionType);
};
diff --git a/native/jni/src/suggest/core/result/suggestions_output_utils.cpp b/native/jni/src/suggest/core/result/suggestions_output_utils.cpp
index 7c37241..2312c7c 100644
--- a/native/jni/src/suggest/core/result/suggestions_output_utils.cpp
+++ b/native/jni/src/suggest/core/result/suggestions_output_utils.cpp
@@ -27,11 +27,94 @@
#include "suggest/core/result/suggestion_results.h"
#include "suggest/core/session/dic_traverse_session.h"
#include "suggest/core/suggest_options.h"
+#include "defines.h"
namespace latinime {
const int SuggestionsOutputUtils::MIN_LEN_FOR_MULTI_WORD_AUTOCORRECT = 16;
+ /* static */ void SuggestionsOutputUtils::outputSuggestionsGesture(
+ const Scoring *const scoringPolicy, DicTraverseSession *traverseSession,
+ const float weightOfLangModelVsSpatialModel,
+ SuggestionResults *const outSuggestionResults) {
+#if DEBUG_EVALUATE_MOST_PROBABLE_STRING
+ const int terminalSize = 0;
+#else
+ const int terminalSize = traverseSession->getDicTraverseCache()->terminalSize();
+#endif
+ std::vector<DicNode> terminals(terminalSize);
+ for (int index = terminalSize - 1; index >= 0; --index) {
+ traverseSession->getDicTraverseCache()->popTerminal(&terminals[index]);
+ }
+
+ const bool outputSecondWordFirstLetterInputIndex =
+ traverseSession->isOnlyOnePointerUsed(0 /* pointerId */);
+ const bool boostExactMatches = traverseSession->getDictionaryStructurePolicy()->
+ getHeaderStructurePolicy()->shouldBoostExactMatches();
+
+ // Output suggestion results here
+ for (auto &terminalDicNode : terminals) {
+ outputSuggestionsOfDicNodeGesture(scoringPolicy, traverseSession, &terminalDicNode,
+ 1.0, boostExactMatches,
+ false, outputSecondWordFirstLetterInputIndex, outSuggestionResults);
+ }
+ }
+
+ /* static */ void SuggestionsOutputUtils::outputSuggestionsOfDicNodeGesture(
+ const Scoring *const scoringPolicy, DicTraverseSession *traverseSession,
+ const DicNode *const terminalDicNode, const float weightOfLangModelVsSpatialModel,
+ const bool boostExactMatches, const bool forceCommitMultiWords,
+ const bool outputSecondWordFirstLetterInputIndex,
+ SuggestionResults *const outSuggestionResults) {
+
+
+ const WordAttributes wordAttributes = traverseSession->getDictionaryStructurePolicy()
+ ->getWordAttributesInContext(terminalDicNode->getPrevWordIds(),
+ terminalDicNode->getWordId(), nullptr /* multiBigramMap */);
+ const bool isExactMatch =
+ ErrorTypeUtils::isExactMatch(terminalDicNode->getContainedErrorTypes());
+ const bool isExactMatchWithIntentionalOmission =
+ ErrorTypeUtils::isExactMatchWithIntentionalOmission(
+ terminalDicNode->getContainedErrorTypes());
+ // TODO: Decide whether the word should be auto-corrected or not here.
+ const bool isAppropriateForAutoCorrection = !ErrorTypeUtils::isMissingExplicitAccent(
+ terminalDicNode->getContainedErrorTypes());
+ const int outputTypeFlags =
+ (wordAttributes.isPossiblyOffensive() ? Dictionary::KIND_FLAG_POSSIBLY_OFFENSIVE : 0)
+ | ((isExactMatch && boostExactMatches) ? Dictionary::KIND_FLAG_EXACT_MATCH : 0)
+ | (isExactMatchWithIntentionalOmission ?
+ Dictionary::KIND_FLAG_EXACT_MATCH_WITH_INTENTIONAL_OMISSION : 0)
+ | (isAppropriateForAutoCorrection ?
+ Dictionary::KIND_FLAG_APPROPRIATE_FOR_AUTOCORRECTION : 0);
+ // Entries that are blacklisted or do not represent a word should not be output.
+ const bool isValidWord = !(wordAttributes.isBlacklisted() || wordAttributes.isNotAWord());
+
+ const bool shouldBlockThisWord = shouldBlockWord(traverseSession->getSuggestOptions(),
+ terminalDicNode, wordAttributes, true /* isLastWord */);
+
+ // Increase output score of top typing suggestion to ensure autocorrection.
+ // TODO: Better integration with java side autocorrection logic.
+ const float compoundDistance = terminalDicNode->getCompoundDistance(1);
+ const int finalScore = compoundDistance * -10000.0f;
+
+ // Don't output invalid or blocked offensive words. However, we still need to submit their
+ // shortcuts if any.
+ if (isValidWord && !shouldBlockThisWord) {
+ int codePoints[MAX_WORD_LENGTH];
+ terminalDicNode->outputResult(codePoints);
+ const int indexToPartialCommit = outputSecondWordFirstLetterInputIndex ?
+ terminalDicNode->getSecondWordFirstInputIndex(
+ traverseSession->getProximityInfoState(0)) :
+ NOT_AN_INDEX;
+
+ outSuggestionResults->addSuggestion(codePoints,
+ terminalDicNode->getTotalNodeCodePointCount(),
+ finalScore, Dictionary::KIND_CORRECTION | outputTypeFlags,
+ indexToPartialCommit, computeFirstWordConfidence(terminalDicNode));
+ }
+
+ }
+
/* static */ void SuggestionsOutputUtils::outputSuggestions(
const Scoring *const scoringPolicy, DicTraverseSession *traverseSession,
const float weightOfLangModelVsSpatialModel,
diff --git a/native/jni/src/suggest/core/result/suggestions_output_utils.h b/native/jni/src/suggest/core/result/suggestions_output_utils.h
index bcb75a4..fc0d04e 100644
--- a/native/jni/src/suggest/core/result/suggestions_output_utils.h
+++ b/native/jni/src/suggest/core/result/suggestions_output_utils.h
@@ -45,12 +45,22 @@
DicTraverseSession *traverseSession, const float weightOfLangModelVsSpatialModel,
SuggestionResults *const outSuggestionResults);
+ static void outputSuggestionsGesture(const Scoring *const scoringPolicy,
+ DicTraverseSession *traverseSession, const float weightOfLangModelVsSpatialModel,
+ SuggestionResults *const outSuggestionResults);
+
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(SuggestionsOutputUtils);
// Inputs longer than this will autocorrect if the suggestion is multi-word
static const int MIN_LEN_FOR_MULTI_WORD_AUTOCORRECT;
+ static void outputSuggestionsOfDicNodeGesture(const Scoring *const scoringPolicy,
+ DicTraverseSession *traverseSession, const DicNode *const terminalDicNode,
+ const float weightOfLangModelVsSpatialModel, const bool boostExactMatches,
+ const bool forceCommitMultiWords, const bool outputSecondWordFirstLetterInputIndex,
+ SuggestionResults *const outSuggestionResults);
+
static void outputSuggestionsOfDicNode(const Scoring *const scoringPolicy,
DicTraverseSession *traverseSession, const DicNode *const terminalDicNode,
const float weightOfLangModelVsSpatialModel, const bool boostExactMatches,
diff --git a/native/jni/src/suggest/core/session/dic_traverse_session.cpp b/native/jni/src/suggest/core/session/dic_traverse_session.cpp
index d7dd5a0..4dc0e38 100644
--- a/native/jni/src/suggest/core/session/dic_traverse_session.cpp
+++ b/native/jni/src/suggest/core/session/dic_traverse_session.cpp
@@ -66,7 +66,8 @@
const int maxPointerCount) {
ASSERT(1 <= maxPointerCount && maxPointerCount <= MAX_POINTER_COUNT_G);
mInputSize = 0;
- for (int i = 0; i < maxPointerCount; ++i) {
+ // change MAX_POINTER_COUNT_G to MAX_POINTER_COUNT, not support 2 pointers
+ for (int i = 0; i < MAX_POINTER_COUNT; ++i) {
mProximityInfoStates[i].initInputParams(i, maxSpatialDistance, getProximityInfo(),
inputCodePoints, inputSize, inputXs, inputYs, times, pointerIds,
// Right now the line below is trying to figure out whether this is a gesture by
diff --git a/native/jni/src/suggest/core/suggest.cpp b/native/jni/src/suggest/core/suggest.cpp
index 52fa5a5..95e9987 100644
--- a/native/jni/src/suggest/core/suggest.cpp
+++ b/native/jni/src/suggest/core/suggest.cpp
@@ -52,6 +52,7 @@
PROF_INIT;
PROF_TIMER_START(0);
const float maxSpatialDistance = TRAVERSAL->getMaxSpatialDistance();
+ const bool isGesture = TRAVERSAL->getMaxPointerCount() == MAX_POINTER_COUNT_G;
DicTraverseSession *tSession = static_cast<DicTraverseSession *>(traverseSession);
tSession->setupForGetSuggestions(pInfo, inputCodePoints, inputSize, inputXs, inputYs, times,
pointerIds, maxSpatialDistance, TRAVERSAL->getMaxPointerCount());
@@ -59,19 +60,36 @@
initializeSearch(tSession);
PROF_TIMER_END(0);
- PROF_TIMER_START(1);
- // keep expanding search dicNodes until all have terminated.
- while (tSession->getDicTraverseCache()->activeSize() > 0) {
- expandCurrentDicNodes(tSession);
- tSession->getDicTraverseCache()->advanceActiveDicNodes();
- tSession->getDicTraverseCache()->advanceInputIndex(inputSize);
+ if (isGesture) {
+ PROF_TIMER_START(1);
+
+ // keep expanding search dicNodes until all have terminated.
+ while (tSession->getDicTraverseCache()->activeSize() > 0) {
+ expandCurrentDicNodesGesture(tSession);
+ tSession->getDicTraverseCache()->advanceActiveDicNodes();
+ tSession->getDicTraverseCache()->advanceInputIndex(inputSize);
+ }
+ PROF_TIMER_END(1);
+ PROF_TIMER_START(2);
+ SuggestionsOutputUtils::outputSuggestionsGesture(
+ SCORING, tSession, weightOfLangModelVsSpatialModel, outSuggestionResults);
+ PROF_TIMER_END(2);
+ } else {
+ PROF_TIMER_START(1);
+
+ // keep expanding search dicNodes until all have terminated.
+ while (tSession->getDicTraverseCache()->activeSize() > 0) {
+ expandCurrentDicNodes(tSession);
+ tSession->getDicTraverseCache()->advanceActiveDicNodes();
+ tSession->getDicTraverseCache()->advanceInputIndex(inputSize);
+ }
+ PROF_TIMER_END(1);
+ PROF_TIMER_START(2);
+ SuggestionsOutputUtils::outputSuggestions(
+ SCORING, tSession, weightOfLangModelVsSpatialModel, outSuggestionResults);
+ PROF_TIMER_END(2);
}
- PROF_TIMER_END(1);
- PROF_TIMER_START(2);
- SuggestionsOutputUtils::outputSuggestions(
- SCORING, tSession, weightOfLangModelVsSpatialModel, outSuggestionResults);
- PROF_TIMER_END(2);
}
/**
@@ -441,4 +459,141 @@
traverseSession->getDicTraverseCache()->copyPushNextActive(&newDicNode);
}
}
+
+ //for gesture
+ void Suggest::expandCurrentDicNodesGesture(DicTraverseSession *traverseSession) const {
+ const int inputSize = traverseSession->getInputSize();
+ DicNodeVector childDicNodes(TRAVERSAL->getDefaultExpandDicNodeSize());
+ DicNode correctionDicNode;
+
+ while (traverseSession->getDicTraverseCache()->activeSize() > 0) {
+ DicNode dicNode;
+ traverseSession->getDicTraverseCache()->popActive(&dicNode);
+
+ if (dicNode.isTotalInputSizeExceedingLimit()) {
+ return;
+ }
+
+ // Only consider typing error corrections if the normalized compound distance is
+ // below a spatial distance threshold.
+ // NOTE: the threshold may need to be updated if scoring model changes.
+ const bool allowsErrorCorrections = TRAVERSAL->allowsErrorCorrections(&dicNode);
+ const int point0Index = dicNode.getInputIndex(0);
+ const bool isCompletion = dicNode.isCompletion(inputSize);
+
+ //const bool isSkip = TRAVERSAL->isSkip(traverseSession, &dicNode);
+ if (point0Index > 0 && TRAVERSAL->isSkip(traverseSession, &dicNode)) {
+ correctionDicNode.initByCopy(&dicNode);
+ processDicNodeAsSkipGesture(traverseSession, &correctionDicNode);
+ }
+
+ childDicNodes.clear();
+ DicNodeUtils::getAllChildDicNodes(&dicNode,
+ traverseSession->getDictionaryStructurePolicy(),
+ &childDicNodes);
+ const int childDicNodesSize = childDicNodes.getSizeAndLock();
+ for (int i = 0; i < childDicNodesSize; ++i) {
+ DicNode *const childDicNode = childDicNodes[i];
+ if (isCompletion) {
+ // Handle forward lookahead when the lexicon letter exceeds the input size.
+ processDicNodeAsMatchGesture(traverseSession, childDicNode);
+ continue;
+ }
+
+ if(childDicNode->canBeIntentionalOmission()) {
+ processExpandedDicNodeGesture(traverseSession, childDicNode);
+ continue;
+ }
+
+ if (point0Index > 0 && TRAVERSAL->isOmission(traverseSession, &dicNode, childDicNode,
+ allowsErrorCorrections)) {
+ // TODO: (Gesture) Change weight between omission and substitution errors
+ // TODO: (Gesture) Terminal node should not be handled as omission
+ correctionDicNode.initByCopy(childDicNode);
+ processDicNodeAsOmissionGesture(traverseSession, &correctionDicNode);
+ }
+ const ProximityType proximityType = TRAVERSAL->getProximityType(
+ traverseSession, &dicNode, childDicNode);
+ switch (proximityType) {
+ // TODO: Consider the difference of proximityType here
+ case MATCH_CHAR:
+ processDicNodeAsMatchGesture(traverseSession, childDicNode);
+ break;
+ default:
+ // Just drop this dicNode and do nothing.
+ break;
+ }
+ }
+
+ }
+ }
+
+ void Suggest::processDicNodeAsSkipGesture(DicTraverseSession *traverseSession,
+ DicNode *dicNode) const {
+ Weighting::addCostAndForwardInputIndexGesture(WEIGHTING, CT_SKIP, traverseSession,
+ 0 /* parentDicNode */, dicNode, 0 /* multiBigramMap */);
+ processExpandedDicNodeGesture(traverseSession, dicNode);
+ }
+
+ void Suggest::processDicNodeAsMatchGesture(DicTraverseSession *traverseSession,
+ DicNode *childDicNode) const {
+ weightChildNodeGesture(traverseSession, childDicNode);
+ processExpandedDicNodeGesture(traverseSession, childDicNode);
+ }
+
+ void Suggest::processDicNodeAsOmissionGesture(DicTraverseSession *traverseSession, DicNode *dicNode) const {
+ DicNodeVector childDicNodes;
+ DicNodeUtils::getAllChildDicNodes(
+ dicNode, traverseSession->getDictionaryStructurePolicy(), &childDicNodes);
+
+ const int size = childDicNodes.getSizeAndLock();
+ for (int i = 0; i < size; i++) {
+ DicNode *const childDicNode = childDicNodes[i];
+ // Treat this word as omission
+ if (!TRAVERSAL->isPossibleOmissionChildNode(traverseSession, dicNode, childDicNode)) {
+ continue;
+ }
+
+ Weighting::addCostAndForwardInputIndexGesture(WEIGHTING, CT_OMISSION, traverseSession,
+ dicNode, childDicNode, 0 /* multiBigramMap */);
+ weightChildNodeGesture(traverseSession, childDicNode);
+ processExpandedDicNodeGesture(traverseSession, childDicNode);
+ }
+ }
+
+ void Suggest::weightChildNodeGesture(DicTraverseSession *traverseSession, DicNode *dicNode) const {
+ const int inputSize = traverseSession->getInputSize();
+ if (dicNode->isCompletion(inputSize)) {
+ Weighting::addCostAndForwardInputIndexGesture(WEIGHTING, CT_COMPLETION, traverseSession,
+ 0 /* parentDicNode */, dicNode, 0 /* multiBigramMap */);
+ } else {
+ Weighting::addCostAndForwardInputIndexGesture(WEIGHTING, CT_MATCH, traverseSession,
+ 0 /* parentDicNode */, dicNode, 0 /* multiBigramMap */);
+ }
+ }
+
+ void Suggest::processExpandedDicNodeGesture(
+ DicTraverseSession *traverseSession, DicNode *dicNode) const {
+ processTerminalDicNodeGesture(traverseSession, dicNode);
+ if (dicNode->getCompoundDistance() < static_cast<float>(MAX_VALUE_FOR_WEIGHTING)) {
+ if (dicNode->hasChildren()) {
+ traverseSession->getDicTraverseCache()->copyPushNextActive(dicNode);
+ }
+ }
+ }
+
+ void Suggest::processTerminalDicNodeGesture(
+ DicTraverseSession *traverseSession, DicNode *dicNode) const {
+ if (dicNode->getCompoundDistance() >= static_cast<float>(MAX_VALUE_FOR_WEIGHTING)) {
+ return;
+ }
+ if (!dicNode->isTerminalDicNode()) {
+ return;
+ }
+ // Create a non-cached node here.
+ DicNode terminalDicNode(*dicNode);
+ Weighting::addCostAndForwardInputIndexGesture(WEIGHTING, CT_TERMINAL, traverseSession, 0,
+ &terminalDicNode, traverseSession->getMultiBigramMap());
+ traverseSession->getDicTraverseCache()->copyPushTerminal(&terminalDicNode);
+ }
} // namespace latinime
diff --git a/native/jni/src/suggest/core/suggest.h b/native/jni/src/suggest/core/suggest.h
index 65d5918..39ae104 100644
--- a/native/jni/src/suggest/core/suggest.h
+++ b/native/jni/src/suggest/core/suggest.h
@@ -73,6 +73,15 @@
void processDicNodeAsMatch(DicTraverseSession *traverseSession,
DicNode *childDicNode) const;
+ void expandCurrentDicNodesGesture(DicTraverseSession *traverseSession) const;
+ void processDicNodeAsOmissionGesture(DicTraverseSession *traverseSession, DicNode *dicNode) const;
+ void processDicNodeAsMatchGesture(DicTraverseSession *traverseSession,
+ DicNode *childDicNode) const;
+ void processDicNodeAsSkipGesture(DicTraverseSession *traverseSession, DicNode *dicNode) const;
+ void processExpandedDicNodeGesture(DicTraverseSession *traverseSession, DicNode *dicNode) const;
+ void processTerminalDicNodeGesture(DicTraverseSession *traverseSession, DicNode *dicNode) const;
+ void weightChildNodeGesture(DicTraverseSession *traverseSession, DicNode *dicNode) const;
+
static const int MIN_CONTINUOUS_SUGGESTION_INPUT_SIZE;
const Traversal *const TRAVERSAL;
diff --git a/native/jni/src/suggest/policyimpl/gesture/gesture_scoring.cpp b/native/jni/src/suggest/policyimpl/gesture/gesture_scoring.cpp
new file mode 100644
index 0000000..ba56915
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/gesture/gesture_scoring.cpp
@@ -0,0 +1,10 @@
+//
+// Created by msj on 2017/3/9.
+//
+
+
+#include "suggest/policyimpl/gesture/gesture_scoring.h"
+
+namespace latinime {
+ const GestureScoring GestureScoring::sInstance;
+} // namespace latinime
\ No newline at end of file
diff --git a/native/jni/src/suggest/policyimpl/gesture/gesture_scoring.h b/native/jni/src/suggest/policyimpl/gesture/gesture_scoring.h
new file mode 100644
index 0000000..574c00e
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/gesture/gesture_scoring.h
@@ -0,0 +1,108 @@
+//
+// Created by msj on 2017/3/9.
+//
+
+#ifndef GOOGLEKEYBOARDV7_GESTURE_SCORING_H
+#define GOOGLEKEYBOARDV7_GESTURE_SCORING_H
+
+#include "defines.h"
+#include "suggest/core/dictionary/error_type_utils.h"
+#include "suggest/core/policy/scoring.h"
+#include "suggest/core/session/dic_traverse_session.h"
+#include "suggest/policyimpl/typing/scoring_params.h"
+
+namespace latinime {
+
+ class DicNode;
+ class DicTraverseSession;
+
+ class GestureScoring : public Scoring {
+ public:
+ static const GestureScoring *getInstance() { return &sInstance; }
+
+ AK_FORCE_INLINE void getMostProbableString(const DicTraverseSession *const traverseSession,
+ const float weightOfLangModelVsSpatialModel,
+ SuggestionResults *const outSuggestionResults) const {}
+
+ AK_FORCE_INLINE float getAdjustedWeightOfLangModelVsSpatialModel(
+ DicTraverseSession *const traverseSession, DicNode *const terminals,
+ const int size) const {
+ return 1.0f;
+ }
+
+ AK_FORCE_INLINE int calculateFinalScore(const float compoundDistance, const int inputSize,
+ const ErrorTypeUtils::ErrorType containedErrorTypes, const bool forceCommit,
+ const bool boostExactMatches, const bool hasProbabilityZero) const {
+ const float maxDistance = ScoringParams::DISTANCE_WEIGHT_LANGUAGE
+ + static_cast<float>(inputSize) * ScoringParams::TYPING_MAX_OUTPUT_SCORE_PER_INPUT;
+ float score = ScoringParams::TYPING_BASE_OUTPUT_SCORE - compoundDistance / maxDistance;
+ if (forceCommit) {
+ score += ScoringParams::AUTOCORRECT_OUTPUT_THRESHOLD;
+ }
+ if (hasProbabilityZero) {
+ // Previously, when both legitimate 0-frequency words (such as distracters) and
+ // offensive words were encoded in the same way, distracters would never show up
+ // when the user blocked offensive words (the default setting, as well as the
+ // setting for regression tests).
+ //
+ // When b/11031090 was fixed and a separate encoding was used for offensive words,
+ // 0-frequency words would no longer be blocked when they were an "exact match"
+ // (where case mismatches and accent mismatches would be considered an "exact
+ // match"). The exact match boosting functionality meant that, for example, when
+ // the user typed "mt" they would be suggested the word "Mt", although they most
+ // probably meant to type "my".
+ //
+ // For this reason, we introduced this change, which does the following:
+ // * Defines the "perfect match" as a really exact match, with no room for case or
+ // accent mismatches
+ // * When the target word has probability zero (as "Mt" does, because it is a
+ // distracter), ONLY boost its score if it is a perfect match.
+ //
+ // By doing this, when the user types "mt", the word "Mt" will NOT be boosted, and
+ // they will get "my". However, if the user makes an explicit effort to type "Mt",
+ // we do boost the word "Mt" so that the user's input is not autocorrected to "My".
+ if (boostExactMatches && ErrorTypeUtils::isPerfectMatch(containedErrorTypes)) {
+ score += ScoringParams::PERFECT_MATCH_PROMOTION;
+ }
+ } else {
+ if (boostExactMatches && ErrorTypeUtils::isExactMatch(containedErrorTypes)) {
+ score += ScoringParams::EXACT_MATCH_PROMOTION;
+ if ((ErrorTypeUtils::MATCH_WITH_WRONG_CASE & containedErrorTypes) != 0) {
+ score -= ScoringParams::CASE_ERROR_PENALTY_FOR_EXACT_MATCH;
+ }
+ if ((ErrorTypeUtils::MATCH_WITH_MISSING_ACCENT & containedErrorTypes) != 0) {
+ score -= ScoringParams::ACCENT_ERROR_PENALTY_FOR_EXACT_MATCH;
+ }
+ if ((ErrorTypeUtils::MATCH_WITH_DIGRAPH & containedErrorTypes) != 0) {
+ score -= ScoringParams::DIGRAPH_PENALTY_FOR_EXACT_MATCH;
+ }
+ }
+ }
+ return static_cast<int>(score * SUGGEST_INTERFACE_OUTPUT_SCALE);
+ }
+
+ AK_FORCE_INLINE float getDoubleLetterDemotionDistanceCost(
+ const DicNode *const terminalDicNode) const {
+ return 0.0f;
+ }
+
+ AK_FORCE_INLINE bool autoCorrectsToMultiWordSuggestionIfTop() const {
+ return true;
+ }
+
+ AK_FORCE_INLINE bool sameAsTyped(const DicTraverseSession *const traverseSession,
+ const DicNode *const dicNode) const {
+ return traverseSession->getProximityInfoState(0)->sameAsTyped(
+ dicNode->getOutputWordBuf(), dicNode->getNodeCodePointCount());
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(GestureScoring);
+ static const GestureScoring sInstance;
+
+ GestureScoring() {}
+ ~GestureScoring() {}
+ };
+} // namespace latinime
+
+#endif //GOOGLEKEYBOARDV7_GESTURE_SCORING_H
diff --git a/native/jni/src/suggest/policyimpl/gesture/gesture_suggest_policy.cpp b/native/jni/src/suggest/policyimpl/gesture/gesture_suggest_policy.cpp
new file mode 100644
index 0000000..13c51e9
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/gesture/gesture_suggest_policy.cpp
@@ -0,0 +1,10 @@
+//
+// Created by msj on 2017/3/9.
+//
+
+#include "suggest/policyimpl/gesture/gesture_suggest_policy.h"
+
+namespace latinime {
+ const GestureSuggestPolicy GestureSuggestPolicy::sInstance;
+} // namespace latinime
+
diff --git a/native/jni/src/suggest/policyimpl/gesture/gesture_suggest_policy.h b/native/jni/src/suggest/policyimpl/gesture/gesture_suggest_policy.h
new file mode 100644
index 0000000..8d035a9
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/gesture/gesture_suggest_policy.h
@@ -0,0 +1,50 @@
+//
+// Created by msj on 2017/3/9.
+//
+
+#ifndef GOOGLEKEYBOARDV7_GESTURE_SUGGEST_POLICY_H
+#define GOOGLEKEYBOARDV7_GESTURE_SUGGEST_POLICY_H
+
+#include "defines.h"
+#include "suggest/core/policy/suggest_policy.h"
+#include "suggest/policyimpl/gesture/gesture_scoring.h"
+#include "suggest/policyimpl/gesture/gesture_traversal.h"
+#include "suggest/policyimpl/gesture/gesture_weighting.h"
+
+namespace latinime {
+
+ class Scoring;
+
+ class Traversal;
+
+ class Weighting;
+
+ class GestureSuggestPolicy : public SuggestPolicy {
+ public:
+ static const GestureSuggestPolicy *getInstance() { return &sInstance; }
+
+ GestureSuggestPolicy() { }
+
+ virtual ~GestureSuggestPolicy() { }
+
+ AK_FORCE_INLINE const Traversal *getTraversal() const {
+ return GestureTraversal::getInstance();
+ }
+
+ AK_FORCE_INLINE const Scoring *getScoring() const {
+ return GestureScoring::getInstance();
+ }
+
+ AK_FORCE_INLINE const Weighting *getWeighting() const {
+ return GestureWeighting::getInstance();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(GestureSuggestPolicy);
+
+ static const GestureSuggestPolicy sInstance;
+ };
+
+} // namespace latinime
+
+#endif //GOOGLEKEYBOARDV7_GESTURE_SUGGEST_POLICY_H
diff --git a/native/jni/src/suggest/policyimpl/gesture/gesture_suggest_policy_factory.cpp b/native/jni/src/suggest/policyimpl/gesture/gesture_suggest_policy_factory.cpp
deleted file mode 100644
index 6d31739..0000000
--- a/native/jni/src/suggest/policyimpl/gesture/gesture_suggest_policy_factory.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * 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.
- */
-
-#include "gesture_suggest_policy_factory.h"
-
-namespace latinime {
- const SuggestPolicy *(*GestureSuggestPolicyFactory::sGestureSuggestFactoryMethod)() = 0;
-} // namespace latinime
diff --git a/native/jni/src/suggest/policyimpl/gesture/gesture_suggest_policy_factory.h b/native/jni/src/suggest/policyimpl/gesture/gesture_suggest_policy_factory.h
index 509b01f..6052d9f 100644
--- a/native/jni/src/suggest/policyimpl/gesture/gesture_suggest_policy_factory.h
+++ b/native/jni/src/suggest/policyimpl/gesture/gesture_suggest_policy_factory.h
@@ -18,27 +18,22 @@
#define LATINIME_GESTURE_SUGGEST_POLICY_FACTORY_H
#include "defines.h"
+#include "gesture_suggest_policy.h"
namespace latinime {
class SuggestPolicy;
-class GestureSuggestPolicyFactory {
- public:
- static void setGestureSuggestPolicyFactoryMethod(const SuggestPolicy *(*factoryMethod)()) {
- sGestureSuggestFactoryMethod = factoryMethod;
- }
+ class SuggestPolicy;
- static const SuggestPolicy *getGestureSuggestPolicy() {
- if (!sGestureSuggestFactoryMethod) {
- return 0;
+ class GestureSuggestPolicyFactory {
+ public:
+ static const SuggestPolicy *getGestureSuggestPolicy() {
+ return GestureSuggestPolicy::getInstance();
}
- return sGestureSuggestFactoryMethod();
- }
- private:
- DISALLOW_COPY_AND_ASSIGN(GestureSuggestPolicyFactory);
- static const SuggestPolicy *(*sGestureSuggestFactoryMethod)();
-};
+ private:
+ DISALLOW_COPY_AND_ASSIGN(GestureSuggestPolicyFactory);
+ };
} // namespace latinime
#endif // LATINIME_GESTURE_SUGGEST_POLICY_FACTORY_H
diff --git a/native/jni/src/suggest/policyimpl/gesture/gesture_traversal.cpp b/native/jni/src/suggest/policyimpl/gesture/gesture_traversal.cpp
new file mode 100644
index 0000000..bfe046e
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/gesture/gesture_traversal.cpp
@@ -0,0 +1,10 @@
+//
+// Created by msj on 2017/3/9.
+//
+
+#include "suggest/policyimpl/gesture/gesture_traversal.h"
+
+namespace latinime {
+ const bool GestureTraversal::CORRECT_OMISSION = true;
+ const GestureTraversal GestureTraversal::sInstance;
+} // namespace latinime
\ No newline at end of file
diff --git a/native/jni/src/suggest/policyimpl/gesture/gesture_traversal.h b/native/jni/src/suggest/policyimpl/gesture/gesture_traversal.h
new file mode 100644
index 0000000..fc298db
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/gesture/gesture_traversal.h
@@ -0,0 +1,190 @@
+//
+// Created by msj on 2017/3/9.
+//
+
+#ifndef GOOGLEKEYBOARDV7_GESTURE_TRAVERSAL_H
+#define GOOGLEKEYBOARDV7_GESTURE_TRAVERSAL_H
+
+
+#include <cstdint>
+
+#include "defines.h"
+#include "suggest/core/dicnode/dic_node.h"
+#include "suggest/core/dicnode/dic_node_vector.h"
+#include "suggest/core/layout/proximity_info_state.h"
+#include "suggest/core/layout/proximity_info_utils.h"
+#include "suggest/core/policy/traversal.h"
+#include "suggest/core/session/dic_traverse_session.h"
+#include "suggest/core/suggest_options.h"
+#include "suggest/policyimpl/typing/scoring_params.h"
+#include "suggest/policyimpl/gesture/scoring_params_g.h"
+#include "utils/char_utils.h"
+
+namespace latinime {
+ class GestureTraversal : public Traversal {
+ public:
+ static const GestureTraversal *getInstance() { return &sInstance; }
+
+ int getMaxPointerCount() const {
+ return MAX_POINTER_COUNT_G;
+ }
+
+ float getMaxSpatialDistance() const {
+ return ScoringParams::MAX_SPATIAL_DISTANCE;
+ }
+
+ int getDefaultExpandDicNodeSize() const {
+ return DicNodeVector::DEFAULT_NODES_SIZE_FOR_OPTIMIZATION;
+ }
+
+ bool allowsErrorCorrections(const DicNode *const dicNode) const {
+ return dicNode->getNormalizedSpatialDistance()
+ < ScoringParamsG::NORMALIZED_SPATIAL_DISTANCE_THRESHOLD_FOR_EDIT;
+ }
+
+ bool isSkip(const DicTraverseSession *const traverseSession,
+ const DicNode *const dicNode) const {
+ const int inputSize = traverseSession->getInputSize();
+ const int point0Index = dicNode->getInputIndex(0);
+ if(point0Index < inputSize) {
+ const float probability = traverseSession->getProximityInfoState(0)->getProbability(point0Index, NOT_AN_INDEX);
+ if(probability < ScoringParamsG::THRESHOLD_FOR_SKIP) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool isOmission(const DicTraverseSession *const traverseSession,
+ const DicNode *const dicNode, const DicNode *const childDicNode,
+ const bool allowsErrorCorrections) const {
+ if (!CORRECT_OMISSION) {
+ return false;
+ }
+ // Note: Always consider intentional omissions (like apostrophes) since they are common.
+ const bool canConsiderOmission = allowsErrorCorrections;
+ if (!canConsiderOmission) {
+ return false;
+ }
+ const int inputSize = traverseSession->getInputSize();
+ // TODO: Don't refer to isCompletion?
+ if (dicNode->isCompletion(inputSize)) {
+ return false;
+ }
+ if (dicNode->canBeIntentionalOmission()) {
+ return true;
+ }
+ const int point0Index = dicNode->getInputIndex(0);
+ const int currentBaseLowerCodePoint =
+ CharUtils::toBaseLowerCase(childDicNode->getNodeCodePoint());
+ const int typedBaseLowerCodePoint =
+ CharUtils::toBaseLowerCase(traverseSession->getProximityInfoState(0)
+ ->getPrimaryCodePointAt(point0Index));
+ return (currentBaseLowerCodePoint != typedBaseLowerCodePoint);
+ }
+
+ ProximityType getProximityType(
+ const DicTraverseSession *const traverseSession, const DicNode *const dicNode,
+ const DicNode *const childDicNode) const {
+ return traverseSession->getProximityInfoState(0)->getProximityTypeG(
+ dicNode->getInputIndex(0), childDicNode->getNodeCodePoint());
+ }
+
+ int getMaxCacheSize(const int inputSize, const float weightForLocale) const {
+ if (inputSize <= 1) {
+ return ScoringParamsG::MAX_CACHE_DIC_NODE_SIZE_FOR_SINGLE_POINT;
+ }
+ if (weightForLocale < ScoringParams::LOCALE_WEIGHT_THRESHOLD_FOR_SMALL_CACHE_SIZE) {
+ return ScoringParams::MAX_CACHE_DIC_NODE_SIZE_FOR_LOW_PROBABILITY_LOCALE;
+ }
+ return ScoringParamsG::MAX_CACHE_DIC_NODE_SIZE;
+ }
+
+ int getTerminalCacheSize() const {
+ return MAX_RESULTS;
+ }
+
+ bool isPossibleOmissionChildNode(
+ const DicTraverseSession *const traverseSession, const DicNode *const parentDicNode,
+ const DicNode *const dicNode) const {
+ const ProximityType proximityType =
+ getProximityType(traverseSession, parentDicNode, dicNode);
+ if (!ProximityInfoUtils::isMatchOrProximityChar(proximityType)) {
+ return false;
+ }
+ return true;
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+ AK_FORCE_INLINE bool isSpaceSubstitutionTerminal(
+ const DicTraverseSession *const traverseSession, const DicNode *const dicNode) const {
+ return false;
+ }
+
+ AK_FORCE_INLINE bool isSpaceOmissionTerminal(
+ const DicTraverseSession *const traverseSession, const DicNode *const dicNode) const {
+ return false;
+ }
+
+ AK_FORCE_INLINE bool shouldDepthLevelCache(
+ const DicTraverseSession *const traverseSession) const {
+ const int inputSize = traverseSession->getInputSize();
+ return traverseSession->isCacheBorderForTyping(inputSize);
+ }
+
+ AK_FORCE_INLINE bool shouldNodeLevelCache(
+ const DicTraverseSession *const traverseSession, const DicNode *const dicNode) const {
+ return false;
+ }
+
+ AK_FORCE_INLINE bool canDoLookAheadCorrection(
+ const DicTraverseSession *const traverseSession, const DicNode *const dicNode) const {
+ const int inputSize = traverseSession->getInputSize();
+ return dicNode->canDoLookAheadCorrection(inputSize);
+ }
+
+ AK_FORCE_INLINE bool needsToTraverseAllUserInput() const {
+ return true;
+ }
+
+
+
+
+
+
+
+ AK_FORCE_INLINE bool isGoodToTraverseNextWord(const DicNode *const dicNode,
+ const int probability) const {
+ if (probability < ScoringParams::THRESHOLD_NEXT_WORD_PROBABILITY) {
+ return false;
+ }
+ const bool shortCappedWord = dicNode->getNodeCodePointCount()
+ < ScoringParams::THRESHOLD_SHORT_WORD_LENGTH && dicNode->isFirstCharUppercase();
+ return !shortCappedWord
+ || probability >= ScoringParams::THRESHOLD_NEXT_WORD_PROBABILITY_FOR_CAPPED;
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(GestureTraversal);
+ static const bool CORRECT_OMISSION;
+ static const GestureTraversal sInstance;
+
+ GestureTraversal() {}
+ ~GestureTraversal() {}
+ };
+} // namespace latinime
+
+
+
+#endif //GOOGLEKEYBOARDV7_GESTURE_TRAVERSAL_H
diff --git a/native/jni/src/suggest/policyimpl/gesture/gesture_weighting.cpp b/native/jni/src/suggest/policyimpl/gesture/gesture_weighting.cpp
new file mode 100644
index 0000000..da12418
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/gesture/gesture_weighting.cpp
@@ -0,0 +1,97 @@
+//
+// Created by msj on 2017/3/9.
+//
+
+#include "suggest/policyimpl/gesture/gesture_weighting.h"
+
+#include "suggest/core/dicnode/dic_node.h"
+#include "suggest/core/layout/proximity_info.h"
+#include "suggest/policyimpl/typing/scoring_params.h"
+
+namespace latinime {
+
+ const GestureWeighting GestureWeighting::sInstance;
+
+ ErrorTypeUtils::ErrorType GestureWeighting::getErrorType(const CorrectionType correctionType,
+ const DicTraverseSession *const traverseSession, const DicNode *const parentDicNode,
+ const DicNode *const dicNode) const {
+ switch (correctionType) {
+ case CT_MATCH:
+ if (isProximityDicNode(traverseSession, dicNode)) {
+ return ErrorTypeUtils::PROXIMITY_CORRECTION;
+ } else if (dicNode->isInDigraph()) {
+ return ErrorTypeUtils::MATCH_WITH_DIGRAPH;
+ } else {
+ // Compare the node code point with original primary code point on the keyboard.
+ const ProximityInfoState *const pInfoState =
+ traverseSession->getProximityInfoState(0);
+ const int primaryCodePoint = pInfoState->getPrimaryCodePointAt(
+ dicNode->getInputIndex(0));
+ const int nodeCodePoint = dicNode->getNodeCodePoint();
+ const int keyIndex = traverseSession->getProximityInfo()->getKeyIndexOf(
+ primaryCodePoint);
+ // TODO: Check whether the input code point is on the keyboard.
+ if (primaryCodePoint == nodeCodePoint) {
+ // Node code point is same as original code point on the keyboard.
+ return ErrorTypeUtils::NOT_AN_ERROR;
+ } else if (CharUtils::toLowerCase(primaryCodePoint) ==
+ CharUtils::toLowerCase(nodeCodePoint)) {
+ // Only cases of the code points are different.
+ return ErrorTypeUtils::MATCH_WITH_WRONG_CASE;
+ } else if (primaryCodePoint == CharUtils::toBaseCodePoint(nodeCodePoint)) {
+ // Node code point is a variant of original code point.
+ return ErrorTypeUtils::MATCH_WITH_MISSING_ACCENT;
+ } else if (CharUtils::toBaseCodePoint(primaryCodePoint)
+ == CharUtils::toBaseCodePoint(nodeCodePoint)) {
+ // Base code points are the same but the code point is intentionally input.
+ if (keyIndex == NOT_AN_INDEX) {
+ return ErrorTypeUtils::MATCH_WITH_MISSING_EXPLICIT_ACCENT;
+ }
+ return ErrorTypeUtils::MATCH_WITH_WRONG_ACCENT;
+ } else if (CharUtils::toLowerCase(primaryCodePoint)
+ == CharUtils::toBaseLowerCase(nodeCodePoint)) {
+ // Node code point is a variant of original code point and the cases are also
+ // different.
+ return ErrorTypeUtils::MATCH_WITH_MISSING_ACCENT
+ | ErrorTypeUtils::MATCH_WITH_WRONG_CASE;
+ } else {
+ if (keyIndex == NOT_AN_INDEX) {
+ return ErrorTypeUtils::MATCH_WITH_MISSING_EXPLICIT_ACCENT
+ | ErrorTypeUtils::MATCH_WITH_WRONG_CASE;
+ }
+ // Base code points are the same and the cases are different.
+ return ErrorTypeUtils::MATCH_WITH_WRONG_ACCENT
+ | ErrorTypeUtils::MATCH_WITH_WRONG_CASE;
+ }
+ }
+ break;
+ case CT_ADDITIONAL_PROXIMITY:
+ // TODO: Change to EDIT_CORRECTION.
+ return ErrorTypeUtils::PROXIMITY_CORRECTION;
+ case CT_OMISSION:
+ if (parentDicNode->canBeIntentionalOmission()) {
+ return ErrorTypeUtils::INTENTIONAL_OMISSION;
+ } else {
+ return ErrorTypeUtils::EDIT_CORRECTION;
+ }
+ break;
+ case CT_SUBSTITUTION:
+ // TODO: Quit settng PROXIMITY_CORRECTION.
+ return ErrorTypeUtils::EDIT_CORRECTION | ErrorTypeUtils::PROXIMITY_CORRECTION;
+ case CT_INSERTION:
+ case CT_TERMINAL_INSERTION:
+ case CT_TRANSPOSITION:
+ return ErrorTypeUtils::EDIT_CORRECTION;
+ case CT_NEW_WORD_SPACE_OMISSION:
+ case CT_NEW_WORD_SPACE_SUBSTITUTION:
+ return ErrorTypeUtils::NEW_WORD;
+ case CT_TERMINAL:
+ return ErrorTypeUtils::NOT_AN_ERROR;
+ case CT_COMPLETION:
+ return ErrorTypeUtils::COMPLETION;
+ default:
+ return ErrorTypeUtils::NOT_AN_ERROR;
+ }
+ }
+} // namespace latinime
+
diff --git a/native/jni/src/suggest/policyimpl/gesture/gesture_weighting.h b/native/jni/src/suggest/policyimpl/gesture/gesture_weighting.h
new file mode 100644
index 0000000..869acae
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/gesture/gesture_weighting.h
@@ -0,0 +1,194 @@
+//
+// Created by msj on 2017/3/9.
+//
+
+#ifndef GOOGLEKEYBOARDV7_GESTURE_WEIGHTING_H
+#define GOOGLEKEYBOARDV7_GESTURE_WEIGHTING_H
+
+
+
+#include "defines.h"
+#include "suggest/core/dicnode/dic_node_utils.h"
+#include "suggest/core/dictionary/error_type_utils.h"
+#include "suggest/core/layout/touch_position_correction_utils.h"
+#include "suggest/core/layout/proximity_info.h"
+#include "suggest/core/policy/weighting.h"
+#include "suggest/core/session/dic_traverse_session.h"
+#include "suggest/policyimpl/typing/scoring_params.h"
+#include "suggest/policyimpl/gesture/scoring_params_g.h"
+#include "utils/char_utils.h"
+
+namespace latinime {
+
+ class DicNode;
+ struct DicNode_InputStateG;
+ class MultiBigramMap;
+
+ class GestureWeighting : public Weighting {
+ public:
+ static const GestureWeighting *getInstance() { return &sInstance; }
+
+ protected:
+ float getTerminalSpatialCost(const DicTraverseSession *const traverseSession,
+ const DicNode *const dicNode) const {
+ const int point0index = dicNode->getInputIndex(0);
+ const int tmpInputSize = traverseSession->getInputSize();
+ float cost = 0;
+ for (int i = point0index; i < tmpInputSize; i++) {
+ cost += traverseSession->getProximityInfoState(0)->getProbability(i, NOT_AN_INDEX);
+ }
+ if (point0index > tmpInputSize) {
+ cost += (point0index - tmpInputSize) * ScoringParamsG::DISTANCE_WEIGHT_EXCEEDING_INPUT_SIZE;
+ }
+
+ return cost;
+ }
+
+ float getTerminalLanguageCost(const DicTraverseSession *const traverseSession,
+ const DicNode *const dicNode, const float dicNodeLanguageImprobability) const {
+ return dicNodeLanguageImprobability * ScoringParamsG::DISTANCE_WEIGHT_LANGUAGE;
+ }
+
+ float getCompletionCost(const DicTraverseSession *const traverseSession,
+ const DicNode *const dicNode) const {
+ // The auto completion starts when the input index is same as the input size
+ const bool firstCompletion = dicNode->getInputIndex(0)
+ == traverseSession->getInputSize();
+ // TODO: Change the cost for the first completion for the gesture?
+ const float cost = firstCompletion ? ScoringParamsG::COST_FIRST_COMPLETION
+ : ScoringParamsG::COST_COMPLETION;
+ return cost;
+ }
+
+ float getSkipCost(const DicTraverseSession *const traverseSession,
+ const DicNode *const dicNode) const {
+ const int pointIndex = dicNode->getInputIndex(0);
+
+ float probability = traverseSession->getProximityInfoState(0)->getProbability(pointIndex, NOT_AN_INDEX);
+
+ return probability;
+ }
+
+ float getOmissionCost(const DicNode *const parentDicNode, const DicNode *const dicNode) const {
+ return (dicNode->isSameNodeCodePoint(parentDicNode))
+ ? ScoringParamsG::OMISSION_COST_SAME_CHAR
+ : ScoringParamsG::OMISSION_COST;
+ }
+
+ float getMatchedCost(const DicTraverseSession *const traverseSession,
+ const DicNode *const dicNode, DicNode_InputStateG *inputStateG) const {
+ const int pointIndex = dicNode->getInputIndex(0);
+ const int baseChar = CharUtils::toBaseLowerCase(dicNode->getNodeCodePoint());
+ const int keyId = traverseSession->getProximityInfo()->getKeyIndexOf(baseChar);
+ const float probability = traverseSession-> getProximityInfoState(0)->getProbability(pointIndex, keyId);
+
+ return probability;
+ }
+
+ bool isProximityDicNode(const DicTraverseSession *const traverseSession,
+ const DicNode *const dicNode) const {
+ const int pointIndex = dicNode->getInputIndex(0);
+ const int primaryCodePoint = CharUtils::toBaseLowerCase(
+ traverseSession->getProximityInfoState(0)->getPrimaryCodePointAt(pointIndex));
+ const int dicNodeChar = CharUtils::toBaseLowerCase(dicNode->getNodeCodePoint());
+ return primaryCodePoint != dicNodeChar;
+ }
+
+ float getTranspositionCost(const DicTraverseSession *const traverseSession,
+ const DicNode *const parentDicNode, const DicNode *const dicNode) const {
+ const int16_t parentPointIndex = parentDicNode->getInputIndex(0);
+ const int prevCodePoint = parentDicNode->getNodeCodePoint();
+ const float distance1 = traverseSession->getProximityInfoState(0)->getPointToKeyLength(
+ parentPointIndex + 1, CharUtils::toBaseLowerCase(prevCodePoint));
+ const int codePoint = dicNode->getNodeCodePoint();
+ const float distance2 = traverseSession->getProximityInfoState(0)->getPointToKeyLength(
+ parentPointIndex, CharUtils::toBaseLowerCase(codePoint));
+ const float distance = distance1 + distance2;
+ const float weightedLengthDistance =
+ distance * ScoringParams::DISTANCE_WEIGHT_LENGTH;
+ return ScoringParams::TRANSPOSITION_COST + weightedLengthDistance;
+ }
+
+ float getInsertionCost(const DicTraverseSession *const traverseSession,
+ const DicNode *const parentDicNode, const DicNode *const dicNode) const {
+ const int16_t insertedPointIndex = parentDicNode->getInputIndex(0);
+ const int prevCodePoint = traverseSession->getProximityInfoState(0)->getPrimaryCodePointAt(
+ insertedPointIndex);
+ const int currentCodePoint = dicNode->getNodeCodePoint();
+ const bool sameCodePoint = prevCodePoint == currentCodePoint;
+ const bool existsAdjacentProximityChars = traverseSession->getProximityInfoState(0)
+ ->existsAdjacentProximityChars(insertedPointIndex);
+ const float dist = traverseSession->getProximityInfoState(0)->getPointToKeyLength(
+ insertedPointIndex + 1, CharUtils::toBaseLowerCase(dicNode->getNodeCodePoint()));
+ const float weightedDistance = dist * ScoringParams::DISTANCE_WEIGHT_LENGTH;
+ const bool singleChar = dicNode->getNodeCodePointCount() == 1;
+ float cost = (singleChar ? ScoringParams::INSERTION_COST_FIRST_CHAR : 0.0f);
+ if (sameCodePoint) {
+ cost += ScoringParams::INSERTION_COST_SAME_CHAR;
+ } else if (existsAdjacentProximityChars) {
+ cost += ScoringParams::INSERTION_COST_PROXIMITY_CHAR;
+ } else {
+ cost += ScoringParams::INSERTION_COST;
+ }
+ return cost + weightedDistance;
+ }
+
+ float getSpaceOmissionCost(const DicTraverseSession *const traverseSession,
+ const DicNode *const dicNode, DicNode_InputStateG *inputStateG) const {
+ const float cost = ScoringParams::SPACE_OMISSION_COST;
+ return cost * traverseSession->getMultiWordCostMultiplier();
+ }
+
+ float getNewWordBigramLanguageCost(const DicTraverseSession *const traverseSession,
+ const DicNode *const dicNode,
+ MultiBigramMap *const multiBigramMap) const {
+ return DicNodeUtils::getBigramNodeImprobability(
+ traverseSession->getDictionaryStructurePolicy(),
+ dicNode, multiBigramMap) * ScoringParams::DISTANCE_WEIGHT_LANGUAGE;
+ }
+
+ float getTerminalInsertionCost(const DicTraverseSession *const traverseSession,
+ const DicNode *const dicNode) const {
+ const int inputIndex = dicNode->getInputIndex(0);
+ const int inputSize = traverseSession->getInputSize();
+ ASSERT(inputIndex < inputSize);
+ // TODO: Implement more efficient logic
+ return ScoringParams::TERMINAL_INSERTION_COST * (inputSize - inputIndex);
+ }
+
+ AK_FORCE_INLINE bool needsToNormalizeCompoundDistance() const {
+ return false;
+ }
+
+ AK_FORCE_INLINE float getAdditionalProximityCost() const {
+ return ScoringParams::ADDITIONAL_PROXIMITY_COST;
+ }
+
+ AK_FORCE_INLINE float getSubstitutionCost() const {
+ return ScoringParams::SUBSTITUTION_COST;
+ }
+
+ AK_FORCE_INLINE float getSpaceSubstitutionCost(const DicTraverseSession *const traverseSession,
+ const DicNode *const dicNode) const {
+ const int inputIndex = dicNode->getInputIndex(0);
+ const float distanceToSpaceKey = traverseSession->getProximityInfoState(0)
+ ->getPointToKeyLength(inputIndex, KEYCODE_SPACE);
+ const float cost = ScoringParams::SPACE_SUBSTITUTION_COST * distanceToSpaceKey;
+ return cost * traverseSession->getMultiWordCostMultiplier();
+ }
+
+ ErrorTypeUtils::ErrorType getErrorType(const CorrectionType correctionType,
+ const DicTraverseSession *const traverseSession,
+ const DicNode *const parentDicNode, const DicNode *const dicNode) const;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(GestureWeighting);
+ static const GestureWeighting sInstance;
+
+ GestureWeighting() {}
+ ~GestureWeighting() {}
+ };
+} // namespace latinime
+
+
+#endif //GOOGLEKEYBOARDV7_GESTURE_WEIGHTING_H
diff --git a/native/jni/src/suggest/policyimpl/gesture/scoring_params_g.cpp b/native/jni/src/suggest/policyimpl/gesture/scoring_params_g.cpp
new file mode 100644
index 0000000..28712cc
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/gesture/scoring_params_g.cpp
@@ -0,0 +1,30 @@
+//
+// Created by msj on 2017/3/14.
+//
+
+#include "suggest/policyimpl/gesture/scoring_params_g.h"
+
+namespace latinime {
+ const float ScoringParamsG::COST_FIRST_COMPLETION = 1.0f;
+ const float ScoringParamsG::COST_COMPLETION = 0.5f;
+ const float ScoringParamsG::NORMALIZED_SPATIAL_DISTANCE_THRESHOLD_FOR_EDIT = 0.5f;
+
+ const float ScoringParamsG::OMISSION_COST_SAME_CHAR = 0.2f;
+ const float ScoringParamsG::OMISSION_COST = 1.5f;
+
+ // 候选项中超出采样长度的长度的惩罚权重
+ const float ScoringParamsG::DISTANCE_WEIGHT_EXCEEDING_INPUT_SIZE = 1.0f;
+
+
+
+ // 这个是 词频补 的权重,和距离信息的负对数组成最终的得分
+ const float ScoringParamsG::DISTANCE_WEIGHT_LANGUAGE = 4.0f;
+
+ // 优先队列的长度,影响遍历词典的 检全率
+ const int ScoringParamsG::MAX_CACHE_DIC_NODE_SIZE = 80;
+ const int ScoringParamsG::MAX_CACHE_DIC_NODE_SIZE_FOR_SINGLE_POINT = 310;
+
+ // 该点是否跳过的阈值,含义为概率的负对数
+ const float ScoringParamsG::THRESHOLD_FOR_SKIP = 1.5f;
+} // namespace latinime
+
diff --git a/native/jni/src/suggest/policyimpl/gesture/scoring_params_g.h b/native/jni/src/suggest/policyimpl/gesture/scoring_params_g.h
new file mode 100644
index 0000000..d681fba
--- /dev/null
+++ b/native/jni/src/suggest/policyimpl/gesture/scoring_params_g.h
@@ -0,0 +1,35 @@
+//
+// Created by msj on 2017/3/14.
+//
+
+#ifndef GOOGLEKEYBOARDV7_SCORING_PARAMS_G_H
+#define GOOGLEKEYBOARDV7_SCORING_PARAMS_G_H
+
+#include "defines.h"
+
+namespace latinime {
+ class ScoringParamsG {
+ public:
+ static const float COST_FIRST_COMPLETION;
+ static const float COST_COMPLETION;
+ static const float NORMALIZED_SPATIAL_DISTANCE_THRESHOLD_FOR_EDIT;
+
+ static const float OMISSION_COST_SAME_CHAR;
+ static const float OMISSION_COST;
+
+ static const float DISTANCE_WEIGHT_EXCEEDING_INPUT_SIZE;
+
+ static const float DISTANCE_WEIGHT_LANGUAGE;
+
+ static const int MAX_CACHE_DIC_NODE_SIZE;
+ static const int MAX_CACHE_DIC_NODE_SIZE_FOR_SINGLE_POINT;
+
+ static const float THRESHOLD_FOR_SKIP;
+ private:
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ScoringParamsG);
+ };
+} // namespace latinime
+
+
+
+#endif //GOOGLEKEYBOARDV7_SCORING_PARAMS_G_H
diff --git a/native/jni/src/suggest/policyimpl/typing/typing_traversal.h b/native/jni/src/suggest/policyimpl/typing/typing_traversal.h
index b9b6314..0d381db 100644
--- a/native/jni/src/suggest/policyimpl/typing/typing_traversal.h
+++ b/native/jni/src/suggest/policyimpl/typing/typing_traversal.h
@@ -73,6 +73,11 @@
return (currentBaseLowerCodePoint != typedBaseLowerCodePoint);
}
+ AK_FORCE_INLINE bool isSkip(const DicTraverseSession *const traverseSession,
+ const DicNode *const dicNode) const {
+ return false;
+ }
+
AK_FORCE_INLINE bool isSpaceSubstitutionTerminal(
const DicTraverseSession *const traverseSession, const DicNode *const dicNode) const {
if (!CORRECT_NEW_WORD_SPACE_SUBSTITUTION) {
diff --git a/native/jni/src/suggest/policyimpl/typing/typing_weighting.h b/native/jni/src/suggest/policyimpl/typing/typing_weighting.h
index 1338ac8..ff402b2 100644
--- a/native/jni/src/suggest/policyimpl/typing/typing_weighting.h
+++ b/native/jni/src/suggest/policyimpl/typing/typing_weighting.h
@@ -52,6 +52,11 @@
return cost;
}
+ float getSkipCost(const DicTraverseSession *const traverseSession,
+ const DicNode *const dicNode) const {
+ return 0;
+ }
+
float getOmissionCost(const DicNode *const parentDicNode, const DicNode *const dicNode) const {
const bool isZeroCostOmission = parentDicNode->isZeroCostOmission();
const bool isIntentionalOmission = parentDicNode->canBeIntentionalOmission();