OpenGrok

Cross Reference: imi_context.cpp
xref: /nv-g11n/inputmethod/sunpinyin2/src/ime-core/imi_context.cpp
Home | History | Annotate | Line # | Download | only in ime-core
      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