Index: third_party/cld/bar/toolbar/cld/i18n/encodings/compact_lang_det/cldutil.cc |
=================================================================== |
--- third_party/cld/bar/toolbar/cld/i18n/encodings/compact_lang_det/cldutil.cc (revision 0) |
+++ third_party/cld/bar/toolbar/cld/i18n/encodings/compact_lang_det/cldutil.cc (revision 0) |
@@ -0,0 +1,879 @@ |
+// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include <string> |
+#include "third_party/cld/bar/toolbar/cld/i18n/encodings/compact_lang_det/cldutil.h" |
+#include "third_party/cld/bar/toolbar/cld/i18n/encodings/compact_lang_det/cldutil_dbg.h" |
+#include "third_party/cld/bar/toolbar/cld/i18n/encodings/compact_lang_det/compact_lang_det_generated_meanscore.h" |
+#include "third_party/cld/bar/toolbar/cld/i18n/encodings/compact_lang_det/utf8propletterscriptnum.h" |
+#include "third_party/cld/bar/toolbar/cld/i18n/encodings/compact_lang_det/win/cld_commandlineflags.h" |
+#include "third_party/cld/bar/toolbar/cld/i18n/encodings/compact_lang_det/win/cld_logging.h" |
+#include "third_party/cld/bar/toolbar/cld/i18n/encodings/compact_lang_det/win/cld_unilib.h" |
+#include "third_party/cld/bar/toolbar/cld/i18n/encodings/compact_lang_det/win/cld_utf.h" |
+#include "third_party/cld/bar/toolbar/cld/i18n/encodings/compact_lang_det/win/cld_utf8statetable.h" |
+ |
+// Runtime routines for hashing, looking up, and scoring |
+// unigrams (CJK), bigrams (CJK), quadgrams, and octagrams. |
+// Unigrams and bigrams are for CJK languages only, including simplified/ |
+// traditional Chinese, Japanese, Korean, Vietnamese Han characters, and |
+// Zhuang Han characters. Surrounding spaces are not considered. |
+// Quadgrams and octagrams for for non-CJK and include two bits indicating |
+// preceding and trailing spaces (word boundaries). |
+ |
+ |
+// Indicator bits for leading/trailing space around quad/octagram |
+// NOTE: 4444 bits are chosen to flip constant bits in hash of four chars of |
+// 1-, 2-, or 3-bytes each. |
+static const uint32 kPreSpaceIndicator = 0x00004444; |
+static const uint32 kPostSpaceIndicator = 0x44440000; |
+ |
+// Little-endian masks for 0..24 bytes picked up as uint32's |
+static const uint32 kWordMask0[4] = { |
+ 0xFFFFFFFF, 0x000000FF, 0x0000FFFF, 0x00FFFFFF |
+}; |
+ |
+static const int kMinCJKUTF8CharBytes = 3; |
+ |
+static const int kMinGramCount = 3; |
+static const int kMaxGramCount = 16; |
+ |
+ |
+ |
+ |
+// Routines to access a hash table of <key:wordhash, value:probs> pairs |
+// Buckets have 4-byte wordhash for sizes < 32K buckets, but only |
+// 2-byte wordhash for sizes >= 32K buckets, with other wordhash bits used as |
+// bucket subscript. |
+// Probs is a packed: three languages plus a subscript for probability table |
+// Buckets have all the keys together, then all the values.Key array never |
+// crosses a cache-line boundary, so no-match case takes exactly one cache miss. |
+// Match case may sometimes take an additional cache miss on value access. |
+// |
+// Other possibilites include 5 or 10 6-byte entries plus pad to make 32 or 64 |
+// byte buckets with single cache miss. |
+// Or 2-byte key and 6-byte value, allowing 5 languages instead of three. |
+//------------------------------------------------------------------------------ |
+ |
+ |
+//------------------------------------------------------------------------------ |
+// Hashing groups of 1/2/4/8 letters, perhaps with spaces or underscores |
+//------------------------------------------------------------------------------ |
+ |
+// Design principles for these hash functions |
+// - Few operations |
+// - Handle 1-, 2-, and 3-byte UTF-8 scripts, ignoring intermixing except in |
+// Latin script expect 1- and 2-byte mixtures. |
+// - Last byte of each character has about 5 bits of information |
+// - Spread good bits around so they can interact in at least two ways |
+// with other characters |
+// - Use add for additional mixing thorugh carries |
+ |
+// CJK Three-byte bigram |
+// ....dddd..cccccc..bbbbbb....aaaa |
+// ..................ffffff..eeeeee |
+// make |
+// ....dddd..cccccc..bbbbbb....aaaa |
+// 000....dddd..cccccc..bbbbbb....a |
+// ..................ffffff..eeeeee |
+// ffffff..eeeeee000000000000000000 |
+// |
+// CJK Four-byte bigram |
+// ..dddddd..cccccc....bbbb....aaaa |
+// ..hhhhhh..gggggg....ffff....eeee |
+// make |
+// ..dddddd..cccccc....bbbb....aaaa |
+// 000..dddddd..cccccc....bbbb....a |
+// ..hhhhhh..gggggg....ffff....eeee |
+// ..ffff....eeee000000000000000000 |
+ |
+// BIGRAM |
+// Pick up 1..8 bytes and hash them via mask/shift/add. NO pre/post |
+// OVERSHOOTS up to 3 bytes |
+// For runtime use of tables |
+uint32 cld::BiHashV25(const char* word_ptr, int bytecount) { |
+ const uint32* word_ptr32 = reinterpret_cast<const uint32*>(word_ptr); |
+ uint32 word0, word1; |
+ if (bytecount <= 4) { |
+ word0 = word_ptr32[0] & kWordMask0[bytecount & 3]; |
+ word0 = word0 ^ (word0 >> 3); |
+ return word0; |
+ } |
+ // Else do 8 bytes |
+ word0 = word_ptr32[0]; |
+ word0 = word0 ^ (word0 >> 3); |
+ word1 = word_ptr32[1] & kWordMask0[bytecount & 3]; |
+ word1 = word1 ^ (word1 << 18); |
+ return word0 + word1; |
+} |
+ |
+// |
+// Ascii-7 One-byte chars |
+// ...ddddd...ccccc...bbbbb...aaaaa |
+// make |
+// ...ddddd...ccccc...bbbbb...aaaaa |
+// 000...ddddd...ccccc...bbbbb...aa |
+// |
+// Latin 1- and 2-byte chars |
+// ...ddddd...ccccc...bbbbb...aaaaa |
+// ...................fffff...eeeee |
+// make |
+// ...ddddd...ccccc...bbbbb...aaaaa |
+// 000...ddddd...ccccc...bbbbb...aa |
+// ...................fffff...eeeee |
+// ...............fffff...eeeee0000 |
+// |
+// Non-CJK Two-byte chars |
+// ...ddddd...........bbbbb........ |
+// ...hhhhh...........fffff........ |
+// make |
+// ...ddddd...........bbbbb........ |
+// 000...ddddd...........bbbbb..... |
+// ...hhhhh...........fffff........ |
+// hhhh...........fffff........0000 |
+// |
+// Non-CJK Three-byte chars |
+// ...........ccccc................ |
+// ...................fffff........ |
+// ...lllll...................iiiii |
+// make |
+// ...........ccccc................ |
+// 000...........ccccc............. |
+// ...................fffff........ |
+// ...............fffff........0000 |
+// ...lllll...................iiiii |
+// .lllll...................iiiii00 |
+// |
+ |
+// QUADGRAM |
+// Pick up 1..12 bytes plus pre/post space and hash them via mask/shift/add |
+// OVERSHOOTS up to 3 bytes |
+// For runtime use of tables |
+uint32 QuadHashV25Mix(const char* word_ptr, int bytecount, uint32 prepost) { |
+ const uint32* word_ptr32 = reinterpret_cast<const uint32*>(word_ptr); |
+ uint32 word0, word1, word2; |
+ if (bytecount <= 4) { |
+ word0 = word_ptr32[0] & kWordMask0[bytecount & 3]; |
+ word0 = word0 ^ (word0 >> 3); |
+ return word0 ^ prepost; |
+ } else if (bytecount <= 8) { |
+ word0 = word_ptr32[0]; |
+ word0 = word0 ^ (word0 >> 3); |
+ word1 = word_ptr32[1] & kWordMask0[bytecount & 3]; |
+ word1 = word1 ^ (word1 << 4); |
+ return (word0 ^ prepost) + word1; |
+ } |
+ // else do 12 bytes |
+ word0 = word_ptr32[0]; |
+ word0 = word0 ^ (word0 >> 3); |
+ word1 = word_ptr32[1]; |
+ word1 = word1 ^ (word1 << 4); |
+ word2 = word_ptr32[2] & kWordMask0[bytecount & 3]; |
+ word2 = word2 ^ (word2 << 2); |
+ return (word0 ^ prepost) + word1 + word2; |
+} |
+ |
+ |
+// QUADGRAM wrapper with surrounding spaces |
+// Pick up 1..12 bytes plus pre/post space and hash them via mask/shift/add |
+// UNDERSHOOTS 1 byte, OVERSHOOTS up to 3 bytes |
+// For runtime use of tables |
+uint32 cld::QuadHashV25(const char* word_ptr, int bytecount) { |
+ uint32 prepost = 0; |
+ if (word_ptr[-1] == ' ') {prepost |= kPreSpaceIndicator;} |
+ if (word_ptr[bytecount] == ' ') {prepost |= kPostSpaceIndicator;} |
+ return QuadHashV25Mix(word_ptr, bytecount, prepost); |
+} |
+ |
+// QUADGRAM wrapper with surrounding underscores (offline use) |
+// Pick up 1..12 bytes plus pre/post '_' and hash them via mask/shift/add |
+// OVERSHOOTS up to 3 bytes |
+// For offline construction of tables |
+uint32 cld::QuadHashV25Underscore(const char* word_ptr, int bytecount) { |
+ const char* local_word_ptr = word_ptr; |
+ int local_bytecount = bytecount; |
+ uint32 prepost = 0; |
+ if (local_word_ptr[0] == '_') { |
+ prepost |= kPreSpaceIndicator; |
+ ++local_word_ptr; |
+ --local_bytecount; |
+ } |
+ if (local_word_ptr[local_bytecount - 1] == '_') { |
+ prepost |= kPostSpaceIndicator; |
+ --local_bytecount; |
+ } |
+ return QuadHashV25Mix(local_word_ptr, local_bytecount, prepost); |
+} |
+ |
+ |
+// OCTAGRAM |
+// Pick up 1..24 bytes plus pre/post space and hash them via mask/shift/add |
+// UNDERSHOOTS 1 byte, OVERSHOOTS up to 3 bytes |
+// |
+// The low 32 bits follow the pattern from above, tuned to different scripts |
+// The high 8 bits are a simple sum of all bytes, shifted by 0/1/2/3 bits each |
+// For runtime use of tables V3 |
+uint64 OctaHash40Mix(const char* word_ptr, int bytecount, uint64 prepost) { |
+ const uint32* word_ptr32 = reinterpret_cast<const uint32*>(word_ptr); |
+ uint64 word0; |
+ uint64 word1; |
+ uint64 sum; |
+ |
+ if (word_ptr[-1] == ' ') {prepost |= kPreSpaceIndicator;} |
+ if (word_ptr[bytecount] == ' ') {prepost |= kPostSpaceIndicator;} |
+ switch ((bytecount - 1) >> 2) { |
+ case 0: // 1..4 bytes |
+ word0 = word_ptr32[0] & kWordMask0[bytecount & 3]; |
+ sum = word0; |
+ word0 = word0 ^ (word0 >> 3); |
+ break; |
+ case 1: // 5..8 bytes |
+ word0 = word_ptr32[0]; |
+ sum = word0; |
+ word0 = word0 ^ (word0 >> 3); |
+ word1 = word_ptr32[1] & kWordMask0[bytecount & 3]; |
+ sum += word1; |
+ word1 = word1 ^ (word1 << 4); |
+ word0 += word1; |
+ break; |
+ case 2: // 9..12 bytes |
+ word0 = word_ptr32[0]; |
+ sum = word0; |
+ word0 = word0 ^ (word0 >> 3); |
+ word1 = word_ptr32[1]; |
+ sum += word1; |
+ word1 = word1 ^ (word1 << 4); |
+ word0 += word1; |
+ word1 = word_ptr32[2] & kWordMask0[bytecount & 3]; |
+ sum += word1; |
+ word1 = word1 ^ (word1 << 2); |
+ word0 += word1; |
+ break; |
+ case 3: // 13..16 bytes |
+ word0 = word_ptr32[0]; |
+ sum = word0; |
+ word0 = word0 ^ (word0 >> 3); |
+ word1 = word_ptr32[1]; |
+ sum += word1; |
+ word1 = word1 ^ (word1 << 4); |
+ word0 += word1; |
+ word1 = word_ptr32[2]; |
+ sum += word1; |
+ word1 = word1 ^ (word1 << 2); |
+ word0 += word1; |
+ word1 = word_ptr32[3] & kWordMask0[bytecount & 3]; |
+ sum += word1; |
+ word1 = word1 ^ (word1 >> 8); |
+ word0 += word1; |
+ break; |
+ case 4: // 17..20 bytes |
+ word0 = word_ptr32[0]; |
+ sum = word0; |
+ word0 = word0 ^ (word0 >> 3); |
+ word1 = word_ptr32[1]; |
+ sum += word1; |
+ word1 = word1 ^ (word1 << 4); |
+ word0 += word1; |
+ word1 = word_ptr32[2]; |
+ sum += word1; |
+ word1 = word1 ^ (word1 << 2); |
+ word0 += word1; |
+ word1 = word_ptr32[3]; |
+ sum += word1; |
+ word1 = word1 ^ (word1 >> 8); |
+ word0 += word1; |
+ word1 = word_ptr32[4] & kWordMask0[bytecount & 3]; |
+ sum += word1; |
+ word1 = word1 ^ (word1 >> 4); |
+ word0 += word1; |
+ break; |
+ default: // 21..24 bytes and higher (ignores beyond 24) |
+ word0 = word_ptr32[0]; |
+ sum = word0; |
+ word0 = word0 ^ (word0 >> 3); |
+ word1 = word_ptr32[1]; |
+ sum += word1; |
+ word1 = word1 ^ (word1 << 4); |
+ word0 += word1; |
+ word1 = word_ptr32[2]; |
+ sum += word1; |
+ word1 = word1 ^ (word1 << 2); |
+ word0 += word1; |
+ word1 = word_ptr32[3]; |
+ sum += word1; |
+ word1 = word1 ^ (word1 >> 8); |
+ word0 += word1; |
+ word1 = word_ptr32[4]; |
+ sum += word1; |
+ word1 = word1 ^ (word1 >> 4); |
+ word0 += word1; |
+ word1 = word_ptr32[5] & kWordMask0[bytecount & 3]; |
+ sum += word1; |
+ word1 = word1 ^ (word1 >> 6); |
+ word0 += word1; |
+ break; |
+ } |
+ |
+ sum += (sum >> 17); // extra 1-bit shift for bytes 2 & 3 |
+ sum += (sum >> 9); // extra 1-bit shift for bytes 1 & 3 |
+ sum = (sum & 0xff) << 32; |
+ return (word0 ^ prepost) + sum; |
+} |
+ |
+// OCTAGRAM wrapper with surrounding spaces |
+// Pick up 1..24 bytes plus pre/post space and hash them via mask/shift/add |
+// UNDERSHOOTS 1 byte, OVERSHOOTS up to 3 bytes |
+// |
+// The low 32 bits follow the pattern from above, tuned to different scripts |
+// The high 8 bits are a simple sum of all bytes, shifted by 0/1/2/3 bits each |
+// For runtime use of tables V3 |
+uint64 cld::OctaHash40(const char* word_ptr, int bytecount) { |
+ uint64 prepost = 0; |
+ if (word_ptr[-1] == ' ') {prepost |= kPreSpaceIndicator;} |
+ if (word_ptr[bytecount] == ' ') {prepost |= kPostSpaceIndicator;} |
+ return OctaHash40Mix(word_ptr, bytecount, prepost); |
+} |
+ |
+ |
+// OCTAGRAM wrapper with surrounding underscores (offline use) |
+// Pick up 1..24 bytes plus pre/post space and hash them via mask/shift/add |
+// UNDERSHOOTS 1 byte, OVERSHOOTS up to 3 bytes |
+// |
+// The low 32 bits follow the pattern from above, tuned to different scripts |
+// The high 8 bits are a simple sum of all bytes, shifted by 0/1/2/3 bits each |
+// For offline construction of tables |
+uint64 cld::OctaHash40underscore(const char* word_ptr, int bytecount) { |
+ const char* local_word_ptr = word_ptr; |
+ int local_bytecount = bytecount; |
+ uint64 prepost = 0; |
+ if (local_word_ptr[0] == '_') { |
+ prepost |= kPreSpaceIndicator; |
+ ++local_word_ptr; |
+ --local_bytecount; |
+ } |
+ if (local_word_ptr[local_bytecount - 1] == '_') { |
+ prepost |= kPostSpaceIndicator; |
+ --local_bytecount; |
+ } |
+ return OctaHash40Mix(local_word_ptr, local_bytecount, prepost); |
+} |
+ |
+ |
+ |
+ |
+//------------------------------------------------------------------------------ |
+// Scoring single groups of letters |
+//------------------------------------------------------------------------------ |
+ |
+// UNIGRAM score one => tote |
+// Input: 1-byte entry of subscript into unigram probs, plus |
+// an accumulator tote. |
+// Output: running sums in tote updated |
+void cld::ProcessProbV25UniTote(int propval, Tote* tote) { |
+ tote->AddGram(); |
+ const UnigramProbArray* pa = &kTargetCTJKVZProbs[propval]; |
+ if (pa->probs[0] > 0) {tote->Add(cld::PackLanguage(CHINESE), pa->probs[0]);} |
+ if (pa->probs[1] > 0) {tote->Add(cld::PackLanguage(CHINESE_T), pa->probs[1]);} |
+ if (pa->probs[2] > 0) {tote->Add(cld::PackLanguage(JAPANESE), pa->probs[2]);} |
+ if (pa->probs[3] > 0) {tote->Add(cld::PackLanguage(KOREAN), pa->probs[3]);} |
+ if (pa->probs[4] > 0) {tote->Add(cld::PackLanguage(VIETNAMESE), pa->probs[4]);} |
+ if (pa->probs[5] > 0) {tote->Add(cld::PackLanguage(ZHUANG), pa->probs[5]);} |
+} |
+ |
+// BIGRAM, QUADGRAM, OCTAGRAM score one => tote |
+// Input: 4-byte entry of 3 language numbers and one probability subscript, plus |
+// an accumulator tote. (language 0 means unused entry) |
+// Output: running sums in tote updated |
+void cld::ProcessProbV25Tote(uint32 probs, Tote* tote) { |
+ tote->AddGram(); |
+ uint8 prob123 = (probs >> 0) & 0xff; |
+ const uint8* prob123_entry = cld::LgProb2TblEntry(prob123); |
+ |
+ uint8 top1 = (probs >> 8) & 0xff; |
+ if (top1 > 0) {tote->Add(top1, cld::LgProb3(prob123_entry, 0));} |
+ uint8 top2 = (probs >> 16) & 0xff; |
+ if (top2 > 0) {tote->Add(top2, cld::LgProb3(prob123_entry, 1));} |
+ uint8 top3 = (probs >> 24) & 0xff; |
+ if (top3 > 0) {tote->Add(top3, cld::LgProb3(prob123_entry, 2));} |
+} |
+ |
+ |
+//------------------------------------------------------------------------------ |
+// Routines to accumulate probabilities |
+//------------------------------------------------------------------------------ |
+ |
+ |
+// UNIGRAM, using UTF-8 property table, advancing by 1/2/4/8 chars |
+// Caller supplies table, such as compact_lang_det_generated_ctjkvz_b1_obj |
+// Score up to n unigrams, returning number of bytes consumed |
+// Updates tote_grams |
+int cld::DoUniScoreV3(const UTF8PropObj* unigram_obj, |
+ const char* isrc, int srclen, int advance_by, |
+ int* tote_grams, int gram_limit, Tote* chunk_tote) { |
+ const char* src = isrc; |
+ if (FLAGS_dbgscore) {DbgScoreInit(src, srclen);} |
+ |
+ // Property-based CJK unigram lookup |
+ if (src[0] == ' ') {++src; --srclen;} |
+ |
+ const uint8* usrc = reinterpret_cast<const uint8*>(src); |
+ int usrclen = srclen; |
+ |
+ while (usrclen > 0) { |
+ int len = kAdvanceOneChar[usrc[0]]; |
+ // Look up property of one UTF-8 character and advance over it |
+ // Return 0 if input length is zero |
+ // Return 0 and advance one byte if input is ill-formed |
+ |
+ int propval = UTF8GenericPropertyBigOneByte(unigram_obj, &usrc, &usrclen); |
+ |
+ if (FLAGS_dbglookup) { |
+ DbgUniTermToStderr(propval, usrc, len); |
+ } |
+ |
+ if (propval > 0) { |
+ ProcessProbV25UniTote(propval, chunk_tote); |
+ ++(*tote_grams); |
+ if (FLAGS_dbgscore) {DbgScoreRecordUni((const char*)usrc, propval, len);} |
+ } |
+ |
+ // Advance by 1/2/4/8 characters (half of quad advance) |
+ if (advance_by == 2) { |
+ // Already advanced by 1 |
+ } else if (advance_by == 4) { |
+ // Advance by 2 chars total, if not at end |
+ if (UTFmax <= usrclen) { |
+ int n = kAdvanceOneChar[*usrc]; usrc += n; usrclen -= n; |
+ } |
+ } else if (advance_by == 8) { |
+ // Advance by 4 chars total, if not at end |
+ if ((UTFmax * 3) <= usrclen) { |
+ int n = kAdvanceOneChar[*usrc]; usrc += n; usrclen -= n; |
+ n = kAdvanceOneChar[*usrc]; usrc += n; usrclen -= n; |
+ n = kAdvanceOneChar[*usrc]; usrc += n; usrclen -= n; |
+ } |
+ } else { |
+ // Advance by 8 chars total, if not at end |
+ if ((UTFmax * 7) <= usrclen) { |
+ int n = kAdvanceOneChar[*usrc]; usrc += n; usrclen -= n; |
+ n = kAdvanceOneChar[*usrc]; usrc += n; usrclen -= n; |
+ n = kAdvanceOneChar[*usrc]; usrc += n; usrclen -= n; |
+ n = kAdvanceOneChar[*usrc]; usrc += n; usrclen -= n; |
+ n = kAdvanceOneChar[*usrc]; usrc += n; usrclen -= n; |
+ n = kAdvanceOneChar[*usrc]; usrc += n; usrclen -= n; |
+ n = kAdvanceOneChar[*usrc]; usrc += n; usrclen -= n; |
+ } |
+ } |
+ DCHECK(usrclen >= 0); |
+ |
+ if (*tote_grams >= gram_limit) { |
+ break; |
+ } |
+ } |
+ if (FLAGS_dbgscore) { |
+ // With advance_by>2, we consume more input to get the same number of quads |
+ int len = src - isrc; |
+ DbgScoreTop(src, (len * 2) / advance_by, chunk_tote); |
+ DbgScoreFlush(); |
+ } |
+ |
+ int consumed2 = reinterpret_cast<const char*>(usrc) - isrc; |
+ return consumed2; |
+} |
+ |
+ |
+// BIGRAM, using hash table, always advancing by 1 char |
+// Caller supplies table, such as &kCjkBiTable_obj or &kGibberishTable_obj |
+// Score all bigrams in isrc, using languages that have bigrams (CJK) |
+// Return number of bigrams that hit in the hash table |
+int cld::DoBigramScoreV3(const cld::CLDTableSummary* bigram_obj, |
+ const char* isrc, int srclen, Tote* chunk_tote) { |
+ int hit_count = 0; |
+ const char* src = isrc; |
+ |
+ // Hashtable-based CJK bigram lookup |
+ const uint8* usrc = reinterpret_cast<const uint8*>(src); |
+ const uint8* usrclimit1 = usrc + srclen - UTFmax; |
+ if (FLAGS_dbgscore) { |
+ fprintf(stderr, " " ); |
+ } |
+ |
+ while (usrc < usrclimit1) { |
+ int len = kAdvanceOneChar[usrc[0]]; |
+ int len2 = kAdvanceOneChar[usrc[len]] + len; |
+ |
+ if ((kMinCJKUTF8CharBytes * 2) <= len2) { // Two CJK chars possible |
+ // Lookup and score this bigram |
+ // Always ignore pre/post spaces |
+ uint32 bihash = BiHashV25(reinterpret_cast<const char*>(usrc), len2); |
+ uint32 probs = QuadHashV3Lookup4(bigram_obj, bihash); |
+ // Now go indirect on the subscript |
+ probs = bigram_obj->kCLDTableInd[probs & |
+ ~bigram_obj->kCLDTableKeyMask]; |
+ |
+ // Process the bigram |
+ if (FLAGS_dbglookup) { |
+ const char* ssrc = reinterpret_cast<const char*>(usrc); |
+ DbgBiTermToStderr(bihash, probs, ssrc, len2); |
+ DbgScoreRecord(NULL, probs, len2); |
+ } else if (FLAGS_dbgscore && (probs != 0)) { |
+ const char* ssrc = reinterpret_cast<const char*>(usrc); |
+ DbgScoreRecord(NULL, probs, len2); |
+ string temp(ssrc, len2); |
+ fprintf(stderr, "%s ", temp.c_str()); |
+ } |
+ |
+ if (probs != 0) { |
+ ProcessProbV25Tote(probs, chunk_tote); |
+ ++hit_count; |
+ } |
+ } |
+ usrc += len; // Advance by one char |
+ } |
+ |
+ if (FLAGS_dbgscore) { |
+ fprintf(stderr, "[%d bigrams scored]\n", hit_count); |
+ DbgScoreState(); |
+ } |
+ return hit_count; |
+} |
+ |
+ |
+ |
+// QUADGRAM, using hash table, advancing by 2/4/8/16 chars |
+// Caller supplies table, such as &kQuadTable_obj or &kGibberishTable_obj |
+// Score up to n quadgrams, returning number of bytes consumed |
+// Updates tote_grams |
+int cld::DoQuadScoreV3(const cld::CLDTableSummary* quadgram_obj, |
+ const char* isrc, int srclen, int advance_by, |
+ int* tote_grams, int gram_limit, Tote* chunk_tote) { |
+ const char* src = isrc; |
+ const char* srclimit = src + srclen; |
+ // Limit is end, which has extra 20 20 20 00 past len |
+ const char* srclimit7 = src + srclen - (UTFmax * 7); |
+ const char* srclimit15 = src + srclen - (UTFmax * 15); |
+ |
+ if (FLAGS_dbgscore) {DbgScoreInit(src, srclen);} |
+ |
+ // Visit all quadgrams |
+ if (src[0] == ' ') {++src;} |
+ while (src < srclimit) { |
+ // Find one quadgram |
+ const char* src_end = src; |
+ src_end += kAdvanceOneCharButSpace[(uint8)src_end[0]]; |
+ src_end += kAdvanceOneCharButSpace[(uint8)src_end[0]]; |
+ const char* src_mid = src_end; |
+ src_end += kAdvanceOneCharButSpace[(uint8)src_end[0]]; |
+ src_end += kAdvanceOneCharButSpace[(uint8)src_end[0]]; |
+ int len = src_end - src; |
+ |
+ // Lookup and score this quadgram |
+ uint32 quadhash = QuadHashV25(src, len); |
+ uint32 probs = QuadHashV3Lookup4(quadgram_obj, quadhash); |
+ // Now go indirect on the subscript |
+ probs = quadgram_obj->kCLDTableInd[probs & |
+ ~quadgram_obj->kCLDTableKeyMask]; |
+ |
+ // Process the quadgram |
+ if (FLAGS_dbglookup) { |
+ DbgQuadTermToStderr(quadhash, probs, src, len); |
+ } |
+ if (probs != 0) { |
+ ProcessProbV25Tote(probs, chunk_tote); |
+ ++(*tote_grams); |
+ if (FLAGS_dbgscore) {DbgScoreRecord(src, probs, len);} |
+ } |
+ |
+ // Advance all the way past word if at end-of-word |
+ if (src_end[0] == ' ') { |
+ src_mid = src_end; |
+ } |
+ |
+ // Advance by 2/4/8/16 characters |
+ if (advance_by == 2) { |
+ src = src_mid; |
+ } else if (advance_by == 4) { |
+ src = src_end; |
+ } else if (advance_by == 8) { |
+ // Advance by 8 chars total (4 more), if not at end |
+ if (src < srclimit7) { |
+ src_end += kAdvanceOneChar[(uint8)src_end[0]]; |
+ src_end += kAdvanceOneChar[(uint8)src_end[0]]; |
+ src_end += kAdvanceOneChar[(uint8)src_end[0]]; |
+ src_end += kAdvanceOneChar[(uint8)src_end[0]]; |
+ } |
+ src = src_end; |
+ } else { |
+ // Advance by 16 chars total (12 more), if not at end |
+ if (src < srclimit15) { |
+ // Advance by ~16 chars by adding 3 * current bytelen |
+ int fourcharlen = src_end - src; |
+ src = src_end + (3 * fourcharlen); |
+ // Advance a bit more if mid-character |
+ src += kAdvanceOneCharSpaceVowel[(uint8)src[0]]; |
+ src += kAdvanceOneCharSpaceVowel[(uint8)src[0]]; |
+ } else { |
+ src = src_end; |
+ } |
+ } |
+ DCHECK(src < srclimit); |
+ src += kAdvanceOneCharSpaceVowel[(uint8)src[0]]; |
+ |
+ if (*tote_grams >= gram_limit) { |
+ break; |
+ } |
+ } |
+ |
+ if (FLAGS_dbgscore) { |
+ // With advance_by>2, we consume more input to get the same number of quads |
+ int len = src - isrc; |
+ DbgScoreTop(src, (len * 2) / advance_by, chunk_tote); |
+ DbgScoreFlush(); |
+ } |
+ |
+ int consumed = src - isrc; |
+ |
+ // If advancing by more than 2, src may have overshot srclimit |
+ if (consumed > srclen) { |
+ consumed = srclen; |
+ } |
+ |
+ return consumed; |
+} |
+ |
+ |
+// OCTAGRAM, using hash table, always advancing by 1 word |
+// Caller supplies table, such as &kLongWord8Table_obj |
+// Score all words in isrc, using languages that have quadgrams |
+// We don't normally use this routine except on the first quadgram run, |
+// but it can be used to resolve unreliable pages. |
+// This routine does not have an optimized advance_by |
+// SOON: Uses indirect language/probability longword |
+// |
+// Return number of words that hit in the hash table |
+int cld::DoOctaScoreV3(const cld::CLDTableSummary* octagram_obj, |
+ const char* isrc, int srclen, Tote* chunk_tote) { |
+ int hit_count = 0; |
+ const char* src = isrc; |
+ const char* srclimit = src + srclen + 1; |
+ // Limit is end+1, to include extra space char (0x20) off the end |
+ // |
+ // Score all words truncated to 8 characters |
+ int charcount = 0; |
+ // Skip any initial space |
+ if (src[0] == ' ') {++src;} |
+ const char* word_ptr = src; |
+ const char* word_end = word_ptr; |
+ if (FLAGS_dbgscore) { |
+ fprintf(stderr, " " ); |
+ } |
+ while (src < srclimit) { |
+ // Terminate previous word or continue current word |
+ if (src[0] == ' ') { |
+ int bytecount = word_end - word_ptr; |
+ // Lookup and score this word |
+ uint64 wordhash40 = OctaHash40(word_ptr, bytecount); |
+ uint32 probs = OctaHashV3Lookup4(octagram_obj, wordhash40); |
+ // Now go indirect on the subscript |
+ probs = octagram_obj->kCLDTableInd[probs & |
+ ~octagram_obj->kCLDTableKeyMask]; |
+ |
+ // // Lookup and score this word |
+ // uint32 wordhash = QuadHashV25(word_ptr, bytecount); |
+ // uint32 probs = WordHashLookup4(wordhash, kLongWord8Table, |
+ // kLongWord8TableSize); |
+ // |
+ if (FLAGS_dbglookup) { |
+ DbgWordTermToStderr(wordhash40, probs, word_ptr, bytecount); |
+ DbgScoreRecord(NULL, probs, bytecount); |
+ } else if (FLAGS_dbgscore && (probs != 0)) { |
+ DbgScoreRecord(NULL, probs, bytecount); |
+ string temp(word_ptr, bytecount); |
+ fprintf(stderr, "%s ", temp.c_str()); |
+ } |
+ |
+ if (probs != 0) { |
+ ProcessProbV25Tote(probs, chunk_tote); |
+ ++hit_count; |
+ } |
+ charcount = 0; |
+ word_ptr = src + 1; // Over the space |
+ word_end = word_ptr; |
+ } else { |
+ ++charcount; |
+ } |
+ |
+ // Advance to next char |
+ src += cld_UniLib::OneCharLen(src); |
+ if (charcount <= 8) { |
+ word_end = src; |
+ } |
+ } |
+ |
+ if (FLAGS_dbgscore) { |
+ fprintf(stderr, "[%d words scored]\n", hit_count); |
+ DbgScoreState(); |
+ } |
+ return hit_count; |
+} |
+ |
+ |
+ |
+//------------------------------------------------------------------------------ |
+// Reliability calculations, for single language and between languages |
+//------------------------------------------------------------------------------ |
+ |
+// Return reliablity of result 0..100 for top two scores |
+// delta==0 is 0% reliable, delta==fully_reliable_thresh is 100% reliable |
+// (on a scale where +1 is a factor of 2 ** 1.6 = 3.02) |
+// Threshold is uni/quadgram increment count, bounded above and below. |
+// |
+// Requiring a factor of 3 improvement (e.g. +1 log base 3) |
+// for each scored quadgram is too stringent, so I've backed this off to a |
+// factor of 2 (e.g. +5/8 log base 3). |
+// |
+// I also somewhat lowered the Min/MaxGramCount limits above |
+// |
+// Added: if fewer than 8 quads/unis, max reliability is 12*n percent |
+// |
+int cld::ReliabilityDelta(int value1, int value2, int gramcount) { |
+ int max_reliability_percent = 100; |
+ if (gramcount < 8) { |
+ max_reliability_percent = 12 * gramcount; |
+ } |
+ int fully_reliable_thresh = (gramcount * 5) >> 3; // see note above |
+ if (fully_reliable_thresh < kMinGramCount) { // Fully = 3..16 |
+ fully_reliable_thresh = kMinGramCount; |
+ } else if (fully_reliable_thresh > kMaxGramCount) { |
+ fully_reliable_thresh = kMaxGramCount; |
+ } |
+ |
+ int delta = value1 - value2; |
+ if (delta >= fully_reliable_thresh) {return max_reliability_percent;} |
+ if (delta <= 0) {return 0;} |
+ return cld::minint(max_reliability_percent, |
+ (100 * delta) / fully_reliable_thresh); |
+} |
+ |
+// Return reliablity of result 0..100 for top score vs. mainsteam score |
+// Values are score per 1024 bytes of input |
+// ratio = max(top/mainstream, mainstream/top) |
+// ratio > 4.0 is 0% reliable, <= 2.0 is 100% reliable |
+// Change: short-text word scoring can give unusually good results. |
+// Let top exceed mainstream by 4x at 50% reliable |
+int cld::ReliabilityMainstream(int topscore, int len, int mean_score) { |
+ if (mean_score == 0) {return 100;} // No reliability data available yet |
+ if (topscore == 0) {return 0;} // zero score = unreliable |
+ if (len == 0) {return 0;} // zero len = unreliable |
+ int top_kb = (topscore << 10) / len; |
+ double ratio; |
+ double ratio_cutoff; |
+ if (top_kb > mean_score) { |
+ ratio = (1.0 * top_kb) / mean_score; |
+ ratio_cutoff = 5.0; // ramp down from 100% to 0%: 3.0-5.0 |
+ } else { |
+ ratio = (1.0 * mean_score) / top_kb; |
+ ratio_cutoff = 4.0; // ramp down from 100% to 0%: 2.0-4.0 |
+ } |
+ if (ratio <= ratio_cutoff - 2.0) {return 100;} |
+ if (ratio > ratio_cutoff) {return 0;} |
+ |
+ int iratio = static_cast<int>(100 * (ratio_cutoff - ratio) / 2.0); |
+ return iratio; |
+} |
+ |
+// Calculate ratio of score per 1KB vs. expected score per 1KB |
+double cld::GetNormalizedScore(Language lang, UnicodeLScript lscript, |
+ int bytes, int score) { |
+ // Average training-data score for this language-script combo, per 1KB |
+ int expected_score = kMeanScore[lang * 4 + LScript4(lscript)]; |
+ if (lscript == ULScript_Common) { |
+ // We don't know the script (only happens with second-chance score) |
+ // Look for first non-zero mean value |
+ for (int i = 0; i < 3; ++i) { |
+ if (kMeanScore[lang * 4 + i] > 0) { |
+ expected_score = kMeanScore[lang * 4 + i]; |
+ } |
+ } |
+ } |
+ if (expected_score < 100) { |
+ expected_score = 1000; |
+ } |
+ |
+ // Our score per 1KB |
+ double our_score = (score << 10) / (bytes ? bytes : 1); // Avoid zdiv |
+ double ratio = our_score / expected_score; |
+ |
+ // Just the raw count normalized as though each language has mean=1000; |
+ ratio = (score * 1000.0) / expected_score; |
+ return ratio; |
+} |
+ |
+// Calculate reliablity of len bytes of script lscript with chunk_tote |
+int cld::GetReliability(int len, UnicodeLScript lscript, |
+ const Tote* chunk_tote) { |
+ Language cur_lang = UnpackLanguage(chunk_tote->Key(0)); |
+ // Average score for this language-script combo |
+ int mean_score = kMeanScore[cur_lang * 4 + LScript4(lscript)]; |
+ if (lscript == ULScript_Common) { |
+ // We don't know the script (only happens with second-chance score) |
+ // Look for first non-zero mean value |
+ for (int i = 0; i < 3; ++i) { |
+ if (kMeanScore[cur_lang * 4 + i] > 0) { |
+ mean_score = kMeanScore[cur_lang * 4 + i]; |
+ } |
+ } |
+ } |
+ int reliability_delta = ReliabilityDelta(chunk_tote->Value(0), |
+ chunk_tote->Value(1), |
+ chunk_tote->GetGramCount()); |
+ |
+ int reliability_main = ReliabilityMainstream(chunk_tote->Value(0), |
+ len, |
+ mean_score); |
+ |
+ int reliability_min = minint(reliability_delta, reliability_main); |
+ |
+ |
+ if (FLAGS_dbgreli) { |
+ char temp1[4]; |
+ char temp2[4]; |
+ cld::DbgLangName3(UnpackLanguage(chunk_tote->Key(0)), temp1); |
+ if (temp1[2] == ' ') {temp1[2] = '\0';} |
+ cld::DbgLangName3(UnpackLanguage(chunk_tote->Key(1)), temp2); |
+ if (temp2[2] == ' ') {temp2[2] = '\0';} |
+ int srclen = len; |
+ fprintf(stderr, "CALC GetReliability gram=%d incr=%d srclen=%d, %s=%d %s=%d " |
+ "top/KB=%d mean/KB=%d del=%d%% reli=%d%% " |
+ "lang/lscript %d %d\n", |
+ chunk_tote->GetGramCount(), |
+ chunk_tote->GetIncrCount(), |
+ srclen, |
+ temp1, chunk_tote->Value(0), |
+ temp2, chunk_tote->Value(1), |
+ (chunk_tote->Value(0) << 10) / (srclen ? srclen : 1), |
+ mean_score, |
+ reliability_delta, |
+ reliability_main, |
+ cur_lang, lscript); |
+ } |
+ |
+ return reliability_min; |
+} |
+ |
+ |
+//------------------------------------------------------------------------------ |
+// Miscellaneous |
+//------------------------------------------------------------------------------ |
+ |
+// Demote all languages except Top40 and plus_one |
+// Do this just before sorting chunk_tote results |
+void cld::DemoteNotTop40(Tote* chunk_tote, int packed_plus_one) { |
+ for (int sub = 0; sub < chunk_tote->MaxSize(); ++sub) { |
+ if (chunk_tote->Key(sub) == 0) continue; |
+ if (chunk_tote->Key(sub) == packed_plus_one) continue; |
+ if (kIsPackedTop40[chunk_tote->Key(sub)]) continue; |
+ // Quarter the score of others |
+ chunk_tote->SetValue(sub, chunk_tote->Value(sub) >> 2); |
+ } |
+} |
Property changes on: third_party\cld\bar\toolbar\cld\i18n\encodings\compact_lang_det\cldutil.cc |
___________________________________________________________________ |
Added: svn:eol-style |
+ LF |