2010-12-01 12:22:15 +00:00
|
|
|
/*
|
|
|
|
**
|
|
|
|
** Copyright 2010, 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.
|
|
|
|
*/
|
|
|
|
|
2010-12-02 09:11:54 +00:00
|
|
|
#include <string.h>
|
|
|
|
|
2010-12-02 05:53:24 +00:00
|
|
|
#define LOG_TAG "LatinIME: bigram_dictionary.cpp"
|
|
|
|
|
2010-12-01 12:22:15 +00:00
|
|
|
#include "bigram_dictionary.h"
|
2011-07-25 05:03:19 +00:00
|
|
|
#include "binary_format.h"
|
2012-05-07 11:14:00 +00:00
|
|
|
#include "bloom_filter.h"
|
|
|
|
#include "dictionary.h"
|
2010-12-01 12:22:15 +00:00
|
|
|
|
|
|
|
namespace latinime {
|
|
|
|
|
2010-12-02 09:11:54 +00:00
|
|
|
BigramDictionary::BigramDictionary(const unsigned char *dict, int maxWordLength,
|
|
|
|
Dictionary *parentDictionary)
|
2012-04-06 08:52:18 +00:00
|
|
|
: DICT(dict), MAX_WORD_LENGTH(maxWordLength), mParentDictionary(parentDictionary) {
|
2011-03-19 00:16:42 +00:00
|
|
|
if (DEBUG_DICT) {
|
2012-01-13 09:01:22 +00:00
|
|
|
AKLOGI("BigramDictionary - constructor");
|
2011-03-19 00:16:42 +00:00
|
|
|
}
|
2010-12-02 09:11:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
BigramDictionary::~BigramDictionary() {
|
|
|
|
}
|
|
|
|
|
|
|
|
bool BigramDictionary::addWordBigram(unsigned short *word, int length, int frequency) {
|
|
|
|
word[length] = 0;
|
|
|
|
if (DEBUG_DICT) {
|
2011-07-08 05:53:50 +00:00
|
|
|
#ifdef FLAG_DBG
|
2010-12-02 09:11:54 +00:00
|
|
|
char s[length + 1];
|
|
|
|
for (int i = 0; i <= length; i++) s[i] = word[i];
|
2012-01-13 09:01:22 +00:00
|
|
|
AKLOGI("Bigram: Found word = %s, freq = %d :", s, frequency);
|
2011-07-13 23:32:57 +00:00
|
|
|
#endif
|
2010-12-02 09:11:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Find the right insertion point
|
|
|
|
int insertAt = 0;
|
|
|
|
while (insertAt < mMaxBigrams) {
|
|
|
|
if (frequency > mBigramFreq[insertAt] || (mBigramFreq[insertAt] == frequency
|
|
|
|
&& length < Dictionary::wideStrLen(mBigramChars + insertAt * MAX_WORD_LENGTH))) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
insertAt++;
|
|
|
|
}
|
2011-03-19 00:16:42 +00:00
|
|
|
if (DEBUG_DICT) {
|
2012-01-13 09:01:22 +00:00
|
|
|
AKLOGI("Bigram: InsertAt -> %d maxBigrams: %d", insertAt, mMaxBigrams);
|
2011-03-19 00:16:42 +00:00
|
|
|
}
|
2010-12-02 09:11:54 +00:00
|
|
|
if (insertAt < mMaxBigrams) {
|
|
|
|
memmove((char*) mBigramFreq + (insertAt + 1) * sizeof(mBigramFreq[0]),
|
|
|
|
(char*) mBigramFreq + insertAt * sizeof(mBigramFreq[0]),
|
|
|
|
(mMaxBigrams - insertAt - 1) * sizeof(mBigramFreq[0]));
|
|
|
|
mBigramFreq[insertAt] = frequency;
|
|
|
|
memmove((char*) mBigramChars + (insertAt + 1) * MAX_WORD_LENGTH * sizeof(short),
|
|
|
|
(char*) mBigramChars + (insertAt ) * MAX_WORD_LENGTH * sizeof(short),
|
|
|
|
(mMaxBigrams - insertAt - 1) * sizeof(short) * MAX_WORD_LENGTH);
|
|
|
|
unsigned short *dest = mBigramChars + (insertAt ) * MAX_WORD_LENGTH;
|
|
|
|
while (length--) {
|
|
|
|
*dest++ = *word++;
|
|
|
|
}
|
|
|
|
*dest = 0; // NULL terminate
|
2011-03-19 00:16:42 +00:00
|
|
|
if (DEBUG_DICT) {
|
2012-01-13 09:01:22 +00:00
|
|
|
AKLOGI("Bigram: Added word at %d", insertAt);
|
2011-03-19 00:16:42 +00:00
|
|
|
}
|
2010-12-02 09:11:54 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-07-25 05:03:19 +00:00
|
|
|
/* Parameters :
|
|
|
|
* prevWord: the word before, the one for which we need to look up bigrams.
|
|
|
|
* prevWordLength: its length.
|
|
|
|
* codes: what user typed, in the same format as for UnigramDictionary::getSuggestions.
|
|
|
|
* codesSize: the size of the codes array.
|
|
|
|
* bigramChars: an array for output, at the same format as outwords for getSuggestions.
|
|
|
|
* bigramFreq: an array to output frequencies.
|
|
|
|
* maxWordLength: the maximum size of a word.
|
|
|
|
* maxBigrams: the maximum number of bigrams fitting in the bigramChars array.
|
|
|
|
* This method returns the number of bigrams this word has, for backward compatibility.
|
|
|
|
* Note: this is not the number of bigrams output in the array, which is the number of
|
|
|
|
* bigrams this word has WHOSE first letter also matches the letter the user typed.
|
|
|
|
* TODO: this may not be a sensible thing to do. It makes sense when the bigrams are
|
|
|
|
* used to match the first letter of the second word, but once the user has typed more
|
|
|
|
* and the bigrams are used to boost unigram result scores, it makes little sense to
|
|
|
|
* reduce their scope to the ones that match the first letter.
|
|
|
|
*/
|
2012-04-23 06:37:07 +00:00
|
|
|
int BigramDictionary::getBigrams(const int32_t *prevWord, int prevWordLength, int *codes,
|
2010-12-02 09:11:54 +00:00
|
|
|
int codesSize, unsigned short *bigramChars, int *bigramFreq, int maxWordLength,
|
2012-03-28 09:21:04 +00:00
|
|
|
int maxBigrams) {
|
2011-07-25 05:03:19 +00:00
|
|
|
// TODO: remove unused arguments, and refrain from storing stuff in members of this class
|
|
|
|
// TODO: have "in" arguments before "out" ones, and make out args explicit in the name
|
2010-12-02 09:11:54 +00:00
|
|
|
mBigramFreq = bigramFreq;
|
|
|
|
mBigramChars = bigramChars;
|
|
|
|
mInputCodes = codes;
|
|
|
|
mMaxBigrams = maxBigrams;
|
|
|
|
|
2011-07-25 05:03:19 +00:00
|
|
|
const uint8_t* const root = DICT;
|
2012-04-24 09:06:51 +00:00
|
|
|
int pos = getBigramListPositionForWord(prevWord, prevWordLength);
|
|
|
|
// getBigramListPositionForWord returns 0 if this word isn't in the dictionary or has no bigrams
|
2012-04-17 07:02:35 +00:00
|
|
|
if (0 == pos) return 0;
|
2011-07-25 05:03:19 +00:00
|
|
|
int bigramFlags;
|
|
|
|
int bigramCount = 0;
|
|
|
|
do {
|
|
|
|
bigramFlags = BinaryFormat::getFlagsAndForwardPointer(root, &pos);
|
|
|
|
uint16_t bigramBuffer[MAX_WORD_LENGTH];
|
|
|
|
const int bigramPos = BinaryFormat::getAttributeAddressAndForwardPointer(root, bigramFlags,
|
|
|
|
&pos);
|
|
|
|
const int length = BinaryFormat::getWordAtAddress(root, bigramPos, MAX_WORD_LENGTH,
|
|
|
|
bigramBuffer);
|
|
|
|
|
2012-02-16 03:22:26 +00:00
|
|
|
// codesSize == 0 means we are trying to find bigram predictions.
|
|
|
|
if (codesSize < 1 || checkFirstCharacter(bigramBuffer)) {
|
2011-07-25 05:03:19 +00:00
|
|
|
const int frequency = UnigramDictionary::MASK_ATTRIBUTE_FREQUENCY & bigramFlags;
|
2012-03-21 06:26:45 +00:00
|
|
|
if (addWordBigram(bigramBuffer, length, frequency)) {
|
|
|
|
++bigramCount;
|
|
|
|
}
|
2010-12-02 09:11:54 +00:00
|
|
|
}
|
2012-04-27 06:50:21 +00:00
|
|
|
} while (UnigramDictionary::FLAG_ATTRIBUTE_HAS_NEXT & bigramFlags);
|
2011-07-25 05:03:19 +00:00
|
|
|
return bigramCount;
|
2010-12-01 12:22:15 +00:00
|
|
|
}
|
2010-12-02 09:11:54 +00:00
|
|
|
|
2012-04-17 07:02:35 +00:00
|
|
|
// Returns a pointer to the start of the bigram list.
|
|
|
|
// If the word is not found or has no bigrams, this function returns 0.
|
2012-04-24 09:06:51 +00:00
|
|
|
int BigramDictionary::getBigramListPositionForWord(const int32_t *prevWord,
|
|
|
|
const int prevWordLength) {
|
2012-05-02 07:00:24 +00:00
|
|
|
if (0 >= prevWordLength) return 0;
|
2012-04-24 09:06:51 +00:00
|
|
|
const uint8_t* const root = DICT;
|
2012-04-17 02:38:44 +00:00
|
|
|
int pos = BinaryFormat::getTerminalPosition(root, prevWord, prevWordLength);
|
|
|
|
|
|
|
|
if (NOT_VALID_WORD == pos) return 0;
|
|
|
|
const int flags = BinaryFormat::getFlagsAndForwardPointer(root, &pos);
|
|
|
|
if (0 == (flags & UnigramDictionary::FLAG_HAS_BIGRAMS)) return 0;
|
|
|
|
if (0 == (flags & UnigramDictionary::FLAG_HAS_MULTIPLE_CHARS)) {
|
|
|
|
BinaryFormat::getCharCodeAndForwardPointer(root, &pos);
|
|
|
|
} else {
|
|
|
|
pos = BinaryFormat::skipOtherCharacters(root, pos);
|
|
|
|
}
|
|
|
|
pos = BinaryFormat::skipChildrenPosition(flags, pos);
|
|
|
|
pos = BinaryFormat::skipFrequency(flags, pos);
|
|
|
|
pos = BinaryFormat::skipShortcuts(root, flags, pos);
|
|
|
|
return pos;
|
|
|
|
}
|
|
|
|
|
2012-05-02 10:05:27 +00:00
|
|
|
void BigramDictionary::fillBigramAddressToFrequencyMapAndFilter(const int32_t *prevWord,
|
|
|
|
const int prevWordLength, std::map<int, int> *map, uint8_t *filter) {
|
|
|
|
memset(filter, 0, BIGRAM_FILTER_BYTE_SIZE);
|
2012-05-02 07:00:24 +00:00
|
|
|
const uint8_t* const root = DICT;
|
|
|
|
int pos = getBigramListPositionForWord(prevWord, prevWordLength);
|
|
|
|
if (0 == pos) return;
|
|
|
|
|
|
|
|
int bigramFlags;
|
|
|
|
do {
|
|
|
|
bigramFlags = BinaryFormat::getFlagsAndForwardPointer(root, &pos);
|
|
|
|
const int frequency = UnigramDictionary::MASK_ATTRIBUTE_FREQUENCY & bigramFlags;
|
|
|
|
const int bigramPos = BinaryFormat::getAttributeAddressAndForwardPointer(root, bigramFlags,
|
|
|
|
&pos);
|
|
|
|
(*map)[bigramPos] = frequency;
|
2012-05-02 10:05:27 +00:00
|
|
|
setInFilter(filter, bigramPos);
|
2012-05-02 07:00:24 +00:00
|
|
|
} while (0 != (UnigramDictionary::FLAG_ATTRIBUTE_HAS_NEXT & bigramFlags));
|
|
|
|
}
|
|
|
|
|
2010-12-02 09:11:54 +00:00
|
|
|
bool BigramDictionary::checkFirstCharacter(unsigned short *word) {
|
|
|
|
// Checks whether this word starts with same character or neighboring characters of
|
|
|
|
// what user typed.
|
|
|
|
|
|
|
|
int *inputCodes = mInputCodes;
|
|
|
|
int maxAlt = MAX_ALTERNATIVES;
|
2012-03-24 06:31:27 +00:00
|
|
|
const unsigned short firstBaseChar = toBaseLowerCase(*word);
|
2010-12-02 09:11:54 +00:00
|
|
|
while (maxAlt > 0) {
|
2012-03-24 06:31:27 +00:00
|
|
|
if (toBaseLowerCase(*inputCodes) == firstBaseChar) {
|
2010-12-02 09:11:54 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
inputCodes++;
|
|
|
|
maxAlt--;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2012-04-27 06:50:21 +00:00
|
|
|
bool BigramDictionary::isValidBigram(const int32_t *word1, int length1, const int32_t *word2,
|
|
|
|
int length2) {
|
|
|
|
const uint8_t* const root = DICT;
|
|
|
|
int pos = getBigramListPositionForWord(word1, length1);
|
|
|
|
// getBigramListPositionForWord returns 0 if this word isn't in the dictionary or has no bigrams
|
|
|
|
if (0 == pos) return false;
|
|
|
|
int nextWordPos = BinaryFormat::getTerminalPosition(root, word2, length2);
|
|
|
|
if (NOT_VALID_WORD == nextWordPos) return false;
|
|
|
|
int bigramFlags;
|
|
|
|
do {
|
|
|
|
bigramFlags = BinaryFormat::getFlagsAndForwardPointer(root, &pos);
|
|
|
|
const int bigramPos = BinaryFormat::getAttributeAddressAndForwardPointer(root, bigramFlags,
|
|
|
|
&pos);
|
|
|
|
if (bigramPos == nextWordPos) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
} while (UnigramDictionary::FLAG_ATTRIBUTE_HAS_NEXT & bigramFlags);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2010-12-01 12:22:15 +00:00
|
|
|
// TODO: Move functions related to bigram to here
|
|
|
|
} // namespace latinime
|