PLearn 0.1
|
00001 // -*- C++ -*- 00002 00003 // WordNetSenseDictionary.cc 00004 // 00005 // Copyright (C) 2004 Hugo Larochelle, Christopher Kermorvant 00006 // 00007 // Redistribution and use in source and binary forms, with or without 00008 // modification, are permitted provided that the following conditions are met: 00009 // 00010 // 1. Redistributions of source code must retain the above copyright 00011 // notice, this list of conditions and the following disclaimer. 00012 // 00013 // 2. Redistributions in binary form must reproduce the above copyright 00014 // notice, this list of conditions and the following disclaimer in the 00015 // documentation and/or other materials provided with the distribution. 00016 // 00017 // 3. The name of the authors may not be used to endorse or promote 00018 // products derived from this software without specific prior written 00019 // permission. 00020 // 00021 // THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 00022 // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 00023 // OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN 00024 // NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 00025 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 00026 // TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 00027 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 00028 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 00029 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 00030 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00031 // 00032 // This file is part of the PLearn library. For more information on the PLearn 00033 // library, go to the PLearn Web site at www.plearn.org 00034 00035 /* ******************************************************* 00036 * $Id: WordNetSenseDictionary.cc 6338 2006-10-23 20:33:09Z larocheh $ 00037 ******************************************************* */ 00038 00039 // Authors: Hugo Larochelle, Christopher Kermorvant 00040 00044 #include "WordNetSenseDictionary.h" 00045 #include <plearn/base/stringutils.h> 00046 #include <plearn/base/lexical_cast.h> 00047 00048 namespace PLearn { 00049 using namespace std; 00050 00051 char* cstr(string& str) 00052 { 00053 char* cstr = new char[str.size() + 1]; 00054 for (unsigned int i = 0; i < str.size(); i++) 00055 *(cstr + i) = str[i]; 00056 cstr[str.size()] = '\0'; 00057 return cstr; 00058 } 00059 00061 string getSynsetKey(SynsetPtr ssp) 00062 { 00063 if(!ssp) PLERROR("In getSynsetKey(): SynsetPtr is NULL"); 00064 string ssk = tostring(ssp->words[0]); 00065 for(int i=1; i<ssp->wcount && i<3; i++) 00066 ssk = ssk + "," + tostring(ssp->words[i]); 00067 ssk = ssk + ":" + tostring(ssp->pos) + "#" + tostring(ssp->hereiam); 00068 return ssk; 00069 } 00070 00072 SynsetPtr getSynsetPtr(string synset_key) 00073 { 00074 vector<string> splits = split(synset_key,",:#"); 00075 char* srch_str = cstr(splits[0]); 00076 string pos = tostring(splits[splits.size()-2]); 00077 int pos_int = getpos(&pos[0]); 00078 long hereiam = tolong(splits[splits.size()-1]); 00079 SynsetPtr ret = read_synset(pos_int,hereiam,srch_str); 00080 delete(srch_str); 00081 return ret; 00082 } 00083 00084 void extractSenses(string word, int wn_pos, string symbol_type, TVec<string>& senses,TVec<int>& tagcnts, TVec< TVec<string> >& ancestors, bool extract_ancestors) 00085 { 00086 char* cword = cstr(word); 00087 SynsetPtr ssp = NULL; 00088 IndexPtr idx = getindex(cword, wn_pos); 00089 ssp = findtheinfo_ds(cword, wn_pos, -HYPERPTR, ALLSENSES); 00090 00091 if (ssp == NULL) 00092 { 00093 if(idx) free_index(idx); 00094 delete cword; 00095 return; 00096 } 00097 00098 int wnsn = 0; 00099 SynsetPtr head = ssp; 00100 // extract all senses for a given word 00101 while (ssp != NULL) 00102 { 00103 ++wnsn; 00104 if(symbol_type == "sense_key") 00105 { 00106 char *charsk = WNSnsToStr(idx, wnsn); 00107 senses.push_back(string(charsk)); 00108 delete charsk; 00109 } 00110 else if(symbol_type == "synset_key") 00111 { 00112 senses.push_back(getSynsetKey(ssp)); 00113 } 00114 else PLERROR("In extractSenses(): symbol_type %s not valid", symbol_type.c_str()); 00115 if(extract_ancestors) 00116 { 00117 TVec< TVec<string> > this_anc_str; 00118 TVec< SynsetPtr > beg_anc(1); 00119 beg_anc[0] = ssp; 00120 if(wn_pos == NOUN) 00121 extractAncestors(beg_anc,this_anc_str, WN_NOUN_NODE); 00122 else if(wn_pos == VERB) 00123 extractAncestors(beg_anc,this_anc_str, WN_VERB_NODE); 00124 else if(wn_pos == ADJ) 00125 extractAncestors(beg_anc,this_anc_str, WN_ADJ_NODE); 00126 else if(wn_pos == ADV) 00127 extractAncestors(beg_anc,this_anc_str, WN_ADV_NODE); 00128 else 00129 PLERROR("In extractSenses(): wn_pos %d invalid", wn_pos); 00130 ancestors.append(this_anc_str); 00131 } 00132 tagcnts.push_back(GetTagcnt(idx,wnsn)); 00133 ssp = ssp->nextss; 00134 } 00135 free_syns(head); 00136 free_index(idx); 00137 delete cword; 00138 } 00139 00140 void extractAncestors(TVec<SynsetPtr> anc, TVec< TVec<string> >& anc_str, string root_node) 00141 { 00142 SynsetPtr a; 00143 SynsetPtr extra_a; 00144 int i=0; 00145 anc_str.resize(anc.length()); 00146 for(int k=0; k<anc_str.length(); k++) 00147 anc_str[k].resize(0); 00148 00149 while(i<anc.length()) 00150 { 00151 // Add sense 00152 a = anc[i]; 00153 anc_str[i].push_back(getSynsetKey(a)); 00154 // Go through one level and start looking for paths 00155 a = a->ptrlist; 00156 while(a != NULL) 00157 { 00158 extra_a = a->nextss; 00159 while(extra_a != NULL) 00160 { 00161 anc.push_back(extra_a); 00162 anc_str.push_back(anc_str[i].copy()); 00163 extra_a = extra_a->nextss; 00164 } 00165 anc_str[i].push_back(getSynsetKey(a)); 00166 a = a->ptrlist; 00167 } 00168 anc_str[i].push_back(root_node); 00169 i++; 00170 } 00171 } 00172 00173 string stemWord(string word) 00174 { 00175 char* input_word = cstr(word); 00176 char* lemma = morphword(input_word, NOUN); 00177 if (lemma == NULL) 00178 { 00179 lemma = morphword(input_word, VERB); 00180 if (lemma == NULL) 00181 { 00182 lemma = morphword(input_word, ADJ); 00183 if (lemma == NULL) 00184 { 00185 lemma = morphword(input_word, ADV); 00186 } 00187 } 00188 } 00189 00190 delete input_word; 00191 if (lemma == NULL) return word; 00192 else return removeblanks(tostring(lemma)); 00193 } 00194 00195 string stemWord(string word, int wn_pos) 00196 { 00197 char* input_word = cstr(word); 00198 char* lemma = morphword(input_word, wn_pos); 00199 delete input_word; 00200 if (lemma == NULL) 00201 return word; 00202 else 00203 return removeblanks(tostring(lemma)); 00204 } 00205 00206 void stemsOfWord(string word, int wn_pos, TVec<string>& stems) 00207 { 00208 stems.resize(0); 00209 stems.push_back(word); 00210 char* input_word = cstr(word); 00211 char* lemma = morphstr(input_word, wn_pos); 00212 string lemma_str; 00213 while(lemma) 00214 { 00215 lemma_str = removeblanks(tostring(lemma)); 00216 if(lemma_str != word) 00217 stems.push_back(lemma_str); 00218 lemma = morphstr(NULL, wn_pos); 00219 } 00220 delete input_word; 00221 } 00222 00223 void stemsOfWord(string word, TVec<string>& stems) 00224 { 00225 stems.resize(0); 00226 stems.push_back(word); 00227 char* input_word = cstr(word); 00228 00229 char* lemma = morphstr(input_word, NOUN); 00230 string lemma_str; 00231 while(lemma) 00232 { 00233 lemma_str = removeblanks(tostring(lemma)); 00234 if(lemma_str != word) 00235 stems.push_back(lemma_str); 00236 lemma = morphstr(NULL, NOUN); 00237 } 00238 00239 lemma = morphstr(input_word, VERB); 00240 while(lemma) 00241 { 00242 lemma_str = removeblanks(tostring(lemma)); 00243 if(lemma_str != word) 00244 stems.push_back(lemma_str); 00245 lemma = morphstr(NULL, VERB); 00246 } 00247 00248 lemma = morphstr(input_word, ADJ); 00249 while(lemma) 00250 { 00251 lemma_str = removeblanks(tostring(lemma)); 00252 if(lemma_str != word) 00253 stems.push_back(lemma_str); 00254 lemma = morphstr(NULL, ADJ); 00255 } 00256 00257 lemma = morphstr(input_word, ADV); 00258 while(lemma) 00259 { 00260 lemma_str = removeblanks(tostring(lemma)); 00261 if(lemma_str != word) 00262 stems.push_back(lemma_str); 00263 lemma = morphstr(NULL, ADV); 00264 } 00265 00266 delete input_word; 00267 } 00268 00269 00270 WordNetSenseDictionary::WordNetSenseDictionary() 00271 : options_stem_words(false), options_to_lower_case(false), symbol_type("sense_key"), use_wordnet_hierarchy(false) 00272 { 00273 if(wninit()<0) 00274 PLERROR("In WordNetSenseDictionary(): could not open WordNet database files"); 00275 } 00276 00277 PLEARN_IMPLEMENT_OBJECT(WordNetSenseDictionary, 00278 "Dictionary of WordNet senses", 00279 "The symbols in the instantiated dictionary are senses (not words!).\n"); 00280 00281 void WordNetSenseDictionary::declareOptions(OptionList& ol) 00282 { 00283 declareOption(ol, "options_stem_words", &WordNetSenseDictionary::options_stem_words, OptionBase::buildoption, "Indication that words given as options should be stemmed when looking at their possible senses"); 00284 declareOption(ol, "options_to_lower_case", &WordNetSenseDictionary::options_to_lower_case, OptionBase::buildoption, "Indication that words given as options should be put to lower case"); 00285 declareOption(ol, "symbol_type", &WordNetSenseDictionary::symbol_type, OptionBase::buildoption, "Type of representation (symbol) of the senses. The currently supported types are:\n" 00286 " - \"sense_key\" (standard WordNet representation)\n" 00287 " - \"synset_key\" (custom made representation, that consists of maximum three words from the synset, the POS category and the byte offset of the synset)\n" 00288 "Note that the Dictionary will always accept \"sense_key\" symbols as input, but will convert it automatically to\n" 00289 "the symbol_type representation and treat it as such."); 00290 declareOption(ol, "possible_values_for_word", &WordNetSenseDictionary::possible_values_for_word, OptionBase::learntoption, "Mapping of the possible id values for a given word"); 00291 declareOption(ol, "use_wordnet_hierarchy", &WordNetSenseDictionary::use_wordnet_hierarchy, OptionBase::buildoption, "Indication that the WordNet hierarchy will be used"); 00292 declareOption(ol, "children", &WordNetSenseDictionary::children, OptionBase::learntoption, "Synset children mapping"); 00293 declareOption(ol, "parents", &WordNetSenseDictionary::parents, OptionBase::learntoption, "Synset parents mapping"); 00294 declareOption(ol, "sense_prior", &WordNetSenseDictionary::sense_prior, OptionBase::learntoption, "Prior distribution for p(sense|word) given by WordNet"); 00295 00296 inherited::declareOptions(ol); 00297 } 00298 00299 void WordNetSenseDictionary::build_() 00300 { 00301 if(use_wordnet_hierarchy && symbol_type == "sense_key") 00302 PLERROR("In WordNetSenseDictionary::build_(): cannot use WordNet hierarchy with symbol type \"sense_key\""); 00303 } 00304 00305 // ### Nothing to add here, simply calls build_ 00306 void WordNetSenseDictionary::build() 00307 { 00308 inherited::build(); 00309 build_(); 00310 } 00311 00312 void WordNetSenseDictionary::getSensesFromWordNet(TVec<string> options) 00313 { 00314 string word = ""; 00315 stems.resize(0); 00316 int wn_pos = -1; 00317 senses.resize(0); 00318 ancestors_vec.resize(0); 00319 tagcnts.resize(0); 00320 00321 word = removeblanks(options[0]); 00322 // This may be unnecessary, given "options_stem_words" 00323 if(options_to_lower_case) word = lowerstring(word); 00324 00325 if(wn_pos < 0) 00326 { 00327 // Extract senses for all possible POS 00328 00329 // NOUN 00330 extractSenses(word,NOUN,symbol_type,senses, tagcnts, ancestors_vec, use_wordnet_hierarchy); 00331 if(options_stem_words) 00332 { 00333 stemsOfWord(word,NOUN,stems); 00334 for(int i=0; i<stems.length(); i++) 00335 if(word != stems[i]) 00336 extractSenses(stems[i],NOUN,symbol_type,senses, tagcnts, ancestors_vec, use_wordnet_hierarchy); 00337 } 00338 00339 // VERB 00340 extractSenses(word,VERB,symbol_type,senses, tagcnts, ancestors_vec, use_wordnet_hierarchy); 00341 if(options_stem_words) 00342 { 00343 stemsOfWord(word,VERB,stems); 00344 for(int i=0; i<stems.length(); i++) 00345 if(word != stems[i]) 00346 extractSenses(stems[i],VERB,symbol_type,senses, tagcnts, ancestors_vec, use_wordnet_hierarchy); 00347 } 00348 00349 // ADJ 00350 extractSenses(word,ADJ,symbol_type,senses, tagcnts, ancestors_vec, use_wordnet_hierarchy); 00351 if(options_stem_words) 00352 { 00353 stemsOfWord(word,ADJ,stems); 00354 for(int i=0; i<stems.length(); i++) 00355 if(word != stems[i]) 00356 extractSenses(stems[i],ADJ,symbol_type,senses, tagcnts, ancestors_vec, use_wordnet_hierarchy); 00357 } 00358 00359 // ADV 00360 extractSenses(word,ADV,symbol_type,senses, tagcnts, ancestors_vec, use_wordnet_hierarchy); 00361 if(options_stem_words) 00362 { 00363 stemsOfWord(word,ADV,stems); 00364 for(int i=0; i<stems.length(); i++) 00365 if(word != stems[i]) 00366 extractSenses(stems[i],ADV,symbol_type,senses, tagcnts, ancestors_vec, use_wordnet_hierarchy); 00367 } 00368 00369 if(tagcnts.length()>0) 00370 { 00371 word_sense = word + " " + senses[0]; 00372 if(sense_prior.find(word_sense) == sense_prior.end()) 00373 { 00374 int sum = 0; 00375 for(int i=0; i<tagcnts.length(); i++) 00376 sum += tagcnts[i]; 00377 for(int i=0; i<tagcnts.length();i++) 00378 { 00379 word_sense = word + " " + senses[i]; 00380 sense_prior[word_sense] = ((real)tagcnts[i])/sum; 00381 } 00382 } 00383 } 00384 } 00385 else 00386 { 00387 // Extract senses only for wn_pos 00388 extractSenses(word,wn_pos,symbol_type,senses, tagcnts, ancestors_vec, use_wordnet_hierarchy); 00389 if(options_stem_words) 00390 { 00391 stemsOfWord(word,wn_pos,stems); 00392 for(int i=0; i<stems.length(); i++) 00393 if(word != stems[i]) 00394 extractSenses(stems[i],wn_pos,symbol_type,senses, tagcnts, ancestors_vec, use_wordnet_hierarchy); 00395 } 00396 00397 PLERROR("In WordNetSenseDictionary::getSensesFromWordNet(): producing sense_prior not implemented yet"); 00398 } 00399 00400 if(use_wordnet_hierarchy) 00401 { 00402 int n1,n2; 00403 string str1,str2; 00404 for(int i=0; i<ancestors_vec.length(); i++) 00405 { 00406 for(int j=0; j<ancestors_vec[i].length(); j++) 00407 { 00408 if(j != ancestors_vec[i].length()-1) 00409 { 00410 str1 = ancestors_vec[i][j]; 00411 str2 = ancestors_vec[i][j+1]; 00412 } 00413 else 00414 { 00415 str1 = ancestors_vec[i].last(); 00416 str2 = WN_ROOT_NODE; 00417 } 00418 n1 = inherited::getId(str1); 00419 n2 = inherited::getId(str2); 00420 00421 if(!isIn(str1) || !isIn(str2)) 00422 continue; 00423 00424 if(parents.find(n1) == parents.end()) 00425 { 00426 TVec<int> p(1); 00427 p[0] = n2; 00428 parents[n1] = p.copy(); 00429 } 00430 else 00431 { 00432 if(parents[n1].find(n2)>=0) 00433 parents[n1].push_back(n2); 00434 } 00435 if(children.find(n2) == children.end()) 00436 { 00437 TVec<int> c(1); 00438 c[0] = n1; 00439 children[n2] = c.copy(); 00440 } 00441 else 00442 { 00443 if(children[n2].find(n1)>=0) 00444 children[n2].push_back(n1); 00445 } 00446 } 00447 } 00448 } 00449 } 00450 00451 int WordNetSenseDictionary::getId(string symbol, TVec<string> options) 00452 { 00453 // This permits converting an input symbol as sense_key 00454 // to some other symbol_type... 00455 if(strchr(symbol.c_str(),'%') && symbol_type != "sense_key") 00456 { 00457 if(symbol_type == "synset_key") 00458 { 00459 char* symbol_cstr = cstr(symbol); 00460 symbol = getSynsetKey(GetSynsetForSense(symbol_cstr)); 00461 delete symbol_cstr; 00462 } 00463 else PLERROR("In getId(): symbol_type %s not valid", symbol_type.c_str()); 00464 } 00465 00466 // Convert WordNet sense number to sense symbol 00467 if(options.length() == 2 && pl_isnumber(symbol)) 00468 { 00469 char* cword = cstr(options[0]); 00470 int wn_pos = -1; 00471 if(strstr(options[1].c_str(),"NN")) wn_pos = NOUN; 00472 if(strstr(options[1].c_str(),"VB")) wn_pos = VERB; 00473 if(strstr(options[1].c_str(),"JJ")) wn_pos = ADJ; 00474 if(strstr(options[1].c_str(),"RB")) wn_pos = ADV; 00475 if (wn_pos < 0) 00476 PLERROR("In WordNetSenseDictionary::extractSenses(): %s is an invalid POS", options[1].c_str()); 00477 00478 SynsetPtr ssp = NULL; 00479 IndexPtr idx = getindex(cword, wn_pos); 00480 ssp = findtheinfo_ds(cword, wn_pos, -HYPERPTR, toint(symbol)); 00481 00482 if (ssp == NULL) 00483 PLERROR("In WordNetSenseDictionary::extractSenses(): cannot find sense with number %s for word %s of POS %s", symbol.c_str(), options[0].c_str(), options[1].c_str()); 00484 00485 if(symbol_type == "sense_key") 00486 { 00487 char *charsk = WNSnsToStr(idx, toint(symbol)); 00488 symbol = string(charsk); 00489 delete charsk; 00490 } 00491 else if(symbol_type == "synset_key") 00492 { 00493 symbol = getSynsetKey(ssp); 00494 } 00495 else PLERROR("In extractSenses(): symbol_type %s not valid", symbol_type.c_str()); 00496 if(ssp->nextss != NULL) 00497 PLERROR("In extractSenses(): more than one possible sense for sense number %s of word %s of POS %s", symbol.c_str(), options[0].c_str(), options[1].c_str()); 00498 free_syns(ssp); 00499 free_index(idx); 00500 delete cword; 00501 } 00502 00503 ret = inherited::getId(symbol,options); 00504 00505 // call getValues, which fills possible_values and gets the ids for all those senses, to do a compatibility check, if necessary 00506 if(update_mode == UPDATE && options.length() != 0) 00507 { 00508 getValues(options, possible_values); 00509 //getSensesFromWordNet(options); 00510 if(possible_values.find(ret) < 0) 00511 PLWARNING("In WordNetSenseDictionary::getId(): sense %s is not among possible symbols",symbol.c_str()); 00512 } 00513 00514 /* 00515 // Extracting new senses. If dictionary can be updated and 00516 // if symbol_type == "synset_key", then need to be sure that all 00517 // synsets were inserted, even if the current synset is !!! 00518 if(update_mode == UPDATE && (symbol_type == "synset_key" || !isIn(symbol,options)) ) 00519 for(int i=0; i<senses.length(); i++) 00520 inherited::getId(senses[i],options); 00521 */ 00522 00523 return ret; 00524 } 00525 00526 void WordNetSenseDictionary::getValues(TVec<string> options, Vec& values) 00527 { 00528 if(options.length() == 0) 00529 return inherited::getValues(options,values); 00530 else 00531 { 00532 //refill_possible_values = 1; 00533 //if(options.length() > 1) PLERROR("In WordNetSenseDictionary::getSensesFromWordNet(): options.length()>1 not supported"); 00534 if(possible_values_for_word.find(options[0]) == possible_values_for_word.end()) 00535 { 00536 getSensesFromWordNet(options); 00537 values.resize(senses.length()); 00538 for(int i=0; i<senses.length(); i++) 00539 values[i] = inherited::getId(senses[i]); 00540 possible_values_for_word[options[0]] = values.copy(); 00541 } 00542 else 00543 { 00544 values.resize(possible_values_for_word[options[0]].length()); 00545 values << possible_values_for_word[options[0]]; 00546 } 00547 } 00548 } 00549 00550 int WordNetSenseDictionary::size(TVec<string> options){ 00551 if(options.length() == 0) 00552 return inherited::size(); 00553 else 00554 { 00555 getValues(options,possible_values); 00556 return possible_values.length(); 00557 } 00558 } 00559 00560 void WordNetSenseDictionary::parentsOf(int sense, TVec<int>& the_parents){ 00561 if(parents.find(sense) == parents.end()) 00562 the_parents.resize(0); 00563 else 00564 { 00565 the_parents.resize(parents[sense].length()); 00566 the_parents << parents[sense]; 00567 } 00568 } 00569 00570 void WordNetSenseDictionary::childrenOf(int sense, TVec<int>& the_children){ 00571 if(children.find(sense) == children.end()) 00572 the_children.resize(0); 00573 else 00574 { 00575 the_children.resize(children[sense].length()); 00576 the_children << children[sense]; 00577 } 00578 } 00579 00580 int WordNetSenseDictionary::rootNode(){ 00581 return inherited::getId(WN_ROOT_NODE); 00582 } 00583 00584 real WordNetSenseDictionary::sensePrior(string word, string sense){ 00585 word_sense = word + " " + sense; 00586 if(sense_prior.find(word_sense) == sense_prior.end()) 00587 return 0; 00588 else 00589 return sense_prior[word_sense]; 00590 } 00591 00592 void WordNetSenseDictionary::makeDeepCopyFromShallowCopy(CopiesMap& copies) 00593 { 00594 inherited::makeDeepCopyFromShallowCopy(copies); 00595 deepCopyField(possible_values, copies); 00596 deepCopyField(stems, copies); 00597 deepCopyField(senses, copies); 00598 deepCopyField(ancestors_vec, copies); 00599 deepCopyField(tagcnts, copies); 00600 deepCopyField(possible_values_for_word, copies); 00601 deepCopyField(children, copies); 00602 deepCopyField(parents, copies); 00603 deepCopyField(sense_prior, copies); 00604 } 00605 00606 } // end of namespace PLearn 00607 00608 00609 /* 00610 Local Variables: 00611 mode:c++ 00612 c-basic-offset:4 00613 c-file-style:"stroustrup" 00614 c-file-offsets:((innamespace . 0)(inline-open . 0)) 00615 indent-tabs-mode:nil 00616 fill-column:79 00617 End: 00618 */ 00619 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=79 :