1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright (c) 2007 Sun Microsystems, Inc. All Rights Reserved. 5 * 6 * The contents of this file are subject to the terms of either the GNU Lesser 7 * General Public License Version 2.1 only ("LGPL") or the Common Development and 8 * Distribution License ("CDDL")(collectively, the "License"). You may not use this 9 * file except in compliance with the License. You can obtain a copy of the CDDL at 10 * http://www.opensource.org/licenses/cddl1.php and a copy of the LGPLv2.1 at 11 * http://www.opensource.org/licenses/lgpl-license.php. See the License for the 12 * specific language governing permissions and limitations under the License. When 13 * distributing the software, include this License Header Notice in each file and 14 * include the full text of the License in the License file as well as the 15 * following notice: 16 * 17 * NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND DISTRIBUTION LICENSE 18 * (CDDL) 19 * For Covered Software in this distribution, this License shall be governed by the 20 * laws of the State of California (excluding conflict-of-law provisions). 21 * Any litigation relating to this License shall be subject to the jurisdiction of 22 * the Federal Courts of the Northern District of California and the state courts 23 * of the State of California, with venue lying in Santa Clara County, California. 24 * 25 * Contributor(s): 26 * 27 * If you wish your version of this file to be governed by only the CDDL or only 28 * the LGPL Version 2.1, indicate your decision by adding "[Contributor]" elects to 29 * include this software in this distribution under the [CDDL or LGPL Version 2.1] 30 * license." If you don't indicate a single choice of license, a recipient has the 31 * option to distribute your version of this file under either the CDDL or the LGPL 32 * Version 2.1, or to extend the choice of license to its licensees as provided 33 * above. However, if you add LGPL Version 2.1 code and therefore, elected the LGPL 34 * Version 2 license, then the option applies only if the new code is made subject 35 * to such option by the copyright holder. 36 */ 37 38 #ifdef HAVE_CONFIG_H 39 #include <config.h> 40 #endif 41 42 #include <algorithm> 43 #include "imi_defines.h" 44 #include "imi_context.h" 45 46 TCandiRank::TCandiRank(bool user, bool best, unsigned len, 47 bool fromLattice, TSentenceScore score) 48 { 49 anony.m_user = (user)?0:1; 50 anony.m_best = (best)?0:1; 51 anony.m_len = (len > 31)?(0):(31-len); 52 anony.m_lattice = (fromLattice)?0:1; 53 54 double ds = -score.log2(); 55 56 //make it 24-bit 57 if (ds > 32767.0) 58 ds = 32767.0; 59 else if (ds < -32768.0) 60 ds = -32768.0; 61 unsigned cost = unsigned((ds+32768.0)*256.0); 62 anony.m_cost = cost; 63 } 64 65 TCandiRank::TCandiRank(bool user, bool best, unsigned len, 66 bool fromLattice, unsigned rank) 67 { 68 anony.m_user = (user)?0:1; 69 anony.m_best = (best)?0:1; 70 anony.m_len = (len > 31)?(0):(31-len); 71 anony.m_lattice = (fromLattice)?0:1; 72 anony.m_cost = rank; 73 } 74 75 void CLatticeFrame::print (std::string prefix) 76 { 77 if (m_bwType & BESTWORD) printf ("B"); 78 if (m_bwType & USER_SELECTED) printf ("U"); 79 printf ("\n"); 80 81 prefix += " "; 82 printf (" Lexicon States:\n"); 83 for_each (m_lexiconStates.begin (), m_lexiconStates.end (), 84 bind2nd (mem_fun_ref (&TLexiconState::print), prefix)); 85 86 printf (" Lattice States:\n"); 87 for_each (m_latticeStates.begin (), m_latticeStates.end (), 88 bind2nd (mem_fun_ref (&TLatticeState::print), prefix)); 89 printf ("\n"); 90 } 91 92 void CIMIContext::printLattice () 93 { 94 std::string prefix; 95 96 for (int i=0; i<=m_tailIdx; ++i) { 97 if (m_lattice[i].m_type == CLatticeFrame::UNUSED) 98 continue; 99 100 printf ("Lattice Frame [%d]:", i); 101 m_lattice[i].print (prefix); 102 } 103 } 104 105 CIMIContext::CIMIContext () 106 : m_tailIdx(1), m_pModel(NULL), m_pPinyinTrie(NULL), m_pUserDict(NULL), m_pHistory(NULL), 107 m_historyPower(3), m_bFullSymbolForwarding(false), m_pGetFullSymbolOp(NULL), 108 m_bFullPunctForwarding(true), m_pGetFullPunctOp(NULL), m_bDynaCandiOrder(true), 109 m_candiStarts(0), m_candiEnds(0), m_csLevel(0), m_bNonCompleteSyllable(true) 110 { 111 m_lattice.resize (MAX_LATTICE_LENGTH); 112 m_lattice[0].m_latticeStates.push_back (TLatticeState (-1.0, 0)); 113 } 114 115 void CIMIContext::setCoreData (CIMIData *pCoreData) 116 { 117 m_pModel = pCoreData->getSlm(); 118 m_pPinyinTrie = pCoreData->getPinyinTrie(); 119 } 120 121 void CIMIContext::clear () 122 { 123 _clearFrom (1); 124 m_tailIdx = 1; 125 m_candiStarts = m_candiEnds = 0; 126 } 127 128 void CIMIContext::_clearFrom (unsigned idx) 129 { 130 for (int i=idx; i<m_tailIdx+1; ++i) 131 m_lattice[i].clear(); 132 } 133 134 bool CIMIContext::buildLattice (IPySegmentor::TSegmentVec &segments, unsigned rebuildFrom, bool doSearch) 135 { 136 _clearFrom (rebuildFrom); 137 138 IPySegmentor::TSegmentVec::iterator it = segments.begin (); 139 IPySegmentor::TSegmentVec::iterator ite = segments.end (); 140 141 unsigned i, j=0; 142 for (; it != ite; ++it) { 143 i = it->m_start; 144 j = i + it->m_len; 145 146 if (i < rebuildFrom-1) 147 continue; 148 149 if (j >= m_lattice.capacity()-1) 150 break; 151 152 if (it->m_type == IPySegmentor::SYLLABLE) 153 _forwardSyllables (i, j, it->m_syllables); 154 else if (it->m_type == IPySegmentor::SYLLABLE_SEP) 155 _forwardSyllableSep (i, j); 156 else 157 _forwardString (i, j, it->m_syllables); 158 } 159 160 _forwardTail (j, j+1); 161 m_tailIdx = j+1; 162 163 return doSearch && searchFrom (rebuildFrom); 164 } 165 166 void CIMIContext::_forwardSyllables (unsigned i, unsigned j, std::vector<unsigned>& syllables) 167 { 168 std::vector<unsigned>::iterator it = syllables.begin (); 169 std::vector<unsigned>::iterator ite = syllables.end (); 170 171 _forwardSingleSyllable (i, j, *it, false); 172 for (++it; it != ite; ++it) 173 _forwardSingleSyllable (i, j, *it, true); 174 } 175 176 177 void CIMIContext::_forwardString (unsigned i, unsigned j, std::vector<unsigned>& strbuf) 178 { 179 if (strbuf.size() == 1) { 180 unsigned ch = strbuf[0]; 181 ispunct(ch)? _forwardPunctChar (i, j, ch): _forwardOrdinaryChar (i, j, ch); 182 } else{ 183 CLatticeFrame &fr = m_lattice[j]; 184 fr.m_wstr.assign (strbuf.begin(), strbuf.end()); 185 fr.m_lexiconStates.push_back (TLexiconState(i, 0)); 186 } 187 } 188 189 void CIMIContext::_forwardSingleSyllable (unsigned i, unsigned j, TSyllable syllable, bool isFuzzy) 190 { 191 const CPinyinTrie::TNode * pn = NULL; 192 193 CLatticeFrame &fr = m_lattice[j]; 194 fr.m_type = CLatticeFrame::SYLLABLE; 195 196 CLexiconStates::iterator it = m_lattice[i].m_lexiconStates.begin (); 197 CLexiconStates::iterator ite = m_lattice[i].m_lexiconStates.end (); 198 for (; it != ite; ++it) { 199 TLexiconState &lxst = *it; 200 bool added_from_sysdict = false; 201 202 if (lxst.m_pPYNode) { 203 // try to match a word from lattice i to lattice j 204 // and if match, we'll count it as a new lexicon on lattice j 205 pn = m_pPinyinTrie->transfer (lxst.m_pPYNode, syllable); 206 if (pn) { 207 added_from_sysdict = true; 208 TLexiconState new_lxst = TLexiconState (lxst.m_start, pn, lxst.m_syls, isFuzzy); 209 new_lxst.m_syls.push_back (syllable); 210 fr.m_lexiconStates.push_back (new_lxst); 211 } 212 } 213 214 if (m_pUserDict && lxst.m_syls.size() < MAX_USRDEF_WORD_LEN) { 215 // try to match a word from user dict 216 CSyllables syls = lxst.m_syls; 217 syls.push_back (syllable); 218 std::vector<CPinyinTrie::TWordIdInfo> words; 219 m_pUserDict->getWords (syls, words); 220 if (!words.empty() || !added_from_sysdict) { 221 // even if the words is empty we'll add a fake lexicon 222 // here. This helps _saveUserDict detect new words. 223 TLexiconState new_lxst = TLexiconState (lxst.m_start, lxst.m_syls, words, isFuzzy); 224 new_lxst.m_syls.push_back (syllable); 225 fr.m_lexiconStates.push_back (new_lxst); 226 } 227 } 228 } 229 230 // last, create a lexicon for single character with only one syllable 231 pn = m_pPinyinTrie->transfer (syllable); 232 if (pn) { 233 CSyllables syls; 234 syls.push_back (syllable); 235 TLexiconState new_lxst = TLexiconState (i, pn, syls, isFuzzy); 236 fr.m_lexiconStates.push_back (new_lxst); 237 } 238 } 239 240 void CIMIContext::_forwardSyllableSep (unsigned i, unsigned j) 241 { 242 CLatticeFrame &fr = m_lattice[j]; 243 fr.m_type = CLatticeFrame::SYLLABLE | CLatticeFrame::SYLLABLE_SEP; 244 fr.m_lexiconStates = m_lattice[i].m_lexiconStates; 245 } 246 247 void CIMIContext::_forwardPunctChar (unsigned i, unsigned j, unsigned ch) 248 { 249 CLatticeFrame &fr = m_lattice[j]; 250 251 wstring wstr; 252 unsigned wid = 0; 253 254 if (m_pGetFullPunctOp) { 255 wstr = (*m_pGetFullPunctOp) (ch); 256 wid = m_pPinyinTrie->getSymbolId (wstr); 257 258 if (!m_bFullPunctForwarding) 259 wstr.clear (); 260 } 261 262 fr.m_type = CLatticeFrame::PUNC; 263 264 if (!wstr.empty()) 265 fr.m_wstr = wstr; 266 else 267 fr.m_wstr.push_back (ch); 268 269 fr.m_lexiconStates.push_back (TLexiconState(i, wid)); 270 } 271 272 void CIMIContext::_forwardOrdinaryChar (unsigned i, unsigned j, unsigned ch) 273 { 274 CLatticeFrame &fr = m_lattice[j]; 275 276 wstring wstr; 277 unsigned wid = 0; 278 279 if (m_pGetFullSymbolOp) { 280 wstr = (*m_pGetFullSymbolOp) (ch); 281 wid = m_pPinyinTrie->getSymbolId (wstr); 282 283 if (!m_bFullSymbolForwarding) 284 wstr.clear (); 285 } 286 287 fr.m_type = wid? CLatticeFrame::SYMBOL: CLatticeFrame::ASCII; 288 289 if (!wstr.empty ()) 290 fr.m_wstr = wstr; 291 else 292 fr.m_wstr.push_back (ch); 293 294 fr.m_lexiconStates.push_back (TLexiconState(i, wid)); 295 } 296 297 void CIMIContext::_forwardTail (unsigned i, unsigned j) 298 { 299 CLatticeFrame &fr = m_lattice[j]; 300 fr.m_type = CLatticeFrame::TAIL; 301 fr.m_lexiconStates.push_back (TLexiconState (i, OOV_WORD_ID)); 302 } 303 304 bool CIMIContext::searchFrom (unsigned idx) 305 { 306 bool affectCandidates = (idx <= m_candiEnds); 307 308 _clearBestPath (); 309 310 for (; idx<=m_tailIdx; ++idx) { 311 CLatticeFrame &fr = m_lattice[idx]; 312 313 if (fr.m_type == CLatticeFrame::UNUSED) 314 continue; 315 316 fr.m_latticeStates.clear (); 317 318 /* user selected word might be cut in next step */ 319 if (fr.m_bwType & CLatticeFrame::USER_SELECTED) 320 _transferBetween (fr.m_bestWord.m_start, idx, fr.m_bestWord.m_wordId); 321 322 CLexiconStates::iterator it = fr.m_lexiconStates.begin (); 323 CLexiconStates::iterator ite = fr.m_lexiconStates.end (); 324 for (; it != ite; ++it) { 325 unsigned word_num = 0; 326 TLexiconState &lxst = *it; 327 const CPinyinTrie::TWordIdInfo *words = lxst.getWords (word_num); 328 329 if (!word_num) 330 continue; 331 332 if (lxst.m_start == m_candiStarts && idx > m_candiEnds) 333 affectCandidates = true; 334 335 /* only selected the word with higher unigram probablities */ 336 int maxsz = it->m_bFuzzy? MAX_LEXICON_TRIES>1: MAX_LEXICON_TRIES; 337 int sz = word_num<maxsz? word_num: maxsz; 338 int i = 0, count = 0; 339 double ic = it->m_bFuzzy? 0.5: 1.0; 340 for (i = 0; count < sz && i < sz && (words[i].m_bSeen || count < 2); ++i) { 341 if (m_csLevel >= words[i].m_csLevel) { 342 _transferBetween (lxst.m_start, idx, words[i].m_id, ic); 343 ++ count; 344 } 345 } 346 347 /* try extra words in history cache */ 348 if (m_pHistory) { 349 for (; i < word_num; ++i) { 350 if (m_csLevel >= words[i].m_csLevel && m_pHistory->seenBefore (words[i].m_id)) 351 _transferBetween (lxst.m_start, idx, words[i].m_id, ic); 352 } 353 } 354 } 355 } 356 357 _backTraceBestPath (); 358 359 return affectCandidates; 360 } 361 362 void CIMIContext::_transferBetween (unsigned start, unsigned end, unsigned wid, double ic) 363 { 364 CLatticeFrame &start_fr = m_lattice[start]; 365 CLatticeFrame &end_fr = m_lattice[end]; 366 367 TLatticeState node (-1.0, end); 368 TSentenceScore efic (ic); 369 370 if ((end_fr.m_bwType & CLatticeFrame::USER_SELECTED) && end_fr.m_bestWord.m_wordId == wid) 371 efic = TSentenceScore (30000, 1.0); 372 373 static double s_history_distribution[11] = {0.0, 0.05, 0.10, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.50}; 374 double weight_h = s_history_distribution[m_historyPower]; 375 double weight_s = 1.0 - weight_h; 376 377 CLatticeStates::iterator it = start_fr.m_latticeStates.begin(); 378 CLatticeStates::iterator ite = start_fr.m_latticeStates.end(); 379 380 for (; it != ite; ++it) { 381 node.m_pBackTraceNode = &(*it); 382 node.m_backTraceWordId = wid; 383 384 double ts = m_pModel->transfer(it->m_slmState, wid, node.m_slmState); 385 m_pModel->historify(node.m_slmState); 386 387 // backward to psuedo root, so wid is probably a user word, save the wid in idx field, 388 // so that later we could get it via CThreadSlm::lastWordId, to calculate p_{cache} correctly. 389 if (node.m_slmState.getLevel() == 0 && m_pHistory && m_pHistory->seenBefore(wid)) 390 node.m_slmState.setIdx(wid); // an psuedo unigram node state 391 392 if (m_pHistory) { 393 unsigned history[2] = {m_pModel->lastWordId(it->m_slmState), wid}; 394 double hpr = m_pHistory->pr(history, history+2); 395 ts = weight_s * ts + weight_h*hpr; 396 } 397 398 node.m_score = it->m_score * efic * TSentenceScore(ts); 399 end_fr.m_latticeStates.push_back (node); 400 } 401 } 402 403 void CIMIContext::_backTraceBestPath () 404 { 405 CLatticeStates& tail_states = m_lattice[m_tailIdx].m_latticeStates; 406 407 // there must be some transfer errors 408 if (tail_states.size() != 1) 409 return; 410 411 TLatticeState *bs = &(tail_states[0]); 412 413 while (bs->m_pBackTraceNode) { 414 unsigned start = bs->m_pBackTraceNode->m_frIdx; 415 unsigned end = bs->m_frIdx; 416 CLatticeFrame & end_fr = m_lattice[end]; 417 418 if (! (end_fr.m_bwType & CLatticeFrame::USER_SELECTED)) { 419 end_fr.m_bwType |= CLatticeFrame::BESTWORD; 420 421 end_fr.m_bestWord.m_start = start; 422 end_fr.m_bestWord.m_end = end; 423 end_fr.m_bestWord.m_wordId = bs->m_backTraceWordId; 424 end_fr.m_bestWord.m_cwstr = end_fr.m_wstr.empty()? 425 _getWstr (bs->m_backTraceWordId): 426 end_fr.m_wstr.c_str(); 427 } 428 429 m_bestPath.push_back (end); 430 bs = bs->m_pBackTraceNode; 431 } 432 433 std::reverse (m_bestPath.begin(), m_bestPath.end()); 434 } 435 436 void CIMIContext::_clearBestPath () 437 { 438 m_bestPath.clear (); 439 } 440 441 unsigned CIMIContext::getBestSentence (wstring& result, unsigned start, unsigned end) 442 { 443 result.clear(); 444 445 if (UINT_MAX == end) end = m_tailIdx - 1; 446 447 while (end > start && m_lattice[end].m_bwType == CLatticeFrame::NO_BESTWORD) 448 end --; 449 450 unsigned i = end, nWordConverted = 0; 451 while (i > start) { 452 CLatticeFrame &fr = m_lattice[i]; 453 result.insert (0, fr.m_bestWord.m_cwstr); 454 i = fr.m_bestWord.m_start; 455 nWordConverted ++; 456 } 457 458 return nWordConverted; 459 } 460 461 struct TCandiPair { 462 CCandidate m_candi; 463 TCandiRank m_Rank; 464 465 TCandiPair() : m_candi(), m_Rank() { } 466 }; 467 468 struct TCandiPairPtr { 469 TCandiPair* m_Ptr; 470 471 TCandiPairPtr(TCandiPair* p=NULL) : m_Ptr(p) 472 { } 473 474 bool 475 operator< (const TCandiPairPtr& b) const 476 { return m_Ptr->m_Rank < b.m_Ptr->m_Rank; } 477 }; 478 479 const TWCHAR *CIMIContext::_getWstr (unsigned wid) 480 { 481 if (wid < m_pPinyinTrie->getWordCount()) 482 return (*m_pPinyinTrie)[wid]; 483 else if (m_pUserDict) 484 return (*m_pUserDict)[wid]; 485 else 486 return NULL; 487 } 488 489 void CIMIContext::getCandidates (unsigned frIdx, CCandidates& result) 490 { 491 TCandiPair cp; 492 static std::map<unsigned, TCandiPair> map; 493 std::map<unsigned, TCandiPair>::iterator it_map; 494 495 map.clear(); 496 result.clear(); 497 498 int len = 1; 499 cp.m_candi.m_start = m_candiStarts = frIdx++; 500 501 for (;frIdx < m_tailIdx; ++frIdx, ++len) { 502 CLatticeFrame &fr = m_lattice[frIdx]; 503 504 cp.m_candi.m_end = frIdx; 505 if (fr.m_bwType != CLatticeFrame::NO_BESTWORD && fr.m_bestWord.m_start == m_candiStarts) { 506 cp.m_candi = fr.m_bestWord; 507 cp.m_Rank = TCandiRank(fr.m_bwType & CLatticeFrame::USER_SELECTED, 508 fr.m_bwType & CLatticeFrame::BESTWORD, 509 0, false, 0); 510 map [cp.m_candi.m_wordId] = cp; 511 } 512 513 if (!fr.isSyllableFrame ()) 514 continue; 515 516 bool found = false; 517 CLexiconStates::iterator it = fr.m_lexiconStates.begin(); 518 CLexiconStates::iterator ite = fr.m_lexiconStates.end(); 519 for (; it != ite; ++it) { 520 TLexiconState & lxst = *it; 521 522 if (lxst.m_start != m_candiStarts) 523 continue; 524 525 found = true; 526 unsigned word_num; 527 const CPinyinTrie::TWordIdInfo *words = lxst.getWords (word_num); 528 529 for (unsigned i=0; i<word_num; ++i) { 530 if (m_csLevel < words[i].m_csLevel) 531 continue; 532 533 cp.m_candi.m_wordId = words[i].m_id; 534 cp.m_candi.m_cwstr = _getWstr (cp.m_candi.m_wordId); 535 536 //sorting according to the order in PinYinTire 537 cp.m_Rank = TCandiRank(false, false, len, false, i); 538 it_map = map.find(cp.m_candi.m_wordId); 539 if (it_map == map.end() || cp.m_Rank < it_map->second.m_Rank) 540 map[cp.m_candi.m_wordId] = cp; 541 } 542 } 543 544 if (!found) break; 545 546 if (m_bDynaCandiOrder) { 547 CLatticeStates::iterator it = fr.m_latticeStates.begin(); 548 CLatticeStates::iterator ite = fr.m_latticeStates.end(); 549 for (; it != ite; ++it) { 550 TLatticeState & ltst = *it; 551 552 if (ltst.m_pBackTraceNode->m_frIdx != m_candiStarts) 553 continue; 554 555 cp.m_candi.m_wordId = ltst.m_backTraceWordId; 556 cp.m_candi.m_cwstr = _getWstr (cp.m_candi.m_wordId); 557 cp.m_Rank = TCandiRank(false, false, len, true, ltst.m_score/ltst.m_pBackTraceNode->m_score); 558 it_map = map.find(cp.m_candi.m_wordId); 559 if (it_map == map.end() || cp.m_Rank < it_map->second.m_Rank) 560 map[cp.m_candi.m_wordId] = cp; 561 } 562 } 563 564 m_candiEnds = frIdx; 565 } 566 567 std::vector<TCandiPairPtr> vec; 568 569 vec.reserve(map.size()); 570 std::map<unsigned, TCandiPair>::iterator it_mapE = map.end(); 571 for (it_map = map.begin(); it_map != it_mapE; ++it_map) 572 vec.push_back(TCandiPairPtr(&(it_map->second))); 573 std::make_heap(vec.begin(), vec.end()); 574 std::sort_heap(vec.begin(), vec.end()); 575 576 for (int i=0, sz=vec.size(); i < sz; ++i) 577 result.push_back(vec[i].m_Ptr->m_candi); 578 } 579 580 unsigned CIMIContext::cancelSelection (unsigned frIdx, bool doSearch) 581 { 582 unsigned ret = frIdx; 583 584 CLatticeFrame &fr = m_lattice[frIdx]; 585 while (fr.m_bwType & CLatticeFrame::IGNORED) { 586 --frIdx; 587 fr = m_lattice[frIdx]; 588 } 589 590 if (fr.m_bwType & CLatticeFrame::USER_SELECTED) { 591 ret = fr.m_bestWord.m_start; 592 fr.m_bwType = CLatticeFrame::NO_BESTWORD; 593 if (doSearch) searchFrom (frIdx); 594 } 595 596 return ret; 597 } 598 599 void CIMIContext::makeSelection (CCandidate &candi, bool doSearch) 600 { 601 CLatticeFrame &fr = m_lattice[candi.m_end]; 602 fr.m_bwType = fr.m_bwType | CLatticeFrame::USER_SELECTED; 603 fr.m_bestWord = candi; 604 if (doSearch) searchFrom (candi.m_end); 605 } 606 607 void CIMIContext::memorize () 608 { 609 _saveUserDict (); 610 _saveHistoryCache (); 611 } 612 613 void CIMIContext::_saveUserDict () 614 { 615 if (!m_pUserDict) 616 return; 617 618 if (m_bestPath.empty()) 619 return; 620 621 bool has_user_selected = false; 622 std::vector<unsigned>::iterator it = m_bestPath.begin(); 623 std::vector<unsigned>::iterator ite = m_bestPath.end() - 1; 624 unsigned s = 0; 625 for (; it != ite; ++it, ++s) { 626 has_user_selected |= (m_lattice[*it].m_bwType & CLatticeFrame::USER_SELECTED); 627 if (!m_lattice[*it].isSyllableFrame ()) 628 break; 629 } 630 631 if (has_user_selected && s >= 2) { 632 CSyllables syls; 633 -- it; 634 CLexiconStates::iterator lxit = m_lattice[*it].m_lexiconStates.begin(); 635 CLexiconStates::iterator lxite = m_lattice[*it].m_lexiconStates.end(); 636 for (; lxit != lxite; ++lxit) { 637 if (lxit->m_start == 0 && !lxit->m_bFuzzy) { 638 syls = lxit->m_syls; 639 break; 640 } 641 } 642 643 if (!syls.empty()) { 644 wstring phrase; 645 getBestSentence (phrase, 0, *it); 646 m_pUserDict->addWord (syls, phrase); 647 } 648 } 649 } 650 651 void CIMIContext::_saveHistoryCache () 652 { 653 if (!m_pHistory) 654 return; 655 656 if (m_bestPath.empty()) 657 return; 658 659 std::vector<unsigned> result; 660 std::vector<unsigned>::const_iterator it = m_bestPath.begin(); 661 std::vector<unsigned>::const_iterator ite = m_bestPath.end() - 1; 662 for (; it != ite; ++it) { 663 CLatticeFrame &fr = m_lattice[*it]; 664 if (fr.isSyllableFrame ()) 665 result.push_back (fr.m_bestWord.m_wordId); 666 else 667 result.push_back (0); 668 } 669 670 if (!result.empty()) 671 m_pHistory->memorize (&(result[0]), &(result[0]) + result.size()); 672 673 } 674 675
