app/src/main/java/fr/neamar/kiss/utils/FuzzyScore.java
/**
     * @param text string converted to codepoints
     * @return true if each character in pattern is found sequentially within text
     */
@param text string converted to codepoints
@return true if each character in pattern is found sequentially within text
@param text string converted to codepoints @return true if each character in pattern is found sequentially within text
public MatchInfo match(int[] text) {
        int score = 0;
        int patternIdx = 0;
        int strIdx = 0;
        int strLength = text.length;
        boolean fullWord = false;
        boolean prevMatched = false;
        boolean prevLower = false;
        boolean prevSeparator = true;       
        Integer bestLetter = null;
        Integer bestLower = null;
        Integer bestLetterIdx = null;
        int bestLetterScore = 0;
        if (matchInfo.matchedIndices != null) {
            matchInfo.matchedIndices.clear();
        }
        while (strIdx != strLength) {
            Integer patternChar = null;
            Integer patternLower = null;
            if (patternIdx != patternLength) {
                patternChar = this.patternChar[patternIdx];
                patternLower = this.patternLower[patternIdx];
            }
            int strChar = text[strIdx];
            int strLower = Character.toLowerCase(strChar);
            int strUpper = Character.toUpperCase(strChar);
            boolean isWhitespace = Character.isWhitespace(strChar);
            boolean nextMatch = patternChar != null && patternLower == strLower;
            boolean rematch = bestLetter != null && bestLower == strLower;
            boolean advanced = nextMatch && bestLetter != null;
            boolean patternRepeat = bestLetter != null && patternChar != null && patternLower.equals(bestLower);
            if (advanced || patternRepeat) {
                score += bestLetterScore;
                if (matchInfo.matchedIndices != null) {
                    matchInfo.matchedIndices.add(bestLetterIdx);
                }
                bestLetter = null;
                bestLower = null;
                bestLetterIdx = null;
                bestLetterScore = 0;
            }
            if (isWhitespace && fullWord) {
                score += full_word_bonus;
            }
            if (nextMatch || rematch) {
                int newScore = 0;
                if (patternIdx == 0) {
                    int penalty = Math.max(strIdx * leading_letter_penalty, max_leading_letter_penalty);
                    score += penalty;
                }
                if (prevMatched && !rematch)
                    newScore += adjacency_bonus;
                if (prevSeparator)
                    newScore += separator_bonus;
                if (prevLower && strChar == strUpper && strLower != strUpper)
                    newScore += camel_bonus;
                if (nextMatch)
                    ++patternIdx;
                if (newScore >= bestLetterScore) {
                    if (bestLetter != null)
                        score += unmatched_letter_penalty;
                    bestLetter = strChar;
                    bestLower = strLower;
                    bestLetterIdx = strIdx;
                    bestLetterScore = newScore;
                    if (prevSeparator)
                        fullWord = true;
                }
                prevMatched = true;
            } else {
                score += unmatched_letter_penalty;
                prevMatched = false;
                fullWord = false;
            }
            prevLower = strChar == strLower && strLower != strUpper;
            prevSeparator = isWhitespace;
            ++strIdx;
        }
        if (bestLetter != null) {
            score += bestLetterScore;
            if (matchInfo.matchedIndices != null) {
                matchInfo.matchedIndices.add(bestLetterIdx);
            }
        }
        if (fullWord) {
            score += full_word_bonus;
        }
        matchInfo.match = patternIdx == patternLength;
        if (matchInfo.match) {
            matchInfo.score = score;
        }
        return matchInfo;
    }
public MatchInfo match ( int [ ] text ) { int score = 0 ; int patternIdx = 0 ; int strIdx = 0 ; int strLength = text . length ; boolean fullWord = false ; boolean prevMatched = false ; boolean prevLower = false ; boolean prevSeparator = true ; Integer bestLetter = null ; Integer bestLower = null ; Integer bestLetterIdx = null ; int bestLetterScore = 0 ; if ( matchInfo . matchedIndices != null ) { matchInfo . matchedIndices . clear ( ) ; } while ( strIdx != strLength ) { Integer patternChar = null ; Integer patternLower = null ; if ( patternIdx != patternLength ) { patternChar = this . patternChar [ patternIdx ] ; patternLower = this . patternLower [ patternIdx ] ; } int strChar = text [ strIdx ] ; int strLower = Character . toLowerCase ( strChar ) ; int strUpper = Character . toUpperCase ( strChar ) ; boolean isWhitespace = Character . isWhitespace ( strChar ) ; boolean nextMatch = patternChar != null && patternLower == strLower ; boolean rematch = bestLetter != null && bestLower == strLower ; boolean advanced = nextMatch && bestLetter != null ; boolean patternRepeat = bestLetter != null && patternChar != null && patternLower . equals ( bestLower ) ; if ( advanced || patternRepeat ) { score += bestLetterScore ; if ( matchInfo . matchedIndices != null ) { matchInfo . matchedIndices . add ( bestLetterIdx ) ; } bestLetter = null ; bestLower = null ; bestLetterIdx = null ; bestLetterScore = 0 ; } if ( isWhitespace && fullWord ) { score += full_word_bonus ; } if ( nextMatch || rematch ) { int newScore = 0 ; if ( patternIdx == 0 ) { int penalty = Math . max ( strIdx * leading_letter_penalty , max_leading_letter_penalty ) ; score += penalty ; } if ( prevMatched && ! rematch ) newScore += adjacency_bonus ; if ( prevSeparator ) newScore += separator_bonus ; if ( prevLower && strChar == strUpper && strLower != strUpper ) newScore += camel_bonus ; if ( nextMatch ) ++ patternIdx ; if ( newScore >= bestLetterScore ) { if ( bestLetter != null ) score += unmatched_letter_penalty ; bestLetter = strChar ; bestLower = strLower ; bestLetterIdx = strIdx ; bestLetterScore = newScore ; if ( prevSeparator ) fullWord = true ; } prevMatched = true ; } else { score += unmatched_letter_penalty ; prevMatched = false ; fullWord = false ; } prevLower = strChar == strLower && strLower != strUpper ; prevSeparator = isWhitespace ; ++ strIdx ; } if ( bestLetter != null ) { score += bestLetterScore ; if ( matchInfo . matchedIndices != null ) { matchInfo . matchedIndices . add ( bestLetterIdx ) ; } } if ( fullWord ) { score += full_word_bonus ; } matchInfo . match = patternIdx == patternLength ; if ( matchInfo . match ) { matchInfo . score = score ; } return matchInfo ; }
@param text string converted to codepoints
 @return true if each character in pattern is found sequentially within text
@param text string converted to codepoints @return true if each character in pattern is found sequentially within text
// Loop variables
 // true so if first letter match gets separator bonus
 // Use "best" matched letter if multiple string letters match the pattern
 // Loop over strings
 // Current char is a separator and we have matched all the previous characters, apply
 // the full match bonus
 // Apply penalty for each letter before the first pattern match
 // Note: std::max because penalties are negative values. So max is smallest penalty.
 // Apply bonus for consecutive bonuses
 // Apply bonus for matches after a separator
 // Apply bonus across camel case boundaries. Includes "clever" isLetter check.
 // Update pattern index IF the next pattern letter was matched
 // Update best letter in text which may be for a "next" letter or a "rematch"
 // Apply penalty for now skipped letter
 // Includes "clever" isLetter check.
 // Apply score for last match
 // Last word full match bonus
